aboutsummaryrefslogtreecommitdiffstats
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c202
-rw-r--r--builtin/am.c139
-rw-r--r--builtin/apply.c4
-rw-r--r--builtin/archive.c8
-rw-r--r--builtin/bisect.c (renamed from builtin/bisect--helper.c)523
-rw-r--r--builtin/blame.c64
-rw-r--r--builtin/branch.c264
-rw-r--r--builtin/bugreport.c43
-rw-r--r--builtin/bundle.c124
-rw-r--r--builtin/cat-file.c260
-rw-r--r--builtin/check-attr.c31
-rw-r--r--builtin/check-ignore.c8
-rw-r--r--builtin/check-mailmap.c4
-rw-r--r--builtin/check-ref-format.c17
-rw-r--r--builtin/checkout--worker.c2
-rw-r--r--builtin/checkout-index.c24
-rw-r--r--builtin/checkout.c231
-rw-r--r--builtin/clean.c27
-rw-r--r--builtin/clone.c238
-rw-r--r--builtin/column.c5
-rw-r--r--builtin/commit-graph.c69
-rw-r--r--builtin/commit-tree.c27
-rw-r--r--builtin/commit.c189
-rw-r--r--builtin/config.c148
-rw-r--r--builtin/count-objects.c14
-rw-r--r--builtin/credential-cache--daemon.c30
-rw-r--r--builtin/credential-cache.c4
-rw-r--r--builtin/credential-store.c37
-rw-r--r--builtin/credential.c2
-rw-r--r--builtin/describe.c42
-rw-r--r--builtin/diagnose.c65
-rw-r--r--builtin/diff-files.c23
-rw-r--r--builtin/diff-index.c24
-rw-r--r--builtin/diff-tree.c22
-rw-r--r--builtin/diff.c74
-rw-r--r--builtin/difftool.c86
-rw-r--r--builtin/env--helper.c100
-rw-r--r--builtin/fast-export.c104
-rw-r--r--builtin/fast-import.c43
-rw-r--r--builtin/fetch-pack.c10
-rw-r--r--builtin/fetch.c934
-rw-r--r--builtin/fmt-merge-msg.c1
-rw-r--r--builtin/for-each-ref.c43
-rw-r--r--builtin/for-each-repo.c27
-rw-r--r--builtin/fsck.c235
-rw-r--r--builtin/fsmonitor--daemon.c154
-rw-r--r--builtin/gc.c367
-rw-r--r--builtin/get-tar-commit-id.c9
-rw-r--r--builtin/grep.c88
-rw-r--r--builtin/hash-object.c15
-rw-r--r--builtin/help.c36
-rw-r--r--builtin/hook.c18
-rw-r--r--builtin/index-pack.c46
-rw-r--r--builtin/init-db.c484
-rw-r--r--builtin/interpret-trailers.c6
-rw-r--r--builtin/log.c291
-rw-r--r--builtin/ls-files.c256
-rw-r--r--builtin/ls-remote.c12
-rw-r--r--builtin/ls-tree.c346
-rw-r--r--builtin/mailinfo.c4
-rw-r--r--builtin/mailsplit.c6
-rw-r--r--builtin/merge-base.c15
-rw-r--r--builtin/merge-file.c36
-rw-r--r--builtin/merge-index.c26
-rw-r--r--builtin/merge-ours.c6
-rw-r--r--builtin/merge-recursive.c14
-rw-r--r--builtin/merge-tree.c287
-rw-r--r--builtin/merge.c231
-rw-r--r--builtin/mktag.c11
-rw-r--r--builtin/mktree.c16
-rw-r--r--builtin/multi-pack-index.c91
-rw-r--r--builtin/mv.c404
-rw-r--r--builtin/name-rev.c64
-rw-r--r--builtin/notes.c259
-rw-r--r--builtin/pack-objects.c502
-rw-r--r--builtin/pack-redundant.c19
-rw-r--r--builtin/pack-refs.c32
-rw-r--r--builtin/patch-id.c122
-rw-r--r--builtin/prune-packed.c1
-rw-r--r--builtin/prune.c28
-rw-r--r--builtin/pull.c206
-rw-r--r--builtin/push.c156
-rw-r--r--builtin/range-diff.c107
-rw-r--r--builtin/read-tree.c47
-rw-r--r--builtin/rebase.c613
-rw-r--r--builtin/receive-pack.c149
-rw-r--r--builtin/reflog.c65
-rw-r--r--builtin/remote-ext.c8
-rw-r--r--builtin/remote-fd.c4
-rw-r--r--builtin/remote.c262
-rw-r--r--builtin/repack.c541
-rw-r--r--builtin/replace.c41
-rw-r--r--builtin/rerere.c7
-rw-r--r--builtin/reset.c166
-rw-r--r--builtin/rev-list.c90
-rw-r--r--builtin/rev-parse.c72
-rw-r--r--builtin/revert.c54
-rw-r--r--builtin/rm.c51
-rw-r--r--builtin/send-pack.c13
-rw-r--r--builtin/shortlog.c114
-rw-r--r--builtin/show-branch.c47
-rw-r--r--builtin/show-index.c5
-rw-r--r--builtin/show-ref.c40
-rw-r--r--builtin/sparse-checkout.c212
-rw-r--r--builtin/stash.c428
-rw-r--r--builtin/stripspace.c10
-rw-r--r--builtin/submodule--helper.c1391
-rw-r--r--builtin/symbolic-ref.c26
-rw-r--r--builtin/tag.c97
-rw-r--r--builtin/unpack-file.c13
-rw-r--r--builtin/unpack-objects.c127
-rw-r--r--builtin/update-index.c151
-rw-r--r--builtin/update-ref.c15
-rw-r--r--builtin/update-server-info.c7
-rw-r--r--builtin/upload-archive.c7
-rw-r--r--builtin/upload-pack.c9
-rw-r--r--builtin/var.c200
-rw-r--r--builtin/verify-commit.c21
-rw-r--r--builtin/verify-pack.c5
-rw-r--r--builtin/verify-tag.c19
-rw-r--r--builtin/worktree.c422
-rw-r--r--builtin/write-tree.c13
122 files changed, 9098 insertions, 5806 deletions
diff --git a/builtin/add.c b/builtin/add.c
index 3ffb86a433..4b0dd798df 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -3,19 +3,25 @@
*
* Copyright (C) 2006 Linus Torvalds
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
-#include "config.h"
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
+#include "advice.h"
+#include "config.h"
#include "lockfile.h"
+#include "editor.h"
#include "dir.h"
+#include "gettext.h"
#include "pathspec.h"
#include "exec-cmd.h"
#include "cache-tree.h"
#include "run-command.h"
#include "parse-options.h"
+#include "path.h"
+#include "preload-index.h"
#include "diff.h"
#include "diffcore.h"
+#include "read-cache.h"
+#include "repository.h"
#include "revision.h"
#include "bulk-checkin.h"
#include "strvec.h"
@@ -33,17 +39,12 @@ static int pathspec_file_nul;
static int include_sparse;
static const char *pathspec_from_file;
-struct update_callback_data {
- int flags;
- int add_errors;
-};
-
static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
{
int i, ret = 0;
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ for (i = 0; i < the_index.cache_nr; i++) {
+ struct cache_entry *ce = the_index.cache[i];
int err;
if (!include_sparse &&
@@ -55,7 +56,7 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
continue;
if (!show_only)
- err = chmod_cache_entry(ce, flip);
+ err = chmod_index_entry(&the_index, ce, flip);
else
err = S_ISREG(ce->ce_mode) ? 0 : -1;
@@ -66,92 +67,12 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
return ret;
}
-static int fix_unmerged_status(struct diff_filepair *p,
- struct update_callback_data *data)
-{
- if (p->status != DIFF_STATUS_UNMERGED)
- return p->status;
- if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode)
- /*
- * This is not an explicit add request, and the
- * path is missing from the working tree (deleted)
- */
- return DIFF_STATUS_DELETED;
- else
- /*
- * Either an explicit add request, or path exists
- * in the working tree. An attempt to explicitly
- * add a path that does not exist in the working tree
- * will be caught as an error by the caller immediately.
- */
- return DIFF_STATUS_MODIFIED;
-}
-
-static void update_callback(struct diff_queue_struct *q,
- struct diff_options *opt, void *cbdata)
-{
- int i;
- struct update_callback_data *data = cbdata;
-
- for (i = 0; i < q->nr; i++) {
- struct diff_filepair *p = q->queue[i];
- const char *path = p->one->path;
-
- if (!include_sparse && !path_in_sparse_checkout(path, &the_index))
- continue;
-
- switch (fix_unmerged_status(p, data)) {
- default:
- die(_("unexpected diff status %c"), p->status);
- case DIFF_STATUS_MODIFIED:
- case DIFF_STATUS_TYPE_CHANGED:
- if (add_file_to_index(&the_index, path, data->flags)) {
- if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
- die(_("updating files failed"));
- data->add_errors++;
- }
- break;
- case DIFF_STATUS_DELETED:
- if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
- break;
- if (!(data->flags & ADD_CACHE_PRETEND))
- remove_file_from_index(&the_index, path);
- if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
- printf(_("remove '%s'\n"), path);
- break;
- }
- }
-}
-
-int add_files_to_cache(const char *prefix,
- const struct pathspec *pathspec, int flags)
-{
- struct update_callback_data data;
- struct rev_info rev;
-
- memset(&data, 0, sizeof(data));
- data.flags = flags;
-
- repo_init_revisions(the_repository, &rev, prefix);
- setup_revisions(0, NULL, &rev, NULL);
- if (pathspec)
- copy_pathspec(&rev.prune_data, pathspec);
- rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
- rev.diffopt.format_callback = update_callback;
- rev.diffopt.format_callback_data = &data;
- rev.diffopt.flags.override_submodule_config = 1;
- rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
- run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
- clear_pathspec(&rev.prune_data);
- return !!data.add_errors;
-}
-
static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
{
int i, retval = 0;
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ for (i = 0; i < the_index.cache_nr; i++) {
+ struct cache_entry *ce = the_index.cache[i];
if (!include_sparse &&
(ce_skip_worktree(ce) ||
@@ -163,7 +84,8 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
continue; /* do not touch non blobs */
if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
continue;
- retval |= add_file_to_cache(ce->name, flags | ADD_CACHE_RENORMALIZE);
+ retval |= add_file_to_index(&the_index, ce->name,
+ flags | ADD_CACHE_RENORMALIZE);
}
return retval;
@@ -228,64 +150,14 @@ static int refresh(int verbose, const struct pathspec *pathspec)
return ret;
}
-int run_add_interactive(const char *revision, const char *patch_mode,
- const struct pathspec *pathspec)
-{
- int status, i;
- struct strvec argv = STRVEC_INIT;
- int use_builtin_add_i =
- git_env_bool("GIT_TEST_ADD_I_USE_BUILTIN", -1);
-
- if (use_builtin_add_i < 0) {
- int experimental;
- if (!git_config_get_bool("add.interactive.usebuiltin",
- &use_builtin_add_i))
- ; /* ok */
- else if (!git_config_get_bool("feature.experimental", &experimental) &&
- experimental)
- use_builtin_add_i = 1;
- }
-
- if (use_builtin_add_i == 1) {
- enum add_p_mode mode;
-
- if (!patch_mode)
- return !!run_add_i(the_repository, pathspec);
-
- if (!strcmp(patch_mode, "--patch"))
- mode = ADD_P_ADD;
- else if (!strcmp(patch_mode, "--patch=stash"))
- mode = ADD_P_STASH;
- else if (!strcmp(patch_mode, "--patch=reset"))
- mode = ADD_P_RESET;
- else if (!strcmp(patch_mode, "--patch=checkout"))
- mode = ADD_P_CHECKOUT;
- else if (!strcmp(patch_mode, "--patch=worktree"))
- mode = ADD_P_WORKTREE;
- else
- die("'%s' not supported", patch_mode);
-
- return !!run_add_p(the_repository, mode, revision, pathspec);
- }
-
- strvec_push(&argv, "add--interactive");
- if (patch_mode)
- strvec_push(&argv, patch_mode);
- if (revision)
- strvec_push(&argv, revision);
- strvec_push(&argv, "--");
- for (i = 0; i < pathspec->nr; i++)
- /* pass original pathspec, to be re-parsed */
- strvec_push(&argv, pathspec->items[i].original);
-
- status = run_command_v_opt(argv.v, RUN_GIT_CMD);
- strvec_clear(&argv);
- return status;
-}
-
int interactive_add(const char **argv, const char *prefix, int patch)
{
struct pathspec pathspec;
+ int unused;
+
+ if (!git_config_get_bool("add.interactive.usebuiltin", &unused))
+ warning(_("the add.interactive.useBuiltin setting has been removed!\n"
+ "See its entry in 'git help config' for details."));
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_FULL |
@@ -293,9 +165,10 @@ int interactive_add(const char **argv, const char *prefix, int patch)
PATHSPEC_PREFIX_ORIGIN,
prefix, argv);
- return run_add_interactive(NULL,
- patch ? "--patch" : NULL,
- &pathspec);
+ if (patch)
+ return !!run_add_p(the_repository, ADD_P_ADD, NULL, &pathspec);
+ else
+ return !!run_add_i(the_repository, &pathspec);
}
static int edit_patch(int argc, const char **argv, const char *prefix)
@@ -308,7 +181,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
die(_("Could not read the index"));
repo_init_revisions(the_repository, &rev, prefix);
@@ -340,6 +213,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
unlink(file);
free(file);
+ release_revisions(&rev);
return 0;
}
@@ -392,7 +266,8 @@ static struct option builtin_add_options[] = {
OPT_END(),
};
-static int add_config(const char *var, const char *value, void *cb)
+static int add_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (!strcmp(var, "add.ignoreerrors") ||
!strcmp(var, "add.ignore-errors")) {
@@ -400,7 +275,10 @@ static int add_config(const char *var, const char *value, void *cb)
return 0;
}
- return git_default_config(var, value, cb);
+ if (git_color_config(var, value, cb) < 0)
+ return -1;
+
+ return git_default_config(var, value, ctx, cb);
}
static const char embedded_advice[] = N_(
@@ -539,7 +417,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
- hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
/*
* Check the "pathspec '%s' did not match any files" block
@@ -582,7 +460,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
(!(addremove || take_worktree_changes)
? ADD_CACHE_IGNORE_REMOVAL : 0));
- if (read_cache_preload(&pathspec) < 0)
+ if (repo_read_index_preload(the_repository, &pathspec, 0) < 0)
die(_("index file corrupt"));
die_in_unpopulated_submodule(&the_index, prefix);
@@ -670,19 +548,21 @@ int cmd_add(int argc, const char **argv, const char *prefix)
string_list_clear(&only_match_skip_worktree, 0);
}
- plug_bulk_checkin();
+ begin_odb_transaction();
if (add_renormalize)
exit_status |= renormalize_tracked_files(&pathspec, flags);
else
- exit_status |= add_files_to_cache(prefix, &pathspec, flags);
+ exit_status |= add_files_to_cache(the_repository, prefix,
+ &pathspec, include_sparse,
+ flags);
if (add_new_files)
exit_status |= add_files(&dir, flags);
if (chmod_arg && pathspec.nr)
exit_status |= chmod_pathspec(&pathspec, chmod_arg[0], show_only);
- unplug_bulk_checkin();
+ end_odb_transaction();
finish:
if (write_locked_index(&the_index, &lock_file,
@@ -690,6 +570,6 @@ finish:
die(_("Unable to write new index file"));
dir_clear(&dir);
- UNLEAK(pathspec);
+ clear_pathspec(&pathspec);
return exit_status;
}
diff --git a/builtin/am.c b/builtin/am.c
index 0f4111bafa..dcb89439b1 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -3,11 +3,16 @@
*
* Based on git-am.sh by Junio C Hamano.
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
-#include "config.h"
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
+#include "abspath.h"
+#include "advice.h"
+#include "config.h"
+#include "editor.h"
+#include "environment.h"
#include "exec-cmd.h"
+#include "gettext.h"
+#include "hex.h"
#include "parse-options.h"
#include "dir.h"
#include "run-command.h"
@@ -22,6 +27,8 @@
#include "diffcore.h"
#include "unpack-trees.h"
#include "branch.h"
+#include "object-name.h"
+#include "preload-index.h"
#include "sequencer.h"
#include "revision.h"
#include "merge-recursive.h"
@@ -33,6 +40,8 @@
#include "apply.h"
#include "string-list.h"
#include "packfile.h"
+#include "pager.h"
+#include "path.h"
#include "repository.h"
#include "pretty.h"
@@ -117,6 +126,7 @@ struct am_state {
/* various operating modes and command line options */
int interactive;
+ int no_verify;
int threeway;
int quiet;
int signoff; /* enum signoff_type */
@@ -472,10 +482,12 @@ static void am_destroy(const struct am_state *state)
*/
static int run_applypatch_msg_hook(struct am_state *state)
{
- int ret;
+ int ret = 0;
assert(state->msg);
- ret = run_hooks_l("applypatch-msg", am_path(state, "final-commit"), NULL);
+
+ if (!state->no_verify)
+ ret = run_hooks_l("applypatch-msg", am_path(state, "final-commit"), NULL);
if (!ret) {
FREE_AND_NULL(state->msg);
@@ -492,24 +504,12 @@ static int run_applypatch_msg_hook(struct am_state *state)
*/
static int run_post_rewrite_hook(const struct am_state *state)
{
- struct child_process cp = CHILD_PROCESS_INIT;
- const char *hook = find_hook("post-rewrite");
- int ret;
-
- if (!hook)
- return 0;
-
- strvec_push(&cp.args, hook);
- strvec_push(&cp.args, "rebase");
-
- cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
- cp.stdout_to_stderr = 1;
- cp.trace2_hook_name = "post-rewrite";
+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
- ret = run_command(&cp);
+ strvec_push(&opt.args, "rebase");
+ opt.path_to_stdin = am_path(state, "rewritten");
- close(cp.in);
- return ret;
+ return run_hooks_opt("post-rewrite", &opt);
}
/**
@@ -1075,7 +1075,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
else
write_state_text(state, "applying", "");
- if (!get_oid("HEAD", &curr_head)) {
+ if (!repo_get_oid(the_repository, "HEAD", &curr_head)) {
write_state_text(state, "abort-safety", oid_to_hex(&curr_head));
if (!state->rebasing)
update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0,
@@ -1118,7 +1118,7 @@ static void am_next(struct am_state *state)
unlink(am_path(state, "original-commit"));
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
- if (!get_oid("HEAD", &head))
+ if (!repo_get_oid(the_repository, "HEAD", &head))
write_state_text(state, "abort-safety", oid_to_hex(&head));
else
write_state_text(state, "abort-safety", "");
@@ -1283,7 +1283,7 @@ static int parse_mail(struct am_state *state, const char *mail)
strbuf_addstr(&msg, "\n\n");
strbuf_addbuf(&msg, &mi.log_message);
- strbuf_stripspace(&msg, 0);
+ strbuf_stripspace(&msg, '\0');
assert(!state->author_name);
state->author_name = strbuf_detach(&author_name, NULL);
@@ -1338,7 +1338,8 @@ static void get_commit_info(struct am_state *state, struct commit *commit)
size_t ident_len;
struct ident_split id;
- buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding());
+ buffer = repo_logmsg_reencode(the_repository, commit, NULL,
+ get_commit_output_encoding());
ident_line = find_commit_header(buffer, "author", &ident_len);
if (!ident_line)
@@ -1370,7 +1371,7 @@ static void get_commit_info(struct am_state *state, struct commit *commit)
die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid));
state->msg = xstrdup(msg + 2);
state->msg_len = strlen(state->msg);
- unuse_commit_buffer(commit, buffer);
+ repo_unuse_commit_buffer(the_repository, commit, buffer);
}
/**
@@ -1397,6 +1398,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
add_pending_object(&rev_info, &commit->object, "");
diff_setup_done(&rev_info.diffopt);
log_tree_commit(&rev_info, commit);
+ release_revisions(&rev_info);
}
/**
@@ -1410,9 +1412,9 @@ static void write_index_patch(const struct am_state *state)
struct rev_info rev_info;
FILE *fp;
- if (!get_oid("HEAD", &head)) {
+ if (!repo_get_oid(the_repository, "HEAD", &head)) {
struct commit *commit = lookup_commit_or_die(&head, "HEAD");
- tree = get_commit_tree(commit);
+ tree = repo_get_commit_tree(the_repository, commit);
} else
tree = lookup_tree(the_repository,
the_repository->hash_algo->empty_tree);
@@ -1429,6 +1431,7 @@ static void write_index_patch(const struct am_state *state)
add_pending_object(&rev_info, &tree->object, "");
diff_setup_done(&rev_info.diffopt);
run_diff_index(&rev_info, 1);
+ release_revisions(&rev_info);
}
/**
@@ -1474,6 +1477,7 @@ static int run_apply(const struct am_state *state, const char *index_file)
int res, opts_left;
int force_apply = 0;
int options = 0;
+ const char **apply_argv;
if (init_apply_state(&apply_state, the_repository, NULL))
BUG("init_apply_state() failed");
@@ -1481,7 +1485,14 @@ static int run_apply(const struct am_state *state, const char *index_file)
strvec_push(&apply_opts, "apply");
strvec_pushv(&apply_opts, state->git_apply_opts.v);
- opts_left = apply_parse_options(apply_opts.nr, apply_opts.v,
+ /*
+ * Build a copy that apply_parse_options() can rearrange.
+ * apply_opts.v keeps referencing the allocated strings for
+ * strvec_clear() to release.
+ */
+ DUP_ARRAY(apply_argv, apply_opts.v, apply_opts.nr);
+
+ opts_left = apply_parse_options(apply_opts.nr, apply_argv,
&apply_state, &force_apply, &options,
NULL);
@@ -1511,14 +1522,15 @@ static int run_apply(const struct am_state *state, const char *index_file)
strvec_clear(&apply_paths);
strvec_clear(&apply_opts);
clear_apply_state(&apply_state);
+ free(apply_argv);
if (res)
return res;
if (index_file) {
/* Reload index as apply_all_patches() will have modified it. */
- discard_cache();
- read_cache_from(index_file);
+ discard_index(&the_index);
+ read_index_from(&the_index, index_file, get_git_dir());
}
return 0;
@@ -1554,14 +1566,14 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
struct commit *result;
char *their_tree_name;
- if (get_oid("HEAD", &our_tree) < 0)
+ if (repo_get_oid(the_repository, "HEAD", &our_tree) < 0)
oidcpy(&our_tree, the_hash_algo->empty_tree);
if (build_fake_ancestor(state, index_path))
return error("could not build fake ancestor");
- discard_cache();
- read_cache_from(index_path);
+ discard_index(&the_index);
+ read_index_from(&the_index, index_path, get_git_dir());
if (write_index_as_tree(&orig_tree, &the_index, index_path, 0, NULL))
return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
@@ -1582,6 +1594,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
diff_setup_done(&rev_info.diffopt);
run_diff_index(&rev_info, 1);
+ release_revisions(&rev_info);
}
if (run_apply(state, index_path))
@@ -1593,8 +1606,8 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
say(state, stdout, _("Falling back to patching base and 3-way merge..."));
- discard_cache();
- read_cache();
+ discard_index(&the_index);
+ repo_read_index(the_repository);
/*
* This is not so wrong. Depending on which base we picked, orig_tree
@@ -1637,13 +1650,13 @@ static void do_commit(const struct am_state *state)
const char *reflog_msg, *author, *committer = NULL;
struct strbuf sb = STRBUF_INIT;
- if (run_hooks("pre-applypatch"))
+ if (!state->no_verify && run_hooks("pre-applypatch"))
exit(1);
- if (write_cache_as_tree(&tree, 0, NULL))
+ if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL))
die(_("git write-tree failed to write a tree"));
- if (!get_oid_commit("HEAD", &parent)) {
+ if (!repo_get_oid_commit(the_repository, "HEAD", &parent)) {
old_oid = &parent;
commit_list_insert(lookup_commit(the_repository, &parent),
&parents);
@@ -1778,7 +1791,8 @@ static void am_run(struct am_state *state, int resume)
unlink(am_path(state, "dirtyindex"));
- if (refresh_and_write_cache(REFRESH_QUIET, 0, 0) < 0)
+ if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0,
+ NULL, NULL, NULL) < 0)
die(_("unable to write index file"));
if (repo_index_has_changes(the_repository, NULL, &sb)) {
@@ -1927,7 +1941,7 @@ static void am_resolve(struct am_state *state, int allow_empty)
}
}
- if (unmerged_cache()) {
+ if (unmerged_index(&the_index)) {
printf_ln(_("You still have unmerged paths in your index.\n"
"You should 'git add' each file with resolved conflicts to mark them as such.\n"
"You might run `git rm` on a file to accept \"deleted by them\" for it."));
@@ -1964,9 +1978,9 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
if (parse_tree(head) || parse_tree(remote))
return -1;
- hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
- refresh_cache(REFRESH_QUIET);
+ refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
@@ -2004,7 +2018,7 @@ static int merge_tree(struct tree *tree)
if (parse_tree(tree))
return -1;
- hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
@@ -2042,12 +2056,12 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
if (!remote_tree)
return error(_("Could not parse object '%s'."), oid_to_hex(remote));
- read_cache_unmerged();
+ repo_read_index_unmerged(the_repository);
if (fast_forward_to(head_tree, head_tree, 1))
return -1;
- if (write_cache_as_tree(&index, 0, NULL))
+ if (write_index_as_tree(&index, &the_index, get_index_file(), 0, NULL))
return -1;
index_tree = parse_tree_indirect(&index);
@@ -2084,7 +2098,7 @@ static void am_skip(struct am_state *state)
am_rerere_clear();
- if (get_oid("HEAD", &head))
+ if (repo_get_oid(the_repository, "HEAD", &head))
oidcpy(&head, the_hash_algo->empty_tree);
if (clean_index(&head, &head))
@@ -2126,7 +2140,7 @@ static int safe_to_abort(const struct am_state *state)
oidclr(&abort_safety);
strbuf_release(&sb);
- if (get_oid("HEAD", &head))
+ if (repo_get_oid(the_repository, "HEAD", &head))
oidclr(&head);
if (oideq(&head, &abort_safety))
@@ -2159,7 +2173,7 @@ static void am_abort(struct am_state *state)
if (!has_curr_head)
oidcpy(&curr_head, the_hash_algo->empty_tree);
- has_orig_head = !get_oid("ORIG_HEAD", &orig_head);
+ has_orig_head = !repo_get_oid(the_repository, "ORIG_HEAD", &orig_head);
if (!has_orig_head)
oidcpy(&orig_head, the_hash_algo->empty_tree);
@@ -2184,14 +2198,12 @@ static int show_patch(struct am_state *state, enum show_patch_type sub_mode)
int len;
if (!is_null_oid(&state->orig_commit)) {
- const char *av[4] = { "show", NULL, "--", NULL };
- char *new_oid_str;
- int ret;
+ struct child_process cmd = CHILD_PROCESS_INIT;
- av[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit));
- ret = run_command_v_opt(av, RUN_GIT_CMD);
- free(new_oid_str);
- return ret;
+ strvec_pushl(&cmd.args, "show", oid_to_hex(&state->orig_commit),
+ "--", NULL);
+ cmd.git_cmd = 1;
+ return run_command(&cmd);
}
switch (sub_mode) {
@@ -2298,17 +2310,6 @@ static int parse_opt_show_current_patch(const struct option *opt, const char *ar
return 0;
}
-static int git_am_config(const char *k, const char *v, void *cb)
-{
- int status;
-
- status = git_gpg_config(k, v, NULL);
- if (status)
- return status;
-
- return git_default_config(k, v, NULL);
-}
-
int cmd_am(int argc, const char **argv, const char *prefix)
{
struct am_state state;
@@ -2328,6 +2329,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT_BOOL('i', "interactive", &state.interactive,
N_("run interactively")),
+ OPT_BOOL('n', "no-verify", &state.no_verify,
+ N_("bypass pre-applypatch and applypatch-msg hooks")),
OPT_HIDDEN_BOOL('b', "binary", &binary,
N_("historical option -- no-op")),
OPT_BOOL('3', "3way", &state.threeway,
@@ -2430,7 +2433,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(usage, options);
- git_config(git_am_config, NULL);
+ git_config(git_default_config, NULL);
am_state_init(&state);
diff --git a/builtin/apply.c b/builtin/apply.c
index 3f099b9605..c18b7ea5d3 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -1,7 +1,7 @@
-#include "cache.h"
#include "builtin.h"
+#include "gettext.h"
#include "parse-options.h"
-#include "lockfile.h"
+#include "repository.h"
#include "apply.h"
static const char * const apply_usage[] = {
diff --git a/builtin/archive.c b/builtin/archive.c
index 7176b041b6..90761fdfee 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -2,12 +2,13 @@
* Copyright (c) 2006 Franck Bui-Huu
* Copyright (c) 2006 Rene Scharfe
*/
-#include "cache.h"
#include "builtin.h"
#include "archive.h"
+#include "gettext.h"
#include "transport.h"
#include "parse-options.h"
#include "pkt-line.h"
+#include "repository.h"
#include "sideband.h"
static void create_output_file(const char *output_file)
@@ -75,13 +76,13 @@ static int run_remote_archiver(int argc, const char **argv,
#define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | \
PARSE_OPT_KEEP_ARGV0 | \
- PARSE_OPT_KEEP_UNKNOWN | \
+ PARSE_OPT_KEEP_UNKNOWN_OPT | \
PARSE_OPT_NO_INTERNAL_HELP )
int cmd_archive(int argc, const char **argv, const char *prefix)
{
const char *exec = "git-upload-archive";
- const char *output = NULL;
+ char *output = NULL;
const char *remote = NULL;
struct option local_opts[] = {
OPT_FILENAME('o', "output", &output,
@@ -106,5 +107,6 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
+ UNLEAK(output);
return write_archive(argc, argv, prefix, the_repository, output, 0);
}
diff --git a/builtin/bisect--helper.c b/builtin/bisect.c
index 8b2b259ff0..65478ef40f 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect.c
@@ -1,11 +1,17 @@
#include "builtin.h"
-#include "cache.h"
+#include "copy.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
#include "parse-options.h"
#include "bisect.h"
#include "refs.h"
#include "dir.h"
#include "strvec.h"
#include "run-command.h"
+#include "oid-array.h"
+#include "path.h"
#include "prompt.h"
#include "quote.h"
#include "revision.h"
@@ -15,23 +21,44 @@ static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
-static GIT_PATH_FUNC(git_path_head_name, "head-name")
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
-static const char * const git_bisect_helper_usage[] = {
- N_("git bisect--helper --bisect-reset [<commit>]"),
- "git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]",
- N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]"
- " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
- "git bisect--helper --bisect-next",
- N_("git bisect--helper --bisect-state (bad|new) [<rev>]"),
- N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
- N_("git bisect--helper --bisect-replay <filename>"),
- N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"),
- "git bisect--helper --bisect-visualize",
- N_("git bisect--helper --bisect-run <cmd>..."),
+#define BUILTIN_GIT_BISECT_START_USAGE \
+ N_("git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]" \
+ " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--]" \
+ " [<pathspec>...]")
+#define BUILTIN_GIT_BISECT_STATE_USAGE \
+ N_("git bisect (good|bad) [<rev>...]")
+#define BUILTIN_GIT_BISECT_TERMS_USAGE \
+ "git bisect terms [--term-good | --term-bad]"
+#define BUILTIN_GIT_BISECT_SKIP_USAGE \
+ N_("git bisect skip [(<rev>|<range>)...]")
+#define BUILTIN_GIT_BISECT_NEXT_USAGE \
+ "git bisect next"
+#define BUILTIN_GIT_BISECT_RESET_USAGE \
+ N_("git bisect reset [<commit>]")
+#define BUILTIN_GIT_BISECT_VISUALIZE_USAGE \
+ "git bisect visualize"
+#define BUILTIN_GIT_BISECT_REPLAY_USAGE \
+ N_("git bisect replay <logfile>")
+#define BUILTIN_GIT_BISECT_LOG_USAGE \
+ "git bisect log"
+#define BUILTIN_GIT_BISECT_RUN_USAGE \
+ N_("git bisect run <cmd>...")
+
+static const char * const git_bisect_usage[] = {
+ BUILTIN_GIT_BISECT_START_USAGE,
+ BUILTIN_GIT_BISECT_STATE_USAGE,
+ BUILTIN_GIT_BISECT_TERMS_USAGE,
+ BUILTIN_GIT_BISECT_SKIP_USAGE,
+ BUILTIN_GIT_BISECT_NEXT_USAGE,
+ BUILTIN_GIT_BISECT_RESET_USAGE,
+ BUILTIN_GIT_BISECT_VISUALIZE_USAGE,
+ BUILTIN_GIT_BISECT_REPLAY_USAGE,
+ BUILTIN_GIT_BISECT_LOG_USAGE,
+ BUILTIN_GIT_BISECT_RUN_USAGE,
NULL
};
@@ -214,24 +241,24 @@ static int bisect_reset(const char *commit)
} else {
struct object_id oid;
- if (get_oid_commit(commit, &oid))
+ if (repo_get_oid_commit(the_repository, commit, &oid))
return error(_("'%s' is not a valid commit"), commit);
strbuf_addstr(&branch, commit);
}
if (!ref_exists("BISECT_HEAD")) {
- struct strvec argv = STRVEC_INIT;
+ struct child_process cmd = CHILD_PROCESS_INIT;
- strvec_pushl(&argv, "checkout", branch.buf, "--", NULL);
- if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
+ cmd.git_cmd = 1;
+ strvec_pushl(&cmd.args, "checkout", "--ignore-other-worktrees",
+ branch.buf, "--", NULL);
+ if (run_command(&cmd)) {
error(_("could not check out original"
" HEAD '%s'. Try 'git bisect"
" reset <commit>'."), branch.buf);
strbuf_release(&branch);
- strvec_clear(&argv);
return -1;
}
- strvec_clear(&argv);
}
strbuf_release(&branch);
@@ -245,7 +272,8 @@ static void log_commit(FILE *fp, char *fmt, const char *state,
struct strbuf commit_msg = STRBUF_INIT;
char *label = xstrfmt(fmt, state);
- format_commit_message(commit, "%s", &commit_msg, &pp);
+ repo_format_commit_message(the_repository, commit, "%s", &commit_msg,
+ &pp);
fprintf(fp, "# %s: [%s] %s\n", label, oid_to_hex(&commit->object.oid),
commit_msg.buf);
@@ -272,7 +300,7 @@ static int bisect_write(const char *state, const char *rev,
goto finish;
}
- if (get_oid(rev, &oid)) {
+ if (repo_get_oid(the_repository, rev, &oid)) {
res = error(_("couldn't get the oid of the rev '%s'"), rev);
goto finish;
}
@@ -329,12 +357,13 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
return 0;
}
-static int mark_good(const char *refname, const struct object_id *oid,
- int flag, void *cb_data)
+static int inc_nr(const char *refname UNUSED,
+ const struct object_id *oid UNUSED,
+ int flag UNUSED, void *cb_data)
{
- int *m_good = (int *)cb_data;
- *m_good = 0;
- return 1;
+ unsigned int *nr = (unsigned int *)cb_data;
+ (*nr)++;
+ return 0;
}
static const char need_bad_and_good_revision_warning[] =
@@ -384,23 +413,64 @@ static int decide_next(const struct bisect_terms *terms,
vocab_good, vocab_bad, vocab_good, vocab_bad);
}
-static int bisect_next_check(const struct bisect_terms *terms,
- const char *current_term)
+static void bisect_status(struct bisect_state *state,
+ const struct bisect_terms *terms)
{
- int missing_good = 1, missing_bad = 1;
char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
char *good_glob = xstrfmt("%s-*", terms->term_good);
if (ref_exists(bad_ref))
- missing_bad = 0;
+ state->nr_bad = 1;
- for_each_glob_ref_in(mark_good, good_glob, "refs/bisect/",
- (void *) &missing_good);
+ for_each_glob_ref_in(inc_nr, good_glob, "refs/bisect/",
+ (void *) &state->nr_good);
free(good_glob);
free(bad_ref);
+}
+
+__attribute__((format (printf, 1, 2)))
+static void bisect_log_printf(const char *fmt, ...)
+{
+ struct strbuf buf = STRBUF_INIT;
+ va_list ap;
+
+ va_start(ap, fmt);
+ strbuf_vaddf(&buf, fmt, ap);
+ va_end(ap);
+
+ printf("%s", buf.buf);
+ append_to_file(git_path_bisect_log(), "# %s", buf.buf);
+
+ strbuf_release(&buf);
+}
- return decide_next(terms, current_term, missing_good, missing_bad);
+static void bisect_print_status(const struct bisect_terms *terms)
+{
+ struct bisect_state state = { 0 };
+
+ bisect_status(&state, terms);
+
+ /* If we had both, we'd already be started, and shouldn't get here. */
+ if (state.nr_good && state.nr_bad)
+ return;
+
+ if (!state.nr_good && !state.nr_bad)
+ bisect_log_printf(_("status: waiting for both good and bad commits\n"));
+ else if (state.nr_good)
+ bisect_log_printf(Q_("status: waiting for bad commit, %d good commit known\n",
+ "status: waiting for bad commit, %d good commits known\n",
+ state.nr_good), state.nr_good);
+ else
+ bisect_log_printf(_("status: waiting for good commit(s), bad commit known\n"));
+}
+
+static int bisect_next_check(const struct bisect_terms *terms,
+ const char *current_term)
+{
+ struct bisect_state state = { 0 };
+ bisect_status(&state, terms);
+ return decide_next(terms, current_term, !state.nr_good, !state.nr_bad);
}
static int get_terms(struct bisect_terms *terms)
@@ -433,7 +503,7 @@ static int bisect_terms(struct bisect_terms *terms, const char *option)
if (get_terms(terms))
return error(_("no terms defined"));
- if (option == NULL) {
+ if (!option) {
printf(_("Your current terms are %s for the old state\n"
"and %s for the new state.\n"),
terms->term_good, terms->term_bad);
@@ -477,7 +547,7 @@ finish:
}
static int add_bisect_ref(const char *refname, const struct object_id *oid,
- int flags, void *cb)
+ int flags UNUSED, void *cb)
{
struct add_bisect_ref_data *data = cb;
@@ -505,7 +575,7 @@ static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs)
* sets up a revision walk.
*/
reset_revision_walk();
- init_revisions(revs, NULL);
+ repo_init_revisions(the_repository, revs, NULL);
setup_revisions(0, NULL, revs, NULL);
for_each_glob_ref_in(add_bisect_ref, bad, "refs/bisect/", &cb);
cb.object_flags = UNINTERESTING;
@@ -541,8 +611,8 @@ static int bisect_skipped_commits(struct bisect_terms *terms)
while ((commit = get_revision(&revs)) != NULL) {
strbuf_reset(&commit_name);
- format_commit_message(commit, "%s",
- &commit_name, &pp);
+ repo_format_commit_message(the_repository, commit, "%s",
+ &commit_name, &pp);
fprintf(fp, "# possible first %s commit: [%s] %s\n",
terms->term_bad, oid_to_hex(&commit->object.oid),
commit_name.buf);
@@ -555,6 +625,7 @@ static int bisect_skipped_commits(struct bisect_terms *terms)
reset_revision_walk();
strbuf_release(&commit_name);
+ release_revisions(&revs);
fclose(fp);
return 0;
}
@@ -570,7 +641,8 @@ static int bisect_successful(struct bisect_terms *terms)
read_ref(bad_ref, &oid);
commit = lookup_commit_reference_by_name(bad_ref);
- format_commit_message(commit, "%s", &commit_name, &pp);
+ repo_format_commit_message(the_repository, commit, "%s", &commit_name,
+ &pp);
res = append_to_file(git_path_bisect_log(), "# first %s commit: [%s] %s\n",
terms->term_bad, oid_to_hex(&commit->object.oid),
@@ -606,13 +678,16 @@ static enum bisect_error bisect_next(struct bisect_terms *terms, const char *pre
static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char *prefix)
{
- if (bisect_next_check(terms, NULL))
+ if (bisect_next_check(terms, NULL)) {
+ bisect_print_status(terms);
return BISECT_OK;
+ }
return bisect_next(terms, prefix);
}
-static enum bisect_error bisect_start(struct bisect_terms *terms, const char **argv, int argc)
+static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,
+ const char **argv)
{
int no_checkout = 0;
int first_parent_only = 0;
@@ -709,7 +784,7 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a
*/
head = resolve_ref_unsafe("HEAD", 0, &head_oid, &flags);
if (!head)
- if (get_oid("HEAD", &head_oid))
+ if (repo_get_oid(the_repository, "HEAD", &head_oid))
return error(_("bad HEAD - I need a HEAD"));
/*
@@ -720,11 +795,12 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a
strbuf_read_file(&start_head, git_path_bisect_start(), 0);
strbuf_trim(&start_head);
if (!no_checkout) {
- struct strvec argv = STRVEC_INIT;
+ struct child_process cmd = CHILD_PROCESS_INIT;
- strvec_pushl(&argv, "checkout", start_head.buf,
+ cmd.git_cmd = 1;
+ strvec_pushl(&cmd.args, "checkout", start_head.buf,
"--", NULL);
- if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
+ if (run_command(&cmd)) {
res = error(_("checking out '%s' failed."
" Try 'git bisect start "
"<valid-branch>'."),
@@ -734,19 +810,12 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a
}
} else {
/* Get the rev from where we start. */
- if (!get_oid(head, &head_oid) &&
+ if (!repo_get_oid(the_repository, head, &head_oid) &&
!starts_with(head, "refs/heads/")) {
strbuf_reset(&start_head);
strbuf_addstr(&start_head, oid_to_hex(&head_oid));
- } else if (!get_oid(head, &head_oid) &&
+ } else if (!repo_get_oid(the_repository, head, &head_oid) &&
skip_prefix(head, "refs/heads/", &head)) {
- /*
- * This error message should only be triggered by
- * cogito usage, and cogito users should understand
- * it relates to cg-seek.
- */
- if (!is_empty_or_missing_file(git_path_head_name()))
- return error(_("won't bisect on cg-seek'ed tree"));
strbuf_addstr(&start_head, head);
} else {
return error(_("bad HEAD - strange symbolic ref"));
@@ -768,7 +837,7 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a
write_file(git_path_bisect_first_parent(), "\n");
if (no_checkout) {
- if (get_oid(start_head.buf, &oid) < 0) {
+ if (repo_get_oid(the_repository, start_head.buf, &oid) < 0) {
res = error(_("invalid ref: '%s'"), start_head.buf);
goto finish;
}
@@ -841,13 +910,13 @@ static int bisect_autostart(struct bisect_terms *terms)
yesno = git_prompt(_("Do you want me to do it for you "
"[Y/n]? "), PROMPT_ECHO);
res = tolower(*yesno) == 'n' ?
- -1 : bisect_start(terms, empty_strvec, 0);
+ -1 : bisect_start(terms, 0, empty_strvec);
return res;
}
-static enum bisect_error bisect_state(struct bisect_terms *terms, const char **argv,
- int argc)
+static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
+ const char **argv)
{
const char *state;
int i, verify_expected = 1;
@@ -873,11 +942,12 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, const char **a
if (argc == 0) {
const char *head = "BISECT_HEAD";
- enum get_oid_result res_head = get_oid(head, &oid);
+ enum get_oid_result res_head = repo_get_oid(the_repository,
+ head, &oid);
if (res_head == MISSING_OBJECT) {
head = "HEAD";
- res_head = get_oid(head, &oid);
+ res_head = repo_get_oid(the_repository, head, &oid);
}
if (res_head)
@@ -893,7 +963,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, const char **a
for (; argc; argc--, argv++) {
struct commit *commit;
- if (get_oid(*argv, &oid)){
+ if (repo_get_oid(the_repository, *argv, &oid)){
error(_("Bad rev input: %s"), *argv);
oid_array_clear(&revs);
return BISECT_FAILED;
@@ -966,7 +1036,7 @@ static int process_replay_line(struct bisect_terms *terms, struct strbuf *line)
struct strvec argv = STRVEC_INIT;
int res;
sq_dequote_to_strvec(rev, &argv);
- res = bisect_start(terms, argv.v, argv.nr);
+ res = bisect_start(terms, argv.nr, argv.v);
strvec_clear(&argv);
return res;
}
@@ -1016,7 +1086,8 @@ static enum bisect_error bisect_replay(struct bisect_terms *terms, const char *f
return bisect_auto_next(terms, NULL);
}
-static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **argv, int argc)
+static enum bisect_error bisect_skip(struct bisect_terms *terms, int argc,
+ const char **argv)
{
int i;
enum bisect_error res;
@@ -1031,7 +1102,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar
struct rev_info revs;
struct commit *commit;
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
setup_revisions(2, argv + i - 1, &revs, NULL);
if (prepare_revision_walk(&revs))
@@ -1041,63 +1112,73 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar
oid_to_hex(&commit->object.oid));
reset_revision_walk();
+ release_revisions(&revs);
} else {
strvec_push(&argv_state, argv[i]);
}
}
- res = bisect_state(terms, argv_state.v, argv_state.nr);
+ res = bisect_state(terms, argv_state.nr, argv_state.v);
strvec_clear(&argv_state);
return res;
}
-static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc)
+static int bisect_visualize(struct bisect_terms *terms, int argc,
+ const char **argv)
{
- struct strvec args = STRVEC_INIT;
- int flags = RUN_COMMAND_NO_STDIN, res = 0;
+ struct child_process cmd = CHILD_PROCESS_INIT;
struct strbuf sb = STRBUF_INIT;
if (bisect_next_check(terms, NULL) != 0)
return BISECT_FAILED;
+ cmd.no_stdin = 1;
if (!argc) {
if ((getenv("DISPLAY") || getenv("SESSIONNAME") || getenv("MSYSTEM") ||
getenv("SECURITYSESSIONID")) && exists_in_PATH("gitk")) {
- strvec_push(&args, "gitk");
+ strvec_push(&cmd.args, "gitk");
} else {
- strvec_push(&args, "log");
- flags |= RUN_GIT_CMD;
+ strvec_push(&cmd.args, "log");
+ cmd.git_cmd = 1;
}
} else {
if (argv[0][0] == '-') {
- strvec_push(&args, "log");
- flags |= RUN_GIT_CMD;
+ strvec_push(&cmd.args, "log");
+ cmd.git_cmd = 1;
} else if (strcmp(argv[0], "tig") && !starts_with(argv[0], "git"))
- flags |= RUN_GIT_CMD;
+ cmd.git_cmd = 1;
- strvec_pushv(&args, argv);
+ strvec_pushv(&cmd.args, argv);
}
- strvec_pushl(&args, "--bisect", "--", NULL);
+ strvec_pushl(&cmd.args, "--bisect", "--", NULL);
strbuf_read_file(&sb, git_path_bisect_names(), 0);
- sq_dequote_to_strvec(sb.buf, &args);
+ sq_dequote_to_strvec(sb.buf, &cmd.args);
strbuf_release(&sb);
- res = run_command_v_opt(args.v, flags);
- strvec_clear(&args);
- return res;
+ return run_command(&cmd);
}
-static int get_first_good(const char *refname, const struct object_id *oid,
- int flag, void *cb_data)
+static int get_first_good(const char *refname UNUSED,
+ const struct object_id *oid,
+ int flag UNUSED, void *cb_data)
{
oidcpy(cb_data, oid);
return 1;
}
-static int verify_good(const struct bisect_terms *terms,
- const char **quoted_argv)
+static int do_bisect_run(const char *command)
+{
+ struct child_process cmd = CHILD_PROCESS_INIT;
+
+ printf(_("running %s\n"), command);
+ cmd.use_shell = 1;
+ strvec_push(&cmd.args, command);
+ return run_command(&cmd);
+}
+
+static int verify_good(const struct bisect_terms *terms, const char *command)
{
int rc;
enum bisect_error res;
@@ -1117,8 +1198,7 @@ static int verify_good(const struct bisect_terms *terms,
if (res != BISECT_OK)
return -1;
- printf(_("running %s\n"), quoted_argv[0]);
- rc = run_command_v_opt(quoted_argv, RUN_USING_SHELL);
+ rc = do_bisect_run(command);
res = bisect_checkout(&current_rev, no_checkout);
if (res != BISECT_OK)
@@ -1127,11 +1207,10 @@ static int verify_good(const struct bisect_terms *terms,
return rc;
}
-static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
+static int bisect_run(struct bisect_terms *terms, int argc, const char **argv)
{
int res = BISECT_OK;
struct strbuf command = STRBUF_INIT;
- struct strvec run_args = STRVEC_INIT;
const char *new_state;
int temporary_stdout_fd, saved_stdout;
int is_first_run = 1;
@@ -1139,18 +1218,15 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
if (bisect_next_check(terms, NULL))
return BISECT_FAILED;
- if (argc)
- sq_quote_argv(&command, argv);
- else {
+ if (!argc) {
error(_("bisect run failed: no command provided."));
return BISECT_FAILED;
}
- strvec_push(&run_args, command.buf);
-
+ sq_quote_argv(&command, argv);
+ strbuf_ltrim(&command);
while (1) {
- printf(_("running %s\n"), command.buf);
- res = run_command_v_opt(run_args.v, RUN_USING_SHELL);
+ res = do_bisect_run(command.buf);
/*
* Exit code 126 and 127 can either come from the shell
@@ -1160,10 +1236,10 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
* missing or non-executable script.
*/
if (is_first_run && (res == 126 || res == 127)) {
- int rc = verify_good(terms, run_args.v);
+ int rc = verify_good(terms, command.buf);
is_first_run = 0;
- if (rc < 0) {
- error(_("unable to verify '%s' on good"
+ if (rc < 0 || 128 <= rc) {
+ error(_("unable to verify %s on good"
" revision"), command.buf);
res = BISECT_FAILED;
break;
@@ -1178,7 +1254,7 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
if (res < 0 || 128 <= res) {
error(_("bisect run failed: exit code %d from"
- " '%s' is < 0 or >= 128"), res, command.buf);
+ " %s is < 0 or >= 128"), res, command.buf);
break;
}
@@ -1200,7 +1276,7 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
saved_stdout = dup(1);
dup2(temporary_stdout_fd, 1);
- res = bisect_state(terms, &new_state, 1);
+ res = bisect_state(terms, 1, &new_state);
fflush(stdout);
dup2(saved_stdout, 1);
@@ -1212,14 +1288,14 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
if (res == BISECT_ONLY_SKIPPED_LEFT)
error(_("bisect run cannot continue any more"));
else if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) {
- printf(_("bisect run success"));
+ puts(_("bisect run success"));
res = BISECT_OK;
} else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
- printf(_("bisect found first bad commit"));
+ puts(_("bisect found first bad commit"));
res = BISECT_OK;
} else if (res) {
- error(_("bisect run failed: 'git bisect--helper --bisect-state"
- " %s' exited with error code %d"), new_state, res);
+ error(_("bisect run failed: 'git bisect %s'"
+ " exited with error code %d"), new_state, res);
} else {
continue;
}
@@ -1227,126 +1303,147 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
}
strbuf_release(&command);
- strvec_clear(&run_args);
return res;
}
-int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
+static int cmd_bisect__reset(int argc, const char **argv, const char *prefix UNUSED)
+{
+ if (argc > 1)
+ return error(_("'%s' requires either no argument or a commit"),
+ "git bisect reset");
+ return bisect_reset(argc ? argv[0] : NULL);
+}
+
+static int cmd_bisect__terms(int argc, const char **argv, const char *prefix UNUSED)
+{
+ int res;
+ struct bisect_terms terms = { 0 };
+
+ if (argc > 1)
+ return error(_("'%s' requires 0 or 1 argument"),
+ "git bisect terms");
+ res = bisect_terms(&terms, argc == 1 ? argv[0] : NULL);
+ free_terms(&terms);
+ return res;
+}
+
+static int cmd_bisect__start(int argc, const char **argv, const char *prefix UNUSED)
{
- enum {
- BISECT_RESET = 1,
- BISECT_NEXT_CHECK,
- BISECT_TERMS,
- BISECT_START,
- BISECT_AUTOSTART,
- BISECT_NEXT,
- BISECT_STATE,
- BISECT_LOG,
- BISECT_REPLAY,
- BISECT_SKIP,
- BISECT_VISUALIZE,
- BISECT_RUN,
- } cmdmode = 0;
- int res = 0, nolog = 0;
+ int res;
+ struct bisect_terms terms = { 0 };
+
+ set_terms(&terms, "bad", "good");
+ res = bisect_start(&terms, argc, argv);
+ free_terms(&terms);
+ return res;
+}
+
+static int cmd_bisect__next(int argc, const char **argv UNUSED, const char *prefix)
+{
+ int res;
+ struct bisect_terms terms = { 0 };
+
+ if (argc)
+ return error(_("'%s' requires 0 arguments"),
+ "git bisect next");
+ get_terms(&terms);
+ res = bisect_next(&terms, prefix);
+ free_terms(&terms);
+ return res;
+}
+
+static int cmd_bisect__log(int argc UNUSED, const char **argv UNUSED, const char *prefix UNUSED)
+{
+ return bisect_log();
+}
+
+static int cmd_bisect__replay(int argc, const char **argv, const char *prefix UNUSED)
+{
+ int res;
+ struct bisect_terms terms = { 0 };
+
+ if (argc != 1)
+ return error(_("no logfile given"));
+ set_terms(&terms, "bad", "good");
+ res = bisect_replay(&terms, argv[0]);
+ free_terms(&terms);
+ return res;
+}
+
+static int cmd_bisect__skip(int argc, const char **argv, const char *prefix UNUSED)
+{
+ int res;
+ struct bisect_terms terms = { 0 };
+
+ set_terms(&terms, "bad", "good");
+ get_terms(&terms);
+ res = bisect_skip(&terms, argc, argv);
+ free_terms(&terms);
+ return res;
+}
+
+static int cmd_bisect__visualize(int argc, const char **argv, const char *prefix UNUSED)
+{
+ int res;
+ struct bisect_terms terms = { 0 };
+
+ get_terms(&terms);
+ res = bisect_visualize(&terms, argc, argv);
+ free_terms(&terms);
+ return res;
+}
+
+static int cmd_bisect__run(int argc, const char **argv, const char *prefix UNUSED)
+{
+ int res;
+ struct bisect_terms terms = { 0 };
+
+ if (!argc)
+ return error(_("'%s' failed: no command provided."), "git bisect run");
+ get_terms(&terms);
+ res = bisect_run(&terms, argc, argv);
+ free_terms(&terms);
+ return res;
+}
+
+int cmd_bisect(int argc, const char **argv, const char *prefix)
+{
+ int res = 0;
+ parse_opt_subcommand_fn *fn = NULL;
struct option options[] = {
- OPT_CMDMODE(0, "bisect-reset", &cmdmode,
- N_("reset the bisection state"), BISECT_RESET),
- OPT_CMDMODE(0, "bisect-next-check", &cmdmode,
- N_("check whether bad or good terms exist"), BISECT_NEXT_CHECK),
- OPT_CMDMODE(0, "bisect-terms", &cmdmode,
- N_("print out the bisect terms"), BISECT_TERMS),
- OPT_CMDMODE(0, "bisect-start", &cmdmode,
- N_("start the bisect session"), BISECT_START),
- OPT_CMDMODE(0, "bisect-next", &cmdmode,
- N_("find the next bisection commit"), BISECT_NEXT),
- OPT_CMDMODE(0, "bisect-state", &cmdmode,
- N_("mark the state of ref (or refs)"), BISECT_STATE),
- OPT_CMDMODE(0, "bisect-log", &cmdmode,
- N_("list the bisection steps so far"), BISECT_LOG),
- OPT_CMDMODE(0, "bisect-replay", &cmdmode,
- N_("replay the bisection process from the given file"), BISECT_REPLAY),
- OPT_CMDMODE(0, "bisect-skip", &cmdmode,
- N_("skip some commits for checkout"), BISECT_SKIP),
- OPT_CMDMODE(0, "bisect-visualize", &cmdmode,
- N_("visualize the bisection"), BISECT_VISUALIZE),
- OPT_CMDMODE(0, "bisect-run", &cmdmode,
- N_("use <cmd>... to automatically bisect"), BISECT_RUN),
- OPT_BOOL(0, "no-log", &nolog,
- N_("no log for BISECT_WRITE")),
+ OPT_SUBCOMMAND("reset", &fn, cmd_bisect__reset),
+ OPT_SUBCOMMAND("terms", &fn, cmd_bisect__terms),
+ OPT_SUBCOMMAND("start", &fn, cmd_bisect__start),
+ OPT_SUBCOMMAND("next", &fn, cmd_bisect__next),
+ OPT_SUBCOMMAND("log", &fn, cmd_bisect__log),
+ OPT_SUBCOMMAND("replay", &fn, cmd_bisect__replay),
+ OPT_SUBCOMMAND("skip", &fn, cmd_bisect__skip),
+ OPT_SUBCOMMAND("visualize", &fn, cmd_bisect__visualize),
+ OPT_SUBCOMMAND("view", &fn, cmd_bisect__visualize),
+ OPT_SUBCOMMAND("run", &fn, cmd_bisect__run),
OPT_END()
};
- struct bisect_terms terms = { .term_good = NULL, .term_bad = NULL };
+ argc = parse_options(argc, argv, prefix, options, git_bisect_usage,
+ PARSE_OPT_SUBCOMMAND_OPTIONAL);
- argc = parse_options(argc, argv, prefix, options,
- git_bisect_helper_usage,
- PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_UNKNOWN);
+ if (!fn) {
+ struct bisect_terms terms = { 0 };
- if (!cmdmode)
- usage_with_options(git_bisect_helper_usage, options);
+ if (!argc)
+ usage_msg_opt(_("need a command"), git_bisect_usage, options);
- switch (cmdmode) {
- case BISECT_RESET:
- if (argc > 1)
- return error(_("--bisect-reset requires either no argument or a commit"));
- res = bisect_reset(argc ? argv[0] : NULL);
- break;
- case BISECT_TERMS:
- if (argc > 1)
- return error(_("--bisect-terms requires 0 or 1 argument"));
- res = bisect_terms(&terms, argc == 1 ? argv[0] : NULL);
- break;
- case BISECT_START:
- set_terms(&terms, "bad", "good");
- res = bisect_start(&terms, argv, argc);
- break;
- case BISECT_NEXT:
- if (argc)
- return error(_("--bisect-next requires 0 arguments"));
- get_terms(&terms);
- res = bisect_next(&terms, prefix);
- break;
- case BISECT_STATE:
set_terms(&terms, "bad", "good");
get_terms(&terms);
- res = bisect_state(&terms, argv, argc);
- break;
- case BISECT_LOG:
- if (argc)
- return error(_("--bisect-log requires 0 arguments"));
- res = bisect_log();
- break;
- case BISECT_REPLAY:
- if (argc != 1)
- return error(_("no logfile given"));
- set_terms(&terms, "bad", "good");
- res = bisect_replay(&terms, argv[0]);
- break;
- case BISECT_SKIP:
- set_terms(&terms, "bad", "good");
- get_terms(&terms);
- res = bisect_skip(&terms, argv, argc);
- break;
- case BISECT_VISUALIZE:
- get_terms(&terms);
- res = bisect_visualize(&terms, argv, argc);
- break;
- case BISECT_RUN:
- if (!argc)
- return error(_("bisect run failed: no command provided."));
- get_terms(&terms);
- res = bisect_run(&terms, argv, argc);
- break;
- default:
- BUG("unknown subcommand %d", cmdmode);
+ if (check_and_set_terms(&terms, argv[0]))
+ usage_msg_optf(_("unknown command: '%s'"), git_bisect_usage,
+ options, argv[0]);
+ res = bisect_state(&terms, argc, argv);
+ free_terms(&terms);
+ } else {
+ argc--;
+ argv++;
+ res = fn(argc, argv, prefix);
}
- free_terms(&terms);
-
- /*
- * Handle early success
- * From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all
- */
- if ((res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) || (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND))
- res = BISECT_OK;
- return -res;
+ return is_bisect_success(res) ? 0 : -res;
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 8d15b68afc..9c987d6567 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -5,10 +5,13 @@
* See COPYING for licensing conditions
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "config.h"
#include "color.h"
#include "builtin.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "repository.h"
#include "commit.h"
#include "diff.h"
@@ -24,12 +27,17 @@
#include "line-log.h"
#include "dir.h"
#include "progress.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "pager.h"
#include "blame.h"
#include "refs.h"
+#include "setup.h"
#include "tag.h"
+#include "write-or-die.h"
static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
+static char annotate_usage[] = N_("git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>");
static const char *blame_opt_usage[] = {
blame_usage,
@@ -38,6 +46,13 @@ static const char *blame_opt_usage[] = {
NULL
};
+static const char *annotate_opt_usage[] = {
+ annotate_usage,
+ "",
+ N_("<rev-opts> are documented in git-rev-list(1)"),
+ NULL
+};
+
static int longest_file;
static int longest_author;
static int max_orig_digits;
@@ -191,13 +206,13 @@ static void get_commit_info(struct commit *commit,
const char *message;
encoding = get_log_output_encoding();
- message = logmsg_reencode(commit, NULL, encoding);
+ message = repo_logmsg_reencode(the_repository, commit, NULL, encoding);
get_ac_line(message, "\nauthor ",
&ret->author, &ret->author_mail,
&ret->author_time, &ret->author_tz);
if (!detailed) {
- unuse_commit_buffer(commit, message);
+ repo_unuse_commit_buffer(the_repository, commit, message);
return;
}
@@ -211,7 +226,7 @@ static void get_commit_info(struct commit *commit,
else
strbuf_addf(&ret->summary, "(%s)", oid_to_hex(&commit->object.oid));
- unuse_commit_buffer(commit, message);
+ repo_unuse_commit_buffer(the_repository, commit, message);
}
/*
@@ -593,8 +608,9 @@ static int read_ancestry(const char *graft_file)
static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect)
{
- const char *uniq = find_unique_abbrev(&suspect->commit->object.oid,
- auto_abbrev);
+ const char *uniq = repo_find_unique_abbrev(the_repository,
+ &suspect->commit->object.oid,
+ auto_abbrev);
int len = strlen(uniq);
if (auto_abbrev < len)
return len;
@@ -677,7 +693,8 @@ static const char *add_prefix(const char *prefix, const char *path)
return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
}
-static int git_blame_config(const char *var, const char *value, void *cb)
+static int git_blame_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (!strcmp(var, "blame.showroot")) {
show_root = git_config_bool(var, value);
@@ -750,7 +767,7 @@ static int git_blame_config(const char *var, const char *value, void *cb)
if (userdiff_config(var, value) < 0)
return -1;
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
}
static int blame_copy_callback(const struct option *option, const char *arg, int unset)
@@ -794,7 +811,7 @@ static int is_a_rev(const char *name)
{
struct object_id oid;
- if (get_oid(name, &oid))
+ if (repo_get_oid(the_repository, name, &oid))
return 0;
return OBJ_NONE < oid_object_info(the_repository, &oid, NULL);
}
@@ -837,7 +854,7 @@ static void build_ignorelist(struct blame_scoreboard *sb,
peel_to_commit_oid, sb);
}
for_each_string_list_item(i, ignore_rev_list) {
- if (get_oid_committish(i->string, &oid) ||
+ if (repo_get_oid_committish(the_repository, i->string, &oid) ||
peel_to_commit_oid(&oid, sb))
die(_("cannot find revision %s to ignore"), i->string);
oidset_insert(&sb->ignore_list, &oid);
@@ -898,6 +915,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
unsigned int range_i;
long anchor;
const int hexsz = the_hash_algo->hexsz;
+ long num_lines = 0;
+ const char *str_usage = cmd_is_annotate ? annotate_usage : blame_usage;
+ const char **opt_usage = cmd_is_annotate ? annotate_opt_usage : blame_opt_usage;
setup_default_color_by_age();
git_config(git_blame_config, &output_option);
@@ -913,12 +933,13 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
parse_options_start(&ctx, argc, argv, prefix, options,
PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
for (;;) {
- switch (parse_options_step(&ctx, options, blame_opt_usage)) {
+ switch (parse_options_step(&ctx, options, opt_usage)) {
case PARSE_OPT_NON_OPTION:
case PARSE_OPT_UNKNOWN:
break;
case PARSE_OPT_HELP:
case PARSE_OPT_ERROR:
+ case PARSE_OPT_SUBCOMMAND:
exit(129);
case PARSE_OPT_COMPLETE:
exit(0);
@@ -932,7 +953,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
ctx.argv[0] = "--children";
reverse = 1;
}
- parse_revision_opt(&revs, &ctx, options, blame_opt_usage);
+ parse_revision_opt(&revs, &ctx, options, opt_usage);
}
parse_done:
revision_opts_finish(&revs);
@@ -1038,7 +1059,7 @@ parse_done:
switch (argc - dashdash_pos - 1) {
case 2: /* (1b) */
if (argc != 4)
- usage_with_options(blame_opt_usage, options);
+ usage_with_options(opt_usage, options);
/* reorder for the new way: <rev> -- <path> */
argv[1] = argv[3];
argv[3] = argv[2];
@@ -1049,11 +1070,11 @@ parse_done:
argv[argc] = NULL;
break;
default:
- usage_with_options(blame_opt_usage, options);
+ usage_with_options(opt_usage, options);
}
} else {
if (argc < 2)
- usage_with_options(blame_opt_usage, options);
+ usage_with_options(opt_usage, options);
if (argc == 3 && is_a_rev(argv[argc - 1])) { /* (2b) */
path = add_prefix(prefix, argv[1]);
argv[1] = argv[2];
@@ -1111,7 +1132,7 @@ parse_done:
nth_line_cb, &sb, lno, anchor,
&bottom, &top, sb.path,
the_repository->index))
- usage(blame_usage);
+ usage(str_usage);
if ((!lno && (top || bottom)) || lno < bottom)
die(Q_("file %s has only %lu line",
"file %s has only %lu lines",
@@ -1129,7 +1150,10 @@ parse_done:
for (range_i = ranges.nr; range_i > 0; --range_i) {
const struct range *r = &ranges.ranges[range_i - 1];
ent = blame_entry_prepend(ent, r->start, r->end, o);
+ num_lines += (r->end - r->start);
}
+ if (!num_lines)
+ num_lines = sb.num_lines;
o->suspects = ent;
prio_queue_put(&sb.commits, o->commit);
@@ -1158,7 +1182,7 @@ parse_done:
sb.found_guilty_entry = &found_guilty_entry;
sb.found_guilty_entry_data = &pi;
if (show_progress)
- pi.progress = start_delayed_progress(_("Blaming lines"), sb.num_lines);
+ pi.progress = start_delayed_progress(_("Blaming lines"), num_lines);
assign_blame(&sb, opt);
@@ -1167,7 +1191,7 @@ parse_done:
if (!incremental)
setup_pager();
else
- return 0;
+ goto cleanup;
blame_sort_final(&sb);
@@ -1201,6 +1225,8 @@ parse_done:
printf("num commits: %d\n", sb.num_commits);
}
+cleanup:
cleanup_scoreboard(&sb);
+ release_revisions(&revs);
return 0;
}
diff --git a/builtin/branch.c b/builtin/branch.c
index 5d00d0b8d3..f9d5cef3b2 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -5,16 +5,20 @@
* Based on git-branch.sh by Junio C Hamano.
*/
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "color.h"
+#include "editor.h"
+#include "environment.h"
#include "refs.h"
#include "commit.h"
-#include "builtin.h"
+#include "gettext.h"
+#include "object-name.h"
#include "remote.h"
#include "parse-options.h"
#include "branch.h"
#include "diff.h"
+#include "path.h"
#include "revision.h"
#include "string-list.h"
#include "column.h"
@@ -41,6 +45,7 @@ static const char *head;
static struct object_id head_oid;
static int recurse_submodules = 0;
static int submodule_propagate_branches = 0;
+static int omit_empty = 0;
static int branch_use_color = -1;
static char branch_colors[][COLOR_MAXLEN] = {
@@ -77,7 +82,8 @@ static unsigned int colopts;
define_list_config_array(color_branch_slots);
-static int git_branch_config(const char *var, const char *value, void *cb)
+static int git_branch_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
const char *slot_name;
@@ -111,7 +117,10 @@ static int git_branch_config(const char *var, const char *value, void *cb)
return 0;
}
- return git_color_default_config(var, value, cb);
+ if (git_color_config(var, value, cb) < 0)
+ return -1;
+
+ return git_default_config(var, value, ctx, cb);
}
static const char *branch_get_color(enum color_branch ix)
@@ -150,17 +159,18 @@ static int branch_merged(int kind, const char *name,
if (!reference_rev)
reference_rev = head_rev;
- merged = in_merge_bases(rev, reference_rev);
+ merged = reference_rev ? repo_in_merge_bases(the_repository, rev,
+ reference_rev) : 0;
/*
* After the safety valve is fully redefined to "check with
* upstream, if any, otherwise with HEAD", we should just
- * return the result of the in_merge_bases() above without
+ * return the result of the repo_in_merge_bases() above without
* any of the following code, but during the transition period,
* a gentle reminder is in order.
*/
if ((head_rev != reference_rev) &&
- in_merge_bases(rev, head_rev) != merged) {
+ (head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0) != merged) {
if (merged)
warning(_("deleting branch '%s' that has been merged to\n"
" '%s', but not yet merged to HEAD."),
@@ -204,7 +214,6 @@ static void delete_branch_config(const char *branchname)
static int delete_branches(int argc, const char **argv, int force, int kinds,
int quiet)
{
- struct worktree **worktrees;
struct commit *head_rev = NULL;
struct object_id oid;
char *name = NULL;
@@ -217,10 +226,11 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
struct string_list_item *item;
int branch_name_pos;
+ const char *fmt_remotes = "refs/remotes/%s";
switch (kinds) {
case FILTER_REFS_REMOTES:
- fmt = "refs/remotes/%s";
+ fmt = fmt_remotes;
/* For subsequent UI messages */
remote_branch = 1;
allowed_interpret = INTERPRET_BRANCH_REMOTE;
@@ -236,13 +246,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
}
branch_name_pos = strcspn(fmt, "%");
- if (!force) {
+ if (!force)
head_rev = lookup_commit_reference(the_repository, &head_oid);
- if (!head_rev)
- die(_("Couldn't look up commit object for HEAD"));
- }
-
- worktrees = get_worktrees();
for (i = 0; i < argc; i++, strbuf_reset(&bname)) {
char *target = NULL;
@@ -253,12 +258,11 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
name = mkpathdup(fmt, bname.buf);
if (kinds == FILTER_REFS_BRANCHES) {
- const struct worktree *wt =
- find_shared_symref(worktrees, "HEAD", name);
- if (wt) {
+ const char *path;
+ if ((path = branch_checked_out(name))) {
error(_("Cannot delete branch '%s' "
"checked out at '%s'"),
- bname.buf, wt->path);
+ bname.buf, path);
ret = 1;
continue;
}
@@ -270,9 +274,25 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
| RESOLVE_REF_ALLOW_BAD_NAME,
&oid, &flags);
if (!target) {
- error(remote_branch
- ? _("remote-tracking branch '%s' not found.")
- : _("branch '%s' not found."), bname.buf);
+ if (remote_branch) {
+ error(_("remote-tracking branch '%s' not found."), bname.buf);
+ } else {
+ char *virtual_name = mkpathdup(fmt_remotes, bname.buf);
+ char *virtual_target = resolve_refdup(virtual_name,
+ RESOLVE_REF_READING
+ | RESOLVE_REF_NO_RECURSE
+ | RESOLVE_REF_ALLOW_BAD_NAME,
+ &oid, &flags);
+ FREE_AND_NULL(virtual_name);
+
+ if (virtual_target)
+ error(_("branch '%s' not found.\n"
+ "Did you forget --remote?"),
+ bname.buf);
+ else
+ error(_("branch '%s' not found."), bname.buf);
+ FREE_AND_NULL(virtual_target);
+ }
ret = 1;
continue;
}
@@ -287,7 +307,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
item = string_list_append(&refs_to_delete, name);
item->util = xstrdup((flags & REF_ISBROKEN) ? "broken"
: (flags & REF_ISSYMREF) ? target
- : find_unique_abbrev(&oid, DEFAULT_ABBREV));
+ : repo_find_unique_abbrev(the_repository, &oid, DEFAULT_ABBREV));
next:
free(target);
@@ -315,7 +335,6 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
free(name);
strbuf_release(&bname);
- free_worktrees(worktrees);
return ret;
}
@@ -350,17 +369,8 @@ static const char *quote_literal_for_format(const char *s)
static struct strbuf buf = STRBUF_INIT;
strbuf_reset(&buf);
- while (*s) {
- const char *ep = strchrnul(s, '%');
- if (s < ep)
- strbuf_add(&buf, s, ep - s);
- if (*ep == '%') {
- strbuf_addstr(&buf, "%%");
- s = ep + 1;
- } else {
- s = ep;
- }
- }
+ while (strbuf_expand_step(&buf, &s))
+ strbuf_addstr(&buf, "%%");
return buf.buf;
}
@@ -456,6 +466,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
if (verify_ref_format(format))
die(_("unable to parse format string"));
+ filter_ahead_behind(the_repository, format, &array);
ref_array_sort(sorting, &array);
for (i = 0; i < array.nr; i++) {
@@ -469,7 +480,8 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
string_list_append(output, out.buf);
} else {
fwrite(out.buf, 1, out.len, stdout);
- putchar('\n');
+ if (out.len || !omit_empty)
+ putchar('\n');
}
}
@@ -494,9 +506,9 @@ static void print_current_branch_name(void)
die(_("HEAD (%s) points outside of refs/heads/"), refname);
}
-static void reject_rebase_or_bisect_branch(const char *target)
+static void reject_rebase_or_bisect_branch(struct worktree **worktrees,
+ const char *target)
{
- struct worktree **worktrees = get_worktrees();
int i;
for (i = 0; worktrees[i]; i++) {
@@ -513,24 +525,50 @@ static void reject_rebase_or_bisect_branch(const char *target)
die(_("Branch %s is being bisected at %s"),
target, wt->path);
}
+}
- free_worktrees(worktrees);
+/*
+ * Update all per-worktree HEADs pointing at the old ref to point the new ref.
+ * This will be used when renaming a branch. Returns 0 if successful, non-zero
+ * otherwise.
+ */
+static int replace_each_worktree_head_symref(struct worktree **worktrees,
+ const char *oldref, const char *newref,
+ const char *logmsg)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; worktrees[i]; i++) {
+ struct ref_store *refs;
+
+ if (worktrees[i]->is_detached)
+ continue;
+ if (!worktrees[i]->head_ref)
+ continue;
+ if (strcmp(oldref, worktrees[i]->head_ref))
+ continue;
+
+ refs = get_worktree_ref_store(worktrees[i]);
+ if (refs_create_symref(refs, "HEAD", newref, logmsg))
+ ret = error(_("HEAD of working tree %s is not updated"),
+ worktrees[i]->path);
+ }
+
+ return ret;
}
+#define IS_HEAD 1
+#define IS_ORPHAN 2
+
static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force)
{
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
const char *interpreted_oldname = NULL;
const char *interpreted_newname = NULL;
- int recovery = 0;
-
- if (!oldname) {
- if (copy)
- die(_("cannot copy the current branch while not on any."));
- else
- die(_("cannot rename the current branch while not on any."));
- }
+ int recovery = 0, oldref_usage = 0;
+ struct worktree **worktrees = get_worktrees();
if (strbuf_check_branch_ref(&oldref, oldname)) {
/*
@@ -543,6 +581,24 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
die(_("Invalid branch name: '%s'"), oldname);
}
+ for (int i = 0; worktrees[i]; i++) {
+ struct worktree *wt = worktrees[i];
+
+ if (wt->head_ref && !strcmp(oldref.buf, wt->head_ref)) {
+ oldref_usage |= IS_HEAD;
+ if (is_null_oid(&wt->head_oid))
+ oldref_usage |= IS_ORPHAN;
+ break;
+ }
+ }
+
+ if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) {
+ if (oldref_usage & IS_HEAD)
+ die(_("No commit on branch '%s' yet."), oldname);
+ else
+ die(_("No branch named '%s'."), oldname);
+ }
+
/*
* A command like "git branch -M currentbranch currentbranch" cannot
* cause the worktree to become inconsistent with HEAD, so allow it.
@@ -552,7 +608,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
else
validate_new_branchname(newname, &newref, force);
- reject_rebase_or_bisect_branch(oldref.buf);
+ reject_rebase_or_bisect_branch(worktrees, oldref.buf);
if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) ||
!skip_prefix(newref.buf, "refs/heads/", &interpreted_newname)) {
@@ -566,8 +622,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
oldref.buf, newref.buf);
- if (!copy &&
- (!head || strcmp(oldname, head) || !is_null_oid(&head_oid)) &&
+ if (!copy && !(oldref_usage & IS_ORPHAN) &&
rename_ref(oldref.buf, newref.buf, logmsg.buf))
die(_("Branch rename failed"));
if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))
@@ -582,35 +637,38 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
interpreted_oldname);
}
- if (!copy &&
- replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
+ if (!copy && (oldref_usage & IS_HEAD) &&
+ replace_each_worktree_head_symref(worktrees, oldref.buf, newref.buf,
+ logmsg.buf))
die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
strbuf_release(&logmsg);
strbuf_addf(&oldsection, "branch.%s", interpreted_oldname);
- strbuf_release(&oldref);
strbuf_addf(&newsection, "branch.%s", interpreted_newname);
- strbuf_release(&newref);
if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0)
die(_("Branch is renamed, but update of config-file failed"));
- if (copy && strcmp(oldname, newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
+ if (copy && strcmp(interpreted_oldname, interpreted_newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
die(_("Branch is copied, but update of config-file failed"));
+ strbuf_release(&oldref);
+ strbuf_release(&newref);
strbuf_release(&oldsection);
strbuf_release(&newsection);
+ free_worktrees(worktrees);
}
static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION")
static int edit_branch_description(const char *branch_name)
{
+ int exists;
struct strbuf buf = STRBUF_INIT;
struct strbuf name = STRBUF_INIT;
- read_branch_desc(&buf, branch_name);
+ exists = !read_branch_desc(&buf, branch_name);
if (!buf.len || buf.buf[buf.len-1] != '\n')
strbuf_addch(&buf, '\n');
- strbuf_commented_addf(&buf,
+ strbuf_commented_addf(&buf, comment_line_char,
_("Please edit the description for the branch\n"
" %s\n"
"Lines starting with '%c' will be stripped.\n"),
@@ -621,10 +679,11 @@ static int edit_branch_description(const char *branch_name)
strbuf_release(&buf);
return -1;
}
- strbuf_stripspace(&buf, 1);
+ strbuf_stripspace(&buf, comment_line_char);
strbuf_addf(&name, "branch.%s.description", branch_name);
- git_config_set(name.buf, buf.len ? buf.buf : NULL);
+ if (buf.len || exists)
+ git_config_set(name.buf, buf.len ? buf.buf : NULL);
strbuf_release(&name);
strbuf_release(&buf);
@@ -642,7 +701,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
int reflog = 0, quiet = 0, icase = 0, force = 0,
recurse_submodules_explicit = 0;
enum branch_track track;
- struct ref_filter filter;
+ struct ref_filter filter = REF_FILTER_INIT;
static struct ref_sorting *sorting;
struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
@@ -676,6 +735,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2),
+ OPT_BOOL(0, "omit-empty", &omit_empty,
+ N_("do not output a newline after empty formatted refs")),
OPT_BIT('c', "copy", &copy, N_("copy a branch and its reflog"), 1),
OPT_BIT('C', NULL, &copy, N_("copy a branch, even if target exists"), 2),
OPT_BOOL('l', "list", &list, N_("list branch names")),
@@ -698,7 +759,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
setup_ref_filter_porcelain_msg();
- memset(&filter, 0, sizeof(filter));
filter.kind = FILTER_REFS_BRANCHES;
filter.abbrev = -1;
@@ -765,6 +825,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (list)
setup_auto_pager("branch", 1);
+ UNLEAK(sorting_options);
+
if (delete) {
if (!argc)
die(_("branch name required"));
@@ -792,57 +854,61 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);
ref_sorting_release(sorting);
+ ref_filter_clear(&filter);
return 0;
} else if (edit_description) {
const char *branch_name;
struct strbuf branch_ref = STRBUF_INIT;
+ struct strbuf buf = STRBUF_INIT;
+ int ret = 1; /* assume failure */
if (!argc) {
if (filter.detached)
die(_("Cannot give description to detached HEAD"));
branch_name = head;
- } else if (argc == 1)
- branch_name = argv[0];
- else
+ } else if (argc == 1) {
+ strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL);
+ branch_name = buf.buf;
+ } else {
die(_("cannot edit description of more than one branch"));
+ }
strbuf_addf(&branch_ref, "refs/heads/%s", branch_name);
- if (!ref_exists(branch_ref.buf)) {
- strbuf_release(&branch_ref);
-
- if (!argc)
- return error(_("No commit on branch '%s' yet."),
- branch_name);
- else
- return error(_("No branch named '%s'."),
- branch_name);
- }
+ if (!ref_exists(branch_ref.buf))
+ error((!argc || branch_checked_out(branch_ref.buf))
+ ? _("No commit on branch '%s' yet.")
+ : _("No branch named '%s'."),
+ branch_name);
+ else if (!edit_branch_description(branch_name))
+ ret = 0; /* happy */
+
strbuf_release(&branch_ref);
+ strbuf_release(&buf);
- if (edit_branch_description(branch_name))
- return 1;
- } else if (copy) {
- if (!argc)
- die(_("branch name required"));
- else if (argc == 1)
- copy_or_rename_branch(head, argv[0], 1, copy > 1);
- else if (argc == 2)
- copy_or_rename_branch(argv[0], argv[1], 1, copy > 1);
- else
- die(_("too many branches for a copy operation"));
- } else if (rename) {
+ return ret;
+ } else if (copy || rename) {
if (!argc)
die(_("branch name required"));
+ else if ((argc == 1) && filter.detached)
+ die(copy? _("cannot copy the current branch while not on any.")
+ : _("cannot rename the current branch while not on any."));
else if (argc == 1)
- copy_or_rename_branch(head, argv[0], 0, rename > 1);
+ copy_or_rename_branch(head, argv[0], copy, copy + rename > 1);
else if (argc == 2)
- copy_or_rename_branch(argv[0], argv[1], 0, rename > 1);
+ copy_or_rename_branch(argv[0], argv[1], copy, copy + rename > 1);
else
- die(_("too many arguments for a rename operation"));
+ die(copy? _("too many branches for a copy operation")
+ : _("too many arguments for a rename operation"));
} else if (new_upstream) {
- struct branch *branch = branch_get(argv[0]);
+ struct branch *branch;
+ struct strbuf buf = STRBUF_INIT;
- if (argc > 1)
+ if (!argc)
+ branch = branch_get(NULL);
+ else if (argc == 1) {
+ strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL);
+ branch = branch_get(buf.buf);
+ } else
die(_("too many arguments to set new upstream"));
if (!branch) {
@@ -853,17 +919,26 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
die(_("no such branch '%s'"), argv[0]);
}
- if (!ref_exists(branch->refname))
+ if (!ref_exists(branch->refname)) {
+ if (!argc || branch_checked_out(branch->refname))
+ die(_("No commit on branch '%s' yet."), branch->name);
die(_("branch '%s' does not exist"), branch->name);
+ }
dwim_and_setup_tracking(the_repository, branch->name,
new_upstream, BRANCH_TRACK_OVERRIDE,
quiet);
+ strbuf_release(&buf);
} else if (unset_upstream) {
- struct branch *branch = branch_get(argv[0]);
+ struct branch *branch;
struct strbuf buf = STRBUF_INIT;
- if (argc > 1)
+ if (!argc)
+ branch = branch_get(NULL);
+ else if (argc == 1) {
+ strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL);
+ branch = branch_get(buf.buf);
+ } else
die(_("too many arguments to unset upstream"));
if (!branch) {
@@ -876,6 +951,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (!branch_has_merge_config(branch))
die(_("Branch '%s' has no upstream information"), branch->name);
+ strbuf_reset(&buf);
strbuf_addf(&buf, "branch.%s.remote", branch->name);
git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);
strbuf_reset(&buf);
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 9de32bc96e..d2ae5c305d 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -1,11 +1,16 @@
#include "builtin.h"
+#include "abspath.h"
+#include "editor.h"
+#include "gettext.h"
#include "parse-options.h"
#include "strbuf.h"
#include "help.h"
#include "compat/compiler.h"
#include "hook.h"
#include "hook-list.h"
-
+#include "diagnose.h"
+#include "object-file.h"
+#include "setup.h"
static void get_system_info(struct strbuf *sys_info)
{
@@ -59,7 +64,8 @@ static void get_populated_hooks(struct strbuf *hook_info, int nongit)
}
static const char * const bugreport_usage[] = {
- N_("git bugreport [-o|--output-directory <file>] [-s|--suffix <format>]"),
+ N_("git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+ " [--diagnose[=<mode>]]"),
NULL
};
@@ -98,16 +104,22 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
int report = -1;
time_t now = time(NULL);
struct tm tm;
+ enum diagnose_mode diagnose = DIAGNOSE_NONE;
char *option_output = NULL;
char *option_suffix = "%Y-%m-%d-%H%M";
const char *user_relative_path = NULL;
char *prefixed_filename;
+ size_t output_path_len;
+ int ret;
const struct option bugreport_options[] = {
+ OPT_CALLBACK_F(0, "diagnose", &diagnose, N_("mode"),
+ N_("create an additional zip archive of detailed diagnostics (default 'stats')"),
+ PARSE_OPT_OPTARG, option_parse_diagnose),
OPT_STRING('o', "output-directory", &option_output, N_("path"),
- N_("specify a destination for the bugreport file")),
+ N_("specify a destination for the bugreport file(s)")),
OPT_STRING('s', "suffix", &option_suffix, N_("format"),
- N_("specify a strftime format suffix for the filename")),
+ N_("specify a strftime format suffix for the filename(s)")),
OPT_END()
};
@@ -119,6 +131,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
option_output ? option_output : "");
strbuf_addstr(&report_path, prefixed_filename);
strbuf_complete(&report_path, '/');
+ output_path_len = report_path.len;
strbuf_addstr(&report_path, "git-bugreport-");
strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
@@ -133,6 +146,20 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
report_path.buf);
}
+ /* Prepare diagnostics, if requested */
+ if (diagnose != DIAGNOSE_NONE) {
+ struct strbuf zip_path = STRBUF_INIT;
+ strbuf_add(&zip_path, report_path.buf, output_path_len);
+ strbuf_addstr(&zip_path, "git-diagnostics-");
+ strbuf_addftime(&zip_path, option_suffix, localtime_r(&now, &tm), 0, 0);
+ strbuf_addstr(&zip_path, ".zip");
+
+ if (create_diagnostics_archive(&zip_path, diagnose))
+ die_errno(_("unable to create diagnostics archive %s"), zip_path.buf);
+
+ strbuf_release(&zip_path);
+ }
+
/* Prepare the report contents */
get_bug_template(&buffer);
@@ -160,7 +187,9 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
user_relative_path);
free(prefixed_filename);
- UNLEAK(buffer);
- UNLEAK(report_path);
- return !!launch_editor(report_path.buf, NULL, NULL);
+ strbuf_release(&buffer);
+
+ ret = !!launch_editor(report_path.buf, NULL, NULL);
+ strbuf_release(&report_path);
+ return ret;
}
diff --git a/builtin/bundle.c b/builtin/bundle.c
index 2adad545a2..3f63631c03 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -1,7 +1,11 @@
#include "builtin.h"
+#include "abspath.h"
+#include "gettext.h"
+#include "setup.h"
#include "strvec.h"
#include "parse-options.h"
-#include "cache.h"
+#include "pkt-line.h"
+#include "repository.h"
#include "bundle.h"
/*
@@ -11,32 +15,42 @@
* bundle supporting "fetch", "pull", and "ls-remote".
*/
-static const char * const builtin_bundle_usage[] = {
- N_("git bundle create [<options>] <file> <git-rev-list args>"),
- N_("git bundle verify [<options>] <file>"),
- N_("git bundle list-heads <file> [<refname>...]"),
- N_("git bundle unbundle <file> [<refname>...]"),
- NULL
+#define BUILTIN_BUNDLE_CREATE_USAGE \
+ N_("git bundle create [-q | --quiet | --progress]\n" \
+ " [--version=<version>] <file> <git-rev-list-args>")
+#define BUILTIN_BUNDLE_VERIFY_USAGE \
+ N_("git bundle verify [-q | --quiet] <file>")
+#define BUILTIN_BUNDLE_LIST_HEADS_USAGE \
+ N_("git bundle list-heads <file> [<refname>...]")
+#define BUILTIN_BUNDLE_UNBUNDLE_USAGE \
+ N_("git bundle unbundle [--progress] <file> [<refname>...]")
+
+static char const * const builtin_bundle_usage[] = {
+ BUILTIN_BUNDLE_CREATE_USAGE,
+ BUILTIN_BUNDLE_VERIFY_USAGE,
+ BUILTIN_BUNDLE_LIST_HEADS_USAGE,
+ BUILTIN_BUNDLE_UNBUNDLE_USAGE,
+ NULL,
};
static const char * const builtin_bundle_create_usage[] = {
- N_("git bundle create [<options>] <file> <git-rev-list args>"),
- NULL
+ BUILTIN_BUNDLE_CREATE_USAGE,
+ NULL
};
static const char * const builtin_bundle_verify_usage[] = {
- N_("git bundle verify [<options>] <file>"),
- NULL
+ BUILTIN_BUNDLE_VERIFY_USAGE,
+ NULL
};
static const char * const builtin_bundle_list_heads_usage[] = {
- N_("git bundle list-heads <file> [<refname>...]"),
- NULL
+ BUILTIN_BUNDLE_LIST_HEADS_USAGE,
+ NULL
};
static const char * const builtin_bundle_unbundle_usage[] = {
- N_("git bundle unbundle <file> [<refname>...]"),
- NULL
+ BUILTIN_BUNDLE_UNBUNDLE_USAGE,
+ NULL
};
static int parse_options_cmd_bundle(int argc,
@@ -45,17 +59,16 @@ static int parse_options_cmd_bundle(int argc,
const char * const usagestr[],
const struct option options[],
char **bundle_file) {
- int newargc;
- newargc = parse_options(argc, argv, NULL, options, usagestr,
+ argc = parse_options(argc, argv, NULL, options, usagestr,
PARSE_OPT_STOP_AT_NON_OPTION);
- if (argc < 1)
- usage_with_options(usagestr, options);
- *bundle_file = prefix_filename(prefix, argv[0]);
- return newargc;
+ if (!argc)
+ usage_msg_opt(_("need a <file> argument"), usagestr, options);
+ *bundle_file = prefix_filename_except_for_dash(prefix, argv[0]);
+ return argc;
}
static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {
- int all_progress_implied = 0;
+ int all_progress_implied = 1;
int progress = isatty(STDERR_FILENO);
struct strvec pack_opts;
int version = -1;
@@ -65,11 +78,12 @@ static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {
N_("do not show progress meter"), 0),
OPT_SET_INT(0, "progress", &progress,
N_("show progress meter"), 1),
- OPT_SET_INT(0, "all-progress", &progress,
- N_("show progress meter during object writing phase"), 2),
- OPT_BOOL(0, "all-progress-implied",
- &all_progress_implied,
- N_("similar to --all-progress when progress meter is shown")),
+ OPT_SET_INT_F(0, "all-progress", &progress,
+ N_("historical; same as --progress"), 2,
+ PARSE_OPT_HIDDEN),
+ OPT_HIDDEN_BOOL(0, "all-progress-implied",
+ &all_progress_implied,
+ N_("historical; does nothing")),
OPT_INTEGER(0, "version", &version,
N_("specify bundle format version")),
OPT_END()
@@ -98,6 +112,23 @@ static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {
return ret;
}
+/*
+ * Similar to read_bundle_header(), but handle "-" as stdin.
+ */
+static int open_bundle(const char *path, struct bundle_header *header,
+ const char **name)
+{
+ if (!strcmp(path, "-")) {
+ if (name)
+ *name = "<stdin>";
+ return read_bundle_header_fd(0, header, "<stdin>");
+ }
+
+ if (name)
+ *name = path;
+ return read_bundle_header(path, header);
+}
+
static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) {
struct bundle_header header = BUNDLE_HEADER_INIT;
int bundle_fd = -1;
@@ -109,22 +140,24 @@ static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) {
OPT_END()
};
char *bundle_file;
+ const char *name;
argc = parse_options_cmd_bundle(argc, argv, prefix,
builtin_bundle_verify_usage, options, &bundle_file);
/* bundle internals use argv[1] as further parameters */
- if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) {
+ if ((bundle_fd = open_bundle(bundle_file, &header, &name)) < 0) {
ret = 1;
goto cleanup;
}
close(bundle_fd);
- if (verify_bundle(the_repository, &header, !quiet)) {
+ if (verify_bundle(the_repository, &header,
+ quiet ? VERIFY_BUNDLE_QUIET : VERIFY_BUNDLE_VERBOSE)) {
ret = 1;
goto cleanup;
}
- fprintf(stderr, _("%s is okay\n"), bundle_file);
+ fprintf(stderr, _("%s is okay\n"), name);
ret = 0;
cleanup:
free(bundle_file);
@@ -145,7 +178,7 @@ static int cmd_bundle_list_heads(int argc, const char **argv, const char *prefix
builtin_bundle_list_heads_usage, options, &bundle_file);
/* bundle internals use argv[1] as further parameters */
- if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) {
+ if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) {
ret = 1;
goto cleanup;
}
@@ -175,7 +208,7 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix)
builtin_bundle_unbundle_usage, options, &bundle_file);
/* bundle internals use argv[1] as further parameters */
- if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) {
+ if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) {
ret = 1;
goto cleanup;
}
@@ -185,7 +218,7 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix)
strvec_pushl(&extra_index_pack_args, "-v", "--progress-title",
_("Unbundling objects"), NULL);
ret = !!unbundle(the_repository, &header, bundle_fd,
- &extra_index_pack_args) ||
+ &extra_index_pack_args, 0) ||
list_bundle_refs(&header, argc, argv);
bundle_header_release(&header);
cleanup:
@@ -195,30 +228,19 @@ cleanup:
int cmd_bundle(int argc, const char **argv, const char *prefix)
{
+ parse_opt_subcommand_fn *fn = NULL;
struct option options[] = {
+ OPT_SUBCOMMAND("create", &fn, cmd_bundle_create),
+ OPT_SUBCOMMAND("verify", &fn, cmd_bundle_verify),
+ OPT_SUBCOMMAND("list-heads", &fn, cmd_bundle_list_heads),
+ OPT_SUBCOMMAND("unbundle", &fn, cmd_bundle_unbundle),
OPT_END()
};
- int result;
argc = parse_options(argc, argv, prefix, options, builtin_bundle_usage,
- PARSE_OPT_STOP_AT_NON_OPTION);
+ 0);
packet_trace_identity("bundle");
- if (argc < 2)
- usage_with_options(builtin_bundle_usage, options);
-
- else if (!strcmp(argv[0], "create"))
- result = cmd_bundle_create(argc, argv, prefix);
- else if (!strcmp(argv[0], "verify"))
- result = cmd_bundle_verify(argc, argv, prefix);
- else if (!strcmp(argv[0], "list-heads"))
- result = cmd_bundle_list_heads(argc, argv, prefix);
- else if (!strcmp(argv[0], "unbundle"))
- result = cmd_bundle_unbundle(argc, argv, prefix);
- else {
- error(_("Unknown subcommand: %s"), argv[0]);
- usage_with_options(builtin_bundle_usage, options);
- }
- return result ? 1 : 0;
+ return !!fn(argc, argv, prefix);
}
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 50cf38999d..694c8538df 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -3,19 +3,28 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
-#include "config.h"
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
+#include "config.h"
+#include "convert.h"
#include "diff.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "ident.h"
#include "parse-options.h"
#include "userdiff.h"
#include "streaming.h"
#include "tree-walk.h"
#include "oid-array.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "replace-object.h"
#include "promisor-remote.h"
+#include "mailmap.h"
+#include "write-or-die.h"
enum batch_mode {
BATCH_MODE_CONTENTS,
@@ -31,18 +40,36 @@ struct batch_options {
int all_objects;
int unordered;
int transform_mode; /* may be 'w' or 'c' for --filters or --textconv */
+ char input_delim;
+ char output_delim;
const char *format;
};
static const char *force_path;
+static struct string_list mailmap = STRING_LIST_INIT_NODUP;
+static int use_mailmap;
+
+static char *replace_idents_using_mailmap(char *, size_t *);
+
+static char *replace_idents_using_mailmap(char *object_buf, size_t *size)
+{
+ struct strbuf sb = STRBUF_INIT;
+ const char *headers[] = { "author ", "committer ", "tagger ", NULL };
+
+ strbuf_attach(&sb, object_buf, *size, *size + 1);
+ apply_mailmap_to_header(&sb, headers, &mailmap);
+ *size = sb.len;
+ return strbuf_detach(&sb, NULL);
+}
+
static int filter_object(const char *path, unsigned mode,
const struct object_id *oid,
char **buf, unsigned long *size)
{
enum object_type type;
- *buf = read_object_file(oid, &type, size);
+ *buf = repo_read_object_file(the_repository, oid, &type, size);
if (!*buf)
return error(_("cannot read object %s '%s'"),
oid_to_hex(oid), path);
@@ -71,6 +98,7 @@ static int stream_blob(const struct object_id *oid)
static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
int unknown_type)
{
+ int ret;
struct object_id oid;
enum object_type type;
char *buf;
@@ -106,25 +134,42 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
if (sb.len) {
printf("%s\n", sb.buf);
strbuf_release(&sb);
- return 0;
+ ret = 0;
+ goto cleanup;
}
break;
case 's':
oi.sizep = &size;
+
+ if (use_mailmap) {
+ oi.typep = &type;
+ oi.contentp = (void**)&buf;
+ }
+
if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0)
die("git cat-file: could not get object info");
+
+ if (use_mailmap && (type == OBJ_COMMIT || type == OBJ_TAG)) {
+ size_t s = size;
+ buf = replace_idents_using_mailmap(buf, &s);
+ size = cast_size_t_to_ulong(s);
+ }
+
printf("%"PRIuMAX"\n", (uintmax_t)size);
- return 0;
+ ret = 0;
+ goto cleanup;
case 'e':
- return !has_object_file(&oid);
+ return !repo_has_object_file(the_repository, &oid);
case 'w':
if (filter_object(path, obj_context.mode,
- &oid, &buf, &size))
- return -1;
+ &oid, &buf, &size)) {
+ ret = -1;
+ goto cleanup;
+ }
break;
case 'c':
@@ -143,15 +188,25 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
const char *ls_args[3] = { NULL };
ls_args[0] = "ls-tree";
ls_args[1] = obj_name;
- return cmd_ls_tree(2, ls_args, NULL);
+ ret = cmd_ls_tree(2, ls_args, NULL);
+ goto cleanup;
}
- if (type == OBJ_BLOB)
- return stream_blob(&oid);
- buf = read_object_file(&oid, &type, &size);
+ if (type == OBJ_BLOB) {
+ ret = stream_blob(&oid);
+ goto cleanup;
+ }
+ buf = repo_read_object_file(the_repository, &oid, &type,
+ &size);
if (!buf)
die("Cannot read object %s", obj_name);
+ if (use_mailmap) {
+ size_t s = size;
+ buf = replace_idents_using_mailmap(buf, &s);
+ size = cast_size_t_to_ulong(s);
+ }
+
/* otherwise just spit out the data */
break;
@@ -162,8 +217,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
if (exp_type_id == OBJ_BLOB) {
struct object_id blob_oid;
if (oid_object_info(the_repository, &oid, NULL) == OBJ_TAG) {
- char *buffer = read_object_file(&oid, &type,
- &size);
+ char *buffer = repo_read_object_file(the_repository,
+ &oid,
+ &type,
+ &size);
const char *target;
if (!skip_prefix(buffer, "object ", &target) ||
get_oid_hex(target, &blob_oid))
@@ -172,8 +229,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
} else
oidcpy(&blob_oid, &oid);
- if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB)
- return stream_blob(&blob_oid);
+ if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB) {
+ ret = stream_blob(&blob_oid);
+ goto cleanup;
+ }
/*
* we attempted to dereference a tag to a blob
* and failed; there may be new dereference
@@ -183,6 +242,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
}
buf = read_object_with_reference(the_repository, &oid,
exp_type_id, &size, NULL);
+
+ if (use_mailmap) {
+ size_t s = size;
+ buf = replace_idents_using_mailmap(buf, &s);
+ size = cast_size_t_to_ulong(s);
+ }
break;
}
default:
@@ -193,9 +258,11 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
die("git cat-file %s: bad file", obj_name);
write_or_die(1, buf, size);
+ ret = 0;
+cleanup:
free(buf);
free(obj_context.path);
- return 0;
+ return ret;
}
struct expand_data {
@@ -241,10 +308,8 @@ static int is_atom(const char *atom, const char *s, int slen)
}
static void expand_atom(struct strbuf *sb, const char *atom, int len,
- void *vdata)
+ struct expand_data *data)
{
- struct expand_data *data = vdata;
-
if (is_atom("objectname", atom, len)) {
if (!data->mark_query)
strbuf_addstr(sb, oid_to_hex(&data->oid));
@@ -278,19 +343,21 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
die("unknown format element: %.*s", len, atom);
}
-static size_t expand_format(struct strbuf *sb, const char *start, void *data)
+static void expand_format(struct strbuf *sb, const char *start,
+ struct expand_data *data)
{
- const char *end;
-
- if (*start != '(')
- return 0;
- end = strchr(start + 1, ')');
- if (!end)
- die("format element '%s' does not end in ')'", start);
-
- expand_atom(sb, start + 1, end - start - 1, data);
-
- return end - start + 1;
+ while (strbuf_expand_step(sb, &start)) {
+ const char *end;
+
+ if (skip_prefix(start, "%", &start) || *start != '(')
+ strbuf_addch(sb, '%');
+ else if (!(end = strchr(start + 1, ')')))
+ die("format element '%s' does not end in ')'", start);
+ else {
+ expand_atom(sb, start + 1, end - start - 1, data);
+ start = end + 1;
+ }
+ }
}
static void batch_write(struct batch_options *opt, const void *data, int len)
@@ -328,9 +395,10 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
if (!textconv_object(the_repository,
data->rest, 0100644, oid,
1, &contents, &size))
- contents = read_object_file(oid,
- &type,
- &size);
+ contents = repo_read_object_file(the_repository,
+ oid,
+ &type,
+ &size);
if (!contents)
die("could not convert '%s' %s",
oid_to_hex(oid), data->rest);
@@ -347,12 +415,20 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
unsigned long size;
void *contents;
- contents = read_object_file(oid, &type, &size);
+ contents = repo_read_object_file(the_repository, oid, &type,
+ &size);
+
+ if (use_mailmap) {
+ size_t s = size;
+ contents = replace_idents_using_mailmap(contents, &s);
+ size = cast_size_t_to_ulong(s);
+ }
+
if (!contents)
die("object %s disappeared", oid_to_hex(oid));
if (type != data->type)
die("object %s changed type!?", oid_to_hex(oid));
- if (data->info.sizep && size != data->size)
+ if (data->info.sizep && size != data->size && !use_mailmap)
die("object %s changed size!?", oid_to_hex(oid));
batch_write(opt, contents, size);
@@ -360,11 +436,12 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
}
}
-static void print_default_format(struct strbuf *scratch, struct expand_data *data)
+static void print_default_format(struct strbuf *scratch, struct expand_data *data,
+ struct batch_options *opt)
{
- strbuf_addf(scratch, "%s %s %"PRIuMAX"\n", oid_to_hex(&data->oid),
+ strbuf_addf(scratch, "%s %s %"PRIuMAX"%c", oid_to_hex(&data->oid),
type_name(data->type),
- (uintmax_t)data->size);
+ (uintmax_t)data->size, opt->output_delim);
}
/*
@@ -382,6 +459,9 @@ static void batch_object_write(const char *obj_name,
if (!data->skip_object_info) {
int ret;
+ if (use_mailmap)
+ data->info.typep = &data->type;
+
if (pack)
ret = packed_object_info(the_repository, pack, offset,
&data->info);
@@ -390,27 +470,39 @@ static void batch_object_write(const char *obj_name,
&data->oid, &data->info,
OBJECT_INFO_LOOKUP_REPLACE);
if (ret < 0) {
- printf("%s missing\n",
- obj_name ? obj_name : oid_to_hex(&data->oid));
+ printf("%s missing%c",
+ obj_name ? obj_name : oid_to_hex(&data->oid), opt->output_delim);
fflush(stdout);
return;
}
+
+ if (use_mailmap && (data->type == OBJ_COMMIT || data->type == OBJ_TAG)) {
+ size_t s = data->size;
+ char *buf = NULL;
+
+ buf = repo_read_object_file(the_repository, &data->oid, &data->type,
+ &data->size);
+ buf = replace_idents_using_mailmap(buf, &s);
+ data->size = cast_size_t_to_ulong(s);
+
+ free(buf);
+ }
}
strbuf_reset(scratch);
if (!opt->format) {
- print_default_format(scratch, data);
+ print_default_format(scratch, data, opt);
} else {
- strbuf_expand(scratch, opt->format, expand_format, data);
- strbuf_addch(scratch, '\n');
+ expand_format(scratch, opt->format, data);
+ strbuf_addch(scratch, opt->output_delim);
}
batch_write(opt, scratch->buf, scratch->len);
if (opt->batch_mode == BATCH_MODE_CONTENTS) {
print_object_or_die(opt, data);
- batch_write(opt, "\n", 1);
+ batch_write(opt, &opt->output_delim, 1);
}
}
@@ -428,22 +520,25 @@ static void batch_one_object(const char *obj_name,
if (result != FOUND) {
switch (result) {
case MISSING_OBJECT:
- printf("%s missing\n", obj_name);
+ printf("%s missing%c", obj_name, opt->output_delim);
break;
case SHORT_NAME_AMBIGUOUS:
- printf("%s ambiguous\n", obj_name);
+ printf("%s ambiguous%c", obj_name, opt->output_delim);
break;
case DANGLING_SYMLINK:
- printf("dangling %"PRIuMAX"\n%s\n",
- (uintmax_t)strlen(obj_name), obj_name);
+ printf("dangling %"PRIuMAX"%c%s%c",
+ (uintmax_t)strlen(obj_name),
+ opt->output_delim, obj_name, opt->output_delim);
break;
case SYMLINK_LOOP:
- printf("loop %"PRIuMAX"\n%s\n",
- (uintmax_t)strlen(obj_name), obj_name);
+ printf("loop %"PRIuMAX"%c%s%c",
+ (uintmax_t)strlen(obj_name),
+ opt->output_delim, obj_name, opt->output_delim);
break;
case NOT_DIR:
- printf("notdir %"PRIuMAX"\n%s\n",
- (uintmax_t)strlen(obj_name), obj_name);
+ printf("notdir %"PRIuMAX"%c%s%c",
+ (uintmax_t)strlen(obj_name),
+ opt->output_delim, obj_name, opt->output_delim);
break;
default:
BUG("unknown get_sha1_with_context result %d\n",
@@ -455,9 +550,9 @@ static void batch_one_object(const char *obj_name,
}
if (ctx.mode == 0) {
- printf("symlink %"PRIuMAX"\n%s\n",
+ printf("symlink %"PRIuMAX"%c%s%c",
(uintmax_t)ctx.symlink_path.len,
- ctx.symlink_path.buf);
+ opt->output_delim, ctx.symlink_path.buf, opt->output_delim);
fflush(stdout);
return;
}
@@ -482,7 +577,7 @@ static int batch_object_cb(const struct object_id *oid, void *vdata)
}
static int collect_loose_object(const struct object_id *oid,
- const char *path,
+ const char *path UNUSED,
void *data)
{
oid_array_append(data, oid);
@@ -490,8 +585,8 @@ static int collect_loose_object(const struct object_id *oid,
}
static int collect_packed_object(const struct object_id *oid,
- struct packed_git *pack,
- uint32_t pos,
+ struct packed_git *pack UNUSED,
+ uint32_t pos UNUSED,
void *data)
{
oid_array_append(data, oid);
@@ -514,7 +609,7 @@ static int batch_unordered_object(const struct object_id *oid,
}
static int batch_unordered_loose(const struct object_id *oid,
- const char *path,
+ const char *path UNUSED,
void *data)
{
return batch_unordered_object(oid, NULL, 0, data);
@@ -602,7 +697,7 @@ static void batch_objects_command(struct batch_options *opt,
struct queued_cmd *queued_cmd = NULL;
size_t alloc = 0, nr = 0;
- while (!strbuf_getline(&input, stdin)) {
+ while (strbuf_getdelim_strip_crlf(&input, stdin, opt->input_delim) != EOF) {
int i;
const struct parse_cmd *cmd = NULL;
const char *p = NULL, *cmd_end;
@@ -655,6 +750,7 @@ static void batch_objects_command(struct batch_options *opt,
free_cmds(queued_cmd, &nr);
}
+ free_cmds(queued_cmd, &nr);
free(queued_cmd);
strbuf_release(&input);
}
@@ -676,9 +772,8 @@ static int batch_objects(struct batch_options *opt)
*/
memset(&data, 0, sizeof(data));
data.mark_query = 1;
- strbuf_expand(&output,
+ expand_format(&output,
opt->format ? opt->format : DEFAULT_FORMAT,
- expand_format,
&data);
data.mark_query = 0;
strbuf_release(&output);
@@ -701,10 +796,10 @@ static int batch_objects(struct batch_options *opt)
if (!memcmp(&data.info, &empty, sizeof(empty)))
data.skip_object_info = 1;
- if (has_promisor_remote())
+ if (repo_has_promisor_remote(the_repository))
warning("This repository uses promisor remotes. Some objects may not be loaded.");
- read_replace_refs = 0;
+ disable_replace_refs();
cb.opt = opt;
cb.expand = &data;
@@ -750,7 +845,7 @@ static int batch_objects(struct batch_options *opt)
goto cleanup;
}
- while (strbuf_getline(&input, stdin) != EOF) {
+ while (strbuf_getdelim_strip_crlf(&input, stdin, opt->input_delim) != EOF) {
if (data.split_on_whitespace) {
/*
* Split at first whitespace, tying off the beginning
@@ -775,12 +870,13 @@ static int batch_objects(struct batch_options *opt)
return retval;
}
-static int git_cat_file_config(const char *var, const char *value, void *cb)
+static int git_cat_file_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (userdiff_config(var, value) < 0)
return -1;
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
}
static int batch_option_callback(const struct option *opt,
@@ -819,6 +915,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
const char *exp_type = NULL, *obj_name = NULL;
struct batch_options batch = {0};
int unknown_type = 0;
+ int input_nul_terminated = 0;
+ int nul_terminated = 0;
const char * const usage[] = {
N_("git cat-file <type> <object>"),
@@ -826,7 +924,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
N_("git cat-file (-t | -s) [--allow-unknown-type] <object>"),
N_("git cat-file (--batch | --batch-check | --batch-command) [--batch-all-objects]\n"
" [--buffer] [--follow-symlinks] [--unordered]\n"
- " [--textconv | --filters]"),
+ " [--textconv | --filters] [-Z]"),
N_("git cat-file (--textconv | --filters)\n"
" [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"),
NULL
@@ -843,6 +941,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'),
OPT_BOOL(0, "allow-unknown-type", &unknown_type,
N_("allow -s and -t to work with broken/corrupt objects")),
+ OPT_BOOL(0, "use-mailmap", &use_mailmap, N_("use mail map file")),
+ OPT_ALIAS(0, "mailmap", "use-mailmap"),
/* Batch mode */
OPT_GROUP(N_("Batch objects requested on stdin (or --batch-all-objects)")),
OPT_CALLBACK_F(0, "batch", &batch, N_("format"),
@@ -853,6 +953,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
N_("like --batch, but don't emit <contents>"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
batch_option_callback),
+ OPT_BOOL_F('z', NULL, &input_nul_terminated, N_("stdin is NUL-terminated"),
+ PARSE_OPT_HIDDEN),
+ OPT_BOOL('Z', NULL, &nul_terminated, N_("stdin and stdout is NUL-terminated")),
OPT_CALLBACK_F(0, "batch-command", &batch, N_("format"),
N_("read commands from stdin"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
@@ -885,6 +988,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
opt_cw = (opt == 'c' || opt == 'w');
opt_epts = (opt == 'e' || opt == 'p' || opt == 't' || opt == 's');
+ if (use_mailmap)
+ read_mailmap(&mailmap);
+
/* --batch-all-objects? */
if (opt == 'b')
batch.all_objects = 1;
@@ -908,6 +1014,18 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
else if (batch.all_objects)
usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
"--batch-all-objects");
+ else if (input_nul_terminated)
+ usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
+ "-z");
+ else if (nul_terminated)
+ usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
+ "-Z");
+
+ batch.input_delim = batch.output_delim = '\n';
+ if (input_nul_terminated)
+ batch.input_delim = '\0';
+ if (nul_terminated)
+ batch.input_delim = batch.output_delim = '\0';
/* Batch defaults */
if (batch.buffer_output < 0)
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index dd83397786..b22ff748c3 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -1,17 +1,23 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "attr.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-name.h"
#include "quote.h"
+#include "repository.h"
+#include "setup.h"
#include "parse-options.h"
+#include "write-or-die.h"
static int all_attrs;
static int cached_attrs;
static int stdin_paths;
+static char *source;
static const char * const check_attr_usage[] = {
-N_("git check-attr [-a | --all | <attr>...] [--] <pathname>..."),
-N_("git check-attr --stdin [-z] [-a | --all | <attr>...]"),
+N_("git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] <pathname>..."),
+N_("git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"),
NULL
};
@@ -23,6 +29,7 @@ static const struct option check_attr_options[] = {
OPT_BOOL(0 , "stdin", &stdin_paths, N_("read file names from stdin")),
OPT_BOOL('z', NULL, &nul_term_line,
N_("terminate input and output records by a NUL character")),
+ OPT_STRING(0, "source", &source, N_("<tree-ish>"), N_("which tree-ish to check attributes at")),
OPT_END()
};
@@ -55,10 +62,10 @@ static void output_attr(struct attr_check *check, const char *file)
}
}
-static void check_attr(const char *prefix,
- struct attr_check *check,
+static void check_attr(const char *prefix, struct attr_check *check,
int collect_all,
const char *file)
+
{
char *full_path =
prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
@@ -73,8 +80,7 @@ static void check_attr(const char *prefix,
free(full_path);
}
-static void check_attr_stdin_paths(const char *prefix,
- struct attr_check *check,
+static void check_attr_stdin_paths(const char *prefix, struct attr_check *check,
int collect_all)
{
struct strbuf buf = STRBUF_INIT;
@@ -105,6 +111,7 @@ static NORETURN void error_with_usage(const char *msg)
int cmd_check_attr(int argc, const char **argv, const char *prefix)
{
struct attr_check *check;
+ struct object_id initialized_oid;
int cnt, i, doubledash, filei;
if (!is_bare_repository())
@@ -115,7 +122,7 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, check_attr_options,
check_attr_usage, PARSE_OPT_KEEP_DASHDASH);
- if (read_cache() < 0) {
+ if (repo_read_index(the_repository) < 0) {
die("invalid cache");
}
@@ -176,6 +183,12 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
}
}
+ if (source) {
+ if (repo_get_oid_tree(the_repository, source, &initialized_oid))
+ die("%s: not a valid tree-ish source", source);
+ set_git_attr_source(source);
+ }
+
if (stdin_paths)
check_attr_stdin_paths(prefix, check, all_attrs);
else {
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 2191256965..906cd96753 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -1,12 +1,14 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "dir.h"
+#include "gettext.h"
#include "quote.h"
#include "pathspec.h"
#include "parse-options.h"
+#include "repository.h"
#include "submodule.h"
+#include "write-or-die.h"
static int quiet, verbose, stdin_paths, show_non_matching, no_index;
static const char * const check_ignore_usage[] = {
@@ -179,7 +181,7 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix)
die(_("--non-matching is only valid with --verbose"));
/* read_cache() is only necessary so we can watch out for submodules. */
- if (!no_index && read_cache() < 0)
+ if (!no_index && repo_read_index(the_repository) < 0)
die(_("index file corrupt"));
setup_standard_excludes(&dir);
diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c
index 7dc47e4793..b8a05b8e07 100644
--- a/builtin/check-mailmap.c
+++ b/builtin/check-mailmap.c
@@ -1,8 +1,12 @@
#include "builtin.h"
#include "config.h"
+#include "gettext.h"
+#include "ident.h"
#include "mailmap.h"
#include "parse-options.h"
+#include "strbuf.h"
#include "string-list.h"
+#include "write-or-die.h"
static int use_stdin;
static const char * const check_mailmap_usage[] = {
diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c
index bc67d3f0a8..5eb6bdc3f6 100644
--- a/builtin/check-ref-format.c
+++ b/builtin/check-ref-format.c
@@ -2,9 +2,9 @@
* GIT - The information manager from hell
*/
-#include "cache.h"
-#include "refs.h"
#include "builtin.h"
+#include "refs.h"
+#include "setup.h"
#include "strbuf.h"
static const char builtin_check_ref_format_usage[] =
@@ -57,6 +57,10 @@ int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
int normalize = 0;
int flags = 0;
const char *refname;
+ char *to_free = NULL;
+ int ret = 1;
+
+ BUG_ON_NON_EMPTY_PREFIX(prefix);
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(builtin_check_ref_format_usage);
@@ -81,11 +85,14 @@ int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
refname = argv[i];
if (normalize)
- refname = collapse_slashes(refname);
+ refname = to_free = collapse_slashes(refname);
if (check_refname_format(refname, flags))
- return 1;
+ goto cleanup;
if (normalize)
printf("%s\n", refname);
- return 0;
+ ret = 0;
+cleanup:
+ free(to_free);
+ return ret;
}
diff --git a/builtin/checkout--worker.c b/builtin/checkout--worker.c
index ede7dc32a4..6b62b5375b 100644
--- a/builtin/checkout--worker.c
+++ b/builtin/checkout--worker.c
@@ -1,9 +1,11 @@
#include "builtin.h"
#include "config.h"
#include "entry.h"
+#include "gettext.h"
#include "parallel-checkout.h"
#include "parse-options.h"
#include "pkt-line.h"
+#include "read-cache-ll.h"
static void packet_to_pc_item(const char *buffer, int len,
struct parallel_checkout_item *pc_item)
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 97e06e8c52..f62f13f2b5 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -4,16 +4,21 @@
* Copyright (C) 2005 Linus Torvalds
*
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "config.h"
#include "dir.h"
+#include "gettext.h"
#include "lockfile.h"
#include "quote.h"
+#include "repository.h"
#include "cache-tree.h"
#include "parse-options.h"
#include "entry.h"
#include "parallel-checkout.h"
+#include "read-cache-ll.h"
+#include "setup.h"
+#include "sparse-index.h"
#define CHECKOUT_ALL 4
static int nul_term_line;
@@ -65,7 +70,7 @@ static void write_tempfile_record(const char *name, const char *prefix)
static int checkout_file(const char *name, const char *prefix)
{
int namelen = strlen(name);
- int pos = cache_name_pos(name, namelen);
+ int pos = index_name_pos(&the_index, name, namelen);
int has_same_name = 0;
int is_file = 0;
int is_skipped = 1;
@@ -75,8 +80,8 @@ static int checkout_file(const char *name, const char *prefix)
if (pos < 0)
pos = -pos - 1;
- while (pos < active_nr) {
- struct cache_entry *ce = active_cache[pos];
+ while (pos < the_index.cache_nr) {
+ struct cache_entry *ce = the_index.cache[pos];
if (ce_namelen(ce) != namelen ||
memcmp(ce->name, name, namelen))
break;
@@ -136,8 +141,8 @@ static int checkout_all(const char *prefix, int prefix_length)
int i, errs = 0;
struct cache_entry *last_ce = NULL;
- for (i = 0; i < active_nr ; i++) {
- struct cache_entry *ce = active_cache[i];
+ for (i = 0; i < the_index.cache_nr ; i++) {
+ struct cache_entry *ce = the_index.cache[i];
if (S_ISSPARSEDIR(ce->ce_mode)) {
if (!ce_skip_worktree(ce))
@@ -151,7 +156,7 @@ static int checkout_all(const char *prefix, int prefix_length)
*/
if (ignore_skip_worktree) {
ensure_full_index(&the_index);
- ce = active_cache[i];
+ ce = the_index.cache[i];
}
}
@@ -249,7 +254,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
- if (read_cache() < 0) {
+ if (repo_read_index(the_repository) < 0) {
die("invalid cache");
}
@@ -270,7 +275,8 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
if (index_opt && !state.base_dir_len && !to_tempfile) {
state.refresh_cache = 1;
state.istate = &the_index;
- hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &lock_file,
+ LOCK_DIE_ON_ERROR);
}
get_parallel_checkout_configs(&pc_workers, &pc_threshold);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 797681481d..f53612f468 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,4 +1,4 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "advice.h"
#include "blob.h"
@@ -9,19 +9,30 @@
#include "config.h"
#include "diff.h"
#include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "hook.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
#include "lockfile.h"
+#include "mem-pool.h"
#include "merge-recursive.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
#include "parse-options.h"
+#include "path.h"
+#include "preload-index.h"
+#include "read-cache.h"
#include "refs.h"
#include "remote.h"
#include "resolve-undo.h"
#include "revision.h"
#include "run-command.h"
+#include "setup.h"
#include "submodule.h"
#include "submodule-config.h"
+#include "symlinks.h"
+#include "trace2.h"
#include "tree.h"
#include "tree-walk.h"
#include "unpack-trees.h"
@@ -29,6 +40,7 @@
#include "xdiff-interface.h"
#include "entry.h"
#include "parallel-checkout.h"
+#include "add-interactive.h"
static const char * const checkout_usage[] = {
N_("git checkout [<options>] <branch>"),
@@ -74,7 +86,7 @@ struct checkout_opts {
const char *ignore_unmerged_opt;
int ignore_unmerged;
int pathspec_file_nul;
- const char *pathspec_from_file;
+ char *pathspec_from_file;
const char *new_branch;
const char *new_branch_force;
@@ -125,7 +137,7 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm
}
static int update_some(const struct object_id *oid, struct strbuf *base,
- const char *pathname, unsigned mode, void *context)
+ const char *pathname, unsigned mode, void *context UNUSED)
{
int len;
struct cache_entry *ce;
@@ -148,9 +160,9 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
* entry in place. Whether it is UPTODATE or not, checkout_entry will
* do the right thing.
*/
- pos = cache_name_pos(ce->name, ce->ce_namelen);
+ pos = index_name_pos(&the_index, ce->name, ce->ce_namelen);
if (pos >= 0) {
- struct cache_entry *old = active_cache[pos];
+ struct cache_entry *old = the_index.cache[pos];
if (ce->ce_mode == old->ce_mode &&
!ce_intent_to_add(old) &&
oideq(&ce->oid, &old->oid)) {
@@ -160,7 +172,8 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
}
}
- add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
+ add_index_entry(&the_index, ce,
+ ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
return 0;
}
@@ -178,8 +191,8 @@ static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
static int skip_same_name(const struct cache_entry *ce, int pos)
{
- while (++pos < active_nr &&
- !strcmp(active_cache[pos]->name, ce->name))
+ while (++pos < the_index.cache_nr &&
+ !strcmp(the_index.cache[pos]->name, ce->name))
; /* skip */
return pos;
}
@@ -187,9 +200,9 @@ static int skip_same_name(const struct cache_entry *ce, int pos)
static int check_stage(int stage, const struct cache_entry *ce, int pos,
int overlay_mode)
{
- while (pos < active_nr &&
- !strcmp(active_cache[pos]->name, ce->name)) {
- if (ce_stage(active_cache[pos]) == stage)
+ while (pos < the_index.cache_nr &&
+ !strcmp(the_index.cache[pos]->name, ce->name)) {
+ if (ce_stage(the_index.cache[pos]) == stage)
return 0;
pos++;
}
@@ -206,8 +219,8 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)
unsigned seen = 0;
const char *name = ce->name;
- while (pos < active_nr) {
- ce = active_cache[pos];
+ while (pos < the_index.cache_nr) {
+ ce = the_index.cache[pos];
if (strcmp(name, ce->name))
break;
seen |= (1 << ce_stage(ce));
@@ -223,15 +236,15 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
const struct checkout *state, int *nr_checkouts,
int overlay_mode)
{
- while (pos < active_nr &&
- !strcmp(active_cache[pos]->name, ce->name)) {
- if (ce_stage(active_cache[pos]) == stage)
- return checkout_entry(active_cache[pos], state,
+ while (pos < the_index.cache_nr &&
+ !strcmp(the_index.cache[pos]->name, ce->name)) {
+ if (ce_stage(the_index.cache[pos]) == stage)
+ return checkout_entry(the_index.cache[pos], state,
NULL, nr_checkouts);
pos++;
}
if (!overlay_mode) {
- unlink_entry(ce);
+ unlink_entry(ce, NULL);
return 0;
}
if (stage == 2)
@@ -243,7 +256,7 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
static int checkout_merged(int pos, const struct checkout *state,
int *nr_checkouts, struct mem_pool *ce_mem_pool)
{
- struct cache_entry *ce = active_cache[pos];
+ struct cache_entry *ce = the_index.cache[pos];
const char *path = ce->name;
mmfile_t ancestor, ours, theirs;
enum ll_merge_result merge_status;
@@ -256,7 +269,7 @@ static int checkout_merged(int pos, const struct checkout *state,
int renormalize = 0;
memset(threeway, 0, sizeof(threeway));
- while (pos < active_nr) {
+ while (pos < the_index.cache_nr) {
int stage;
stage = ce_stage(ce);
if (!stage || strcmp(path, ce->name))
@@ -265,7 +278,7 @@ static int checkout_merged(int pos, const struct checkout *state,
if (stage == 2)
mode = create_ce_mode(ce->ce_mode);
pos++;
- ce = active_cache[pos];
+ ce = the_index.cache[pos];
}
if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2]))
return error(_("path '%s' does not have necessary versions"), path);
@@ -391,8 +404,8 @@ static int checkout_worktree(const struct checkout_opts *opts,
if (pc_workers > 1)
init_parallel_checkout();
- for (pos = 0; pos < active_nr; pos++) {
- struct cache_entry *ce = active_cache[pos];
+ for (pos = 0; pos < the_index.cache_nr; pos++) {
+ struct cache_entry *ce = the_index.cache[pos];
if (ce->ce_flags & CE_MATCHED) {
if (!ce_stage(ce)) {
errs |= checkout_entry(ce, &state,
@@ -417,7 +430,7 @@ static int checkout_worktree(const struct checkout_opts *opts,
mem_pool_discard(&ce_mem_pool, should_validate_cache_entries());
remove_marked_cache_entries(&the_index, 1);
remove_scheduled_dirs();
- errs |= finish_delayed_checkout(&state, &nr_checkouts, opts->show_progress);
+ errs |= finish_delayed_checkout(&state, opts->show_progress);
if (opts->count_checkout_paths) {
if (nr_unmerged)
@@ -430,8 +443,8 @@ static int checkout_worktree(const struct checkout_opts *opts,
"Updated %d paths from %s",
nr_checkouts),
nr_checkouts,
- find_unique_abbrev(&opts->source_tree->object.oid,
- DEFAULT_ABBREV));
+ repo_find_unique_abbrev(the_repository, &opts->source_tree->object.oid,
+ DEFAULT_ABBREV));
else if (!nr_unmerged || nr_checkouts)
fprintf_ln(stderr, Q_("Updated %d path from the index",
"Updated %d paths from the index",
@@ -487,18 +500,31 @@ static int checkout_paths(const struct checkout_opts *opts,
die(_("'%s' must be used when '%s' is not specified"),
"--worktree", "--source");
- if (opts->checkout_index && !opts->checkout_worktree &&
- opts->writeout_stage)
- die(_("'%s' or '%s' cannot be used with %s"),
- "--ours", "--theirs", "--staged");
-
- if (opts->checkout_index && !opts->checkout_worktree &&
- opts->merge)
- die(_("'%s' or '%s' cannot be used with %s"),
- "--merge", "--conflict", "--staged");
+ /*
+ * Reject --staged option to the restore command when combined with
+ * merge-related options. Use the accept_ref flag to distinguish it
+ * from the checkout command, which does not accept --staged anyway.
+ *
+ * `restore --ours|--theirs --worktree --staged` could mean resolving
+ * conflicted paths to one side in both the worktree and the index,
+ * but does not currently.
+ *
+ * `restore --merge|--conflict=<style>` already recreates conflicts
+ * in both the worktree and the index, so adding --staged would be
+ * meaningless.
+ */
+ if (!opts->accept_ref && opts->checkout_index) {
+ if (opts->writeout_stage)
+ die(_("'%s' or '%s' cannot be used with %s"),
+ "--ours", "--theirs", "--staged");
+
+ if (opts->merge)
+ die(_("'%s' or '%s' cannot be used with %s"),
+ "--merge", "--conflict", "--staged");
+ }
if (opts->patch_mode) {
- const char *patch_mode;
+ enum add_p_mode patch_mode;
const char *rev = new_branch_info->name;
char rev_oid[GIT_MAX_HEXSZ + 1];
@@ -516,19 +542,20 @@ static int checkout_paths(const struct checkout_opts *opts,
rev = oid_to_hex_r(rev_oid, &new_branch_info->commit->object.oid);
if (opts->checkout_index && opts->checkout_worktree)
- patch_mode = "--patch=checkout";
+ patch_mode = ADD_P_CHECKOUT;
else if (opts->checkout_index && !opts->checkout_worktree)
- patch_mode = "--patch=reset";
+ patch_mode = ADD_P_RESET;
else if (!opts->checkout_index && opts->checkout_worktree)
- patch_mode = "--patch=worktree";
+ patch_mode = ADD_P_WORKTREE;
else
BUG("either flag must have been set, worktree=%d, index=%d",
opts->checkout_worktree, opts->checkout_index);
- return run_add_interactive(rev, patch_mode, &opts->pathspec);
+ return !!run_add_p(the_repository, patch_mode, rev,
+ &opts->pathspec);
}
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
- if (read_cache_preload(&opts->pathspec) < 0)
+ if (repo_read_index_preload(the_repository, &opts->pathspec, 0) < 0)
return error(_("index file corrupt"));
if (opts->source_tree)
@@ -540,13 +567,13 @@ static int checkout_paths(const struct checkout_opts *opts,
* Make sure all pathspecs participated in locating the paths
* to be checked out.
*/
- for (pos = 0; pos < active_nr; pos++)
+ for (pos = 0; pos < the_index.cache_nr; pos++)
if (opts->overlay_mode)
- mark_ce_for_checkout_overlay(active_cache[pos],
+ mark_ce_for_checkout_overlay(the_index.cache[pos],
ps_matched,
opts);
else
- mark_ce_for_checkout_no_overlay(active_cache[pos],
+ mark_ce_for_checkout_no_overlay(the_index.cache[pos],
ps_matched,
opts);
@@ -561,8 +588,8 @@ static int checkout_paths(const struct checkout_opts *opts,
unmerge_marked_index(&the_index);
/* Any unmerged paths? */
- for (pos = 0; pos < active_nr; pos++) {
- const struct cache_entry *ce = active_cache[pos];
+ for (pos = 0; pos < the_index.cache_nr; pos++) {
+ const struct cache_entry *ce = the_index.cache[pos];
if (ce->ce_flags & CE_MATCHED) {
if (!ce_stage(ce))
continue;
@@ -626,24 +653,27 @@ static void show_local_changes(struct object *head,
repo_init_revisions(the_repository, &rev, NULL);
rev.diffopt.flags = opts->flags;
rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
+ rev.diffopt.flags.recursive = 1;
diff_setup_done(&rev.diffopt);
add_pending_object(&rev, head, NULL);
run_diff_index(&rev, 0);
- object_array_clear(&rev.pending);
+ release_revisions(&rev);
}
static void describe_detached_head(const char *msg, struct commit *commit)
{
struct strbuf sb = STRBUF_INIT;
- if (!parse_commit(commit))
+ if (!repo_parse_commit(the_repository, commit))
pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
if (print_sha1_ellipsis()) {
fprintf(stderr, "%s %s... %s\n", msg,
- find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf);
+ repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV),
+ sb.buf);
} else {
fprintf(stderr, "%s %s %s\n", msg,
- find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf);
+ repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV),
+ sb.buf);
}
strbuf_release(&sb);
}
@@ -697,7 +727,8 @@ static void setup_branch_path(struct branch_info *branch)
* If this is a ref, resolve it; otherwise, look up the OID for our
* expression. Failure here is okay.
*/
- if (!dwim_ref(branch->name, strlen(branch->name), &branch->oid, &branch->refname, 0))
+ if (!repo_dwim_ref(the_repository, branch->name, strlen(branch->name),
+ &branch->oid, &branch->refname, 0))
repo_get_oid_committish(the_repository, branch->name, &branch->oid);
strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
@@ -710,6 +741,26 @@ static void setup_branch_path(struct branch_info *branch)
branch->path = strbuf_detach(&buf, NULL);
}
+static void init_topts(struct unpack_trees_options *topts, int merge,
+ int show_progress, int overwrite_ignore,
+ struct commit *old_commit)
+{
+ memset(topts, 0, sizeof(*topts));
+ topts->head_idx = -1;
+ topts->src_index = &the_index;
+ topts->dst_index = &the_index;
+
+ setup_unpack_trees_porcelain(topts, "checkout");
+
+ topts->initial_checkout = is_index_unborn(&the_index);
+ topts->update = 1;
+ topts->merge = 1;
+ topts->quiet = merge && old_commit;
+ topts->verbose_update = show_progress;
+ topts->fn = twoway_merge;
+ topts->preserve_ignored = !overwrite_ignore;
+}
+
static int merge_working_tree(const struct checkout_opts *opts,
struct branch_info *old_branch_info,
struct branch_info *new_branch_info,
@@ -719,17 +770,18 @@ static int merge_working_tree(const struct checkout_opts *opts,
struct lock_file lock_file = LOCK_INIT;
struct tree *new_tree;
- hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
- if (read_cache_preload(NULL) < 0)
+ repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
+ if (repo_read_index_preload(the_repository, NULL, 0) < 0)
return error(_("index file corrupt"));
- resolve_undo_clear();
+ resolve_undo_clear_index(&the_index);
if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
if (new_branch_info->commit)
BUG("'switch --orphan' should never accept a commit as starting point");
new_tree = parse_tree_indirect(the_hash_algo->empty_tree);
} else
- new_tree = get_commit_tree(new_branch_info->commit);
+ new_tree = repo_get_commit_tree(the_repository,
+ new_branch_info->commit);
if (opts->discard_changes) {
ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info);
if (ret)
@@ -740,32 +792,20 @@ static int merge_working_tree(const struct checkout_opts *opts,
struct unpack_trees_options topts;
const struct object_id *old_commit_oid;
- memset(&topts, 0, sizeof(topts));
- topts.head_idx = -1;
- topts.src_index = &the_index;
- topts.dst_index = &the_index;
+ refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
- setup_unpack_trees_porcelain(&topts, "checkout");
-
- refresh_cache(REFRESH_QUIET);
-
- if (unmerged_cache()) {
+ if (unmerged_index(&the_index)) {
error(_("you need to resolve your current index first"));
return 1;
}
/* 2-way merge to the new branch */
- topts.initial_checkout = is_cache_unborn();
- topts.update = 1;
- topts.merge = 1;
- topts.quiet = opts->merge && old_branch_info->commit;
- topts.verbose_update = opts->show_progress;
- topts.fn = twoway_merge;
+ init_topts(&topts, opts->merge, opts->show_progress,
+ opts->overwrite_ignore, old_branch_info->commit);
init_checkout_metadata(&topts.meta, new_branch_info->refname,
new_branch_info->commit ?
&new_branch_info->commit->object.oid :
&new_branch_info->oid, NULL);
- topts.preserve_ignored = !opts->overwrite_ignore;
old_commit_oid = old_branch_info->commit ?
&old_branch_info->commit->object.oid :
@@ -803,7 +843,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
*/
if (!old_branch_info->commit)
return 1;
- old_tree = get_commit_tree(old_branch_info->commit);
+ old_tree = repo_get_commit_tree(the_repository,
+ old_branch_info->commit);
if (repo_index_has_changes(the_repository, old_tree, &sb))
die(_("cannot continue with staged changes in "
@@ -823,7 +864,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
* entries in the index.
*/
- add_files_to_cache(NULL, NULL, 0);
+ add_files_to_cache(the_repository, NULL, NULL, 0, 0);
init_merge_options(&o, the_repository);
o.verbosity = 0;
work = write_in_core_index_as_tree(the_repository);
@@ -834,7 +875,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
if (ret)
return ret;
o.ancestor = old_branch_info->name;
- if (old_branch_info->name == NULL) {
+ if (!old_branch_info->name) {
strbuf_add_unique_abbrev(&old_commit_shortname,
&old_branch_info->commit->object.oid,
DEFAULT_ABBREV);
@@ -858,7 +899,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
}
}
- if (!cache_tree_fully_valid(active_cache_tree))
+ if (!cache_tree_fully_valid(the_index.cache_tree))
cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
@@ -875,7 +916,7 @@ static void report_tracking(struct branch_info *new_branch_info)
struct strbuf sb = STRBUF_INIT;
struct branch *branch = branch_get(new_branch_info->name);
- if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL))
+ if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL, 1))
return;
fputs(sb.buf, stdout);
strbuf_release(&sb);
@@ -981,7 +1022,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
static int add_pending_uninteresting_ref(const char *refname,
const struct object_id *oid,
- int flags, void *cb_data)
+ int flags UNUSED, void *cb_data)
{
add_pending_oid(cb_data, refname, oid, UNINTERESTING);
return 0;
@@ -992,7 +1033,7 @@ static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
strbuf_addstr(sb, " ");
strbuf_add_unique_abbrev(sb, &commit->object.oid, DEFAULT_ABBREV);
strbuf_addch(sb, ' ');
- if (!parse_commit(commit))
+ if (!repo_parse_commit(the_repository, commit))
pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
strbuf_addch(sb, '\n');
}
@@ -1048,7 +1089,7 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
" git branch <new-branch-name> %s\n\n",
/* Give ngettext() the count */
lost),
- find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+ repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
}
/*
@@ -1082,6 +1123,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
/* Clean up objects used, as they will be reused. */
repo_clear_commit_marks(the_repository, ALL_REV_FLAGS);
+ release_revisions(&revs);
}
static int switch_branches(const struct checkout_opts *opts,
@@ -1147,7 +1189,8 @@ static int switch_branches(const struct checkout_opts *opts,
return ret || writeout_error;
}
-static int git_checkout_config(const char *var, const char *value, void *cb)
+static int git_checkout_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
struct checkout_opts *opts = cb;
@@ -1163,7 +1206,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
if (starts_with(var, "submodule."))
return git_default_submodule_config(var, value, NULL);
- return git_xmerge_config(var, value, NULL);
+ return git_xmerge_config(var, value, ctx, NULL);
}
static void setup_new_branch_info_and_source_tree(
@@ -1191,7 +1234,8 @@ static void setup_new_branch_info_and_source_tree(
*source_tree = parse_tree_indirect(rev);
} else {
parse_commit_or_die(new_branch_info->commit);
- *source_tree = get_commit_tree(new_branch_info->commit);
+ *source_tree = repo_get_commit_tree(the_repository,
+ new_branch_info->commit);
}
}
@@ -1259,7 +1303,7 @@ static int parse_branchname_arg(int argc, const char **argv,
* between A and B, A...B names that merge base.
*
* (b) If <something> is _not_ a commit, either "--" is present
- * or <something> is not a path, no -t or -b was given, and
+ * or <something> is not a path, no -t or -b was given,
* and there is a tracking branch whose name is <something>
* in one and only one remote (or if the branch exists on the
* remote named in checkout.defaultRemote), then this is a
@@ -1309,7 +1353,7 @@ static int parse_branchname_arg(int argc, const char **argv,
if (!strcmp(arg, "-"))
arg = "@{-1}";
- if (get_oid_mb(arg, rev)) {
+ if (repo_get_oid_mb(the_repository, arg, rev)) {
/*
* Either case (3) or (4), with <something> not being
* a commit, or an attempt to use case (1) with an
@@ -1406,7 +1450,8 @@ static void die_expecting_a_branch(const struct branch_info *branch_info)
char *to_free;
int code;
- if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free, 0) == 1) {
+ if (repo_dwim_ref(the_repository, branch_info->name,
+ strlen(branch_info->name), &oid, &to_free, 0) == 1) {
const char *ref = to_free;
if (skip_prefix(ref, "refs/tags/", &ref))
@@ -1460,6 +1505,8 @@ static void die_if_some_operation_in_progress(void)
"or \"git worktree add\"."));
if (state.bisect_in_progress)
warning(_("you are switching branch while bisecting"));
+
+ wt_status_state_free_buffers(&state);
}
static int checkout_branch(struct checkout_opts *opts,
@@ -1646,8 +1693,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
}
if (opts->conflict_style) {
+ struct key_value_info kvi = KVI_INIT;
+ struct config_context ctx = {
+ .kvi = &kvi,
+ };
opts->merge = 1; /* implied */
- git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
+ git_xmerge_config("merge.conflictstyle", opts->conflict_style,
+ &ctx, NULL);
}
if (opts->force) {
opts->discard_changes = 1;
@@ -1733,7 +1785,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
} else if (!opts->accept_ref && opts->from_treeish) {
struct object_id rev;
- if (get_oid_mb(opts->from_treeish, &rev))
+ if (repo_get_oid_mb(the_repository, opts->from_treeish, &rev))
die(_("could not resolve %s"), opts->from_treeish);
setup_new_branch_info_and_source_tree(new_branch_info,
@@ -1861,6 +1913,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
options, checkout_usage, &new_branch_info);
branch_info_release(&new_branch_info);
clear_pathspec(&opts.pathspec);
+ free(opts.pathspec_from_file);
FREE_AND_NULL(options);
return ret;
}
diff --git a/builtin/clean.c b/builtin/clean.c
index 5466636e66..49c224e626 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -6,12 +6,17 @@
* Based on git-clean.sh by Pavel Roskin
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
#include "config.h"
#include "dir.h"
+#include "gettext.h"
#include "parse-options.h"
+#include "path.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
#include "string-list.h"
#include "quote.h"
#include "column.h"
@@ -26,7 +31,7 @@ static struct string_list del_list = STRING_LIST_INIT_DUP;
static unsigned int colopts;
static const char *const builtin_clean_usage[] = {
- N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
+ N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] [<pathspec>...]"),
NULL
};
@@ -99,7 +104,8 @@ struct menu_stuff {
define_list_config_array(color_interactive_slots);
-static int git_clean_config(const char *var, const char *value, void *cb)
+static int git_clean_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
const char *slot_name;
@@ -126,8 +132,10 @@ static int git_clean_config(const char *var, const char *value, void *cb)
return 0;
}
- /* inspect the color.ui config variable and others */
- return git_color_default_config(var, value, cb);
+ if (git_color_config(var, value, cb) < 0)
+ return -1;
+
+ return git_default_config(var, value, ctx, cb);
}
static const char *clean_get_color(enum color_clean ix)
@@ -560,7 +568,7 @@ static int parse_choice(struct menu_stuff *menu_stuff,
/*
* Implement a git-add-interactive compatible UI, which is borrowed
- * from git-add--interactive.perl.
+ * from add-interactive.c.
*
* Return value:
*
@@ -1012,7 +1020,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
die(_("index file corrupt"));
pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option");
@@ -1031,7 +1039,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
struct stat st;
const char *rel;
- if (!cache_name_is_other(ent->name, ent->len))
+ if (!index_name_is_other(&the_index, ent->name, ent->len))
continue;
if (lstat(ent->name, &st))
@@ -1092,5 +1100,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
strbuf_release(&buf);
string_list_clear(&del_list, 0);
string_list_clear(&exclude_list, 0);
+ clear_pathspec(&pathspec);
return (errors != 0);
}
diff --git a/builtin/clone.c b/builtin/clone.c
index 7d3a3ffb40..c65378b3d2 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -8,15 +8,22 @@
* Clone a repository into a different directory that does not yet exist.
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
+#include "abspath.h"
+#include "advice.h"
#include "config.h"
+#include "copy.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "lockfile.h"
#include "parse-options.h"
#include "fetch-pack.h"
#include "refs.h"
#include "refspec.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
#include "tree.h"
#include "tree-walk.h"
#include "unpack-trees.h"
@@ -29,11 +36,15 @@
#include "branch.h"
#include "remote.h"
#include "run-command.h"
+#include "setup.h"
#include "connected.h"
#include "packfile.h"
+#include "path.h"
+#include "pkt-line.h"
#include "list-objects-filter-options.h"
#include "hook.h"
#include "bundle.h"
+#include "bundle-uri.h"
/*
* Overall FIXMEs:
@@ -72,11 +83,12 @@ static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP;
static int option_dissociate;
static int max_jobs = -1;
static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
-static struct list_objects_filter_options filter_options;
+static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT;
static int option_filter_submodules = -1; /* unspecified */
static int config_filter_submodules = -1; /* unspecified */
static struct string_list server_options = STRING_LIST_INIT_NODUP;
static int option_remote_submodules;
+static const char *bundle_uri;
static int recurse_submodules_cb(const struct option *opt,
const char *arg, int unset)
@@ -160,6 +172,8 @@ static struct option builtin_clone_options[] = {
N_("any cloned submodules will use their remote-tracking branch")),
OPT_BOOL(0, "sparse", &option_sparse_checkout,
N_("initialize sparse-checkout file to include only files at root")),
+ OPT_STRING(0, "bundle-uri", &bundle_uri,
+ N_("uri"), N_("a URI for downloading bundles before fetching from origin remote")),
OPT_END()
};
@@ -322,8 +336,18 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
iter = dir_iterator_begin(src->buf, DIR_ITERATOR_PEDANTIC);
- if (!iter)
+ if (!iter) {
+ if (errno == ENOTDIR) {
+ int saved_errno = errno;
+ struct stat st;
+
+ if (!lstat(src->buf, &st) && S_ISLNK(st.st_mode))
+ die(_("'%s' is a symlink, refusing to clone with --local"),
+ src->buf);
+ errno = saved_errno;
+ }
die_errno(_("failed to start iterator over '%s'"), src->buf);
+ }
strbuf_addch(src, '/');
src_len = src->len;
@@ -496,6 +520,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
/* if --branch=tag, pull the requested tag explicitly */
get_fetch_map(remote_head, tag_refspec, &tail, 0);
}
+ free_refs(remote_head);
} else {
int i;
for (i = 0; i < refspec->nr; i++)
@@ -542,9 +567,9 @@ static void write_followtags(const struct ref *refs, const char *msg)
continue;
if (ends_with(ref->name, "^{}"))
continue;
- if (!has_object_file_with_flags(&ref->old_oid,
- OBJECT_INFO_QUICK |
- OBJECT_INFO_SKIP_FETCH_OBJECT))
+ if (!repo_has_object_file_with_flags(the_repository, &ref->old_oid,
+ OBJECT_INFO_QUICK |
+ OBJECT_INFO_SKIP_FETCH_OBJECT))
continue;
update_ref(msg, ref->name, &ref->old_oid, NULL, 0,
UPDATE_REFS_DIE_ON_ERR);
@@ -608,7 +633,7 @@ static void update_remote_refs(const struct ref *refs,
}
static void update_head(const struct ref *our, const struct ref *remote,
- const char *msg)
+ const char *unborn, const char *msg)
{
const char *head;
if (our && skip_prefix(our->name, "refs/heads/", &head)) {
@@ -634,14 +659,23 @@ static void update_head(const struct ref *our, const struct ref *remote,
*/
update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
UPDATE_REFS_DIE_ON_ERR);
+ } else if (unborn && skip_prefix(unborn, "refs/heads/", &head)) {
+ /*
+ * Unborn head from remote; same as "our" case above except
+ * that we have no ref to update.
+ */
+ if (create_symref("HEAD", unborn, NULL) < 0)
+ die(_("unable to update HEAD"));
+ if (!option_bare)
+ install_branch_config(0, head, remote_name, unborn);
}
}
static int git_sparse_checkout_init(const char *repo)
{
- struct strvec argv = STRVEC_INIT;
+ struct child_process cmd = CHILD_PROCESS_INIT;
int result = 0;
- strvec_pushl(&argv, "-C", repo, "sparse-checkout", "set", NULL);
+ strvec_pushl(&cmd.args, "-C", repo, "sparse-checkout", "set", NULL);
/*
* We must apply the setting in the current process
@@ -649,12 +683,12 @@ static int git_sparse_checkout_init(const char *repo)
*/
core_apply_sparse_checkout = 1;
- if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
+ cmd.git_cmd = 1;
+ if (run_command(&cmd)) {
error(_("failed to initialize sparse-checkout"));
result = 1;
}
- strvec_clear(&argv);
return result;
}
@@ -674,7 +708,7 @@ static int checkout(int submodule_progress, int filter_submodules)
head = resolve_refdup("HEAD", RESOLVE_REF_READING, &oid, NULL);
if (!head) {
warning(_("remote HEAD refers to nonexistent ref, "
- "unable to checkout.\n"));
+ "unable to checkout"));
return 0;
}
if (!strcmp(head, "HEAD")) {
@@ -689,7 +723,7 @@ static int checkout(int submodule_progress, int filter_submodules)
/* We need to be in the new work tree for the checkout */
setup_work_tree();
- hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
memset(&opts, 0, sizeof opts);
opts.update = 1;
@@ -719,43 +753,45 @@ static int checkout(int submodule_progress, int filter_submodules)
oid_to_hex(&oid), "1", NULL);
if (!err && (option_recurse_submodules.nr > 0)) {
- struct strvec args = STRVEC_INIT;
- strvec_pushl(&args, "submodule", "update", "--require-init", "--recursive", NULL);
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ strvec_pushl(&cmd.args, "submodule", "update", "--require-init",
+ "--recursive", NULL);
if (option_shallow_submodules == 1)
- strvec_push(&args, "--depth=1");
+ strvec_push(&cmd.args, "--depth=1");
if (max_jobs != -1)
- strvec_pushf(&args, "--jobs=%d", max_jobs);
+ strvec_pushf(&cmd.args, "--jobs=%d", max_jobs);
if (submodule_progress)
- strvec_push(&args, "--progress");
+ strvec_push(&cmd.args, "--progress");
if (option_verbosity < 0)
- strvec_push(&args, "--quiet");
+ strvec_push(&cmd.args, "--quiet");
if (option_remote_submodules) {
- strvec_push(&args, "--remote");
- strvec_push(&args, "--no-fetch");
+ strvec_push(&cmd.args, "--remote");
+ strvec_push(&cmd.args, "--no-fetch");
}
if (filter_submodules && filter_options.choice)
- strvec_pushf(&args, "--filter=%s",
+ strvec_pushf(&cmd.args, "--filter=%s",
expand_list_objects_filter_spec(&filter_options));
if (option_single_branch >= 0)
- strvec_push(&args, option_single_branch ?
+ strvec_push(&cmd.args, option_single_branch ?
"--single-branch" :
"--no-single-branch");
- err = run_command_v_opt(args.v, RUN_GIT_CMD);
- strvec_clear(&args);
+ cmd.git_cmd = 1;
+ err = run_command(&cmd);
}
return err;
}
-static int git_clone_config(const char *k, const char *v, void *cb)
+static int git_clone_config(const char *k, const char *v,
+ const struct config_context *ctx, void *cb)
{
if (!strcmp(k, "clone.defaultremotename")) {
free(remote_name);
@@ -766,17 +802,19 @@ static int git_clone_config(const char *k, const char *v, void *cb)
if (!strcmp(k, "clone.filtersubmodules"))
config_filter_submodules = git_config_bool(k, v);
- return git_default_config(k, v, cb);
+ return git_default_config(k, v, ctx, cb);
}
-static int write_one_config(const char *key, const char *value, void *data)
+static int write_one_config(const char *key, const char *value,
+ const struct config_context *ctx,
+ void *data)
{
/*
* give git_clone_config a chance to write config values back to the
* environment, since git_config_set_multivar_gently only deals with
* config-file writes
*/
- int apply_failed = git_clone_config(key, value, data);
+ int apply_failed = git_clone_config(key, value, ctx, data);
if (apply_failed)
return apply_failed;
@@ -850,11 +888,15 @@ static void write_refspec_config(const char *src_ref_prefix,
static void dissociate_from_references(void)
{
- static const char* argv[] = { "repack", "-a", "-d", NULL };
char *alternates = git_pathdup("objects/info/alternates");
if (!access(alternates, F_OK)) {
- if (run_command_v_opt(argv, RUN_GIT_CMD|RUN_COMMAND_NO_STDIN))
+ struct child_process cmd = CHILD_PROCESS_INIT;
+
+ cmd.git_cmd = 1;
+ cmd.no_stdin = 1;
+ strvec_pushl(&cmd.args, "repack", "-a", "-d", NULL);
+ if (run_command(&cmd))
die(_("cannot repack to clean up"));
if (unlink(alternates) && errno != ENOENT)
die_errno(_("cannot unlink temporary alternates file"));
@@ -873,11 +915,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
int is_bundle = 0, is_local;
int reject_shallow = 0;
const char *repo_name, *repo, *work_tree, *git_dir;
+ char *repo_to_free = NULL;
char *path = NULL, *dir, *display_repo = NULL;
int dest_exists, real_dest_exists = 0;
const struct ref *refs, *remote_head;
struct ref *remote_head_points_at = NULL;
const struct ref *our_head_points_at;
+ char *unborn_head = NULL;
struct ref *mapped_refs = NULL;
const struct ref *ref;
struct strbuf key = STRBUF_INIT;
@@ -888,6 +932,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
int err = 0, complete_refs_before_fetch = 1;
int submodule_progress;
int filter_submodules = 0;
+ int hash_algo;
+ const int do_not_override_repo_unix_permissions = -1;
struct transport_ls_refs_options transport_ls_refs_options =
TRANSPORT_LS_REFS_OPTIONS_INIT;
@@ -916,20 +962,20 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
option_bare = 1;
if (option_bare) {
- if (option_origin)
- die(_("options '%s' and '%s %s' cannot be used together"),
- "--bare", "--origin", option_origin);
if (real_git_dir)
die(_("options '%s' and '%s' cannot be used together"), "--bare", "--separate-git-dir");
option_no_checkout = 1;
}
+ if (bundle_uri && deepen)
+ die(_("--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-exclude"));
+
repo_name = argv[0];
path = get_repo_path(repo_name, &is_bundle);
if (path) {
FREE_AND_NULL(path);
- repo = absolute_pathdup(repo_name);
+ repo = repo_to_free = absolute_pathdup(repo_name);
} else if (strchr(repo_name, ':')) {
repo = repo_name;
display_repo = transport_anonymize_url(repo);
@@ -1055,7 +1101,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
- INIT_DB_QUIET);
+ do_not_override_repo_unix_permissions, INIT_DB_QUIET);
if (real_git_dir) {
free((char *)git_dir);
@@ -1108,12 +1154,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
* apply the remote name provided by --origin only after this second
* call to git_config, to ensure it overrides all config-based values.
*/
- if (option_origin != NULL) {
+ if (option_origin) {
free(remote_name);
remote_name = xstrdup(option_origin);
}
- if (remote_name == NULL)
+ if (!remote_name)
remote_name = xstrdup("origin");
if (!valid_remote_name(remote_name))
@@ -1223,6 +1269,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (transport->smart_options && !deepen && !filter_options.choice)
transport->smart_options->check_self_contained_and_connected = 1;
+ /*
+ * Before fetching from the remote, download and install bundle
+ * data from the --bundle-uri option.
+ */
+ if (bundle_uri) {
+ int has_heuristic = 0;
+
+ /* At this point, we need the_repository to match the cloned repo. */
+ if (repo_init(the_repository, git_dir, work_tree))
+ warning(_("failed to initialize the repo, skipping bundle URI"));
+ else if (fetch_bundle_uri(the_repository, bundle_uri, &has_heuristic))
+ warning(_("failed to fetch objects from bundle URI '%s'"),
+ bundle_uri);
+ else if (has_heuristic)
+ git_config_set_gently("fetch.bundleuri", bundle_uri);
+ }
strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
refspec_ref_prefixes(&remote->fetch,
@@ -1239,15 +1301,36 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (refs)
mapped_refs = wanted_peer_refs(refs, &remote->fetch);
- if (mapped_refs) {
- int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
+ if (!bundle_uri) {
+ /*
+ * Populate transport->got_remote_bundle_uri and
+ * transport->bundle_uri. We might get nothing.
+ */
+ transport_get_remote_bundle_uri(transport);
+
+ if (transport->bundles &&
+ hashmap_get_size(&transport->bundles->bundles)) {
+ /* At this point, we need the_repository to match the cloned repo. */
+ if (repo_init(the_repository, git_dir, work_tree))
+ warning(_("failed to initialize the repo, skipping bundle URI"));
+ else if (fetch_bundle_list(the_repository,
+ transport->bundles))
+ warning(_("failed to fetch advertised bundles"));
+ } else {
+ clear_bundle_list(transport->bundles);
+ FREE_AND_NULL(transport->bundles);
+ }
+ }
/*
* Now that we know what algorithm the remote side is using,
* let's set ours to the same thing.
*/
- initialize_repository_version(hash_algo, 1);
- repo_set_hash_algo(the_repository, hash_algo);
+ hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
+ initialize_repository_version(hash_algo, 1);
+ repo_set_hash_algo(the_repository, hash_algo);
+
+ if (mapped_refs) {
/*
* transport_get_remote_refs() may return refs with null sha-1
* in mapped_refs (see struct transport->get_refs_list
@@ -1268,51 +1351,49 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (transport_fetch_refs(transport, mapped_refs))
die(_("remote transport reported error"));
}
-
- remote_head = find_ref_by_name(refs, "HEAD");
- remote_head_points_at =
- guess_remote_head(remote_head, mapped_refs, 0);
-
- if (option_branch) {
- our_head_points_at =
- find_remote_branch(mapped_refs, option_branch);
-
- if (!our_head_points_at)
- die(_("Remote branch %s not found in upstream %s"),
- option_branch, remote_name);
- }
- else
- our_head_points_at = remote_head_points_at;
}
- else {
- const char *branch;
- const char *ref;
- char *ref_free = NULL;
- if (option_branch)
- die(_("Remote branch %s not found in upstream %s"),
- option_branch, remote_name);
+ remote_head = find_ref_by_name(refs, "HEAD");
+ remote_head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
- warning(_("You appear to have cloned an empty repository."));
+ if (option_branch) {
+ our_head_points_at = find_remote_branch(mapped_refs, option_branch);
+ if (!our_head_points_at)
+ die(_("Remote branch %s not found in upstream %s"),
+ option_branch, remote_name);
+ } else if (remote_head_points_at) {
+ our_head_points_at = remote_head_points_at;
+ } else if (remote_head) {
our_head_points_at = NULL;
- remote_head_points_at = NULL;
- remote_head = NULL;
- option_no_checkout = 1;
+ } else {
+ const char *branch;
+
+ if (!mapped_refs) {
+ warning(_("You appear to have cloned an empty repository."));
+ option_no_checkout = 1;
+ }
if (transport_ls_refs_options.unborn_head_target &&
skip_prefix(transport_ls_refs_options.unborn_head_target,
"refs/heads/", &branch)) {
- ref = transport_ls_refs_options.unborn_head_target;
- create_symref("HEAD", ref, reflog_msg.buf);
+ unborn_head = xstrdup(transport_ls_refs_options.unborn_head_target);
} else {
branch = git_default_branch_name(0);
- ref_free = xstrfmt("refs/heads/%s", branch);
- ref = ref_free;
+ unborn_head = xstrfmt("refs/heads/%s", branch);
}
- if (!option_bare)
- install_branch_config(0, branch, remote_name, ref);
- free(ref_free);
+ /*
+ * We may have selected a local default branch name "foo",
+ * and even though the remote's HEAD does not point there,
+ * it may still have a "foo" branch. If so, set it up so
+ * that we can follow the usual checkout code later.
+ *
+ * Note that for an empty repo we'll already have set
+ * option_no_checkout above, which would work against us here.
+ * But for an empty repo, find_remote_branch() can never find
+ * a match.
+ */
+ our_head_points_at = find_remote_branch(mapped_refs, branch);
}
write_refspec_config(src_ref_prefix, our_head_points_at,
@@ -1332,7 +1413,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
branch_top.buf, reflog_msg.buf, transport,
!is_local);
- update_head(our_head_points_at, remote_head, reflog_msg.buf);
+ update_head(our_head_points_at, remote_head, unborn_head, reflog_msg.buf);
/*
* We want to show progress for recursive submodule clones iff
@@ -1359,9 +1440,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
strbuf_release(&key);
free_refs(mapped_refs);
free_refs(remote_head_points_at);
+ free(unborn_head);
free(dir);
free(path);
- UNLEAK(repo);
+ free(repo_to_free);
junk_mode = JUNK_LEAVE_ALL;
transport_ls_refs_options_release(&transport_ls_refs_options);
diff --git a/builtin/column.c b/builtin/column.c
index 158fdf53d9..a83be8bc99 100644
--- a/builtin/column.c
+++ b/builtin/column.c
@@ -1,6 +1,6 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
+#include "gettext.h"
#include "strbuf.h"
#include "parse-options.h"
#include "string-list.h"
@@ -12,7 +12,8 @@ static const char * const builtin_column_usage[] = {
};
static unsigned int colopts;
-static int column_config(const char *var, const char *value, void *cb)
+static int column_config(const char *var, const char *value,
+ const struct config_context *ctx UNUSED, void *cb)
{
return git_column_config(var, value, cb, &colopts);
}
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 51c4040ea6..c88389df24 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -1,22 +1,28 @@
#include "builtin.h"
+#include "commit.h"
#include "config.h"
#include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "lockfile.h"
#include "parse-options.h"
#include "repository.h"
#include "commit-graph.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "progress.h"
+#include "replace-object.h"
#include "tag.h"
+#include "trace2.h"
#define BUILTIN_COMMIT_GRAPH_VERIFY_USAGE \
- N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]")
+ N_("git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]")
#define BUILTIN_COMMIT_GRAPH_WRITE_USAGE \
- N_("git commit-graph write [--object-dir <objdir>] [--append] " \
- "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] " \
- "[--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress] " \
- "<split options>")
+ N_("git commit-graph write [--object-dir <dir>] [--append]\n" \
+ " [--split[=<strategy>]] [--reachable | --stdin-packs | --stdin-commits]\n" \
+ " [--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress]\n" \
+ " <split options>")
static const char * builtin_commit_graph_verify_usage[] = {
BUILTIN_COMMIT_GRAPH_VERIFY_USAGE,
@@ -58,7 +64,7 @@ static struct option *add_common_options(struct option *to)
return parse_options_concat(common_opts, to);
}
-static int graph_verify(int argc, const char **argv)
+static int graph_verify(int argc, const char **argv, const char *prefix)
{
struct commit_graph *graph = NULL;
struct object_directory *odb = NULL;
@@ -67,6 +73,7 @@ static int graph_verify(int argc, const char **argv)
int fd;
struct stat st;
int flags = 0;
+ int ret;
static struct option builtin_commit_graph_verify_options[] = {
OPT_BOOL(0, "shallow", &opts.shallow,
@@ -80,7 +87,7 @@ static int graph_verify(int argc, const char **argv)
trace2_cmd_mode("verify");
opts.progress = isatty(2);
- argc = parse_options(argc, argv, NULL,
+ argc = parse_options(argc, argv, prefix,
options,
builtin_commit_graph_verify_usage, 0);
if (argc)
@@ -111,8 +118,9 @@ static int graph_verify(int argc, const char **argv)
if (!graph)
return !!open_ok;
- UNLEAK(graph);
- return verify_commit_graph(the_repository, graph, flags);
+ ret = verify_commit_graph(the_repository, graph, flags);
+ free_commit_graph(graph);
+ return ret;
}
extern int read_replace_refs;
@@ -179,10 +187,11 @@ static int write_option_max_new_filters(const struct option *opt,
}
static int git_commit_graph_write_config(const char *var, const char *value,
- void *cb)
+ const struct config_context *ctx,
+ void *cb UNUSED)
{
if (!strcmp(var, "commitgraph.maxnewfilters"))
- write_opts.max_new_filters = git_config_int(var, value);
+ write_opts.max_new_filters = git_config_int(var, value, ctx->kvi);
/*
* No need to fall-back to 'git_default_config', since this was already
* called in 'cmd_commit_graph()'.
@@ -190,7 +199,7 @@ static int git_commit_graph_write_config(const char *var, const char *value,
return 0;
}
-static int graph_write(int argc, const char **argv)
+static int graph_write(int argc, const char **argv, const char *prefix)
{
struct string_list pack_indexes = STRING_LIST_INIT_DUP;
struct strbuf buf = STRBUF_INIT;
@@ -241,7 +250,7 @@ static int graph_write(int argc, const char **argv)
git_config(git_commit_graph_write_config, &opts);
- argc = parse_options(argc, argv, NULL,
+ argc = parse_options(argc, argv, prefix,
options,
builtin_commit_graph_write_usage, 0);
if (argc)
@@ -267,8 +276,8 @@ static int graph_write(int argc, const char **argv)
if (opts.reachable) {
if (write_commit_graph_reachable(odb, flags, &write_opts))
- return 1;
- return 0;
+ result = 1;
+ goto cleanup;
}
if (opts.stdin_packs) {
@@ -307,26 +316,22 @@ cleanup:
int cmd_commit_graph(int argc, const char **argv, const char *prefix)
{
- struct option *builtin_commit_graph_options = common_opts;
+ parse_opt_subcommand_fn *fn = NULL;
+ struct option builtin_commit_graph_options[] = {
+ OPT_SUBCOMMAND("verify", &fn, graph_verify),
+ OPT_SUBCOMMAND("write", &fn, graph_write),
+ OPT_END(),
+ };
+ struct option *options = parse_options_concat(builtin_commit_graph_options, common_opts);
git_config(git_default_config, NULL);
- argc = parse_options(argc, argv, prefix,
- builtin_commit_graph_options,
- builtin_commit_graph_usage,
- PARSE_OPT_STOP_AT_NON_OPTION);
- if (!argc)
- goto usage;
- read_replace_refs = 0;
+ disable_replace_refs();
save_commit_buffer = 0;
- if (!strcmp(argv[0], "verify"))
- return graph_verify(argc, argv);
- else if (argc && !strcmp(argv[0], "write"))
- return graph_write(argc, argv);
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_commit_graph_usage, 0);
+ FREE_AND_NULL(options);
- error(_("unrecognized subcommand: %s"), argv[0]);
-usage:
- usage_with_options(builtin_commit_graph_usage,
- builtin_commit_graph_options);
+ return fn(argc, argv, prefix);
}
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 63ea322933..02625e7176 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -3,20 +3,23 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
-#include "object-store.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-store-ll.h"
#include "repository.h"
#include "commit.h"
#include "tree.h"
-#include "builtin.h"
#include "utf8.h"
#include "gpg-interface.h"
#include "parse-options.h"
static const char * const commit_tree_usage[] = {
- N_("git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] "
- "[(-F <file>)...] <tree>"),
+ N_("git commit-tree <tree> [(-p <parent>)...]"),
+ N_("git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+ " [(-F <file>)...] <tree>"),
NULL
};
@@ -36,14 +39,6 @@ static void new_parent(struct commit *parent, struct commit_list **parents_p)
commit_list_insert(parent, parents_p);
}
-static int commit_tree_config(const char *var, const char *value, void *cb)
-{
- int status = git_gpg_config(var, value, NULL);
- if (status)
- return status;
- return git_default_config(var, value, cb);
-}
-
static int parse_parent_arg_callback(const struct option *opt,
const char *arg, int unset)
{
@@ -52,7 +47,7 @@ static int parse_parent_arg_callback(const struct option *opt,
BUG_ON_OPT_NEG_NOARG(unset, arg);
- if (get_oid_commit(arg, &oid))
+ if (repo_get_oid_commit(the_repository, arg, &oid))
die(_("not a valid object name %s"), arg);
assert_oid_type(&oid, OBJ_COMMIT);
@@ -120,7 +115,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
OPT_END()
};
- git_config(commit_tree_config, NULL);
+ git_config(git_default_config, NULL);
if (argc < 2 || !strcmp(argv[1], "-h"))
usage_with_options(commit_tree_usage, options);
@@ -130,7 +125,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
if (argc != 1)
die(_("must give exactly one tree"));
- if (get_oid_tree(argv[0], &tree_oid))
+ if (repo_get_oid_tree(the_repository, argv[0], &tree_oid))
die(_("not a valid object name %s"), argv[0]);
if (!buffer.len) {
diff --git a/builtin/commit.c b/builtin/commit.c
index 009a1de0a3..7da5f92448 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -5,17 +5,20 @@
* Based on git-commit.sh by Junio C Hamano and Linus Torvalds
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
+#include "advice.h"
#include "config.h"
#include "lockfile.h"
#include "cache-tree.h"
#include "color.h"
#include "dir.h"
-#include "builtin.h"
+#include "editor.h"
+#include "environment.h"
#include "diff.h"
#include "diffcore.h"
#include "commit.h"
+#include "gettext.h"
#include "revision.h"
#include "wt-status.h"
#include "run-command.h"
@@ -24,7 +27,11 @@
#include "log-tree.h"
#include "strbuf.h"
#include "utf8.h"
+#include "object-name.h"
#include "parse-options.h"
+#include "path.h"
+#include "preload-index.h"
+#include "read-cache.h"
#include "string-list.h"
#include "rerere.h"
#include "unpack-trees.h"
@@ -33,6 +40,7 @@
#include "gpg-interface.h"
#include "column.h"
#include "sequencer.h"
+#include "sparse-index.h"
#include "mailmap.h"
#include "help.h"
#include "commit-reach.h"
@@ -40,12 +48,19 @@
#include "pretty.h"
static const char * const builtin_commit_usage[] = {
- N_("git commit [<options>] [--] <pathspec>..."),
+ N_("git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+ " [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|reword):]<commit>)]\n"
+ " [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
+ " [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+ " [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+ " [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+ " [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+ " [--] [<pathspec>...]"),
NULL
};
static const char * const builtin_status_usage[] = {
- N_("git status [<options>] [--] <pathspec>..."),
+ N_("git status [<options>] [--] [<pathspec>...]"),
NULL
};
@@ -139,7 +154,7 @@ static int opt_pass_trailer(const struct option *opt, const char *arg, int unset
{
BUG_ON_OPT_NEG(unset);
- strvec_pushl(&trailer_args, "--trailer", arg, NULL);
+ strvec_pushl(opt->value, "--trailer", arg, NULL);
return 0;
}
@@ -265,8 +280,8 @@ static int list_paths(struct string_list *list, const char *with_tree,
/* TODO: audit for interaction with sparse-index. */
ensure_full_index(&the_index);
- for (i = 0; i < active_nr; i++) {
- const struct cache_entry *ce = active_cache[i];
+ for (i = 0; i < the_index.cache_nr; i++) {
+ const struct cache_entry *ce = the_index.cache[i];
struct string_list_item *item;
if (ce->ce_flags & CE_UPDATE)
@@ -295,10 +310,10 @@ static void add_remove_files(struct string_list *list)
continue;
if (!lstat(p->string, &st)) {
- if (add_to_cache(p->string, &st, 0))
+ if (add_to_index(&the_index, p->string, &st, 0))
die(_("updating files failed"));
} else
- remove_file_from_cache(p->string);
+ remove_file_from_index(&the_index, p->string);
}
}
@@ -309,7 +324,7 @@ static void create_base_index(const struct commit *current_head)
struct tree_desc t;
if (!current_head) {
- discard_cache();
+ discard_index(&the_index);
return;
}
@@ -336,7 +351,7 @@ static void refresh_cache_or_die(int refresh_flags)
* refresh_flags contains REFRESH_QUIET, so the only errors
* are for unmerged entries.
*/
- if (refresh_cache(refresh_flags | REFRESH_IN_PORCELAIN))
+ if (refresh_index(&the_index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL))
die_resolve_conflict("commit");
}
@@ -375,12 +390,13 @@ static const char *prepare_index(const char **argv, const char *prefix,
(!amend || (fixup_message && strcmp(fixup_prefix, "amend"))))))
die(_("No paths with --include/--only does not make sense."));
- if (read_cache_preload(&pathspec) < 0)
+ if (repo_read_index_preload(the_repository, &pathspec, 0) < 0)
die(_("index file corrupt"));
if (interactive) {
char *old_index_env = NULL, *old_repo_index_file;
- hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &index_lock,
+ LOCK_DIE_ON_ERROR);
refresh_cache_or_die(refresh_flags);
@@ -403,9 +419,10 @@ static const char *prepare_index(const char **argv, const char *prefix,
unsetenv(INDEX_ENVIRONMENT);
FREE_AND_NULL(old_index_env);
- discard_cache();
- read_cache_from(get_lock_file_path(&index_lock));
- if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
+ discard_index(&the_index);
+ read_index_from(&the_index, get_lock_file_path(&index_lock),
+ get_git_dir());
+ if (cache_tree_update(&the_index, WRITE_TREE_SILENT) == 0) {
if (reopen_lock_file(&index_lock) < 0)
die(_("unable to write index file"));
if (write_locked_index(&the_index, &index_lock, 0))
@@ -431,10 +448,12 @@ static const char *prepare_index(const char **argv, const char *prefix,
* (B) on failure, rollback the real index.
*/
if (all || (also && pathspec.nr)) {
- hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
- add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
+ repo_hold_locked_index(the_repository, &index_lock,
+ LOCK_DIE_ON_ERROR);
+ add_files_to_cache(the_repository, also ? prefix : NULL,
+ &pathspec, 0, 0);
refresh_cache_or_die(refresh_flags);
- update_main_cache_tree(WRITE_TREE_SILENT);
+ cache_tree_update(&the_index, WRITE_TREE_SILENT);
if (write_locked_index(&the_index, &index_lock, 0))
die(_("unable to write new_index file"));
commit_style = COMMIT_NORMAL;
@@ -452,11 +471,12 @@ static const char *prepare_index(const char **argv, const char *prefix,
* We still need to refresh the index here.
*/
if (!only && !pathspec.nr) {
- hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &index_lock,
+ LOCK_DIE_ON_ERROR);
refresh_cache_or_die(refresh_flags);
- if (active_cache_changed
- || !cache_tree_fully_valid(active_cache_tree))
- update_main_cache_tree(WRITE_TREE_SILENT);
+ if (the_index.cache_changed
+ || !cache_tree_fully_valid(the_index.cache_tree))
+ cache_tree_update(&the_index, WRITE_TREE_SILENT);
if (write_locked_index(&the_index, &index_lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write new_index file"));
@@ -498,14 +518,14 @@ static const char *prepare_index(const char **argv, const char *prefix,
if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
exit(1);
- discard_cache();
- if (read_cache() < 0)
+ discard_index(&the_index);
+ if (repo_read_index(the_repository) < 0)
die(_("cannot read the index"));
- hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
add_remove_files(&partial);
- refresh_cache(REFRESH_QUIET);
- update_main_cache_tree(WRITE_TREE_SILENT);
+ refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+ cache_tree_update(&the_index, WRITE_TREE_SILENT);
if (write_locked_index(&the_index, &index_lock, 0))
die(_("unable to write new_index file"));
@@ -516,14 +536,14 @@ static const char *prepare_index(const char **argv, const char *prefix,
create_base_index(current_head);
add_remove_files(&partial);
- refresh_cache(REFRESH_QUIET);
+ refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
if (write_locked_index(&the_index, &false_lock, 0))
die(_("unable to write temporary index file"));
- discard_cache();
+ discard_index(&the_index);
ret = get_lock_file_path(&false_lock);
- read_cache_from(ret);
+ read_index_from(&the_index, ret, get_git_dir());
out:
string_list_clear(&partial, 0);
clear_pathspec(&pathspec);
@@ -546,7 +566,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
s->index_file = index_file;
s->fp = fp;
s->nowarn = nowarn;
- s->is_initial = get_oid(s->reference, &oid) ? 1 : 0;
+ s->is_initial = repo_get_oid(the_repository, s->reference, &oid) ? 1 : 0;
if (!s->is_initial)
oidcpy(&s->oid_commit, &oid);
s->status_format = status_format;
@@ -701,15 +721,15 @@ static void prepare_amend_commit(struct commit *commit, struct strbuf *sb,
{
const char *buffer, *subject, *fmt;
- buffer = get_commit_buffer(commit, NULL);
+ buffer = repo_get_commit_buffer(the_repository, commit, NULL);
find_commit_subject(buffer, &subject);
/*
* If we amend the 'amend!' commit then we don't want to
* duplicate the subject line.
*/
fmt = starts_with(subject, "amend!") ? "%b" : "%B";
- format_commit_message(commit, fmt, sb, ctx);
- unuse_commit_buffer(commit, buffer);
+ repo_format_commit_message(the_repository, commit, fmt, sb, ctx);
+ repo_unuse_commit_buffer(the_repository, commit, buffer);
}
static int prepare_to_commit(const char *index_file, const char *prefix,
@@ -747,10 +767,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
struct commit *c;
c = lookup_commit_reference_by_name(squash_message);
if (!c)
- die(_("could not lookup commit %s"), squash_message);
+ die(_("could not lookup commit '%s'"), squash_message);
ctx.output_encoding = get_commit_output_encoding();
- format_commit_message(c, "squash! %s\n\n", &sb,
- &ctx);
+ repo_format_commit_message(the_repository, c,
+ "squash! %s\n\n", &sb,
+ &ctx);
}
}
@@ -781,10 +802,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
char *fmt;
commit = lookup_commit_reference_by_name(fixup_commit);
if (!commit)
- die(_("could not lookup commit %s"), fixup_commit);
+ die(_("could not lookup commit '%s'"), fixup_commit);
ctx.output_encoding = get_commit_output_encoding();
fmt = xstrfmt("%s! %%s\n\n", fixup_prefix);
- format_commit_message(commit, fmt, &sb, &ctx);
+ repo_format_commit_message(the_repository, commit, fmt, &sb,
+ &ctx);
free(fmt);
hook_arg1 = "message";
@@ -861,7 +883,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
}
s->fp = fopen_for_writing(git_path_commit_editmsg());
- if (s->fp == NULL)
+ if (!s->fp)
die_errno(_("could not open '%s'"), git_path_commit_editmsg());
/* Ignore status.displayCommentPrefix: we do need comments in COMMIT_EDITMSG. */
@@ -875,7 +897,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
s->hints = 0;
if (clean_message_contents)
- strbuf_stripspace(&sb, 0);
+ strbuf_stripspace(&sb, '\0');
if (signoff)
append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0);
@@ -980,21 +1002,21 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
struct object_id oid;
const char *parent = "HEAD";
- if (!active_nr && read_cache() < 0)
+ if (!the_index.initialized && repo_read_index(the_repository) < 0)
die(_("Cannot read index"));
if (amend)
parent = "HEAD^1";
- if (get_oid(parent, &oid)) {
+ if (repo_get_oid(the_repository, parent, &oid)) {
int i, ita_nr = 0;
/* TODO: audit for interaction with sparse-index. */
ensure_full_index(&the_index);
- for (i = 0; i < active_nr; i++)
- if (ce_intent_to_add(active_cache[i]))
+ for (i = 0; i < the_index.cache_nr; i++)
+ if (ce_intent_to_add(the_index.cache[i]))
ita_nr++;
- committable = active_nr - ita_nr > 0;
+ committable = the_index.cache_nr - ita_nr > 0;
} else {
/*
* Unless the user did explicitly request a submodule
@@ -1022,7 +1044,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
struct child_process run_trailer = CHILD_PROCESS_INIT;
strvec_pushl(&run_trailer.args, "interpret-trailers",
- "--in-place", git_path_commit_editmsg(), NULL);
+ "--in-place", "--no-divider",
+ git_path_commit_editmsg(), NULL);
strvec_pushv(&run_trailer.args, trailer_args.v);
run_trailer.git_cmd = 1;
if (run_command(&run_trailer))
@@ -1061,11 +1084,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
* and could have updated it. We must do this before we invoke
* the editor and after we invoke run_status above.
*/
- discard_cache();
+ discard_index(&the_index);
}
- read_cache_from(index_file);
+ read_index_from(&the_index, index_file, get_git_dir());
- if (update_main_cache_tree(0)) {
+ if (cache_tree_update(&the_index, 0)) {
error(_("Error building trees"));
return 0;
}
@@ -1100,7 +1123,6 @@ static const char *find_author_by_nickname(const char *name)
struct rev_info revs;
struct commit *commit;
struct strbuf buf = STRBUF_INIT;
- struct string_list mailmap = STRING_LIST_INIT_NODUP;
const char *av[20];
int ac = 0;
@@ -1111,7 +1133,8 @@ static const char *find_author_by_nickname(const char *name)
av[++ac] = buf.buf;
av[++ac] = NULL;
setup_revisions(ac, av, &revs, NULL);
- revs.mailmap = &mailmap;
+ revs.mailmap = xmalloc(sizeof(struct string_list));
+ string_list_init_nodup(revs.mailmap);
read_mailmap(revs.mailmap);
if (prepare_revision_walk(&revs))
@@ -1121,8 +1144,9 @@ static const char *find_author_by_nickname(const char *name)
struct pretty_print_context ctx = {0};
ctx.date_mode.type = DATE_NORMAL;
strbuf_release(&buf);
- format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
- clear_mailmap(&mailmap);
+ repo_format_commit_message(the_repository, commit,
+ "%aN <%aE>", &buf, &ctx);
+ release_revisions(&revs);
return strbuf_detach(&buf, NULL);
}
die(_("--author '%s' is not 'Name <email>' and matches no existing author"), name);
@@ -1167,9 +1191,9 @@ static const char *read_commit_message(const char *name)
commit = lookup_commit_reference_by_name(name);
if (!commit)
- die(_("could not lookup commit %s"), name);
+ die(_("could not lookup commit '%s'"), name);
out_enc = get_commit_output_encoding();
- return logmsg_reencode(commit, NULL, out_enc);
+ return repo_logmsg_reencode(the_repository, commit, NULL, out_enc);
}
/*
@@ -1383,7 +1407,8 @@ static int parse_status_slot(const char *slot)
return LOOKUP_CONFIG(color_status_slots, slot);
}
-static int git_status_config(const char *k, const char *v, void *cb)
+static int git_status_config(const char *k, const char *v,
+ const struct config_context *ctx, void *cb)
{
struct wt_status *s = cb;
const char *slot_name;
@@ -1392,7 +1417,8 @@ static int git_status_config(const char *k, const char *v, void *cb)
return git_column_config(k, v, "status", &s->colopts);
if (!strcmp(k, "status.submodulesummary")) {
int is_bool;
- s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
+ s->submodule_summary = git_config_bool_or_int(k, v, ctx->kvi,
+ &is_bool);
if (is_bool && s->submodule_summary)
s->submodule_summary = -1;
return 0;
@@ -1452,11 +1478,11 @@ static int git_status_config(const char *k, const char *v, void *cb)
}
if (!strcmp(k, "diff.renamelimit")) {
if (s->rename_limit == -1)
- s->rename_limit = git_config_int(k, v);
+ s->rename_limit = git_config_int(k, v, ctx->kvi);
return 0;
}
if (!strcmp(k, "status.renamelimit")) {
- s->rename_limit = git_config_int(k, v);
+ s->rename_limit = git_config_int(k, v, ctx->kvi);
return 0;
}
if (!strcmp(k, "diff.renames")) {
@@ -1468,7 +1494,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
s->detect_rename = git_config_rename(k, v);
return 0;
}
- return git_diff_ui_config(k, v, NULL);
+ return git_diff_ui_config(k, v, ctx, NULL);
}
int cmd_status(int argc, const char **argv, const char *prefix)
@@ -1549,11 +1575,11 @@ int cmd_status(int argc, const char **argv, const char *prefix)
&s.pathspec, NULL, NULL);
if (use_optional_locks())
- fd = hold_locked_index(&index_lock, 0);
+ fd = repo_hold_locked_index(the_repository, &index_lock, 0);
else
fd = -1;
- s.is_initial = get_oid(s.reference, &oid) ? 1 : 0;
+ s.is_initial = repo_get_oid(the_repository, s.reference, &oid) ? 1 : 0;
if (!s.is_initial)
oidcpy(&s.oid_commit, &oid);
@@ -1583,10 +1609,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)
return 0;
}
-static int git_commit_config(const char *k, const char *v, void *cb)
+static int git_commit_config(const char *k, const char *v,
+ const struct config_context *ctx, void *cb)
{
struct wt_status *s = cb;
- int status;
if (!strcmp(k, "commit.template"))
return git_config_pathname(&template_file, k, v);
@@ -1602,14 +1628,12 @@ static int git_commit_config(const char *k, const char *v, void *cb)
}
if (!strcmp(k, "commit.verbose")) {
int is_bool;
- config_commit_verbose = git_config_bool_or_int(k, v, &is_bool);
+ config_commit_verbose = git_config_bool_or_int(k, v, ctx->kvi,
+ &is_bool);
return 0;
}
- status = git_gpg_config(k, v, NULL);
- if (status)
- return status;
- return git_status_config(k, v, s);
+ return git_status_config(k, v, ctx, s);
}
int cmd_commit(int argc, const char **argv, const char *prefix)
@@ -1633,7 +1657,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword specified commit")),
OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
- OPT_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
+ OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
@@ -1687,6 +1711,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
struct commit *current_head = NULL;
struct commit_extra_header *extra = NULL;
struct strbuf err = STRBUF_INIT;
+ int ret = 0;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_commit_usage, builtin_commit_options);
@@ -1699,11 +1724,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
s.colopts = 0;
- if (get_oid("HEAD", &oid))
+ if (repo_get_oid(the_repository, "HEAD", &oid))
current_head = NULL;
else {
current_head = lookup_commit_or_die(&oid, "HEAD");
- if (parse_commit(current_head))
+ if (repo_parse_commit(the_repository, current_head))
die(_("could not parse HEAD commit"));
}
verbose = -1; /* unspecified */
@@ -1721,8 +1746,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
running hooks, writing the trees, and interacting with the user. */
if (!prepare_to_commit(index_file, prefix,
current_head, &s, &author_ident)) {
+ ret = 1;
rollback_index_files();
- return 1;
+ goto cleanup;
}
/* Determine parents */
@@ -1814,13 +1840,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
append_merge_tag_headers(parents, &tail);
}
- if (commit_tree_extended(sb.buf, sb.len, &active_cache_tree->oid,
+ if (commit_tree_extended(sb.buf, sb.len, &the_index.cache_tree->oid,
parents, &oid, author_ident.buf, NULL,
sign_commit, extra)) {
rollback_index_files();
die(_("failed to write commit object"));
}
- strbuf_release(&author_ident);
free_commit_extra_headers(extra);
if (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,
@@ -1862,7 +1887,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
apply_autostash(git_path_merge_autostash(the_repository));
- UNLEAK(err);
- UNLEAK(sb);
- return 0;
+cleanup:
+ strbuf_release(&author_ident);
+ strbuf_release(&err);
+ strbuf_release(&sb);
+ return ret;
}
diff --git a/builtin/config.c b/builtin/config.c
index e7b88a9c08..11a4d4ef14 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -1,10 +1,18 @@
#include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
#include "config.h"
#include "color.h"
+#include "editor.h"
+#include "environment.h"
+#include "repository.h"
+#include "gettext.h"
+#include "ident.h"
#include "parse-options.h"
#include "urlmatch.h"
+#include "path.h"
#include "quote.h"
+#include "setup.h"
+#include "strbuf.h"
#include "worktree.h"
static const char *const builtin_config_usage[] = {
@@ -185,36 +193,42 @@ static void check_argc(int argc, int min, int max)
usage_builtin_config();
}
-static void show_config_origin(struct strbuf *buf)
+static void show_config_origin(const struct key_value_info *kvi,
+ struct strbuf *buf)
{
const char term = end_nul ? '\0' : '\t';
- strbuf_addstr(buf, current_config_origin_type());
+ strbuf_addstr(buf, config_origin_type_name(kvi->origin_type));
strbuf_addch(buf, ':');
if (end_nul)
- strbuf_addstr(buf, current_config_name());
+ strbuf_addstr(buf, kvi->filename ? kvi->filename : "");
else
- quote_c_style(current_config_name(), buf, NULL, 0);
+ quote_c_style(kvi->filename ? kvi->filename : "", buf, NULL, 0);
strbuf_addch(buf, term);
}
-static void show_config_scope(struct strbuf *buf)
+static void show_config_scope(const struct key_value_info *kvi,
+ struct strbuf *buf)
{
const char term = end_nul ? '\0' : '\t';
- const char *scope = config_scope_name(current_config_scope());
+ const char *scope = config_scope_name(kvi->scope);
strbuf_addstr(buf, N_(scope));
strbuf_addch(buf, term);
}
-static int show_all_config(const char *key_, const char *value_, void *cb)
+static int show_all_config(const char *key_, const char *value_,
+ const struct config_context *ctx,
+ void *cb UNUSED)
{
+ const struct key_value_info *kvi = ctx->kvi;
+
if (show_origin || show_scope) {
struct strbuf buf = STRBUF_INIT;
if (show_scope)
- show_config_scope(&buf);
+ show_config_scope(kvi, &buf);
if (show_origin)
- show_config_origin(&buf);
+ show_config_origin(kvi, &buf);
/* Use fwrite as "buf" can contain \0's if "end_null" is set. */
fwrite(buf.buf, 1, buf.len, stdout);
strbuf_release(&buf);
@@ -232,12 +246,13 @@ struct strbuf_list {
int alloc;
};
-static int format_config(struct strbuf *buf, const char *key_, const char *value_)
+static int format_config(struct strbuf *buf, const char *key_,
+ const char *value_, const struct key_value_info *kvi)
{
if (show_scope)
- show_config_scope(buf);
+ show_config_scope(kvi, buf);
if (show_origin)
- show_config_origin(buf);
+ show_config_origin(kvi, buf);
if (show_keys)
strbuf_addstr(buf, key_);
if (!omit_values) {
@@ -246,13 +261,14 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
if (type == TYPE_INT)
strbuf_addf(buf, "%"PRId64,
- git_config_int64(key_, value_ ? value_ : ""));
+ git_config_int64(key_, value_ ? value_ : "", kvi));
else if (type == TYPE_BOOL)
strbuf_addstr(buf, git_config_bool(key_, value_) ?
"true" : "false");
else if (type == TYPE_BOOL_OR_INT) {
int is_bool, v;
- v = git_config_bool_or_int(key_, value_, &is_bool);
+ v = git_config_bool_or_int(key_, value_, kvi,
+ &is_bool);
if (is_bool)
strbuf_addstr(buf, v ? "true" : "false");
else
@@ -291,9 +307,11 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
return 0;
}
-static int collect_config(const char *key_, const char *value_, void *cb)
+static int collect_config(const char *key_, const char *value_,
+ const struct config_context *ctx, void *cb)
{
struct strbuf_list *values = cb;
+ const struct key_value_info *kvi = ctx->kvi;
if (!use_key_regexp && strcmp(key_, key))
return 0;
@@ -308,7 +326,7 @@ static int collect_config(const char *key_, const char *value_, void *cb)
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_);
+ return format_config(&values->items[values->nr++], key_, value_, kvi);
}
static int get_value(const char *key_, const char *regex_, unsigned flags)
@@ -366,14 +384,18 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)
}
config_with_options(collect_config, &values,
- &given_config_source, &config_options);
+ &given_config_source, the_repository,
+ &config_options);
if (!values.nr && default_value) {
+ struct key_value_info kvi = KVI_INIT;
struct strbuf *item;
+
+ kvi_from_param(&kvi);
ALLOC_GROW(values.items, values.nr + 1, values.alloc);
item = &values.items[values.nr++];
strbuf_init(item, 0);
- if (format_config(item, key_, default_value) < 0)
+ if (format_config(item, key_, default_value, &kvi) < 0)
die(_("failed to format default config value: %s"),
default_value);
}
@@ -402,7 +424,8 @@ free_strings:
return ret;
}
-static char *normalize_value(const char *key, const char *value)
+static char *normalize_value(const char *key, const char *value,
+ struct key_value_info *kvi)
{
if (!value)
return NULL;
@@ -417,12 +440,12 @@ static char *normalize_value(const char *key, const char *value)
*/
return xstrdup(value);
if (type == TYPE_INT)
- return xstrfmt("%"PRId64, git_config_int64(key, value));
+ return xstrfmt("%"PRId64, git_config_int64(key, value, kvi));
if (type == TYPE_BOOL)
return xstrdup(git_config_bool(key, value) ? "true" : "false");
if (type == TYPE_BOOL_OR_INT) {
int is_bool, v;
- v = git_config_bool_or_int(key, value, &is_bool);
+ v = git_config_bool_or_int(key, value, kvi, &is_bool);
if (!is_bool)
return xstrfmt("%d", v);
else
@@ -458,7 +481,9 @@ static const char *get_color_slot;
static const char *get_colorbool_slot;
static char parsed_color[COLOR_MAXLEN];
-static int git_get_color_config(const char *var, const char *value, void *cb)
+static int git_get_color_config(const char *var, const char *value,
+ const struct config_context *ctx UNUSED,
+ void *cb UNUSED)
{
if (!strcmp(var, get_color_slot)) {
if (!value)
@@ -476,7 +501,8 @@ static void get_color(const char *var, const char *def_color)
get_color_found = 0;
parsed_color[0] = '\0';
config_with_options(git_get_color_config, NULL,
- &given_config_source, &config_options);
+ &given_config_source, the_repository,
+ &config_options);
if (!get_color_found && def_color) {
if (color_parse(def_color, parsed_color) < 0)
@@ -490,7 +516,8 @@ static int get_colorbool_found;
static int get_diff_color_found;
static int get_color_ui_found;
static int git_get_colorbool_config(const char *var, const char *value,
- void *cb)
+ const struct config_context *ctx UNUSED,
+ void *data UNUSED)
{
if (!strcmp(var, get_colorbool_slot))
get_colorbool_found = git_config_colorbool(var, value);
@@ -508,7 +535,8 @@ static int get_colorbool(const char *var, int print)
get_diff_color_found = -1;
get_color_ui_found = -1;
config_with_options(git_get_colorbool_config, NULL,
- &given_config_source, &config_options);
+ &given_config_source, the_repository,
+ &config_options);
if (get_colorbool_found < 0) {
if (!strcmp(get_colorbool_slot, "color.diff"))
@@ -545,13 +573,17 @@ static void check_write(void)
struct urlmatch_current_candidate_value {
char value_is_null;
struct strbuf value;
+ struct key_value_info kvi;
};
-static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
+static int urlmatch_collect_fn(const char *var, const char *value,
+ const struct config_context *ctx,
+ void *cb)
{
struct string_list *values = cb;
struct string_list_item *item = string_list_insert(values, var);
struct urlmatch_current_candidate_value *matched = item->util;
+ const struct key_value_info *kvi = ctx->kvi;
if (!matched) {
matched = xmalloc(sizeof(*matched));
@@ -560,6 +592,7 @@ static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
} else {
strbuf_reset(&matched->value);
}
+ matched->kvi = *kvi;
if (value) {
strbuf_addstr(&matched->value, value);
@@ -597,7 +630,8 @@ static int get_urlmatch(const char *var, const char *url)
}
config_with_options(urlmatch_config_entry, &config,
- &given_config_source, &config_options);
+ &given_config_source, the_repository,
+ &config_options);
ret = !values.nr;
@@ -606,7 +640,8 @@ static int get_urlmatch(const char *var, const char *url)
struct strbuf buf = STRBUF_INIT;
format_config(&buf, item->string,
- matched->value_is_null ? NULL : matched->value.buf);
+ matched->value_is_null ? NULL : matched->value.buf,
+ &matched->kvi);
fwrite(buf.buf, 1, buf.len, stdout);
strbuf_release(&buf);
@@ -637,8 +672,10 @@ static char *default_user_config(void)
int cmd_config(int argc, const char **argv, const char *prefix)
{
int nongit = !startup_info->have_repository;
- char *value;
+ char *value = NULL;
int flags = 0;
+ int ret = 0;
+ struct key_value_info default_kvi = KVI_INIT;
given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
@@ -702,7 +739,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
given_config_source.scope = CONFIG_SCOPE_LOCAL;
} else if (use_worktree_config) {
struct worktree **worktrees = get_worktrees();
- if (repository_format_worktree_config)
+ if (the_repository->repository_format_worktree_config)
given_config_source.file = git_pathdup("config.worktree");
else if (worktrees[0] && worktrees[1])
die(_("--worktree cannot be used with multiple "
@@ -816,7 +853,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
if (actions == ACTION_LIST) {
check_argc(argc, 0, 0);
if (config_with_options(show_all_config, NULL,
- &given_config_source,
+ &given_config_source, the_repository,
&config_options) < 0) {
if (given_config_source.file)
die_errno(_("unable to read config file '%s'"),
@@ -854,44 +891,38 @@ int cmd_config(int argc, const char **argv, const char *prefix)
free(config_file);
}
else if (actions == ACTION_SET) {
- int ret;
check_write();
check_argc(argc, 2, 2);
- value = normalize_value(argv[0], argv[1]);
- UNLEAK(value);
+ value = normalize_value(argv[0], argv[1], &default_kvi);
ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
if (ret == CONFIG_NOTHING_SET)
error(_("cannot overwrite multiple values with a single value\n"
" Use a regexp, --add or --replace-all to change %s."), argv[0]);
- return ret;
}
else if (actions == ACTION_SET_ALL) {
check_write();
check_argc(argc, 2, 3);
- value = normalize_value(argv[0], argv[1]);
- UNLEAK(value);
- return git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], value, argv[2],
- flags);
+ value = normalize_value(argv[0], argv[1], &default_kvi);
+ ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], value, argv[2],
+ flags);
}
else if (actions == ACTION_ADD) {
check_write();
check_argc(argc, 2, 2);
- value = normalize_value(argv[0], argv[1]);
- UNLEAK(value);
- return git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], value,
- CONFIG_REGEX_NONE,
- flags);
+ value = normalize_value(argv[0], argv[1], &default_kvi);
+ ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], value,
+ CONFIG_REGEX_NONE,
+ flags);
}
else if (actions == ACTION_REPLACE_ALL) {
check_write();
check_argc(argc, 2, 3);
- value = normalize_value(argv[0], argv[1]);
- UNLEAK(value);
- return git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], value, argv[2],
- flags | CONFIG_FLAGS_MULTI_REPLACE);
+ value = normalize_value(argv[0], argv[1], &default_kvi);
+ ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], value, argv[2],
+ flags | CONFIG_FLAGS_MULTI_REPLACE);
}
else if (actions == ACTION_GET) {
check_argc(argc, 1, 2);
@@ -932,26 +963,28 @@ int cmd_config(int argc, const char **argv, const char *prefix)
flags | CONFIG_FLAGS_MULTI_REPLACE);
}
else if (actions == ACTION_RENAME_SECTION) {
- int ret;
check_write();
check_argc(argc, 2, 2);
ret = git_config_rename_section_in_file(given_config_source.file,
argv[0], argv[1]);
if (ret < 0)
return ret;
- if (ret == 0)
+ else if (!ret)
die(_("no such section: %s"), argv[0]);
+ else
+ ret = 0;
}
else if (actions == ACTION_REMOVE_SECTION) {
- int ret;
check_write();
check_argc(argc, 1, 1);
ret = git_config_rename_section_in_file(given_config_source.file,
argv[0], NULL);
if (ret < 0)
return ret;
- if (ret == 0)
+ else if (!ret)
die(_("no such section: %s"), argv[0]);
+ else
+ ret = 0;
}
else if (actions == ACTION_GET_COLOR) {
check_argc(argc, 1, 2);
@@ -964,5 +997,6 @@ int cmd_config(int argc, const char **argv, const char *prefix)
return get_colorbool(argv[0], argc == 2);
}
- return 0;
+ free(value);
+ return ret;
}
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index 07b9419596..97cdfb0ac5 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -4,15 +4,17 @@
* Copyright (c) 2006 Junio C Hamano
*/
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "path.h"
#include "repository.h"
-#include "builtin.h"
#include "parse-options.h"
#include "quote.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
static unsigned long garbage;
static off_t size_garbage;
@@ -57,7 +59,8 @@ static void loose_garbage(const char *path)
report_garbage(PACKDIR_FILE_GARBAGE, path);
}
-static int count_loose(const struct object_id *oid, const char *path, void *data)
+static int count_loose(const struct object_id *oid, const char *path,
+ void *data UNUSED)
{
struct stat st;
@@ -72,7 +75,8 @@ static int count_loose(const struct object_id *oid, const char *path, void *data
return 0;
}
-static int count_cruft(const char *basename, const char *path, void *data)
+static int count_cruft(const char *basename UNUSED, const char *path,
+ void *data UNUSED)
{
loose_garbage(path);
return 0;
diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c
index 4c6c89ab0d..3a6a750a8e 100644
--- a/builtin/credential-cache--daemon.c
+++ b/builtin/credential-cache--daemon.c
@@ -1,4 +1,7 @@
#include "builtin.h"
+#include "abspath.h"
+#include "gettext.h"
+#include "object-file.h"
#include "parse-options.h"
#ifndef NO_UNIX_SOCKETS
@@ -34,19 +37,22 @@ static struct credential_cache_entry *lookup_credential(const struct credential
int i;
for (i = 0; i < entries_nr; i++) {
struct credential *e = &entries[i].item;
- if (credential_match(c, e))
+ if (credential_match(c, e, 0))
return &entries[i];
}
return NULL;
}
-static void remove_credential(const struct credential *c)
+static void remove_credential(const struct credential *c, int match_password)
{
struct credential_cache_entry *e;
- e = lookup_credential(c);
- if (e)
- e->expiration = 0;
+ int i;
+ for (i = 0; i < entries_nr; i++) {
+ e = &entries[i];
+ if (credential_match(c, &e->item, match_password))
+ e->expiration = 0;
+ }
}
static timestamp_t check_expirations(void)
@@ -127,6 +133,12 @@ static void serve_one_client(FILE *in, FILE *out)
if (e) {
fprintf(out, "username=%s\n", e->item.username);
fprintf(out, "password=%s\n", e->item.password);
+ if (e->item.password_expiry_utc != TIME_MAX)
+ fprintf(out, "password_expiry_utc=%"PRItime"\n",
+ e->item.password_expiry_utc);
+ if (e->item.oauth_refresh_token)
+ fprintf(out, "oauth_refresh_token=%s\n",
+ e->item.oauth_refresh_token);
}
}
else if (!strcmp(action.buf, "exit")) {
@@ -141,14 +153,14 @@ static void serve_one_client(FILE *in, FILE *out)
exit(0);
}
else if (!strcmp(action.buf, "erase"))
- remove_credential(&c);
+ remove_credential(&c, 1);
else if (!strcmp(action.buf, "store")) {
if (timeout < 0)
warning("cache client didn't specify a timeout");
else if (!c.username || !c.password)
warning("cache client gave us a partial credential");
else {
- remove_credential(&c);
+ remove_credential(&c, 0);
cache_credential(&c, timeout);
}
}
@@ -267,7 +279,7 @@ int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix)
const char *socket_path;
int ignore_sighup = 0;
static const char *usage[] = {
- "git-credential-cache--daemon [opts] <socket_path>",
+ "git credential-cache--daemon [--debug] <socket-path>",
NULL
};
int debug = 0;
@@ -305,7 +317,7 @@ int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix)
int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix)
{
const char * const usage[] = {
- "git credential-cache--daemon [options] <action>",
+ "git credential-cache--daemon [--debug] <socket-path>",
"",
"credential-cache--daemon is disabled in this build of Git",
NULL
diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c
index 78c02ad531..43b9d0e5b1 100644
--- a/builtin/credential-cache.c
+++ b/builtin/credential-cache.c
@@ -1,5 +1,9 @@
#include "builtin.h"
+#include "gettext.h"
#include "parse-options.h"
+#include "path.h"
+#include "strbuf.h"
+#include "write-or-die.h"
#ifndef NO_UNIX_SOCKETS
diff --git a/builtin/credential-store.c b/builtin/credential-store.c
index 62a4f3c265..4a492411bb 100644
--- a/builtin/credential-store.c
+++ b/builtin/credential-store.c
@@ -1,16 +1,20 @@
#include "builtin.h"
#include "config.h"
+#include "gettext.h"
#include "lockfile.h"
#include "credential.h"
+#include "path.h"
#include "string-list.h"
#include "parse-options.h"
+#include "write-or-die.h"
static struct lock_file credential_lock;
static int parse_credential_file(const char *fn,
struct credential *c,
void (*match_cb)(struct credential *),
- void (*other_cb)(struct strbuf *))
+ void (*other_cb)(struct strbuf *),
+ int match_password)
{
FILE *fh;
struct strbuf line = STRBUF_INIT;
@@ -27,7 +31,7 @@ static int parse_credential_file(const char *fn,
while (strbuf_getline_lf(&line, fh) != EOF) {
if (!credential_from_url_gently(&entry, line.buf, 1) &&
entry.username && entry.password &&
- credential_match(c, &entry)) {
+ credential_match(c, &entry, match_password)) {
found_credential = 1;
if (match_cb) {
match_cb(&entry);
@@ -57,7 +61,7 @@ static void print_line(struct strbuf *buf)
}
static void rewrite_credential_file(const char *fn, struct credential *c,
- struct strbuf *extra)
+ struct strbuf *extra, int match_password)
{
int timeout_ms = 1000;
@@ -66,11 +70,30 @@ static void rewrite_credential_file(const char *fn, struct credential *c,
die_errno(_("unable to get credential storage lock in %d ms"), timeout_ms);
if (extra)
print_line(extra);
- parse_credential_file(fn, c, NULL, print_line);
+ parse_credential_file(fn, c, NULL, print_line, match_password);
if (commit_lock_file(&credential_lock) < 0)
die_errno("unable to write credential store");
}
+static int is_rfc3986_unreserved(char ch)
+{
+ return isalnum(ch) ||
+ ch == '-' || ch == '_' || ch == '.' || ch == '~';
+}
+
+static int is_rfc3986_reserved_or_unreserved(char ch)
+{
+ if (is_rfc3986_unreserved(ch))
+ return 1;
+ switch (ch) {
+ case '!': case '*': case '\'': case '(': case ')': case ';':
+ case ':': case '@': case '&': case '=': case '+': case '$':
+ case ',': case '/': case '?': case '#': case '[': case ']':
+ return 1;
+ }
+ return 0;
+}
+
static void store_credential_file(const char *fn, struct credential *c)
{
struct strbuf buf = STRBUF_INIT;
@@ -88,7 +111,7 @@ static void store_credential_file(const char *fn, struct credential *c)
is_rfc3986_reserved_or_unreserved);
}
- rewrite_credential_file(fn, c, &buf);
+ rewrite_credential_file(fn, c, &buf, 0);
strbuf_release(&buf);
}
@@ -135,7 +158,7 @@ static void remove_credential(const struct string_list *fns, struct credential *
return;
for_each_string_list_item(fn, fns)
if (!access(fn->string, F_OK))
- rewrite_credential_file(fn->string, c, NULL);
+ rewrite_credential_file(fn->string, c, NULL, 1);
}
static void lookup_credential(const struct string_list *fns, struct credential *c)
@@ -143,7 +166,7 @@ static void lookup_credential(const struct string_list *fns, struct credential *
struct string_list_item *fn;
for_each_string_list_item(fn, fns)
- if (parse_credential_file(fn->string, c, print_entry, NULL))
+ if (parse_credential_file(fn->string, c, print_entry, NULL, 0))
return; /* Found credential */
}
diff --git a/builtin/credential.c b/builtin/credential.c
index d7b304fa08..7010752987 100644
--- a/builtin/credential.c
+++ b/builtin/credential.c
@@ -6,7 +6,7 @@
static const char usage_msg[] =
"git credential (fill|approve|reject)";
-int cmd_credential(int argc, const char **argv, const char *prefix)
+int cmd_credential(int argc, const char **argv, const char *prefix UNUSED)
{
const char *op;
struct credential c = CREDENTIAL_INIT;
diff --git a/builtin/describe.c b/builtin/describe.c
index 42159cd26b..7ce23e1b8e 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -1,30 +1,37 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
#include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "lockfile.h"
#include "commit.h"
#include "tag.h"
#include "blob.h"
#include "refs.h"
-#include "builtin.h"
#include "exec-cmd.h"
+#include "object-name.h"
#include "parse-options.h"
+#include "read-cache-ll.h"
#include "revision.h"
#include "diff.h"
#include "hashmap.h"
+#include "setup.h"
#include "strvec.h"
#include "run-command.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "list-objects.h"
#include "commit-slab.h"
+#include "wildmatch.h"
#define MAX_TAGS (FLAG_BITS - 1)
define_commit_slab(commit_names, struct commit_name *);
static const char * const describe_usage[] = {
- N_("git describe [<options>] [<commit-ish>...]"),
- N_("git describe [<options>] --dirty"),
+ N_("git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"),
+ N_("git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"),
+ N_("git describe <blob>"),
NULL
};
@@ -63,7 +70,7 @@ static const char *prio_names[] = {
N_("head"), N_("lightweight"), N_("annotated"),
};
-static int commit_name_neq(const void *unused_cmp_data,
+static int commit_name_neq(const void *cmp_data UNUSED,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
const void *peeled)
@@ -140,7 +147,8 @@ static void add_to_known_names(const char *path,
}
}
-static int get_name(const char *path, const struct object_id *oid, int flag, void *cb_data)
+static int get_name(const char *path, const struct object_id *oid,
+ int flag UNUSED, void *cb_data UNUSED)
{
int is_tag = 0;
struct object_id peeled;
@@ -259,7 +267,7 @@ static unsigned long finish_depth_computation(
best->depth++;
while (parents) {
struct commit *p = parents->item;
- parse_commit(p);
+ repo_parse_commit(the_repository, p);
if (!(p->object.flags & SEEN))
commit_list_insert_by_date(p, list);
p->object.flags |= c->object.flags;
@@ -296,7 +304,8 @@ static void append_name(struct commit_name *n, struct strbuf *dst)
static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst)
{
- strbuf_addf(dst, "-%d-g%s", depth, find_unique_abbrev(oid, abbrev));
+ strbuf_addf(dst, "-%d-g%s", depth,
+ repo_find_unique_abbrev(the_repository, oid, abbrev));
}
static void describe_commit(struct object_id *oid, struct strbuf *dst)
@@ -401,7 +410,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
}
while (parents) {
struct commit *p = parents->item;
- parse_commit(p);
+ repo_parse_commit(the_repository, p);
if (!(p->object.flags & SEEN))
commit_list_insert_by_date(p, &list);
p->object.flags |= c->object.flags;
@@ -517,6 +526,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)
traverse_commit_list(&revs, process_commit, process_object, &pcd);
reset_revision_walk();
+ release_revisions(&revs);
}
static void describe(const char *arg, int last_one)
@@ -528,7 +538,7 @@ static void describe(const char *arg, int last_one)
if (debug)
fprintf(stderr, _("describe %s\n"), arg);
- if (get_oid(arg, &oid))
+ if (repo_get_oid(the_repository, arg, &oid))
die(_("Not a valid object name %s"), arg);
cmit = lookup_commit_reference_gently(the_repository, &oid, 1);
@@ -650,10 +660,13 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
int fd, result;
setup_work_tree();
- read_cache();
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+ repo_read_index(the_repository);
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
NULL, NULL, NULL);
- fd = hold_locked_index(&index_lock, 0);
+ fd = repo_hold_locked_index(the_repository,
+ &index_lock, 0);
if (0 <= fd)
repo_update_index_if_able(the_repository, &index_lock);
@@ -667,6 +680,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
suffix = NULL;
else
suffix = dirty;
+ release_revisions(&revs);
}
describe("HEAD", 1);
} else if (dirty) {
diff --git a/builtin/diagnose.c b/builtin/diagnose.c
new file mode 100644
index 0000000000..4f22eb2b55
--- /dev/null
+++ b/builtin/diagnose.c
@@ -0,0 +1,65 @@
+#include "builtin.h"
+#include "abspath.h"
+#include "gettext.h"
+#include "object-file.h"
+#include "parse-options.h"
+#include "diagnose.h"
+
+static const char * const diagnose_usage[] = {
+ N_("git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+ " [--mode=<mode>]"),
+ NULL
+};
+
+int cmd_diagnose(int argc, const char **argv, const char *prefix)
+{
+ struct strbuf zip_path = STRBUF_INIT;
+ time_t now = time(NULL);
+ struct tm tm;
+ enum diagnose_mode mode = DIAGNOSE_STATS;
+ char *option_output = NULL;
+ char *option_suffix = "%Y-%m-%d-%H%M";
+ char *prefixed_filename;
+
+ const struct option diagnose_options[] = {
+ OPT_STRING('o', "output-directory", &option_output, N_("path"),
+ N_("specify a destination for the diagnostics archive")),
+ OPT_STRING('s', "suffix", &option_suffix, N_("format"),
+ N_("specify a strftime format suffix for the filename")),
+ OPT_CALLBACK_F(0, "mode", &mode, "(stats|all)",
+ N_("specify the content of the diagnostic archive"),
+ PARSE_OPT_NONEG, option_parse_diagnose),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, diagnose_options,
+ diagnose_usage, 0);
+
+ /* Prepare the path to put the result */
+ prefixed_filename = prefix_filename(prefix,
+ option_output ? option_output : "");
+ strbuf_addstr(&zip_path, prefixed_filename);
+ strbuf_complete(&zip_path, '/');
+
+ strbuf_addstr(&zip_path, "git-diagnostics-");
+ strbuf_addftime(&zip_path, option_suffix, localtime_r(&now, &tm), 0, 0);
+ strbuf_addstr(&zip_path, ".zip");
+
+ switch (safe_create_leading_directories(zip_path.buf)) {
+ case SCLD_OK:
+ case SCLD_EXISTS:
+ break;
+ default:
+ die_errno(_("could not create leading directories for '%s'"),
+ zip_path.buf);
+ }
+
+ /* Prepare diagnostics */
+ if (create_diagnostics_archive(&zip_path, mode))
+ die_errno(_("unable to create diagnostics archive %s"),
+ zip_path.buf);
+
+ free(prefixed_filename);
+ strbuf_release(&zip_path);
+ return 0;
+}
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 70103c4095..50330b8dd2 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -3,18 +3,19 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "diff.h"
#include "diff-merges.h"
#include "commit.h"
+#include "preload-index.h"
+#include "repository.h"
#include "revision.h"
-#include "builtin.h"
#include "submodule.h"
static const char diff_files_usage[] =
"git diff-files [-q] [-0 | -1 | -2 | -3 | -c | --cc] [<common-diff-options>] [<path>...]"
+"\n"
COMMON_DIFF_OPTIONS_HELP;
int cmd_diff_files(int argc, const char **argv, const char *prefix)
@@ -27,6 +28,10 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
usage(diff_files_usage);
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
repo_init_revisions(the_repository, &rev, prefix);
rev.abbrev = 0;
@@ -75,10 +80,14 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
(rev.diffopt.output_format & DIFF_FORMAT_PATCH))
diff_merges_set_dense_combined_if_unset(&rev);
- if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
- perror("read_cache_preload");
- return -1;
+ if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) {
+ perror("repo_read_index_preload");
+ result = -1;
+ goto cleanup;
}
result = run_diff_files(&rev, options);
- return diff_result_code(&rev.diffopt, result);
+ result = diff_result_code(&rev.diffopt, result);
+cleanup:
+ release_revisions(&rev);
+ return result;
}
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 5fd23ab5b6..9db7139b83 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -1,16 +1,19 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "diff.h"
#include "diff-merges.h"
#include "commit.h"
+#include "preload-index.h"
+#include "repository.h"
#include "revision.h"
-#include "builtin.h"
+#include "setup.h"
+#include "sparse-index.h"
#include "submodule.h"
static const char diff_cache_usage[] =
-"git diff-index [-m] [--cached] "
+"git diff-index [-m] [--cached] [--merge-base] "
"[<common-diff-options>] <tree-ish> [<path>...]"
+"\n"
COMMON_DIFF_OPTIONS_HELP;
int cmd_diff_index(int argc, const char **argv, const char *prefix)
@@ -61,15 +64,16 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
usage(diff_cache_usage);
if (!(option & DIFF_INDEX_CACHED)) {
setup_work_tree();
- if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
- perror("read_cache_preload");
+ if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) {
+ perror("repo_read_index_preload");
return -1;
}
- } else if (read_cache() < 0) {
- perror("read_cache");
+ } else if (repo_read_index(the_repository) < 0) {
+ perror("repo_read_index");
return -1;
}
result = run_diff_index(&rev, option);
- UNLEAK(rev);
- return diff_result_code(&rev.diffopt, result);
+ result = diff_result_code(&rev.diffopt, result);
+ release_revisions(&rev);
+ return result;
}
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index 116097a404..d62caa6c8b 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -1,12 +1,16 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
#include "config.h"
#include "diff.h"
#include "commit.h"
+#include "gettext.h"
+#include "hex.h"
#include "log-tree.h"
-#include "builtin.h"
#include "submodule.h"
+#include "read-cache-ll.h"
#include "repository.h"
+#include "revision.h"
+#include "tree.h"
static struct rev_info log_tree_opt;
@@ -83,8 +87,10 @@ static int diff_tree_stdin(char *line)
}
static const char diff_tree_usage[] =
-"git diff-tree [--stdin] [-m] [-c | --cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
-"[<common-diff-options>] <tree-ish> [<tree-ish>] [<path>...]\n"
+"git diff-tree [--stdin] [-m] [-s] [-v] [--no-commit-id] [--pretty]\n"
+" [-t] [-r] [-c | --cc] [--combined-all-paths] [--root] [--merge-base]\n"
+" [<common-diff-options>] <tree-ish> [<tree-ish>] [<path>...]\n"
+"\n"
" -r diff recursively\n"
" -c show combined diff for merge commits\n"
" --cc show combined diff for merge commits removing uninteresting hunks\n"
@@ -117,8 +123,12 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
usage(diff_tree_usage);
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
repo_init_revisions(the_repository, opt, prefix);
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
die(_("index file corrupt"));
opt->abbrev = 0;
opt->diff = 1;
diff --git a/builtin/diff.c b/builtin/diff.c
index bb7fafca61..b19530c996 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -3,23 +3,27 @@
*
* Copyright (c) 2006 Junio C Hamano
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
#include "config.h"
#include "ewah/ewok.h"
#include "lockfile.h"
#include "color.h"
#include "commit.h"
#include "blob.h"
+#include "gettext.h"
#include "tag.h"
#include "diff.h"
#include "diff-merges.h"
#include "diffcore.h"
+#include "preload-index.h"
+#include "read-cache-ll.h"
#include "revision.h"
#include "log-tree.h"
-#include "builtin.h"
+#include "setup.h"
#include "submodule.h"
#include "oid-array.h"
+#include "tree.h"
#define DIFF_NO_INDEX_EXPLICIT 1
#define DIFF_NO_INDEX_IMPLICIT 2
@@ -30,7 +34,8 @@ static const char builtin_diff_usage[] =
" or: git diff [<options>] [--merge-base] <commit> [<commit>...] <commit> [--] [<path>...]\n"
" or: git diff [<options>] <commit>...<commit> [--] [<path>...]\n"
" or: git diff [<options>] <blob> <blob>\n"
-" or: git diff [<options>] --no-index [--] <path> <path>\n"
+" or: git diff [<options>] --no-index [--] <path> <path>"
+"\n"
COMMON_DIFF_OPTIONS_HELP;
static const char *blob_path(struct object_array_entry *entry)
@@ -73,7 +78,7 @@ static void stuff_change(struct diff_options *opt,
}
static int builtin_diff_b_f(struct rev_info *revs,
- int argc, const char **argv,
+ int argc, const char **argv UNUSED,
struct object_array_entry **blob)
{
/* Blob vs file in the working tree*/
@@ -108,7 +113,7 @@ static int builtin_diff_b_f(struct rev_info *revs,
}
static int builtin_diff_blobs(struct rev_info *revs,
- int argc, const char **argv,
+ int argc, const char **argv UNUSED,
struct object_array_entry **blob)
{
const unsigned mode = canon_mode(S_IFREG | 0644);
@@ -156,12 +161,13 @@ static int builtin_diff_index(struct rev_info *revs,
usage(builtin_diff_usage);
if (!(option & DIFF_INDEX_CACHED)) {
setup_work_tree();
- if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
- perror("read_cache_preload");
+ if (repo_read_index_preload(the_repository,
+ &revs->diffopt.pathspec, 0) < 0) {
+ perror("repo_read_index_preload");
return -1;
}
- } else if (read_cache() < 0) {
- perror("read_cache");
+ } else if (repo_read_index(the_repository) < 0) {
+ perror("repo_read_cache");
return -1;
}
return run_diff_index(revs, option);
@@ -207,9 +213,9 @@ static int builtin_diff_tree(struct rev_info *revs,
}
static int builtin_diff_combined(struct rev_info *revs,
- int argc, const char **argv,
+ int argc, const char **argv UNUSED,
struct object_array_entry *ent,
- int ents)
+ int ents, int first_non_parent)
{
struct oid_array parents = OID_ARRAY_INIT;
int i;
@@ -217,11 +223,18 @@ static int builtin_diff_combined(struct rev_info *revs,
if (argc > 1)
usage(builtin_diff_usage);
+ if (first_non_parent < 0)
+ die(_("no merge given, only parents."));
+ if (first_non_parent >= ents)
+ BUG("first_non_parent out of range: %d", first_non_parent);
+
diff_merges_set_dense_combined_if_unset(revs);
- for (i = 1; i < ents; i++)
- oid_array_append(&parents, &ent[i].item->oid);
- diff_tree_combined(&ent[0].item->oid, &parents, revs);
+ for (i = 0; i < ents; i++) {
+ if (i != first_non_parent)
+ oid_array_append(&parents, &ent[i].item->oid);
+ }
+ diff_tree_combined(&ent[first_non_parent].item->oid, &parents, revs);
oid_array_clear(&parents);
return 0;
}
@@ -231,12 +244,13 @@ static void refresh_index_quietly(void)
struct lock_file lock_file = LOCK_INIT;
int fd;
- fd = hold_locked_index(&lock_file, 0);
+ fd = repo_hold_locked_index(the_repository, &lock_file, 0);
if (fd < 0)
return;
- discard_cache();
- read_cache();
- refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
+ discard_index(&the_index);
+ repo_read_index(the_repository);
+ refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL,
+ NULL);
repo_update_index_if_able(the_repository, &lock_file);
}
@@ -271,8 +285,9 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
diff_merges_set_dense_combined_if_unset(revs);
setup_work_tree();
- if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
- perror("read_cache_preload");
+ if (repo_read_index_preload(the_repository, &revs->diffopt.pathspec,
+ 0) < 0) {
+ perror("repo_read_index_preload");
return -1;
}
return run_diff_files(revs, options);
@@ -352,7 +367,7 @@ static void symdiff_prepare(struct rev_info *rev, struct symdiff *sym)
othercount++;
continue;
}
- if (map == NULL)
+ if (!map)
map = bitmap_new();
bitmap_set(map, i);
}
@@ -385,6 +400,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
int i;
struct rev_info rev;
struct object_array ent = OBJECT_ARRAY_INIT;
+ int first_non_parent = -1;
int blobs = 0, paths = 0;
struct object_array_entry *blob[2];
int nongit = 0, no_index = 0;
@@ -536,13 +552,18 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
if (!obj)
die(_("invalid object '%s' given."), name);
if (obj->type == OBJ_COMMIT)
- obj = &get_commit_tree(((struct commit *)obj))->object;
+ obj = &repo_get_commit_tree(the_repository,
+ ((struct commit *)obj))->object;
if (obj->type == OBJ_TREE) {
if (sdiff.skip && bitmap_get(sdiff.skip, i))
continue;
obj->flags |= flags;
add_object_array(obj, name, &ent);
+ if (first_non_parent < 0 &&
+ (i >= rev.cmdline.nr || /* HEAD by hand. */
+ rev.cmdline.rev[i].whence != REV_CMD_PARENTS_ONLY))
+ first_non_parent = ent.nr - 1;
} else if (obj->type == OBJ_BLOB) {
if (2 <= blobs)
die(_("more than two blobs given: '%s'"), name);
@@ -590,12 +611,13 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
&ent.objects[0], &ent.objects[1]);
} else
result = builtin_diff_combined(&rev, argc, argv,
- ent.objects, ent.nr);
+ ent.objects, ent.nr,
+ first_non_parent);
result = diff_result_code(&rev.diffopt, result);
if (1 < rev.diffopt.skip_stat_unmatch)
refresh_index_quietly();
- UNLEAK(rev);
- UNLEAK(ent);
+ release_revisions(&rev);
+ object_array_clear(&ent);
UNLEAK(blob);
return result;
}
diff --git a/builtin/difftool.c b/builtin/difftool.c
index faa3507369..0f5eae9cd4 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -11,19 +11,27 @@
*
* Copyright (C) 2016 Johannes Schindelin
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
-#include "config.h"
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
+#include "abspath.h"
+#include "config.h"
+#include "copy.h"
#include "run-command.h"
+#include "environment.h"
#include "exec-cmd.h"
+#include "gettext.h"
+#include "hex.h"
#include "parse-options.h"
+#include "read-cache-ll.h"
+#include "sparse-index.h"
#include "strvec.h"
#include "strbuf.h"
#include "lockfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
#include "dir.h"
#include "entry.h"
+#include "setup.h"
static int trust_exit_code;
@@ -32,20 +40,24 @@ static const char *const builtin_difftool_usage[] = {
NULL
};
-static int difftool_config(const char *var, const char *value, void *cb)
+static int difftool_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (!strcmp(var, "difftool.trustexitcode")) {
trust_exit_code = git_config_bool(var, value);
return 0;
}
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
}
static int print_tool_help(void)
{
- const char *argv[] = { "mergetool", "--tool-help=diff", NULL };
- return run_command_v_opt(argv, RUN_GIT_CMD);
+ struct child_process cmd = CHILD_PROCESS_INIT;
+
+ cmd.git_cmd = 1;
+ strvec_pushl(&cmd.args, "mergetool", "--tool-help=diff", NULL);
+ return run_command(&cmd);
}
static int parse_index_info(char *p, int *mode1, int *mode2,
@@ -125,10 +137,10 @@ struct working_tree_entry {
char path[FLEX_ARRAY];
};
-static int working_tree_entry_cmp(const void *unused_cmp_data,
+static int working_tree_entry_cmp(const void *cmp_data UNUSED,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
- const void *unused_keydata)
+ const void *keydata UNUSED)
{
const struct working_tree_entry *a, *b;
@@ -148,10 +160,10 @@ struct pair_entry {
const char path[FLEX_ARRAY];
};
-static int pair_cmp(const void *unused_cmp_data,
+static int pair_cmp(const void *cmp_data UNUSED,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
- const void *unused_keydata)
+ const void *keydata UNUSED)
{
const struct pair_entry *a, *b;
@@ -184,7 +196,7 @@ struct path_entry {
char path[FLEX_ARRAY];
};
-static int path_entry_cmp(const void *unused_cmp_data,
+static int path_entry_cmp(const void *cmp_data UNUSED,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
const void *key)
@@ -217,7 +229,7 @@ static void changed_files(struct hashmap *result, const char *index_path,
update_index.use_shell = 0;
update_index.clean_on_exit = 1;
update_index.dir = workdir;
- strvec_pushf(&update_index.env_array, "GIT_INDEX_FILE=%s", index_path);
+ strvec_pushf(&update_index.env, "GIT_INDEX_FILE=%s", index_path);
/* Ignore any errors of update-index */
run_command(&update_index);
@@ -230,7 +242,7 @@ static void changed_files(struct hashmap *result, const char *index_path,
diff_files.clean_on_exit = 1;
diff_files.out = -1;
diff_files.dir = workdir;
- strvec_pushf(&diff_files.env_array, "GIT_INDEX_FILE=%s", index_path);
+ strvec_pushf(&diff_files.env, "GIT_INDEX_FILE=%s", index_path);
if (start_command(&diff_files))
die("could not obtain raw diff");
fp = xfdopen(diff_files.out, "r");
@@ -292,7 +304,8 @@ static char *get_symlink(const struct object_id *oid, const char *path)
} else {
enum object_type type;
unsigned long size;
- data = read_object_file(oid, &type, &size);
+ data = repo_read_object_file(the_repository, oid, &type,
+ &size);
if (!data)
die(_("could not read object %s for symlink %s"),
oid_to_hex(oid), path);
@@ -358,10 +371,10 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
struct hashmap symlinks2 = HASHMAP_INIT(pair_cmp, NULL);
struct hashmap_iter iter;
struct pair_entry *entry;
- struct index_state wtindex;
+ struct index_state wtindex = INDEX_STATE_INIT(the_repository);
struct checkout lstate, rstate;
- int flags = RUN_GIT_CMD, err = 0;
- const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL };
+ int err = 0;
+ struct child_process cmd = CHILD_PROCESS_INIT;
struct hashmap wt_modified, tmp_modified;
int indices_loaded = 0;
@@ -384,8 +397,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
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;
@@ -563,16 +574,17 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
}
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
+ strvec_push(&cmd.args, extcmd);
+ } else {
+ strvec_push(&cmd.args, "difftool--helper");
+ cmd.git_cmd = 1;
setenv("GIT_DIFFTOOL_DIRDIFF", "true", 1);
- ret = run_command_v_opt(helper_argv, flags);
+ }
+ strvec_pushl(&cmd.args, ldir.buf, rdir.buf, NULL);
+ ret = run_command(&cmd);
/* TODO: audit for interaction with sparse-index. */
ensure_full_index(&wtindex);
@@ -675,14 +687,14 @@ static int run_file_diff(int prompt, const char *prefix,
child->git_cmd = 1;
child->dir = prefix;
- strvec_pushv(&child->env_array, env);
+ strvec_pushv(&child->env, env);
return run_command(child);
}
int cmd_difftool(int argc, const char **argv, const char *prefix)
{
- int use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0,
+ int use_gui_tool = -1, dir_diff = 0, prompt = -1, symlinks = 0,
tool_help = 0, no_index = 0;
static char *difftool_cmd = NULL, *extcmd = NULL;
struct option builtin_difftool_options[] = {
@@ -716,7 +728,7 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
symlinks = has_symlinks;
argc = parse_options(argc, argv, prefix, builtin_difftool_options,
- builtin_difftool_usage, PARSE_OPT_KEEP_UNKNOWN |
+ builtin_difftool_usage, PARSE_OPT_KEEP_UNKNOWN_OPT |
PARSE_OPT_KEEP_DASHDASH);
if (tool_help)
@@ -732,13 +744,21 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
} else if (dir_diff)
die(_("options '%s' and '%s' cannot be used together"), "--dir-diff", "--no-index");
- die_for_incompatible_opt3(use_gui_tool, "--gui",
+ die_for_incompatible_opt3(use_gui_tool == 1, "--gui",
!!difftool_cmd, "--tool",
!!extcmd, "--extcmd");
- if (use_gui_tool)
+ /*
+ * Explicitly specified GUI option is forwarded to git-mergetool--lib.sh;
+ * empty or unset means "use the difftool.guiDefault config or default to
+ * false".
+ */
+ if (use_gui_tool == 1)
setenv("GIT_MERGETOOL_GUI", "true", 1);
- else if (difftool_cmd) {
+ else if (use_gui_tool == 0)
+ setenv("GIT_MERGETOOL_GUI", "false", 1);
+
+ if (difftool_cmd) {
if (*difftool_cmd)
setenv("GIT_DIFF_TOOL", difftool_cmd, 1);
else
diff --git a/builtin/env--helper.c b/builtin/env--helper.c
deleted file mode 100644
index 27349098b0..0000000000
--- a/builtin/env--helper.c
+++ /dev/null
@@ -1,100 +0,0 @@
-#include "builtin.h"
-#include "config.h"
-#include "parse-options.h"
-
-static char const * const env__helper_usage[] = {
- N_("git env--helper --type=[bool|ulong] <options> <env-var>"),
- NULL
-};
-
-enum cmdmode {
- ENV_HELPER_TYPE_BOOL = 1,
- ENV_HELPER_TYPE_ULONG
-};
-
-static int option_parse_type(const struct option *opt, const char *arg,
- int unset)
-{
- enum cmdmode *cmdmode = opt->value;
-
- BUG_ON_OPT_NEG(unset);
-
- if (!strcmp(arg, "bool"))
- *cmdmode = ENV_HELPER_TYPE_BOOL;
- else if (!strcmp(arg, "ulong"))
- *cmdmode = ENV_HELPER_TYPE_ULONG;
- else
- die(_("unrecognized --type argument, %s"), arg);
-
- return 0;
-}
-
-int cmd_env__helper(int argc, const char **argv, const char *prefix)
-{
- int exit_code = 0;
- const char *env_variable = NULL;
- const char *env_default = NULL;
- int ret;
- int ret_int, default_int;
- unsigned long ret_ulong, default_ulong;
- enum cmdmode cmdmode = 0;
- struct option opts[] = {
- OPT_CALLBACK_F(0, "type", &cmdmode, N_("type"),
- N_("value is given this type"), PARSE_OPT_NONEG,
- option_parse_type),
- OPT_STRING(0, "default", &env_default, N_("value"),
- N_("default for git_env_*(...) to fall back on")),
- OPT_BOOL(0, "exit-code", &exit_code,
- N_("be quiet only use git_env_*() value as exit code")),
- OPT_END(),
- };
-
- argc = parse_options(argc, argv, prefix, opts, env__helper_usage,
- PARSE_OPT_KEEP_UNKNOWN);
- if (env_default && !*env_default)
- usage_with_options(env__helper_usage, opts);
- if (!cmdmode)
- usage_with_options(env__helper_usage, opts);
- if (argc != 1)
- usage_with_options(env__helper_usage, opts);
- env_variable = argv[0];
-
- switch (cmdmode) {
- case ENV_HELPER_TYPE_BOOL:
- if (env_default) {
- default_int = git_parse_maybe_bool(env_default);
- if (default_int == -1) {
- error(_("option `--default' expects a boolean value with `--type=bool`, not `%s`"),
- env_default);
- usage_with_options(env__helper_usage, opts);
- }
- } else {
- default_int = 0;
- }
- ret_int = git_env_bool(env_variable, default_int);
- if (!exit_code)
- puts(ret_int ? "true" : "false");
- ret = ret_int;
- break;
- case ENV_HELPER_TYPE_ULONG:
- if (env_default) {
- if (!git_parse_ulong(env_default, &default_ulong)) {
- error(_("option `--default' expects an unsigned long value with `--type=ulong`, not `%s`"),
- env_default);
- usage_with_options(env__helper_usage, opts);
- }
- } else {
- default_ulong = 0;
- }
- ret_ulong = git_env_ulong(env_variable, default_ulong);
- if (!exit_code)
- printf("%lu\n", ret_ulong);
- ret = ret_ulong;
- break;
- default:
- BUG("unknown <type> value");
- break;
- }
-
- return !ret;
-}
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 1355b5a96d..56dc69fac1 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -4,11 +4,13 @@
* Copyright (C) 2007 Johannes E. Schindelin
*/
#include "builtin.h"
-#include "cache.h"
#include "config.h"
+#include "gettext.h"
+#include "hex.h"
#include "refs.h"
#include "refspec.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
#include "commit.h"
#include "object.h"
#include "tag.h"
@@ -109,7 +111,7 @@ static struct decoration idnums;
static uint32_t last_idnum;
struct anonymized_entry {
struct hashmap_entry hash;
- const char *anon;
+ char *anon;
const char orig[FLEX_ARRAY];
};
@@ -119,7 +121,7 @@ struct anonymized_entry_key {
size_t orig_len;
};
-static int anonymized_entry_cmp(const void *unused_cmp_data,
+static int anonymized_entry_cmp(const void *cmp_data UNUSED,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
const void *keydata)
@@ -138,43 +140,56 @@ static int anonymized_entry_cmp(const void *unused_cmp_data,
return strcmp(a->orig, b->orig);
}
+static struct anonymized_entry *add_anonymized_entry(struct hashmap *map,
+ unsigned hash,
+ const char *orig, size_t len,
+ char *anon)
+{
+ struct anonymized_entry *ret, *old;
+
+ if (!map->cmpfn)
+ hashmap_init(map, anonymized_entry_cmp, NULL, 0);
+
+ FLEX_ALLOC_MEM(ret, orig, orig, len);
+ hashmap_entry_init(&ret->hash, hash);
+ ret->anon = anon;
+ old = hashmap_put_entry(map, ret, hash);
+
+ if (old) {
+ free(old->anon);
+ free(old);
+ }
+
+ return ret;
+}
+
/*
* Basically keep a cache of X->Y so that we can repeatedly replace
* the same anonymized string with another. The actual generation
* is farmed out to the generate function.
*/
static const char *anonymize_str(struct hashmap *map,
- char *(*generate)(void *),
- const char *orig, size_t len,
- void *data)
+ char *(*generate)(void),
+ const char *orig, size_t len)
{
struct anonymized_entry_key key;
struct anonymized_entry *ret;
- if (!map->cmpfn)
- hashmap_init(map, anonymized_entry_cmp, NULL, 0);
-
hashmap_entry_init(&key.hash, memhash(orig, len));
key.orig = orig;
key.orig_len = len;
/* First check if it's a token the user configured manually... */
- if (anonymized_seeds.cmpfn)
- ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key);
- else
- ret = NULL;
+ ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key);
/* ...otherwise check if we've already seen it in this context... */
if (!ret)
ret = hashmap_get_entry(map, &key, hash, &key);
/* ...and finally generate a new mapping if necessary */
- if (!ret) {
- FLEX_ALLOC_MEM(ret, orig, orig, len);
- hashmap_entry_init(&ret->hash, key.hash.hash);
- ret->anon = generate(data);
- hashmap_put(map, &ret->hash);
- }
+ if (!ret)
+ ret = add_anonymized_entry(map, key.hash.hash,
+ orig, len, generate());
return ret->anon;
}
@@ -187,12 +202,12 @@ static const char *anonymize_str(struct hashmap *map,
*/
static void anonymize_path(struct strbuf *out, const char *path,
struct hashmap *map,
- char *(*generate)(void *))
+ char *(*generate)(void))
{
while (*path) {
const char *end_of_component = strchrnul(path, '/');
size_t len = end_of_component - path;
- const char *c = anonymize_str(map, generate, path, len, NULL);
+ const char *c = anonymize_str(map, generate, path, len);
strbuf_addstr(out, c);
path = end_of_component;
if (*path)
@@ -296,7 +311,7 @@ static void export_blob(const struct object_id *oid)
object = (struct object *)lookup_blob(the_repository, oid);
eaten = 0;
} else {
- buf = read_object_file(oid, &type, &size);
+ buf = repo_read_object_file(the_repository, oid, &type, &size);
if (!buf)
die("could not read blob %s", oid_to_hex(oid));
if (check_object_signature(the_repository, oid, buf, size,
@@ -367,7 +382,7 @@ static void print_path_1(const char *path)
printf("%s", path);
}
-static char *anonymize_path_component(void *data)
+static char *anonymize_path_component(void)
{
static int counter;
struct strbuf out = STRBUF_INIT;
@@ -389,7 +404,7 @@ static void print_path(const char *path)
}
}
-static char *generate_fake_oid(void *data)
+static char *generate_fake_oid(void)
{
static uint32_t counter = 1; /* avoid null oid */
const unsigned hashsz = the_hash_algo->rawsz;
@@ -405,11 +420,11 @@ static const char *anonymize_oid(const char *oid_hex)
{
static struct hashmap objs;
size_t len = strlen(oid_hex);
- return anonymize_str(&objs, generate_fake_oid, oid_hex, len, NULL);
+ return anonymize_str(&objs, generate_fake_oid, oid_hex, len);
}
static void show_filemodify(struct diff_queue_struct *q,
- struct diff_options *options, void *data)
+ struct diff_options *options UNUSED, void *data)
{
int i;
struct string_list *changed = data;
@@ -502,7 +517,7 @@ static const char *find_encoding(const char *begin, const char *end)
return bol;
}
-static char *anonymize_ref_component(void *data)
+static char *anonymize_ref_component(void)
{
static int counter;
struct strbuf out = STRBUF_INIT;
@@ -542,13 +557,13 @@ static const char *anonymize_refname(const char *refname)
* We do not even bother to cache commit messages, as they are unlikely
* to be repeated verbatim, and it is not that interesting when they are.
*/
-static char *anonymize_commit_message(const char *old)
+static char *anonymize_commit_message(void)
{
static int counter;
return xstrfmt("subject %d\n\nbody\n", counter++);
}
-static char *anonymize_ident(void *data)
+static char *anonymize_ident(void)
{
static int counter;
struct strbuf out = STRBUF_INIT;
@@ -591,7 +606,7 @@ static void anonymize_ident_line(const char **beg, const char **end)
len = split.mail_end - split.name_begin;
ident = anonymize_str(&idents, anonymize_ident,
- split.name_begin, len, NULL);
+ split.name_begin, len);
strbuf_addstr(out, ident);
strbuf_addch(out, ' ');
strbuf_add(out, split.date_begin, split.tz_end - split.date_begin);
@@ -618,7 +633,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
parse_commit_or_die(commit);
- commit_buffer = get_commit_buffer(commit, NULL);
+ commit_buffer = repo_get_commit_buffer(the_repository, commit, NULL);
author = strstr(commit_buffer, "\nauthor ");
if (!author)
die("could not find author in commit %s",
@@ -669,7 +684,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
mark_next_object(&commit->object);
if (anonymize) {
- reencoded = anonymize_commit_message(message);
+ reencoded = anonymize_commit_message();
} else if (encoding) {
switch(reencode_mode) {
case REENCODE_YES:
@@ -699,7 +714,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
? strlen(message) : 0),
reencoded ? reencoded : message ? message : "");
free(reencoded);
- unuse_commit_buffer(commit, commit_buffer);
+ repo_unuse_commit_buffer(the_repository, commit, commit_buffer);
for (i = 0, p = commit->parents; p; p = p->next) {
struct object *obj = &p->item->object;
@@ -732,7 +747,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
show_progress();
}
-static char *anonymize_tag(void *data)
+static char *anonymize_tag(void)
{
static int counter;
struct strbuf out = STRBUF_INIT;
@@ -766,7 +781,8 @@ static void handle_tag(const char *name, struct tag *tag)
return;
}
- buf = read_object_file(&tag->object.oid, &type, &size);
+ buf = repo_read_object_file(the_repository, &tag->object.oid, &type,
+ &size);
if (!buf)
die("could not read tag %s", oid_to_hex(&tag->object.oid));
message = memmem(buf, size, "\n\n", 2);
@@ -794,7 +810,7 @@ static void handle_tag(const char *name, struct tag *tag)
if (message) {
static struct hashmap tags;
message = anonymize_str(&tags, anonymize_tag,
- message, message_size, NULL);
+ message, message_size);
message_size = strlen(message);
}
}
@@ -917,7 +933,8 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
if (e->flags & UNINTERESTING)
continue;
- if (dwim_ref(e->name, strlen(e->name), &oid, &full_name, 0) != 1)
+ if (repo_dwim_ref(the_repository, e->name, strlen(e->name),
+ &oid, &full_name, 0) != 1)
continue;
if (refspecs.nr) {
@@ -1125,11 +1142,6 @@ static void handle_deletes(void)
}
}
-static char *anonymize_seed(void *data)
-{
- return xstrdup(data);
-}
-
static int parse_opt_anonymize_map(const struct option *opt,
const char *arg, int unset)
{
@@ -1151,7 +1163,8 @@ static int parse_opt_anonymize_map(const struct option *opt,
if (!keylen || !*value)
return error(_("--anonymize-map token cannot be empty"));
- anonymize_str(map, anonymize_seed, arg, keylen, (void *)value);
+ add_anonymized_entry(map, memhash(arg, keylen), arg, keylen,
+ xstrdup(value));
return 0;
}
@@ -1221,7 +1234,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
revs.sources = &revision_sources;
revs.rewrite_parents = 1;
argc = parse_options(argc, argv, prefix, options, fast_export_usage,
- PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN);
+ PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT);
argc = setup_revisions(argc, argv, &revs, NULL);
if (argc > 1)
usage_with_options (fast_export_usage, options);
@@ -1276,6 +1289,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
printf("done\n");
refspec_clear(&refspecs);
+ release_revisions(&revs);
return 0;
}
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 28d3193c38..4dbb10aff3 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -1,5 +1,8 @@
#include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "repository.h"
#include "config.h"
#include "lockfile.h"
@@ -9,13 +12,16 @@
#include "commit.h"
#include "delta.h"
#include "pack.h"
+#include "path.h"
#include "refs.h"
#include "csum-file.h"
#include "quote.h"
#include "dir.h"
#include "run-command.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
#include "mem-pool.h"
#include "commit-reach.h"
#include "khash.h"
@@ -46,7 +52,7 @@ struct object_entry {
depth : DEPTH_BITS;
};
-static int object_entry_hashcmp(const void *map_data,
+static int object_entry_hashcmp(const void *map_data UNUSED,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
const void *keydata)
@@ -175,6 +181,7 @@ static FILE *pack_edges;
static unsigned int show_stats = 1;
static int global_argc;
static const char **global_argv;
+static const char *global_prefix;
/* Memory pools */
static struct mem_pool fi_mem_pool = {
@@ -436,7 +443,7 @@ static void set_checkpoint_signal(void)
#else
-static void checkpoint_signal(int signo)
+static void checkpoint_signal(int signo UNUSED)
{
checkpoint_requested = 1;
}
@@ -1265,7 +1272,7 @@ static void load_tree(struct tree_entry *root)
die("Can't load tree %s", oid_to_hex(oid));
} else {
enum object_type type;
- buf = read_object_file(oid, &type, &size);
+ buf = repo_read_object_file(the_repository, oid, &type, &size);
if (!buf || type != OBJ_TREE)
die("Can't load tree %s", oid_to_hex(oid));
}
@@ -1625,7 +1632,7 @@ static int update_branch(struct branch *b)
if (!old_cmit || !new_cmit)
return error("Branch %s is missing commits.", b->name);
- if (!in_merge_bases(old_cmit, new_cmit)) {
+ if (!repo_in_merge_bases(the_repository, old_cmit, new_cmit)) {
warning("Not updating %s"
" (new tip %s does not contain %s)",
b->name, oid_to_hex(&b->oid),
@@ -2486,7 +2493,7 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
if (commit_oe->type != OBJ_COMMIT)
die("Mark :%" PRIuMAX " not a commit", commit_mark);
oidcpy(&commit_oid, &commit_oe->idx.oid);
- } else if (!get_oid(p, &commit_oid)) {
+ } else if (!repo_get_oid(the_repository, p, &commit_oid)) {
unsigned long size;
char *buf = read_object_with_reference(the_repository,
&commit_oid,
@@ -2599,7 +2606,7 @@ static int parse_objectish(struct branch *b, const char *objectish)
} else
parse_from_existing(b);
}
- } else if (!get_oid(objectish, &b->oid)) {
+ } else if (!repo_get_oid(the_repository, objectish, &b->oid)) {
parse_from_existing(b);
if (is_null_oid(&b->oid))
b->delete = 1;
@@ -2654,7 +2661,7 @@ static struct hash_list *parse_merge(unsigned int *count)
if (oe->type != OBJ_COMMIT)
die("Mark :%" PRIuMAX " not a commit", idnum);
oidcpy(&n->oid, &oe->idx.oid);
- } else if (!get_oid(from, &n->oid)) {
+ } else if (!repo_get_oid(the_repository, from, &n->oid)) {
unsigned long size;
char *buf = read_object_with_reference(the_repository,
&n->oid,
@@ -2827,7 +2834,7 @@ static void parse_new_tag(const char *arg)
oe = find_mark(marks, from_mark);
type = oe->type;
oidcpy(&oid, &oe->idx.oid);
- } else if (!get_oid(from, &oid)) {
+ } else if (!repo_get_oid(the_repository, from, &oid)) {
struct object_entry *oe = find_object(&oid);
if (!oe) {
type = oid_object_info(the_repository, &oid, NULL);
@@ -2936,7 +2943,7 @@ static void cat_blob(struct object_entry *oe, struct object_id *oid)
char *buf;
if (!oe || oe->pack_id == MAX_PACK_ID) {
- buf = read_object_file(oid, &type, &size);
+ buf = repo_read_object_file(the_repository, oid, &type, &size);
} else {
type = oe->type;
buf = gfi_unpack_entry(oe, &size);
@@ -3044,7 +3051,8 @@ static struct object_entry *dereference(struct object_entry *oe,
buf = gfi_unpack_entry(oe, &size);
} else {
enum object_type unused;
- buf = read_object_file(oid, &unused, &size);
+ buf = repo_read_object_file(the_repository, oid, &unused,
+ &size);
}
if (!buf)
die("Can't load object %s", oid_to_hex(oid));
@@ -3245,7 +3253,7 @@ static void parse_alias(void)
static char* make_fast_import_path(const char *path)
{
if (!relative_marks_paths || is_absolute_path(path))
- return xstrdup(path);
+ return prefix_filename(global_prefix, path);
return git_pathdup("info/fast-import/%s", path);
}
@@ -3316,9 +3324,11 @@ static void option_cat_blob_fd(const char *fd)
static void option_export_pack_edges(const char *edges)
{
+ char *fn = prefix_filename(global_prefix, edges);
if (pack_edges)
fclose(pack_edges);
- pack_edges = xfopen(edges, "a");
+ pack_edges = xfopen(fn, "a");
+ free(fn);
}
static void option_rewrite_submodules(const char *arg, struct string_list *list)
@@ -3333,11 +3343,13 @@ static void option_rewrite_submodules(const char *arg, struct string_list *list)
f++;
CALLOC_ARRAY(ms, 1);
+ f = prefix_filename(global_prefix, f);
fp = fopen(f, "r");
if (!fp)
die_errno("cannot read '%s'", f);
read_mark_file(&ms, fp, insert_oid_entry);
fclose(fp);
+ free(f);
string_list_insert(list, s)->util = ms;
}
@@ -3465,7 +3477,7 @@ static void git_pack_config(void)
pack_idx_opts.version = indexversion_value;
if (pack_idx_opts.version > 2)
git_die_config("pack.indexversion",
- "bad pack.indexversion=%"PRIu32, pack_idx_opts.version);
+ "bad pack.indexVersion=%"PRIu32, pack_idx_opts.version);
}
if (!git_config_get_ulong("pack.packsizelimit", &packsizelimit_value))
max_packsize = packsizelimit_value;
@@ -3551,6 +3563,7 @@ int cmd_fast_import(int argc, const char **argv, const char *prefix)
global_argc = argc;
global_argv = argv;
+ global_prefix = prefix;
rc_free = mem_pool_alloc(&fi_mem_pool, cmd_save * sizeof(*rc_free));
for (i = 0; i < (cmd_save - 1); i++)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index f045bbbe94..44c05ee86c 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -1,4 +1,7 @@
#include "builtin.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-file.h"
#include "pkt-line.h"
#include "fetch-pack.h"
#include "remote.h"
@@ -40,7 +43,7 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
(*sought)[*nr - 1] = ref;
}
-int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
+int cmd_fetch_pack(int argc, const char **argv, const char *prefix UNUSED)
{
int i, ret;
struct ref *ref = NULL;
@@ -62,6 +65,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
packet_trace_identity("fetch-pack");
memset(&args, 0, sizeof(args));
+ list_objects_filter_init(&args.filter_options);
args.uploadpack = "git-upload-pack";
for (i = 1; i < argc && *argv[i] == '-'; i++) {
@@ -210,8 +214,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
int flags = args.verbose ? CONNECT_VERBOSE : 0;
if (args.diag_url)
flags |= CONNECT_DIAG_URL;
- conn = git_connect(fd, dest, args.uploadpack,
- flags);
+ conn = git_connect(fd, dest, "git-upload-pack",
+ args.uploadpack, flags);
if (!conn)
return args.diag_url ? 0 : 1;
}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index e3791f09ed..a01ecad574 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1,15 +1,20 @@
/*
* "git fetch"
*/
-#include "cache.h"
+#include "builtin.h"
+#include "advice.h"
#include "config.h"
+#include "gettext.h"
+#include "environment.h"
+#include "hex.h"
#include "repository.h"
#include "refs.h"
#include "refspec.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
#include "oidset.h"
+#include "oid-array.h"
#include "commit.h"
-#include "builtin.h"
#include "string-list.h"
#include "remote.h"
#include "transport.h"
@@ -22,13 +27,19 @@
#include "strvec.h"
#include "utf8.h"
#include "packfile.h"
+#include "pager.h"
+#include "path.h"
+#include "pkt-line.h"
#include "list-objects-filter-options.h"
#include "commit-reach.h"
#include "branch.h"
#include "promisor-remote.h"
#include "commit-graph.h"
#include "shallow.h"
+#include "trace.h"
+#include "trace2.h"
#include "worktree.h"
+#include "bundle-uri.h"
#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
@@ -46,25 +57,35 @@ enum {
TAGS_SET = 2
};
-static int fetch_prune_config = -1; /* unspecified */
-static int fetch_show_forced_updates = 1;
+enum display_format {
+ DISPLAY_FORMAT_FULL,
+ DISPLAY_FORMAT_COMPACT,
+ DISPLAY_FORMAT_PORCELAIN,
+};
+
+struct display_state {
+ struct strbuf buf;
+
+ int refcol_width;
+ enum display_format format;
+
+ char *url;
+ int url_len, shown_url;
+};
+
static uint64_t forced_updates_ms = 0;
static int prefetch = 0;
static int prune = -1; /* unspecified */
#define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
-static int fetch_prune_tags_config = -1; /* unspecified */
static int prune_tags = -1; /* unspecified */
#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
-static int all, append, dry_run, force, keep, multiple, update_head_ok;
+static int append, dry_run, force, keep, update_head_ok;
static int write_fetch_head = 1;
static int verbosity, deepen_relative, set_upstream, refetch;
static int progress = -1;
-static int enable_auto_gc = 1;
-static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
-static int max_jobs = -1, submodule_fetch_jobs_config = -1;
-static int fetch_parallel_config = 1;
+static int tags = TAGS_DEFAULT, update_shallow, deepen;
static int atomic_fetch;
static enum transport_family family;
static const char *depth;
@@ -74,58 +95,77 @@ static struct string_list deepen_not = STRING_LIST_INIT_NODUP;
static struct strbuf default_rla = STRBUF_INIT;
static struct transport *gtransport;
static struct transport *gsecondary;
-static const char *submodule_prefix = "";
-static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
-static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
-static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
-static int shown_url = 0;
static struct refspec refmap = REFSPEC_INIT_FETCH;
-static struct list_objects_filter_options filter_options;
+static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT;
static struct string_list server_options = STRING_LIST_INIT_DUP;
static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
-static int fetch_write_commit_graph = -1;
-static int stdin_refspecs = 0;
-static int negotiate_only;
-static int git_fetch_config(const char *k, const char *v, void *cb)
+struct fetch_config {
+ enum display_format display_format;
+ int prune;
+ int prune_tags;
+ int show_forced_updates;
+ int recurse_submodules;
+ int parallel;
+ int submodule_fetch_jobs;
+};
+
+static int git_fetch_config(const char *k, const char *v,
+ const struct config_context *ctx, void *cb)
{
+ struct fetch_config *fetch_config = cb;
+
if (!strcmp(k, "fetch.prune")) {
- fetch_prune_config = git_config_bool(k, v);
+ fetch_config->prune = git_config_bool(k, v);
return 0;
}
if (!strcmp(k, "fetch.prunetags")) {
- fetch_prune_tags_config = git_config_bool(k, v);
+ fetch_config->prune_tags = git_config_bool(k, v);
return 0;
}
if (!strcmp(k, "fetch.showforcedupdates")) {
- fetch_show_forced_updates = git_config_bool(k, v);
+ fetch_config->show_forced_updates = git_config_bool(k, v);
return 0;
}
if (!strcmp(k, "submodule.recurse")) {
int r = git_config_bool(k, v) ?
RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
- recurse_submodules = r;
+ fetch_config->recurse_submodules = r;
}
if (!strcmp(k, "submodule.fetchjobs")) {
- submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v);
+ fetch_config->submodule_fetch_jobs = parse_submodule_fetchjobs(k, v, ctx->kvi);
return 0;
} else if (!strcmp(k, "fetch.recursesubmodules")) {
- recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
+ fetch_config->recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
return 0;
}
if (!strcmp(k, "fetch.parallel")) {
- fetch_parallel_config = git_config_int(k, v);
- if (fetch_parallel_config < 0)
+ fetch_config->parallel = git_config_int(k, v, ctx->kvi);
+ if (fetch_config->parallel < 0)
die(_("fetch.parallel cannot be negative"));
+ if (!fetch_config->parallel)
+ fetch_config->parallel = online_cpus();
return 0;
}
- return git_default_config(k, v, cb);
+ if (!strcmp(k, "fetch.output")) {
+ if (!v)
+ return config_error_nonbool(k);
+ else if (!strcasecmp(v, "full"))
+ fetch_config->display_format = DISPLAY_FORMAT_FULL;
+ else if (!strcasecmp(v, "compact"))
+ fetch_config->display_format = DISPLAY_FORMAT_COMPACT;
+ else
+ die(_("invalid value for '%s': '%s'"),
+ "fetch.output", v);
+ }
+
+ return git_default_config(k, v, ctx, cb);
}
static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
@@ -141,92 +181,6 @@ static int parse_refmap_arg(const struct option *opt, const char *arg, int unset
return 0;
}
-static struct option builtin_fetch_options[] = {
- OPT__VERBOSITY(&verbosity),
- OPT_BOOL(0, "all", &all,
- N_("fetch from all remotes")),
- OPT_BOOL(0, "set-upstream", &set_upstream,
- N_("set upstream for git pull/fetch")),
- OPT_BOOL('a', "append", &append,
- N_("append to .git/FETCH_HEAD instead of overwriting")),
- OPT_BOOL(0, "atomic", &atomic_fetch,
- N_("use atomic transaction to update references")),
- OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
- N_("path to upload pack on remote end")),
- OPT__FORCE(&force, N_("force overwrite of local reference"), 0),
- OPT_BOOL('m', "multiple", &multiple,
- N_("fetch from multiple remotes")),
- OPT_SET_INT('t', "tags", &tags,
- N_("fetch all tags and associated objects"), TAGS_SET),
- OPT_SET_INT('n', NULL, &tags,
- N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
- OPT_INTEGER('j', "jobs", &max_jobs,
- N_("number of submodules fetched in parallel")),
- OPT_BOOL(0, "prefetch", &prefetch,
- N_("modify the refspec to place all refs within refs/prefetch/")),
- OPT_BOOL('p', "prune", &prune,
- N_("prune remote-tracking branches no longer on remote")),
- OPT_BOOL('P', "prune-tags", &prune_tags,
- N_("prune local tags no longer on remote and clobber changed tags")),
- OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"),
- N_("control recursive fetching of submodules"),
- PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
- OPT_BOOL(0, "dry-run", &dry_run,
- N_("dry run")),
- OPT_BOOL(0, "write-fetch-head", &write_fetch_head,
- N_("write fetched references to the FETCH_HEAD file")),
- OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
- OPT_BOOL('u', "update-head-ok", &update_head_ok,
- N_("allow updating of HEAD ref")),
- OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
- OPT_STRING(0, "depth", &depth, N_("depth"),
- N_("deepen history of shallow clone")),
- OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
- N_("deepen history of shallow repository based on time")),
- OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
- N_("deepen history of shallow clone, excluding rev")),
- OPT_INTEGER(0, "deepen", &deepen_relative,
- N_("deepen history of shallow clone")),
- OPT_SET_INT_F(0, "unshallow", &unshallow,
- N_("convert to a complete repository"),
- 1, PARSE_OPT_NONEG),
- OPT_SET_INT_F(0, "refetch", &refetch,
- N_("re-fetch without negotiating common commits"),
- 1, PARSE_OPT_NONEG),
- { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
- N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
- OPT_CALLBACK_F(0, "recurse-submodules-default",
- &recurse_submodules_default, N_("on-demand"),
- N_("default for recursive fetching of submodules "
- "(lower priority than config files)"),
- PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules),
- OPT_BOOL(0, "update-shallow", &update_shallow,
- N_("accept refs that update .git/shallow")),
- OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"),
- N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg),
- OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
- OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
- TRANSPORT_FAMILY_IPV4),
- OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
- TRANSPORT_FAMILY_IPV6),
- OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
- N_("report that we have only objects reachable from this object")),
- OPT_BOOL(0, "negotiate-only", &negotiate_only,
- N_("do not fetch a packfile; instead, print ancestors of negotiation tips")),
- OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
- OPT_BOOL(0, "auto-maintenance", &enable_auto_gc,
- N_("run 'maintenance --auto' after fetching")),
- OPT_BOOL(0, "auto-gc", &enable_auto_gc,
- N_("run 'maintenance --auto' after fetching")),
- OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
- N_("check for forced-updates on all updated branches")),
- OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
- N_("write the commit-graph after fetching")),
- OPT_BOOL(0, "stdin", &stdin_refspecs,
- N_("accept refspecs from stdin")),
- OPT_END()
-};
-
static void unlock_pack(unsigned int flags)
{
if (gtransport)
@@ -301,7 +255,7 @@ struct refname_hash_entry {
char refname[FLEX_ARRAY];
};
-static int refname_hash_entry_cmp(const void *hashmap_cmp_fn_data,
+static int refname_hash_entry_cmp(const void *hashmap_cmp_fn_data UNUSED,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
const void *keydata)
@@ -329,7 +283,7 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,
static int add_one_refname(const char *refname,
const struct object_id *oid,
- int flag, void *cbdata)
+ int flag UNUSED, void *cbdata)
{
struct hashmap *refname_map = cbdata;
@@ -404,9 +358,9 @@ static void find_non_local_tags(const struct ref *refs,
*/
if (ends_with(ref->name, "^{}")) {
if (item &&
- !has_object_file_with_flags(&ref->old_oid, quick_flags) &&
+ !repo_has_object_file_with_flags(the_repository, &ref->old_oid, quick_flags) &&
!oidset_contains(&fetch_oids, &ref->old_oid) &&
- !has_object_file_with_flags(&item->oid, quick_flags) &&
+ !repo_has_object_file_with_flags(the_repository, &item->oid, quick_flags) &&
!oidset_contains(&fetch_oids, &item->oid))
clear_item(item);
item = NULL;
@@ -420,7 +374,7 @@ static void find_non_local_tags(const struct ref *refs,
* fetch.
*/
if (item &&
- !has_object_file_with_flags(&item->oid, quick_flags) &&
+ !repo_has_object_file_with_flags(the_repository, &item->oid, quick_flags) &&
!oidset_contains(&fetch_oids, &item->oid))
clear_item(item);
@@ -441,7 +395,7 @@ static void find_non_local_tags(const struct ref *refs,
* checked to see if it needs fetching.
*/
if (item &&
- !has_object_file_with_flags(&item->oid, quick_flags) &&
+ !repo_has_object_file_with_flags(the_repository, &item->oid, quick_flags) &&
!oidset_contains(&fetch_oids, &item->oid))
clear_item(item);
@@ -490,7 +444,9 @@ static void filter_prefetch_refspec(struct refspec *rs)
continue;
if (!rs->items[i].dst ||
(rs->items[i].src &&
- !strncmp(rs->items[i].src, "refs/tags/", 10))) {
+ !strncmp(rs->items[i].src,
+ ref_namespace[NAMESPACE_TAGS].ref,
+ strlen(ref_namespace[NAMESPACE_TAGS].ref)))) {
int j;
free(rs->items[i].src);
@@ -506,7 +462,7 @@ static void filter_prefetch_refspec(struct refspec *rs)
}
old_dst = rs->items[i].dst;
- strbuf_addstr(&new_dst, "refs/prefetch/");
+ strbuf_addstr(&new_dst, ref_namespace[NAMESPACE_PREFETCH].ref);
/*
* If old_dst starts with "refs/", then place
@@ -736,76 +692,97 @@ out:
return ret;
}
-static int refcol_width = 10;
-static int compact_format;
-
-static void adjust_refcol_width(const struct ref *ref)
+static int refcol_width(const struct ref *ref_map, int compact_format)
{
- int max, rlen, llen, len;
+ const struct ref *ref;
+ int max, width = 10;
- /* uptodate lines are only shown on high verbosity level */
- if (verbosity <= 0 && oideq(&ref->peer_ref->old_oid, &ref->old_oid))
- return;
+ max = term_columns();
+ if (compact_format)
+ max = max * 2 / 3;
- max = term_columns();
- rlen = utf8_strwidth(prettify_refname(ref->name));
+ for (ref = ref_map; ref; ref = ref->next) {
+ int rlen, llen = 0, len;
- llen = utf8_strwidth(prettify_refname(ref->peer_ref->name));
+ if (ref->status == REF_STATUS_REJECT_SHALLOW ||
+ !ref->peer_ref ||
+ !strcmp(ref->name, "HEAD"))
+ continue;
- /*
- * rough estimation to see if the output line is too long and
- * should not be counted (we can't do precise calculation
- * anyway because we don't know if the error explanation part
- * will be printed in update_local_ref)
- */
- if (compact_format) {
- llen = 0;
- max = max * 2 / 3;
+ /* uptodate lines are only shown on high verbosity level */
+ if (verbosity <= 0 && oideq(&ref->peer_ref->old_oid, &ref->old_oid))
+ continue;
+
+ rlen = utf8_strwidth(prettify_refname(ref->name));
+ if (!compact_format)
+ llen = utf8_strwidth(prettify_refname(ref->peer_ref->name));
+
+ /*
+ * rough estimation to see if the output line is too long and
+ * should not be counted (we can't do precise calculation
+ * anyway because we don't know if the error explanation part
+ * will be printed in update_local_ref)
+ */
+ len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
+ if (len >= max)
+ continue;
+
+ if (width < rlen)
+ width = rlen;
}
- len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
- if (len >= max)
- return;
- /*
- * Not precise calculation for compact mode because '*' can
- * appear on the left hand side of '->' and shrink the column
- * back.
- */
- if (refcol_width < rlen)
- refcol_width = rlen;
+ return width;
}
-static void prepare_format_display(struct ref *ref_map)
+static void display_state_init(struct display_state *display_state, struct ref *ref_map,
+ const char *raw_url, enum display_format format)
{
- struct ref *rm;
- const char *format = "full";
+ int i;
- if (verbosity < 0)
- return;
+ memset(display_state, 0, sizeof(*display_state));
+ strbuf_init(&display_state->buf, 0);
+ display_state->format = format;
- git_config_get_string_tmp("fetch.output", &format);
- if (!strcasecmp(format, "full"))
- compact_format = 0;
- else if (!strcasecmp(format, "compact"))
- compact_format = 1;
+ if (raw_url)
+ display_state->url = transport_anonymize_url(raw_url);
else
- die(_("invalid value for '%s': '%s'"),
- "fetch.output", format);
+ display_state->url = xstrdup("foreign");
- for (rm = ref_map; rm; rm = rm->next) {
- if (rm->status == REF_STATUS_REJECT_SHALLOW ||
- !rm->peer_ref ||
- !strcmp(rm->name, "HEAD"))
- continue;
+ display_state->url_len = strlen(display_state->url);
+ for (i = display_state->url_len - 1; display_state->url[i] == '/' && 0 <= i; i--)
+ ;
+ display_state->url_len = i + 1;
+ if (4 < i && !strncmp(".git", display_state->url + i - 3, 4))
+ display_state->url_len = i - 3;
+
+ if (verbosity < 0)
+ return;
- adjust_refcol_width(rm);
+ switch (display_state->format) {
+ case DISPLAY_FORMAT_FULL:
+ case DISPLAY_FORMAT_COMPACT:
+ display_state->refcol_width = refcol_width(ref_map,
+ display_state->format == DISPLAY_FORMAT_COMPACT);
+ break;
+ case DISPLAY_FORMAT_PORCELAIN:
+ /* We don't need to precompute anything here. */
+ break;
+ default:
+ BUG("unexpected display format %d", display_state->format);
}
}
-static void print_remote_to_local(struct strbuf *display,
+static void display_state_release(struct display_state *display_state)
+{
+ strbuf_release(&display_state->buf);
+ free(display_state->url);
+}
+
+static void print_remote_to_local(struct display_state *display_state,
const char *remote, const char *local)
{
- strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local);
+ strbuf_addf(&display_state->buf, "%-*s -> %s",
+ display_state->refcol_width, remote, local);
}
static int find_and_replace(struct strbuf *haystack,
@@ -835,14 +812,14 @@ static int find_and_replace(struct strbuf *haystack,
return 1;
}
-static void print_compact(struct strbuf *display,
+static void print_compact(struct display_state *display_state,
const char *remote, const char *local)
{
struct strbuf r = STRBUF_INIT;
struct strbuf l = STRBUF_INIT;
if (!strcmp(remote, local)) {
- strbuf_addf(display, "%-*s -> *", refcol_width, remote);
+ strbuf_addf(&display_state->buf, "%-*s -> *", display_state->refcol_width, remote);
return;
}
@@ -851,42 +828,74 @@ static void print_compact(struct strbuf *display,
if (!find_and_replace(&r, local, "*"))
find_and_replace(&l, remote, "*");
- print_remote_to_local(display, r.buf, l.buf);
+ print_remote_to_local(display_state, r.buf, l.buf);
strbuf_release(&r);
strbuf_release(&l);
}
-static void format_display(struct strbuf *display, char code,
- const char *summary, const char *error,
- const char *remote, const char *local,
- int summary_width)
+static void display_ref_update(struct display_state *display_state, char code,
+ const char *summary, const char *error,
+ const char *remote, const char *local,
+ const struct object_id *old_oid,
+ const struct object_id *new_oid,
+ int summary_width)
{
- int width;
+ FILE *f = stderr;
if (verbosity < 0)
return;
- width = (summary_width + strlen(summary) - gettext_width(summary));
+ strbuf_reset(&display_state->buf);
- strbuf_addf(display, "%c %-*s ", code, width, summary);
- if (!compact_format)
- print_remote_to_local(display, remote, local);
- else
- print_compact(display, remote, local);
- if (error)
- strbuf_addf(display, " (%s)", error);
+ switch (display_state->format) {
+ case DISPLAY_FORMAT_FULL:
+ case DISPLAY_FORMAT_COMPACT: {
+ int width;
+
+ if (!display_state->shown_url) {
+ strbuf_addf(&display_state->buf, _("From %.*s\n"),
+ display_state->url_len, display_state->url);
+ display_state->shown_url = 1;
+ }
+
+ width = (summary_width + strlen(summary) - gettext_width(summary));
+ remote = prettify_refname(remote);
+ local = prettify_refname(local);
+
+ strbuf_addf(&display_state->buf, " %c %-*s ", code, width, summary);
+
+ if (display_state->format != DISPLAY_FORMAT_COMPACT)
+ print_remote_to_local(display_state, remote, local);
+ else
+ print_compact(display_state, remote, local);
+
+ if (error)
+ strbuf_addf(&display_state->buf, " (%s)", error);
+
+ break;
+ }
+ case DISPLAY_FORMAT_PORCELAIN:
+ strbuf_addf(&display_state->buf, "%c %s %s %s", code,
+ oid_to_hex(old_oid), oid_to_hex(new_oid), local);
+ f = stdout;
+ break;
+ default:
+ BUG("unexpected display format %d", display_state->format);
+ };
+ strbuf_addch(&display_state->buf, '\n');
+
+ fputs(display_state->buf.buf, f);
}
static int update_local_ref(struct ref *ref,
struct ref_transaction *transaction,
- const char *remote, const struct ref *remote_ref,
- struct strbuf *display, int summary_width,
- struct worktree **worktrees)
+ struct display_state *display_state,
+ const struct ref *remote_ref,
+ int summary_width,
+ const struct fetch_config *config)
{
struct commit *current = NULL, *updated;
- const struct worktree *wt;
- const char *pretty_ref = prettify_refname(ref->name);
int fast_forward = 0;
if (!repo_has_object_file(the_repository, &ref->new_oid))
@@ -894,23 +903,23 @@ static int update_local_ref(struct ref *ref,
if (oideq(&ref->old_oid, &ref->new_oid)) {
if (verbosity > 0)
- format_display(display, '=', _("[up to date]"), NULL,
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, '=', _("[up to date]"), NULL,
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
return 0;
}
if (!update_head_ok &&
- (wt = find_shared_symref(worktrees, "HEAD", ref->name)) &&
- !wt->is_bare && !is_null_oid(&ref->old_oid)) {
+ !is_null_oid(&ref->old_oid) &&
+ branch_checked_out(ref->name)) {
/*
* If this is the head, and it's not okay to update
* the head, and the old value of the head isn't empty...
*/
- format_display(display, '!', _("[rejected]"),
- wt->is_current ?
- _("can't fetch in current branch") :
- _("checked out in another worktree"),
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, '!', _("[rejected]"),
+ _("can't fetch into checked-out branch"),
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
return 1;
}
@@ -919,13 +928,16 @@ static int update_local_ref(struct ref *ref,
if (force || ref->force) {
int r;
r = s_update_ref("updating tag", ref, transaction, 0);
- format_display(display, r ? '!' : 't', _("[tag update]"),
- r ? _("unable to update local ref") : NULL,
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, r ? '!' : 't', _("[tag update]"),
+ r ? _("unable to update local ref") : NULL,
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
return r;
} else {
- format_display(display, '!', _("[rejected]"), _("would clobber existing tag"),
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, '!', _("[rejected]"),
+ _("would clobber existing tag"),
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
return 1;
}
}
@@ -943,11 +955,10 @@ static int update_local_ref(struct ref *ref,
* Base this on the remote's ref name, as it's
* more likely to follow a standard layout.
*/
- const char *name = remote_ref ? remote_ref->name : "";
- if (starts_with(name, "refs/tags/")) {
+ if (starts_with(remote_ref->name, "refs/tags/")) {
msg = "storing tag";
what = _("[new tag]");
- } else if (starts_with(name, "refs/heads/")) {
+ } else if (starts_with(remote_ref->name, "refs/heads/")) {
msg = "storing head";
what = _("[new branch]");
} else {
@@ -956,15 +967,17 @@ static int update_local_ref(struct ref *ref,
}
r = s_update_ref(msg, ref, transaction, 0);
- format_display(display, r ? '!' : '*', what,
- r ? _("unable to update local ref") : NULL,
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, r ? '!' : '*', what,
+ r ? _("unable to update local ref") : NULL,
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
return r;
}
- if (fetch_show_forced_updates) {
+ if (config->show_forced_updates) {
uint64_t t_before = getnanotime();
- fast_forward = in_merge_bases(current, updated);
+ fast_forward = repo_in_merge_bases(the_repository, current,
+ updated);
forced_updates_ms += (getnanotime() - t_before) / 1000000;
} else {
fast_forward = 1;
@@ -978,9 +991,10 @@ static int update_local_ref(struct ref *ref,
strbuf_addstr(&quickref, "..");
strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
r = s_update_ref("fast-forward", ref, transaction, 1);
- format_display(display, r ? '!' : ' ', quickref.buf,
- r ? _("unable to update local ref") : NULL,
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, r ? '!' : ' ', quickref.buf,
+ r ? _("unable to update local ref") : NULL,
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
strbuf_release(&quickref);
return r;
} else if (force || ref->force) {
@@ -990,14 +1004,16 @@ static int update_local_ref(struct ref *ref,
strbuf_addstr(&quickref, "...");
strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
r = s_update_ref("forced-update", ref, transaction, 1);
- format_display(display, r ? '!' : '+', quickref.buf,
- r ? _("unable to update local ref") : _("forced update"),
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, r ? '!' : '+', quickref.buf,
+ r ? _("unable to update local ref") : _("forced update"),
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
strbuf_release(&quickref);
return r;
} else {
- format_display(display, '!', _("[rejected]"), _("non-fast-forward"),
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, '!', _("[rejected]"), _("non-fast-forward"),
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
return 1;
}
}
@@ -1107,39 +1123,35 @@ N_("it took %.2f seconds to check forced updates; you can use\n"
"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates false'\n"
"to avoid this check\n");
-static int store_updated_refs(const char *raw_url, const char *remote_name,
+static int store_updated_refs(struct display_state *display_state,
+ const char *remote_name,
int connectivity_checked,
struct ref_transaction *transaction, struct ref *ref_map,
- struct fetch_head *fetch_head, struct worktree **worktrees)
+ struct fetch_head *fetch_head,
+ const struct fetch_config *config)
{
- int url_len, i, rc = 0;
- struct strbuf note = STRBUF_INIT, err = STRBUF_INIT;
+ int rc = 0;
+ struct strbuf note = STRBUF_INIT;
const char *what, *kind;
struct ref *rm;
- char *url;
int want_status;
int summary_width = 0;
if (verbosity >= 0)
summary_width = transport_summary_width(ref_map);
- if (raw_url)
- url = transport_anonymize_url(raw_url);
- else
- url = xstrdup("foreign");
-
if (!connectivity_checked) {
struct check_connected_options opt = CHECK_CONNECTED_INIT;
+ opt.exclude_hidden_refs_section = "fetch";
rm = ref_map;
if (check_connected(iterate_ref_map, &rm, &opt)) {
- rc = error(_("%s did not send all necessary objects\n"), url);
+ rc = error(_("%s did not send all necessary objects\n"),
+ display_state->url);
goto abort;
}
}
- prepare_format_display(ref_map);
-
/*
* We do a pass for each fetch_head_status type in their enum order, so
* merged entries are written before not-for-merge. That lets readers
@@ -1199,7 +1211,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
ref->force = rm->peer_ref->force;
}
- if (recurse_submodules != RECURSE_SUBMODULES_OFF &&
+ if (config->recurse_submodules != RECURSE_SUBMODULES_OFF &&
(!rm->peer_ref || !oideq(&ref->old_oid, &ref->new_oid))) {
check_for_new_submodule_commits(&rm->old_oid);
}
@@ -1207,25 +1219,17 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
if (!strcmp(rm->name, "HEAD")) {
kind = "";
what = "";
- }
- else if (skip_prefix(rm->name, "refs/heads/", &what))
+ } else if (skip_prefix(rm->name, "refs/heads/", &what)) {
kind = "branch";
- else if (skip_prefix(rm->name, "refs/tags/", &what))
+ } else if (skip_prefix(rm->name, "refs/tags/", &what)) {
kind = "tag";
- else if (skip_prefix(rm->name, "refs/remotes/", &what))
+ } else if (skip_prefix(rm->name, "refs/remotes/", &what)) {
kind = "remote-tracking branch";
- else {
+ } else {
kind = "";
what = rm->name;
}
- url_len = strlen(url);
- for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
- ;
- url_len = i + 1;
- if (4 < i && !strncmp(".git", url + i - 3, 4))
- url_len = i - 3;
-
strbuf_reset(&note);
if (*what) {
if (*kind)
@@ -1235,13 +1239,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
append_fetch_head(fetch_head, &rm->old_oid,
rm->fetch_head_status,
- note.buf, url, url_len);
+ note.buf, display_state->url,
+ display_state->url_len);
- strbuf_reset(&note);
if (ref) {
- rc |= update_local_ref(ref, transaction, what,
- rm, &note, summary_width,
- worktrees);
+ rc |= update_local_ref(ref, transaction, display_state,
+ rm, summary_width, config);
free(ref);
} else if (write_fetch_head || dry_run) {
/*
@@ -1249,18 +1252,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
* would be written to FETCH_HEAD, if --dry-run
* is set).
*/
- format_display(&note, '*',
- *kind ? kind : "branch", NULL,
- *what ? what : "HEAD",
- "FETCH_HEAD", summary_width);
- }
- if (note.len) {
- if (!shown_url) {
- fprintf(stderr, _("From %.*s\n"),
- url_len, url);
- shown_url = 1;
- }
- fprintf(stderr, " %s\n", note.buf);
+ display_ref_update(display_state, '*',
+ *kind ? kind : "branch", NULL,
+ rm->name,
+ "FETCH_HEAD",
+ &rm->new_oid, &rm->old_oid,
+ summary_width);
}
}
}
@@ -1271,7 +1268,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
"branches"), remote_name);
if (advice_enabled(ADVICE_FETCH_SHOW_FORCED_UPDATES)) {
- if (!fetch_show_forced_updates) {
+ if (!config->show_forced_updates) {
warning(_(warn_show_forced_updates));
} else if (forced_updates_ms > FORCED_UPDATES_DELAY_WARNING_IN_MS) {
warning(_(warn_time_show_forced_updates),
@@ -1281,8 +1278,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
abort:
strbuf_release(&note);
- strbuf_release(&err);
- free(url);
return rc;
}
@@ -1320,20 +1315,22 @@ static int check_exist_and_connected(struct ref *ref_map)
* we need all direct targets to exist.
*/
for (r = rm; r; r = r->next) {
- if (!has_object_file_with_flags(&r->old_oid,
- OBJECT_INFO_SKIP_FETCH_OBJECT))
+ if (!repo_has_object_file_with_flags(the_repository, &r->old_oid,
+ OBJECT_INFO_SKIP_FETCH_OBJECT))
return -1;
}
opt.quiet = 1;
+ opt.exclude_hidden_refs_section = "fetch";
return check_connected(iterate_ref_map, &rm, &opt);
}
-static int fetch_and_consume_refs(struct transport *transport,
+static int fetch_and_consume_refs(struct display_state *display_state,
+ struct transport *transport,
struct ref_transaction *transaction,
struct ref *ref_map,
struct fetch_head *fetch_head,
- struct worktree **worktrees)
+ const struct fetch_config *config)
{
int connectivity_checked = 1;
int ret;
@@ -1354,9 +1351,9 @@ static int fetch_and_consume_refs(struct transport *transport,
}
trace2_region_enter("fetch", "consume_refs", the_repository);
- ret = store_updated_refs(transport->url, transport->remote->name,
+ ret = store_updated_refs(display_state, transport->remote->name,
connectivity_checked, transaction, ref_map,
- fetch_head, worktrees);
+ fetch_head, config);
trace2_region_leave("fetch", "consume_refs", the_repository);
out:
@@ -1364,32 +1361,18 @@ out:
return ret;
}
-static int prune_refs(struct refspec *rs,
+static int prune_refs(struct display_state *display_state,
+ struct refspec *rs,
struct ref_transaction *transaction,
- struct ref *ref_map,
- const char *raw_url)
+ struct ref *ref_map)
{
- int url_len, i, result = 0;
+ int result = 0;
struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map);
struct strbuf err = STRBUF_INIT;
- char *url;
const char *dangling_msg = dry_run
? _(" (%s will become dangling)")
: _(" (%s has become dangling)");
- if (raw_url)
- url = transport_anonymize_url(raw_url);
- else
- url = xstrdup("foreign");
-
- url_len = strlen(url);
- for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
- ;
-
- url_len = i + 1;
- if (4 < i && !strncmp(".git", url + i - 3, 4))
- url_len = i - 3;
-
if (!dry_run) {
if (transaction) {
for (ref = stale_refs; ref; ref = ref->next) {
@@ -1413,39 +1396,30 @@ static int prune_refs(struct refspec *rs,
int summary_width = transport_summary_width(stale_refs);
for (ref = stale_refs; ref; ref = ref->next) {
- struct strbuf sb = STRBUF_INIT;
- if (!shown_url) {
- fprintf(stderr, _("From %.*s\n"), url_len, url);
- shown_url = 1;
- }
- format_display(&sb, '-', _("[deleted]"), NULL,
- _("(none)"), prettify_refname(ref->name),
- summary_width);
- fprintf(stderr, " %s\n",sb.buf);
- strbuf_release(&sb);
+ display_ref_update(display_state, '-', _("[deleted]"), NULL,
+ _("(none)"), ref->name,
+ &ref->new_oid, &ref->old_oid,
+ summary_width);
warn_dangling_symref(stderr, dangling_msg, ref->name);
}
}
cleanup:
strbuf_release(&err);
- free(url);
free_refs(stale_refs);
return result;
}
-static void check_not_current_branch(struct ref *ref_map,
- struct worktree **worktrees)
+static void check_not_current_branch(struct ref *ref_map)
{
- const struct worktree *wt;
+ const char *path;
for (; ref_map; ref_map = ref_map->next)
if (ref_map->peer_ref &&
- (wt = find_shared_symref(worktrees, "HEAD",
- ref_map->peer_ref->name)) &&
- !wt->is_bare)
+ starts_with(ref_map->peer_ref->name, "refs/heads/") &&
+ (path = branch_checked_out(ref_map->peer_ref->name)))
die(_("refusing to fetch into branch '%s' "
"checked out at '%s'"),
- ref_map->peer_ref->name, wt->path);
+ ref_map->peer_ref->name, path);
}
static int truncate_fetch_head(void)
@@ -1471,8 +1445,9 @@ static void set_option(struct transport *transport, const char *name, const char
}
-static int add_oid(const char *refname, const struct object_id *oid, int flags,
- void *cb_data)
+static int add_oid(const char *refname UNUSED,
+ const struct object_id *oid,
+ int flags UNUSED, void *cb_data)
{
struct oid_array *oids = cb_data;
@@ -1490,7 +1465,7 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
int old_nr;
if (!has_glob_specials(s)) {
struct object_id oid;
- if (get_oid(s, &oid))
+ if (repo_get_oid(the_repository, s, &oid))
die(_("%s is not a valid object"), s);
if (!has_object(the_repository, &oid, 0))
die(_("the object %s does not exist"), s);
@@ -1545,11 +1520,12 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
return transport;
}
-static int backfill_tags(struct transport *transport,
+static int backfill_tags(struct display_state *display_state,
+ struct transport *transport,
struct ref_transaction *transaction,
struct ref *ref_map,
struct fetch_head *fetch_head,
- struct worktree **worktrees)
+ const struct fetch_config *config)
{
int retcode, cannot_reuse;
@@ -1570,7 +1546,8 @@ static int backfill_tags(struct transport *transport,
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
- retcode = fetch_and_consume_refs(transport, transaction, ref_map, fetch_head, worktrees);
+ retcode = fetch_and_consume_refs(display_state, transport, transaction, ref_map,
+ fetch_head, config);
if (gsecondary) {
transport_disconnect(gsecondary);
@@ -1581,17 +1558,18 @@ static int backfill_tags(struct transport *transport,
}
static int do_fetch(struct transport *transport,
- struct refspec *rs)
+ struct refspec *rs,
+ const struct fetch_config *config)
{
struct ref_transaction *transaction = NULL;
struct ref *ref_map = NULL;
+ struct display_state display_state = { 0 };
int autotags = (transport->remote->fetch_tags == 1);
int retcode = 0;
const struct ref *remote_refs;
struct transport_ls_refs_options transport_ls_refs_options =
TRANSPORT_LS_REFS_OPTIONS_INIT;
int must_list_refs = 1;
- struct worktree **worktrees = get_worktrees();
struct fetch_head fetch_head = { 0 };
struct strbuf err = STRBUF_INIT;
@@ -1625,9 +1603,21 @@ static int do_fetch(struct transport *transport,
break;
}
}
- } else if (transport->remote && transport->remote->fetch.nr)
- refspec_ref_prefixes(&transport->remote->fetch,
- &transport_ls_refs_options.ref_prefixes);
+ } else {
+ struct branch *branch = branch_get(NULL);
+
+ if (transport->remote->fetch.nr)
+ refspec_ref_prefixes(&transport->remote->fetch,
+ &transport_ls_refs_options.ref_prefixes);
+ if (branch_has_merge_config(branch) &&
+ !strcmp(branch->remote_name, transport->remote->name)) {
+ int i;
+ for (i = 0; i < branch->merge_nr; i++) {
+ strvec_push(&transport_ls_refs_options.ref_prefixes,
+ branch->merge[i]->src);
+ }
+ }
+ }
if (tags == TAGS_SET || tags == TAGS_DEFAULT) {
must_list_refs = 1;
@@ -1649,12 +1639,15 @@ static int do_fetch(struct transport *transport,
ref_map = get_ref_map(transport->remote, remote_refs, rs,
tags, &autotags);
if (!update_head_ok)
- check_not_current_branch(ref_map, worktrees);
+ check_not_current_branch(ref_map);
retcode = open_fetch_head(&fetch_head);
if (retcode)
goto cleanup;
+ display_state_init(&display_state, ref_map, transport->url,
+ config->display_format);
+
if (atomic_fetch) {
transaction = ref_transaction_begin(&err);
if (!transaction) {
@@ -1672,17 +1665,17 @@ static int do_fetch(struct transport *transport,
* don't care whether --tags was specified.
*/
if (rs->nr) {
- retcode = prune_refs(rs, transaction, ref_map, transport->url);
+ retcode = prune_refs(&display_state, rs, transaction, ref_map);
} else {
- retcode = prune_refs(&transport->remote->fetch,
- transaction, ref_map,
- transport->url);
+ retcode = prune_refs(&display_state, &transport->remote->fetch,
+ transaction, ref_map);
}
if (retcode != 0)
retcode = 1;
}
- if (fetch_and_consume_refs(transport, transaction, ref_map, &fetch_head, worktrees)) {
+ if (fetch_and_consume_refs(&display_state, transport, transaction, ref_map,
+ &fetch_head, config)) {
retcode = 1;
goto cleanup;
}
@@ -1704,8 +1697,8 @@ static int do_fetch(struct transport *transport,
* when `--atomic` is passed: in that case we'll abort
* the transaction and don't commit anything.
*/
- if (backfill_tags(transport, transaction, tags_ref_map,
- &fetch_head, worktrees))
+ if (backfill_tags(&display_state, transport, transaction, tags_ref_map,
+ &fetch_head, config))
retcode = 1;
}
@@ -1787,10 +1780,10 @@ cleanup:
error("%s", err.buf);
}
+ display_state_release(&display_state);
close_fetch_head(&fetch_head);
strbuf_release(&err);
free_refs(ref_map);
- free_worktrees(worktrees);
return retcode;
}
@@ -1807,7 +1800,9 @@ struct remote_group_data {
struct string_list *list;
};
-static int get_remote_group(const char *key, const char *value, void *priv)
+static int get_remote_group(const char *key, const char *value,
+ const struct config_context *ctx UNUSED,
+ void *priv)
{
struct remote_group_data *g = priv;
@@ -1842,7 +1837,8 @@ static int add_remote_or_group(const char *name, struct string_list *list)
return 1;
}
-static void add_options_to_argv(struct strvec *argv)
+static void add_options_to_argv(struct strvec *argv,
+ const struct fetch_config *config)
{
if (dry_run)
strvec_push(argv, "--dry-run");
@@ -1856,9 +1852,11 @@ static void add_options_to_argv(struct strvec *argv)
strvec_push(argv, "--force");
if (keep)
strvec_push(argv, "--keep");
- if (recurse_submodules == RECURSE_SUBMODULES_ON)
+ if (config->recurse_submodules == RECURSE_SUBMODULES_ON)
strvec_push(argv, "--recurse-submodules");
- else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
+ else if (config->recurse_submodules == RECURSE_SUBMODULES_OFF)
+ strvec_push(argv, "--no-recurse-submodules");
+ else if (config->recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
strvec_push(argv, "--recurse-submodules=on-demand");
if (tags == TAGS_SET)
strvec_push(argv, "--tags");
@@ -1874,6 +1872,10 @@ static void add_options_to_argv(struct strvec *argv)
strvec_push(argv, "--ipv4");
else if (family == TRANSPORT_FAMILY_IPV6)
strvec_push(argv, "--ipv6");
+ if (!write_fetch_head)
+ strvec_push(argv, "--no-write-fetch-head");
+ if (config->display_format == DISPLAY_FORMAT_PORCELAIN)
+ strvec_pushf(argv, "--porcelain");
}
/* Fetch multiple remotes in parallel */
@@ -1882,9 +1884,11 @@ struct parallel_fetch_state {
const char **argv;
struct string_list *remotes;
int next, result;
+ const struct fetch_config *config;
};
-static int fetch_next_remote(struct child_process *cp, struct strbuf *out,
+static int fetch_next_remote(struct child_process *cp,
+ struct strbuf *out UNUSED,
void *cb, void **task_cb)
{
struct parallel_fetch_state *state = cb;
@@ -1900,13 +1904,14 @@ static int fetch_next_remote(struct child_process *cp, struct strbuf *out,
strvec_push(&cp->args, remote);
cp->git_cmd = 1;
- if (verbosity >= 0)
+ if (verbosity >= 0 && state->config->display_format != DISPLAY_FORMAT_PORCELAIN)
printf(_("Fetching %s\n"), remote);
return 1;
}
-static int fetch_failed_to_start(struct strbuf *out, void *cb, void *task_cb)
+static int fetch_failed_to_start(struct strbuf *out UNUSED,
+ void *cb, void *task_cb)
{
struct parallel_fetch_state *state = cb;
const char *remote = task_cb;
@@ -1931,7 +1936,8 @@ static int fetch_finished(int result, struct strbuf *out,
return 0;
}
-static int fetch_multiple(struct string_list *list, int max_children)
+static int fetch_multiple(struct string_list *list, int max_children,
+ const struct fetch_config *config)
{
int i, result = 0;
struct strvec argv = STRVEC_INIT;
@@ -1942,34 +1948,47 @@ static int fetch_multiple(struct string_list *list, int max_children)
return errcode;
}
- strvec_pushl(&argv, "fetch", "--append", "--no-auto-gc",
+ /*
+ * Cancel out the fetch.bundleURI config when running subprocesses,
+ * to avoid fetching from the same bundle list multiple times.
+ */
+ strvec_pushl(&argv, "-c", "fetch.bundleURI=",
+ "fetch", "--append", "--no-auto-gc",
"--no-write-commit-graph", NULL);
- add_options_to_argv(&argv);
+ add_options_to_argv(&argv, config);
if (max_children != 1 && list->nr != 1) {
- struct parallel_fetch_state state = { argv.v, list, 0, 0 };
+ struct parallel_fetch_state state = { argv.v, list, 0, 0, config };
+ const struct run_process_parallel_opts opts = {
+ .tr2_category = "fetch",
+ .tr2_label = "parallel/fetch",
+
+ .processes = max_children,
+
+ .get_next_task = &fetch_next_remote,
+ .start_failure = &fetch_failed_to_start,
+ .task_finished = &fetch_finished,
+ .data = &state,
+ };
strvec_push(&argv, "--end-of-options");
- result = run_processes_parallel_tr2(max_children,
- &fetch_next_remote,
- &fetch_failed_to_start,
- &fetch_finished,
- &state,
- "fetch", "parallel/fetch");
-
- if (!result)
- result = state.result;
+
+ run_processes_parallel(&opts);
+ result = state.result;
} else
for (i = 0; i < list->nr; i++) {
const char *name = list->items[i].string;
- strvec_push(&argv, name);
- if (verbosity >= 0)
+ struct child_process cmd = CHILD_PROCESS_INIT;
+
+ strvec_pushv(&cmd.args, argv.v);
+ strvec_push(&cmd.args, name);
+ if (verbosity >= 0 && config->display_format != DISPLAY_FORMAT_PORCELAIN)
printf(_("Fetching %s\n"), name);
- if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
+ cmd.git_cmd = 1;
+ if (run_command(&cmd)) {
error(_("could not fetch %s"), name);
result = 1;
}
- strvec_pop(&argv);
}
strvec_clear(&argv);
@@ -1993,7 +2012,7 @@ static inline void fetch_one_setup_partial(struct remote *remote)
* If no prior partial clone/fetch and the current fetch DID NOT
* request a partial-fetch, do a normal fetch.
*/
- if (!has_promisor_remote() && !filter_options.choice)
+ if (!repo_has_promisor_remote(the_repository) && !filter_options.choice)
return;
/*
@@ -2018,7 +2037,8 @@ static inline void fetch_one_setup_partial(struct remote *remote)
}
static int fetch_one(struct remote *remote, int argc, const char **argv,
- int prune_tags_ok, int use_stdin_refspecs)
+ int prune_tags_ok, int use_stdin_refspecs,
+ const struct fetch_config *config)
{
struct refspec rs = REFSPEC_INIT_FETCH;
int i;
@@ -2036,8 +2056,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
/* no command line request */
if (0 <= remote->prune)
prune = remote->prune;
- else if (0 <= fetch_prune_config)
- prune = fetch_prune_config;
+ else if (0 <= config->prune)
+ prune = config->prune;
else
prune = PRUNE_BY_DEFAULT;
}
@@ -2046,8 +2066,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
/* no command line request */
if (0 <= remote->prune_tags)
prune_tags = remote->prune_tags;
- else if (0 <= fetch_prune_tags_config)
- prune_tags = fetch_prune_tags_config;
+ else if (0 <= config->prune_tags)
+ prune_tags = config->prune_tags;
else
prune_tags = PRUNE_TAGS_BY_DEFAULT;
}
@@ -2085,7 +2105,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
sigchain_push_common(unlock_pack_on_signal);
atexit(unlock_pack_atexit);
sigchain_push(SIGPIPE, SIG_IGN);
- exit_code = do_fetch(gtransport, &rs);
+ exit_code = do_fetch(gtransport, &rs, config);
sigchain_pop(SIGPIPE);
refspec_clear(&rs);
transport_disconnect(gtransport);
@@ -2095,11 +2115,119 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
int cmd_fetch(int argc, const char **argv, const char *prefix)
{
- int i;
+ struct fetch_config config = {
+ .display_format = DISPLAY_FORMAT_FULL,
+ .prune = -1,
+ .prune_tags = -1,
+ .show_forced_updates = 1,
+ .recurse_submodules = RECURSE_SUBMODULES_DEFAULT,
+ .parallel = 1,
+ .submodule_fetch_jobs = -1,
+ };
+ const char *submodule_prefix = "";
+ const char *bundle_uri;
struct string_list list = STRING_LIST_INIT_DUP;
struct remote *remote = NULL;
+ int all = 0, multiple = 0;
int result = 0;
int prune_tags_ok = 1;
+ int enable_auto_gc = 1;
+ int unshallow = 0;
+ int max_jobs = -1;
+ int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
+ int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
+ int fetch_write_commit_graph = -1;
+ int stdin_refspecs = 0;
+ int negotiate_only = 0;
+ int porcelain = 0;
+ int i;
+
+ struct option builtin_fetch_options[] = {
+ OPT__VERBOSITY(&verbosity),
+ OPT_BOOL(0, "all", &all,
+ N_("fetch from all remotes")),
+ OPT_BOOL(0, "set-upstream", &set_upstream,
+ N_("set upstream for git pull/fetch")),
+ OPT_BOOL('a', "append", &append,
+ N_("append to .git/FETCH_HEAD instead of overwriting")),
+ OPT_BOOL(0, "atomic", &atomic_fetch,
+ N_("use atomic transaction to update references")),
+ OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
+ N_("path to upload pack on remote end")),
+ OPT__FORCE(&force, N_("force overwrite of local reference"), 0),
+ OPT_BOOL('m', "multiple", &multiple,
+ N_("fetch from multiple remotes")),
+ OPT_SET_INT('t', "tags", &tags,
+ N_("fetch all tags and associated objects"), TAGS_SET),
+ OPT_SET_INT('n', NULL, &tags,
+ N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
+ OPT_INTEGER('j', "jobs", &max_jobs,
+ N_("number of submodules fetched in parallel")),
+ OPT_BOOL(0, "prefetch", &prefetch,
+ N_("modify the refspec to place all refs within refs/prefetch/")),
+ OPT_BOOL('p', "prune", &prune,
+ N_("prune remote-tracking branches no longer on remote")),
+ OPT_BOOL('P', "prune-tags", &prune_tags,
+ N_("prune local tags no longer on remote and clobber changed tags")),
+ OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"),
+ N_("control recursive fetching of submodules"),
+ PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
+ OPT_BOOL(0, "dry-run", &dry_run,
+ N_("dry run")),
+ OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
+ OPT_BOOL(0, "write-fetch-head", &write_fetch_head,
+ N_("write fetched references to the FETCH_HEAD file")),
+ OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
+ OPT_BOOL('u', "update-head-ok", &update_head_ok,
+ N_("allow updating of HEAD ref")),
+ OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
+ OPT_STRING(0, "depth", &depth, N_("depth"),
+ N_("deepen history of shallow clone")),
+ OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
+ N_("deepen history of shallow repository based on time")),
+ OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
+ N_("deepen history of shallow clone, excluding rev")),
+ OPT_INTEGER(0, "deepen", &deepen_relative,
+ N_("deepen history of shallow clone")),
+ OPT_SET_INT_F(0, "unshallow", &unshallow,
+ N_("convert to a complete repository"),
+ 1, PARSE_OPT_NONEG),
+ OPT_SET_INT_F(0, "refetch", &refetch,
+ N_("re-fetch without negotiating common commits"),
+ 1, PARSE_OPT_NONEG),
+ { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
+ N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
+ OPT_CALLBACK_F(0, "recurse-submodules-default",
+ &recurse_submodules_default, N_("on-demand"),
+ N_("default for recursive fetching of submodules "
+ "(lower priority than config files)"),
+ PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules),
+ OPT_BOOL(0, "update-shallow", &update_shallow,
+ N_("accept refs that update .git/shallow")),
+ OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"),
+ N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg),
+ OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
+ OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
+ TRANSPORT_FAMILY_IPV4),
+ OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
+ TRANSPORT_FAMILY_IPV6),
+ OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
+ N_("report that we have only objects reachable from this object")),
+ OPT_BOOL(0, "negotiate-only", &negotiate_only,
+ N_("do not fetch a packfile; instead, print ancestors of negotiation tips")),
+ OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+ OPT_BOOL(0, "auto-maintenance", &enable_auto_gc,
+ N_("run 'maintenance --auto' after fetching")),
+ OPT_BOOL(0, "auto-gc", &enable_auto_gc,
+ N_("run 'maintenance --auto' after fetching")),
+ OPT_BOOL(0, "show-forced-updates", &config.show_forced_updates,
+ N_("check for forced-updates on all updated branches")),
+ OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
+ N_("write the commit-graph after fetching")),
+ OPT_BOOL(0, "stdin", &stdin_refspecs,
+ N_("accept refspecs from stdin")),
+ OPT_END()
+ };
packet_trace_identity("fetch");
@@ -2113,7 +2241,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
free(anon);
}
- git_config(git_fetch_config, NULL);
+ git_config(git_fetch_config, &config);
if (the_repository->gitdir) {
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
@@ -2123,7 +2251,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
builtin_fetch_options, builtin_fetch_usage, 0);
if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT)
- recurse_submodules = recurse_submodules_cli;
+ config.recurse_submodules = recurse_submodules_cli;
if (negotiate_only) {
switch (recurse_submodules_cli) {
@@ -2134,7 +2262,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
* submodules. Skip it by setting recurse_submodules to
* RECURSE_SUBMODULES_OFF.
*/
- recurse_submodules = RECURSE_SUBMODULES_OFF;
+ config.recurse_submodules = RECURSE_SUBMODULES_OFF;
break;
default:
@@ -2143,15 +2271,35 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
}
}
- if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
- int *sfjc = submodule_fetch_jobs_config == -1
- ? &submodule_fetch_jobs_config : NULL;
- int *rs = recurse_submodules == RECURSE_SUBMODULES_DEFAULT
- ? &recurse_submodules : NULL;
+ if (config.recurse_submodules != RECURSE_SUBMODULES_OFF) {
+ int *sfjc = config.submodule_fetch_jobs == -1
+ ? &config.submodule_fetch_jobs : NULL;
+ int *rs = config.recurse_submodules == RECURSE_SUBMODULES_DEFAULT
+ ? &config.recurse_submodules : NULL;
fetch_config_from_gitmodules(sfjc, rs);
}
+
+ if (porcelain) {
+ switch (recurse_submodules_cli) {
+ case RECURSE_SUBMODULES_OFF:
+ case RECURSE_SUBMODULES_DEFAULT:
+ /*
+ * Reference updates in submodules would be ambiguous
+ * in porcelain mode, so we reject this combination.
+ */
+ config.recurse_submodules = RECURSE_SUBMODULES_OFF;
+ break;
+
+ default:
+ die(_("options '%s' and '%s' cannot be used together"),
+ "--porcelain", "--recurse-submodules");
+ }
+
+ config.display_format = DISPLAY_FORMAT_PORCELAIN;
+ }
+
if (negotiate_only && !negotiation_tip.nr)
die(_("--negotiate-only needs one or more --negotiation-tip=*"));
@@ -2181,12 +2329,23 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (dry_run)
write_fetch_head = 0;
+ if (!max_jobs)
+ max_jobs = online_cpus();
+
+ if (!git_config_get_string_tmp("fetch.bundleuri", &bundle_uri) &&
+ fetch_bundle_uri(the_repository, bundle_uri, NULL))
+ warning(_("failed to fetch bundles from '%s'"), bundle_uri);
+
if (all) {
if (argc == 1)
die(_("fetch --all does not take a repository argument"));
else if (argc > 1)
die(_("fetch --all does not make sense with refspecs"));
(void) for_each_remote(get_one_remote_for_fetch, &list);
+
+ /* do not do fetch_multiple() of one */
+ if (list.nr == 1)
+ remote = remote_get(list.items[0].string);
} else if (argc == 0) {
/* No arguments -- use default remote */
remote = remote_get(NULL);
@@ -2211,6 +2370,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
argv++;
}
}
+ string_list_remove_duplicates(&list, 0);
if (negotiate_only) {
struct oidset acked_commits = OIDSET_INIT;
@@ -2236,9 +2396,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
printf("%s\n", oid_to_hex(oid));
oidset_clear(&acked_commits);
} else if (remote) {
- if (filter_options.choice || has_promisor_remote())
+ if (filter_options.choice || repo_has_promisor_remote(the_repository))
fetch_one_setup_partial(remote);
- result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs);
+ result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs,
+ &config);
} else {
int max_children = max_jobs;
@@ -2255,26 +2416,35 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
"from one remote"));
if (max_children < 0)
- max_children = fetch_parallel_config;
+ max_children = config.parallel;
/* TODO should this also die if we have a previous partial-clone? */
- result = fetch_multiple(&list, max_children);
+ result = fetch_multiple(&list, max_children, &config);
}
- if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
+ /*
+ * This is only needed after fetch_one(), which does not fetch
+ * submodules by itself.
+ *
+ * When we fetch from multiple remotes, fetch_multiple() has
+ * already updated submodules to grab commits necessary for
+ * the fetched history from each remote, so there is no need
+ * to fetch submodules from here.
+ */
+ if (!result && remote && (config.recurse_submodules != RECURSE_SUBMODULES_OFF)) {
struct strvec options = STRVEC_INIT;
int max_children = max_jobs;
if (max_children < 0)
- max_children = submodule_fetch_jobs_config;
+ max_children = config.submodule_fetch_jobs;
if (max_children < 0)
- max_children = fetch_parallel_config;
+ max_children = config.parallel;
- add_options_to_argv(&options);
+ add_options_to_argv(&options, &config);
result = fetch_submodules(the_repository,
&options,
submodule_prefix,
- recurse_submodules,
+ config.recurse_submodules,
recurse_submodules_default,
verbosity < 0,
max_children);
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 8d8fd393f8..0f9855b680 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -1,6 +1,7 @@
#include "builtin.h"
#include "config.h"
#include "fmt-merge-msg.h"
+#include "gettext.h"
#include "parse-options.h"
static const char * const fmt_merge_msg_usage[] = {
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 6f62f40d12..350bfa6e81 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -1,10 +1,13 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
+#include "gettext.h"
#include "refs.h"
#include "object.h"
#include "parse-options.h"
#include "ref-filter.h"
+#include "strbuf.h"
+#include "strvec.h"
+#include "commit-reach.h"
static char const * const for_each_ref_usage[] = {
N_("git for-each-ref [<options>] [<pattern>]"),
@@ -19,12 +22,14 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
int i;
struct ref_sorting *sorting;
struct string_list sorting_options = STRING_LIST_INIT_DUP;
- int maxcount = 0, icase = 0;
+ int maxcount = 0, icase = 0, omit_empty = 0;
struct ref_array array;
- struct ref_filter filter;
+ struct ref_filter filter = REF_FILTER_INIT;
struct ref_format format = REF_FORMAT_INIT;
struct strbuf output = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
+ int from_stdin = 0;
+ struct strvec vec = STRVEC_INIT;
struct option opts[] = {
OPT_BIT('s', "shell", &format.quote_style,
@@ -35,11 +40,14 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
N_("quote placeholders suitably for python"), QUOTE_PYTHON),
OPT_BIT(0 , "tcl", &format.quote_style,
N_("quote placeholders suitably for Tcl"), QUOTE_TCL),
+ OPT_BOOL(0, "omit-empty", &omit_empty,
+ N_("do not output a newline after empty formatted refs")),
OPT_GROUP(""),
OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
OPT__COLOR(&format.use_color, N_("respect format colors")),
+ OPT_REF_FILTER_EXCLUDE(&filter),
OPT_REF_SORT(&sorting_options),
OPT_CALLBACK(0, "points-at", &filter.points_at,
N_("object"), N_("print only refs which points at the given object"),
@@ -49,11 +57,11 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
+ OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")),
OPT_END(),
};
memset(&array, 0, sizeof(array));
- memset(&filter, 0, sizeof(filter));
format.format = "%(objectname) %(objecttype)\t%(refname)";
@@ -75,9 +83,27 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
filter.ignore_case = icase;
- filter.name_patterns = argv;
+ if (from_stdin) {
+ struct strbuf line = STRBUF_INIT;
+
+ if (argv[0])
+ die(_("unknown arguments supplied with --stdin"));
+
+ while (strbuf_getline(&line, stdin) != EOF)
+ strvec_push(&vec, line.buf);
+
+ strbuf_release(&line);
+
+ /* vec.v is NULL-terminated, just like 'argv'. */
+ filter.name_patterns = vec.v;
+ } else {
+ filter.name_patterns = argv;
+ }
+
filter.match_as_path = 1;
filter_refs(&array, &filter, FILTER_REFS_ALL);
+ filter_ahead_behind(the_repository, &format, &array);
+
ref_array_sort(sorting, &array);
if (!maxcount || array.nr < maxcount)
@@ -88,14 +114,15 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
if (format_ref_array_item(array.items[i], &format, &output, &err))
die("%s", err.buf);
fwrite(output.buf, 1, output.len, stdout);
- putchar('\n');
+ if (output.len || !omit_empty)
+ putchar('\n');
}
strbuf_release(&err);
strbuf_release(&output);
ref_array_clear(&array);
- free_commit_list(filter.with_commit);
- free_commit_list(filter.no_commit);
+ ref_filter_clear(&filter);
ref_sorting_release(sorting);
+ strvec_clear(&vec);
return 0;
}
diff --git a/builtin/for-each-repo.c b/builtin/for-each-repo.c
index fd86e5a861..28186b30f5 100644
--- a/builtin/for-each-repo.c
+++ b/builtin/for-each-repo.c
@@ -1,12 +1,14 @@
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
+#include "config.h"
+#include "gettext.h"
#include "parse-options.h"
+#include "path.h"
+#include "repository.h"
#include "run-command.h"
#include "string-list.h"
static const char * const for_each_repo_usage[] = {
- N_("git for-each-repo --config=<config> <command-args>"),
+ N_("git for-each-repo --config=<config> [--] <arguments>"),
NULL
};
@@ -14,13 +16,16 @@ static int run_command_on_repo(const char *path, int argc, const char ** argv)
{
int i;
struct child_process child = CHILD_PROCESS_INIT;
+ char *abspath = interpolate_path(path, 0);
child.git_cmd = 1;
- strvec_pushl(&child.args, "-C", path, NULL);
+ strvec_pushl(&child.args, "-C", abspath, NULL);
for (i = 0; i < argc; i++)
strvec_push(&child.args, argv[i]);
+ free(abspath);
+
return run_command(&child);
}
@@ -29,6 +34,7 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
static const char *config_key = NULL;
int i, result = 0;
const struct string_list *values;
+ int err;
const struct option options[] = {
OPT_STRING(0, "config", &config_key, N_("config"),
@@ -42,14 +48,11 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
if (!config_key)
die(_("missing --config=<config>"));
- values = repo_config_get_value_multi(the_repository,
- config_key);
-
- /*
- * Do nothing on an empty list, which is equivalent to the case
- * where the config variable does not exist at all.
- */
- if (!values)
+ err = repo_config_get_string_multi(the_repository, config_key, &values);
+ if (err < 0)
+ usage_msg_optf(_("got bad config --config=%s"),
+ for_each_repo_usage, options, config_key);
+ else if (err)
return 0;
for (i = 0; !result && i < values->nr; i++)
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 9e54892311..768bebe268 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -1,6 +1,6 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
#include "builtin.h"
-#include "cache.h"
+#include "gettext.h"
+#include "hex.h"
#include "repository.h"
#include "config.h"
#include "commit.h"
@@ -18,9 +18,18 @@
#include "streaming.h"
#include "decorate.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
+#include "read-cache-ll.h"
+#include "replace-object.h"
+#include "resolve-undo.h"
#include "run-command.h"
+#include "sparse-index.h"
#include "worktree.h"
+#include "pack-revindex.h"
+#include "pack-bitmap.h"
#define REACHABLE 0x0001
#define SEEN 0x0002
@@ -50,6 +59,8 @@ static int name_objects;
#define ERROR_REFS 010
#define ERROR_COMMIT_GRAPH 020
#define ERROR_MULTI_PACK_INDEX 040
+#define ERROR_PACK_REV_INDEX 0100
+#define ERROR_BITMAP 0200
static const char *describe_object(const struct object_id *oid)
{
@@ -227,20 +238,22 @@ static void mark_unreachable_referents(const struct object_id *oid)
options.walk = mark_used;
fsck_walk(obj, NULL, &options);
+ if (obj->type == OBJ_TREE)
+ free_tree_buffer((struct tree *)obj);
}
static int mark_loose_unreachable_referents(const struct object_id *oid,
- const char *path,
- void *data)
+ const char *path UNUSED,
+ void *data UNUSED)
{
mark_unreachable_referents(oid);
return 0;
}
static int mark_packed_unreachable_referents(const struct object_id *oid,
- struct packed_git *pack,
- uint32_t pos,
- void *data)
+ struct packed_git *pack UNUSED,
+ uint32_t pos UNUSED,
+ void *data UNUSED)
{
mark_unreachable_referents(oid);
return 0;
@@ -436,9 +449,6 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
out:
if (obj->type == OBJ_TREE)
free_tree_buffer((struct tree *)obj);
- if (obj->type == OBJ_COMMIT)
- free_commit_buffer(the_repository->parsed_objects,
- (struct commit *)obj);
return err;
}
@@ -487,8 +497,9 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
}
static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
- const char *email, timestamp_t timestamp, int tz,
- const char *message, void *cb_data)
+ const char *email UNUSED,
+ timestamp_t timestamp, int tz UNUSED,
+ const char *message UNUSED, void *cb_data)
{
const char *refname = cb_data;
@@ -501,8 +512,9 @@ static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid
return 0;
}
-static int fsck_handle_reflog(const char *logname, const struct object_id *oid,
- int flag, void *cb_data)
+static int fsck_handle_reflog(const char *logname,
+ const struct object_id *oid UNUSED,
+ int flag UNUSED, void *cb_data)
{
struct strbuf refname = STRBUF_INIT;
@@ -513,7 +525,7 @@ static int fsck_handle_reflog(const char *logname, const struct object_id *oid,
}
static int fsck_handle_ref(const char *refname, const struct object_id *oid,
- int flag, void *cb_data)
+ int flag UNUSED, void *cb_data UNUSED)
{
struct object *obj;
@@ -659,14 +671,15 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data)
return 0; /* keep checking other objects, even if we saw an error */
}
-static int fsck_cruft(const char *basename, const char *path, void *data)
+static int fsck_cruft(const char *basename, const char *path,
+ void *data UNUSED)
{
if (!starts_with(basename, "tmp_obj_"))
fprintf_ln(stderr, _("bad sha1 file: %s"), path);
return 0;
}
-static int fsck_subdir(unsigned int nr, const char *path, void *data)
+static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data)
{
struct for_each_loose_cb *cb_data = data;
struct progress *progress = cb_data->progress;
@@ -730,19 +743,19 @@ static int fsck_head_link(const char *head_ref_name,
return 0;
}
-static int fsck_cache_tree(struct cache_tree *it)
+static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
{
int i;
int err = 0;
if (verbose)
- fprintf_ln(stderr, _("Checking cache tree"));
+ fprintf_ln(stderr, _("Checking cache tree of %s"), index_path);
if (0 <= it->entry_count) {
struct object *obj = parse_object(the_repository, &it->oid);
if (!obj) {
- error(_("%s: invalid sha1 pointer in cache-tree"),
- oid_to_hex(&it->oid));
+ error(_("%s: invalid sha1 pointer in cache-tree of %s"),
+ oid_to_hex(&it->oid), index_path);
errors_found |= ERROR_REFS;
return 1;
}
@@ -753,10 +766,81 @@ static int fsck_cache_tree(struct cache_tree *it)
err |= objerror(obj, _("non-tree in cache-tree"));
}
for (i = 0; i < it->subtree_nr; i++)
- err |= fsck_cache_tree(it->down[i]->cache_tree);
+ err |= fsck_cache_tree(it->down[i]->cache_tree, index_path);
return err;
}
+static int fsck_resolve_undo(struct index_state *istate,
+ const char *index_path)
+{
+ struct string_list_item *item;
+ struct string_list *resolve_undo = istate->resolve_undo;
+
+ if (!resolve_undo)
+ return 0;
+
+ for_each_string_list_item(item, resolve_undo) {
+ const char *path = item->string;
+ struct resolve_undo_info *ru = item->util;
+ int i;
+
+ if (!ru)
+ continue;
+ for (i = 0; i < 3; i++) {
+ struct object *obj;
+
+ if (!ru->mode[i] || !S_ISREG(ru->mode[i]))
+ continue;
+
+ obj = parse_object(the_repository, &ru->oid[i]);
+ if (!obj) {
+ error(_("%s: invalid sha1 pointer in resolve-undo of %s"),
+ oid_to_hex(&ru->oid[i]),
+ index_path);
+ errors_found |= ERROR_REFS;
+ continue;
+ }
+ obj->flags |= USED;
+ fsck_put_object_name(&fsck_walk_options, &ru->oid[i],
+ ":(%d):%s", i, path);
+ mark_object_reachable(obj);
+ }
+ }
+ return 0;
+}
+
+static void fsck_index(struct index_state *istate, const char *index_path,
+ int is_current_worktree)
+{
+ unsigned int i;
+
+ /* TODO: audit for interaction with sparse-index. */
+ ensure_full_index(istate);
+ for (i = 0; i < istate->cache_nr; i++) {
+ unsigned int mode;
+ struct blob *blob;
+ struct object *obj;
+
+ mode = istate->cache[i]->ce_mode;
+ if (S_ISGITLINK(mode))
+ continue;
+ blob = lookup_blob(the_repository,
+ &istate->cache[i]->oid);
+ if (!blob)
+ continue;
+ obj = &blob->object;
+ obj->flags |= USED;
+ fsck_put_object_name(&fsck_walk_options, &obj->oid,
+ "%s:%s",
+ is_current_worktree ? "" : index_path,
+ istate->cache[i]->name);
+ mark_object_reachable(obj);
+ }
+ if (istate->cache_tree)
+ fsck_cache_tree(istate->cache_tree, index_path);
+ fsck_resolve_undo(istate, index_path);
+}
+
static void mark_object_for_connectivity(const struct object_id *oid)
{
struct object *obj = lookup_unknown_object(the_repository, oid);
@@ -764,24 +848,59 @@ static void mark_object_for_connectivity(const struct object_id *oid)
}
static int mark_loose_for_connectivity(const struct object_id *oid,
- const char *path,
- void *data)
+ const char *path UNUSED,
+ void *data UNUSED)
{
mark_object_for_connectivity(oid);
return 0;
}
static int mark_packed_for_connectivity(const struct object_id *oid,
- struct packed_git *pack,
- uint32_t pos,
- void *data)
+ struct packed_git *pack UNUSED,
+ uint32_t pos UNUSED,
+ void *data UNUSED)
{
mark_object_for_connectivity(oid);
return 0;
}
+static int check_pack_rev_indexes(struct repository *r, int show_progress)
+{
+ struct progress *progress = NULL;
+ uint32_t pack_count = 0;
+ int res = 0;
+
+ if (show_progress) {
+ for (struct packed_git *p = get_all_packs(r); p; p = p->next)
+ pack_count++;
+ progress = start_delayed_progress("Verifying reverse pack-indexes", pack_count);
+ pack_count = 0;
+ }
+
+ for (struct packed_git *p = get_all_packs(r); p; p = p->next) {
+ int load_error = load_pack_revindex_from_disk(p);
+
+ if (load_error < 0) {
+ error(_("unable to load rev-index for pack '%s'"), p->pack_name);
+ res = ERROR_PACK_REV_INDEX;
+ } else if (!load_error &&
+ !load_pack_revindex(r, p) &&
+ verify_pack_revindex(p)) {
+ error(_("invalid rev-index for pack '%s'"), p->pack_name);
+ res = ERROR_PACK_REV_INDEX;
+ }
+ display_progress(progress, ++pack_count);
+ }
+ stop_progress(&progress);
+
+ return res;
+}
+
static char const * const fsck_usage[] = {
- N_("git fsck [<options>] [<object>...]"),
+ N_("git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+ " [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+ " [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+ " [--[no-]name-objects] [<object>...]"),
NULL
};
@@ -812,7 +931,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
fetch_if_missing = 0;
errors_found = 0;
- read_replace_refs = 0;
+ disable_replace_refs();
+ save_commit_buffer = 0;
argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
@@ -880,7 +1000,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
struct object_id oid;
- if (!get_oid(arg, &oid)) {
+ if (!repo_get_oid(the_repository, arg, &oid)) {
struct object *obj = lookup_object(the_repository,
&oid);
@@ -913,33 +1033,36 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
}
if (keep_cache_objects) {
+ struct worktree **worktrees, **p;
+
verify_index_checksum = 1;
verify_ce_order = 1;
- read_cache();
- /* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
- for (i = 0; i < active_nr; i++) {
- unsigned int mode;
- struct blob *blob;
- struct object *obj;
- mode = active_cache[i]->ce_mode;
- if (S_ISGITLINK(mode))
- continue;
- blob = lookup_blob(the_repository,
- &active_cache[i]->oid);
- if (!blob)
- continue;
- obj = &blob->object;
- obj->flags |= USED;
- fsck_put_object_name(&fsck_walk_options, &obj->oid,
- ":%s", active_cache[i]->name);
- mark_object_reachable(obj);
+ worktrees = get_worktrees();
+ for (p = worktrees; *p; p++) {
+ struct worktree *wt = *p;
+ struct index_state istate =
+ INDEX_STATE_INIT(the_repository);
+ char *path;
+
+ /*
+ * Make a copy since the buffer is reusable
+ * and may get overwritten by other calls
+ * while we're examining the index.
+ */
+ path = xstrdup(worktree_git_path(wt, "index"));
+ read_index_from(&istate, path, get_worktree_git_dir(wt));
+ fsck_index(&istate, path, wt->is_current);
+ discard_index(&istate);
+ free(path);
}
- if (active_cache_tree)
- fsck_cache_tree(active_cache_tree);
+ free_worktrees(worktrees);
}
+ errors_found |= check_pack_rev_indexes(the_repository, show_progress);
+ if (verify_bitmap_files(the_repository))
+ errors_found |= ERROR_BITMAP;
+
check_connectivity();
if (the_repository->settings.core_commit_graph) {
@@ -951,6 +1074,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
commit_graph_verify.git_cmd = 1;
strvec_pushl(&commit_graph_verify.args, "commit-graph",
"verify", "--object-dir", odb->path, NULL);
+ if (show_progress)
+ strvec_push(&commit_graph_verify.args, "--progress");
+ else
+ strvec_push(&commit_graph_verify.args, "--no-progress");
if (run_command(&commit_graph_verify))
errors_found |= ERROR_COMMIT_GRAPH;
}
@@ -965,6 +1092,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
midx_verify.git_cmd = 1;
strvec_pushl(&midx_verify.args, "multi-pack-index",
"verify", "--object-dir", odb->path, NULL);
+ if (show_progress)
+ strvec_push(&midx_verify.args, "--progress");
+ else
+ strvec_push(&midx_verify.args, "--no-progress");
if (run_command(&midx_verify))
errors_found |= ERROR_MULTI_PACK_INDEX;
}
diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c
index 46be55a461..7e99c4d61b 100644
--- a/builtin/fsmonitor--daemon.c
+++ b/builtin/fsmonitor--daemon.c
@@ -1,19 +1,27 @@
#include "builtin.h"
+#include "abspath.h"
#include "config.h"
+#include "environment.h"
+#include "gettext.h"
#include "parse-options.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
#include "fsmonitor-ipc.h"
+#include "fsmonitor-path-utils.h"
+#include "fsmonitor-settings.h"
+#include "compat/fsmonitor/fsm-health.h"
#include "compat/fsmonitor/fsm-listen.h"
#include "fsmonitor--daemon.h"
#include "simple-ipc.h"
#include "khash.h"
#include "pkt-line.h"
+#include "trace.h"
+#include "trace2.h"
static const char * const builtin_fsmonitor__daemon_usage[] = {
N_("git fsmonitor--daemon start [<options>]"),
N_("git fsmonitor--daemon run [<options>]"),
- N_("git fsmonitor--daemon stop"),
- N_("git fsmonitor--daemon status"),
+ "git fsmonitor--daemon stop",
+ "git fsmonitor--daemon status",
NULL
};
@@ -30,10 +38,11 @@ static int fsmonitor__start_timeout_sec = 60;
#define FSMONITOR__ANNOUNCE_STARTUP "fsmonitor.announcestartup"
static int fsmonitor__announce_startup = 0;
-static int fsmonitor_config(const char *var, const char *value, void *cb)
+static int fsmonitor_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (!strcmp(var, FSMONITOR__IPC_THREADS)) {
- int i = git_config_int(var, value);
+ int i = git_config_int(var, value, ctx->kvi);
if (i < 1)
return error(_("value of '%s' out of range: %d"),
FSMONITOR__IPC_THREADS, i);
@@ -42,7 +51,7 @@ static int fsmonitor_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, FSMONITOR__START_TIMEOUT)) {
- int i = git_config_int(var, value);
+ int i = git_config_int(var, value, ctx->kvi);
if (i < 0)
return error(_("value of '%s' out of range: %d"),
FSMONITOR__START_TIMEOUT, i);
@@ -52,7 +61,7 @@ static int fsmonitor_config(const char *var, const char *value, void *cb)
if (!strcmp(var, FSMONITOR__ANNOUNCE_STARTUP)) {
int is_bool;
- int i = git_config_bool_or_int(var, value, &is_bool);
+ int i = git_config_bool_or_int(var, value, ctx->kvi, &is_bool);
if (i < 0)
return error(_("value of '%s' not bool or int: %d"),
var, i);
@@ -60,7 +69,7 @@ static int fsmonitor_config(const char *var, const char *value, void *cb)
return 0;
}
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
}
/*
@@ -708,6 +717,7 @@ static int do_handle_client(struct fsmonitor_daemon_state *state,
"fsmonitor: unsupported V1 protocol '%s'"),
command);
do_trivial = 1;
+ do_cookie = 1;
} else {
/* We have "builtin:*" */
@@ -717,6 +727,7 @@ static int do_handle_client(struct fsmonitor_daemon_state *state,
"fsmonitor: invalid V2 protocol token '%s'",
command);
do_trivial = 1;
+ do_cookie = 1;
} else {
/*
@@ -1136,6 +1147,18 @@ void fsmonitor_publish(struct fsmonitor_daemon_state *state,
pthread_mutex_unlock(&state->main_lock);
}
+static void *fsm_health__thread_proc(void *_state)
+{
+ struct fsmonitor_daemon_state *state = _state;
+
+ trace2_thread_start("fsm-health");
+
+ fsm_health__loop(state);
+
+ trace2_thread_exit();
+ return NULL;
+}
+
static void *fsm_listen__thread_proc(void *_state)
{
struct fsmonitor_daemon_state *state = _state;
@@ -1174,6 +1197,9 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
*/
.uds_disallow_chdir = 0
};
+ int health_started = 0;
+ int listener_started = 0;
+ int err = 0;
/*
* Start the IPC thread pool before the we've started the file
@@ -1181,28 +1207,44 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
* before we need it.
*/
if (ipc_server_run_async(&state->ipc_server_data,
- fsmonitor_ipc__get_path(), &ipc_opts,
+ state->path_ipc.buf, &ipc_opts,
handle_client, state))
return error_errno(
_("could not start IPC thread pool on '%s'"),
- fsmonitor_ipc__get_path());
+ state->path_ipc.buf);
/*
* Start the fsmonitor listener thread to collect filesystem
* events.
*/
if (pthread_create(&state->listener_thread, NULL,
- fsm_listen__thread_proc, state) < 0) {
+ fsm_listen__thread_proc, state)) {
ipc_server_stop_async(state->ipc_server_data);
- ipc_server_await(state->ipc_server_data);
+ err = error(_("could not start fsmonitor listener thread"));
+ goto cleanup;
+ }
+ listener_started = 1;
- return error(_("could not start fsmonitor listener thread"));
+ /*
+ * Start the health thread to watch over our process.
+ */
+ if (pthread_create(&state->health_thread, NULL,
+ fsm_health__thread_proc, state)) {
+ ipc_server_stop_async(state->ipc_server_data);
+ err = error(_("could not start fsmonitor health thread"));
+ goto cleanup;
}
+ health_started = 1;
/*
* The daemon is now fully functional in background threads.
+ * Our primary thread should now just wait while the threads
+ * do all the work.
+ */
+cleanup:
+ /*
* Wait for the IPC thread pool to shutdown (whether by client
- * request or from filesystem activity).
+ * request, from filesystem activity, or an error).
*/
ipc_server_await(state->ipc_server_data);
@@ -1211,15 +1253,29 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
* event from the IPC thread pool, but it doesn't hurt to tell
* it again. And wait for it to shutdown.
*/
- fsm_listen__stop_async(state);
- pthread_join(state->listener_thread, NULL);
+ if (listener_started) {
+ fsm_listen__stop_async(state);
+ pthread_join(state->listener_thread, NULL);
+ }
+
+ if (health_started) {
+ fsm_health__stop_async(state);
+ pthread_join(state->health_thread, NULL);
+ }
- return state->error_code;
+ if (err)
+ return err;
+ if (state->listen_error_code)
+ return state->listen_error_code;
+ if (state->health_error_code)
+ return state->health_error_code;
+ return 0;
}
static int fsmonitor_run_daemon(void)
{
struct fsmonitor_daemon_state state;
+ const char *home;
int err;
memset(&state, 0, sizeof(state));
@@ -1227,7 +1283,8 @@ static int fsmonitor_run_daemon(void)
hashmap_init(&state.cookies, cookies_cmp, NULL, 0);
pthread_mutex_init(&state.main_lock, NULL);
pthread_cond_init(&state.cookies_cond, NULL);
- state.error_code = 0;
+ state.listen_error_code = 0;
+ state.health_error_code = 0;
state.current_token_data = fsmonitor_new_token_data();
/* Prepare to (recursively) watch the <worktree-root> directory. */
@@ -1235,6 +1292,11 @@ static int fsmonitor_run_daemon(void)
strbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));
state.nr_paths_watching = 1;
+ strbuf_init(&state.alias.alias, 0);
+ strbuf_init(&state.alias.points_to, 0);
+ if ((err = fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)))
+ goto done;
+
/*
* We create and delete cookie files somewhere inside the .git
* directory to help us keep sync with the file system. If
@@ -1290,6 +1352,16 @@ static int fsmonitor_run_daemon(void)
strbuf_addch(&state.path_cookie_prefix, '/');
/*
+ * We create a named-pipe or unix domain socket inside of the
+ * ".git" directory. (Well, on Windows, we base our named
+ * pipe in the NPFS on the absolute path of the git
+ * directory.)
+ */
+ strbuf_init(&state.path_ipc, 0);
+ strbuf_addstr(&state.path_ipc,
+ absolute_path(fsmonitor_ipc__get_path(the_repository)));
+
+ /*
* Confirm that we can create platform-specific resources for the
* filesystem listener before we bother starting all the threads.
*/
@@ -1298,18 +1370,44 @@ static int fsmonitor_run_daemon(void)
goto done;
}
+ if (fsm_health__ctor(&state)) {
+ err = error(_("could not initialize health thread"));
+ goto done;
+ }
+
+ /*
+ * CD out of the worktree root directory.
+ *
+ * The common Git startup mechanism causes our CWD to be the
+ * root of the worktree. On Windows, this causes our process
+ * to hold a locked handle on the CWD. This prevents the
+ * worktree from being moved or deleted while the daemon is
+ * running.
+ *
+ * We assume that our FS and IPC listener threads have either
+ * opened all of the handles that they need or will do
+ * everything using absolute paths.
+ */
+ home = getenv("HOME");
+ if (home && *home && chdir(home))
+ die_errno(_("could not cd home '%s'"), home);
+
err = fsmonitor_run_daemon_1(&state);
done:
pthread_cond_destroy(&state.cookies_cond);
pthread_mutex_destroy(&state.main_lock);
fsm_listen__dtor(&state);
+ fsm_health__dtor(&state);
ipc_server_free(state.ipc_server_data);
strbuf_release(&state.path_worktree_watch);
strbuf_release(&state.path_gitdir_watch);
strbuf_release(&state.path_cookie_prefix);
+ strbuf_release(&state.path_ipc);
+ strbuf_release(&state.alias.alias);
+ strbuf_release(&state.alias.points_to);
return err;
}
@@ -1423,6 +1521,7 @@ static int try_to_start_background_daemon(void)
int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
{
const char *subcmd;
+ enum fsmonitor_reason reason;
int detach_console = 0;
struct option options[] = {
@@ -1449,6 +1548,23 @@ int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
die(_("invalid 'ipc-threads' value (%d)"),
fsmonitor__ipc_threads);
+ prepare_repo_settings(the_repository);
+ /*
+ * If the repo is fsmonitor-compatible, explicitly set IPC-mode
+ * (without bothering to load the `core.fsmonitor` config settings).
+ *
+ * If the repo is not compatible, the repo-settings will be set to
+ * incompatible rather than IPC, so we can use one of the __get
+ * routines to detect the discrepancy.
+ */
+ fsm_settings__set_ipc(the_repository);
+
+ reason = fsm_settings__get_reason(the_repository);
+ if (reason > FSMONITOR_REASON_OK)
+ die("%s",
+ fsm_settings__get_incompatible_msg(the_repository,
+ reason));
+
if (!strcmp(subcmd, "start"))
return !!try_to_start_background_daemon();
@@ -1465,7 +1581,7 @@ int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
}
#else
-int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
+int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix UNUSED)
{
struct option options[] = {
OPT_END()
diff --git a/builtin/gc.c b/builtin/gc.c
index b335cffa33..19d73067aa 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -11,6 +11,10 @@
*/
#include "builtin.h"
+#include "abspath.h"
+#include "date.h"
+#include "environment.h"
+#include "hex.h"
#include "repository.h"
#include "config.h"
#include "tempfile.h"
@@ -22,16 +26,21 @@
#include "commit.h"
#include "commit-graph.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
#include "pack.h"
#include "pack-objects.h"
+#include "path.h"
#include "blob.h"
#include "tree.h"
#include "promisor-remote.h"
#include "refs.h"
#include "remote.h"
#include "exec-cmd.h"
+#include "gettext.h"
#include "hook.h"
+#include "setup.h"
+#include "trace2.h"
#define FAILED_RUN "failed to run %s"
@@ -42,6 +51,7 @@ static const char * const builtin_gc_usage[] = {
static int pack_refs = 1;
static int prune_reflogs = 1;
+static int cruft_packs = 1;
static int aggressive_depth = 50;
static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
@@ -152,6 +162,7 @@ static void gc_config(void)
git_config_get_int("gc.auto", &gc_auto_threshold);
git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
git_config_get_bool("gc.autodetach", &detach_auto);
+ git_config_get_bool("gc.cruftpacks", &cruft_packs);
git_config_get_expiry("gc.pruneexpire", &prune_expire);
git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
git_config_get_expiry("gc.logexpiry", &gc_log_expire);
@@ -165,10 +176,11 @@ static void gc_config(void)
struct maintenance_run_opts;
static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts)
{
- struct strvec pack_refs_cmd = STRVEC_INIT;
- strvec_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
+ struct child_process cmd = CHILD_PROCESS_INIT;
- return run_command_v_opt(pack_refs_cmd.v, RUN_GIT_CMD);
+ cmd.git_cmd = 1;
+ strvec_pushl(&cmd.args, "pack-refs", "--all", "--prune", NULL);
+ return run_command(&cmd);
}
static int too_many_loose_objects(void)
@@ -210,7 +222,7 @@ static struct packed_git *find_base_packs(struct string_list *packs,
struct packed_git *p, *base = NULL;
for (p = get_all_packs(the_repository); p; p = p->next) {
- if (!p->pack_local)
+ if (!p->pack_local || p->is_cruft)
continue;
if (limit) {
if (p->pack_size >= limit)
@@ -281,7 +293,7 @@ static uint64_t total_ram(void)
static uint64_t estimate_repack_memory(struct packed_git *pack)
{
- unsigned long nr_objects = approximate_object_count();
+ unsigned long nr_objects = repo_approximate_object_count(the_repository);
size_t os_cache, heap;
if (!pack || !nr_objects)
@@ -321,7 +333,7 @@ static uint64_t estimate_repack_memory(struct packed_git *pack)
return os_cache + heap;
}
-static int keep_one_pack(struct string_list_item *item, void *data)
+static int keep_one_pack(struct string_list_item *item, void *data UNUSED)
{
strvec_pushf(&repack, "--keep-pack=%s", basename(item->string));
return 0;
@@ -331,7 +343,11 @@ static void add_repack_all_option(struct string_list *keep_pack)
{
if (prune_expire && !strcmp(prune_expire, "now"))
strvec_push(&repack, "-a");
- else {
+ else if (cruft_packs) {
+ strvec_push(&repack, "--cruft");
+ if (prune_expire)
+ strvec_pushf(&repack, "--cruft-expiration=%s", prune_expire);
+ } else {
strvec_push(&repack, "-A");
if (prune_expire)
strvec_pushf(&repack, "--unpack-unreachable=%s", prune_expire);
@@ -446,7 +462,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
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)
+ if (fp)
fclose(fp);
if (should_exit) {
if (fd >= 0)
@@ -530,8 +546,14 @@ static void gc_before_repack(void)
if (pack_refs && maintenance_task_pack_refs(NULL))
die(FAILED_RUN, "pack-refs");
- if (prune_reflogs && run_command_v_opt(reflog.v, RUN_GIT_CMD))
- die(FAILED_RUN, reflog.v[0]);
+ if (prune_reflogs) {
+ struct child_process cmd = CHILD_PROCESS_INIT;
+
+ cmd.git_cmd = 1;
+ strvec_pushv(&cmd.args, reflog.v);
+ if (run_command(&cmd))
+ die(FAILED_RUN, reflog.v[0]);
+ }
}
int cmd_gc(int argc, const char **argv, const char *prefix)
@@ -545,12 +567,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
int daemonized = 0;
int keep_largest_pack = -1;
timestamp_t dummy;
+ struct child_process rerere_cmd = CHILD_PROCESS_INIT;
struct option builtin_gc_options[] = {
OPT__QUIET(&quiet, N_("suppress progress reporting")),
{ OPTION_STRING, 0, "prune", &prune_expire, N_("date"),
N_("prune unreferenced objects"),
PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
+ OPT_BOOL(0, "cruft", &cruft_packs, N_("pack unreferenced objects separately")),
OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"),
PARSE_OPT_NOCOMPLETE),
@@ -574,7 +598,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
/* default expiry time, overwritten in gc_config */
gc_config();
if (parse_expiry_date(gc_log_expire, &gc_log_expire_time))
- die(_("failed to parse gc.logexpiry value %s"), gc_log_expire);
+ die(_("failed to parse gc.logExpiry value %s"), gc_log_expire);
if (pack_refs < 0)
pack_refs = !is_bare_repository();
@@ -665,29 +689,44 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
gc_before_repack();
if (!repository_format_precious_objects) {
- if (run_command_v_opt(repack.v,
- RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE))
+ struct child_process repack_cmd = CHILD_PROCESS_INIT;
+
+ repack_cmd.git_cmd = 1;
+ repack_cmd.close_object_store = 1;
+ strvec_pushv(&repack_cmd.args, repack.v);
+ if (run_command(&repack_cmd))
die(FAILED_RUN, repack.v[0]);
if (prune_expire) {
+ struct child_process prune_cmd = CHILD_PROCESS_INIT;
+
+ /* run `git prune` even if using cruft packs */
strvec_push(&prune, prune_expire);
if (quiet)
strvec_push(&prune, "--no-progress");
- if (has_promisor_remote())
+ if (repo_has_promisor_remote(the_repository))
strvec_push(&prune,
"--exclude-promisor-objects");
- if (run_command_v_opt(prune.v, RUN_GIT_CMD))
+ prune_cmd.git_cmd = 1;
+ strvec_pushv(&prune_cmd.args, prune.v);
+ if (run_command(&prune_cmd))
die(FAILED_RUN, prune.v[0]);
}
}
if (prune_worktrees_expire) {
+ struct child_process prune_worktrees_cmd = CHILD_PROCESS_INIT;
+
strvec_push(&prune_worktrees, prune_worktrees_expire);
- if (run_command_v_opt(prune_worktrees.v, RUN_GIT_CMD))
+ prune_worktrees_cmd.git_cmd = 1;
+ strvec_pushv(&prune_worktrees_cmd.args, prune_worktrees.v);
+ if (run_command(&prune_worktrees_cmd))
die(FAILED_RUN, prune_worktrees.v[0]);
}
- if (run_command_v_opt(rerere.v, RUN_GIT_CMD))
+ rerere_cmd.git_cmd = 1;
+ strvec_pushv(&rerere_cmd.args, rerere.v);
+ if (run_command(&rerere_cmd))
die(FAILED_RUN, rerere.v[0]);
report_garbage = report_pack_garbage;
@@ -697,7 +736,6 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
clean_pack_garbage();
}
- prepare_repo_settings(the_repository);
if (the_repository->settings.gc_write_commit_graph == 1)
write_commit_graph_reachable(the_repository->objects->odb,
!quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0,
@@ -768,8 +806,9 @@ struct cg_auto_data {
int limit;
};
-static int dfs_on_ref(const char *refname,
- const struct object_id *oid, int flags,
+static int dfs_on_ref(const char *refname UNUSED,
+ const struct object_id *oid,
+ int flags UNUSED,
void *cb_data)
{
struct cg_auto_data *data = (struct cg_auto_data *)cb_data;
@@ -786,7 +825,7 @@ static int dfs_on_ref(const char *refname,
commit = lookup_commit(the_repository, oid);
if (!commit)
return 0;
- if (parse_commit(commit) ||
+ if (repo_parse_commit(the_repository, commit) ||
commit_graph_position(commit) != COMMIT_NOT_FROM_GRAPH)
return 0;
@@ -803,7 +842,7 @@ static int dfs_on_ref(const char *refname,
commit = pop_commit(&stack);
for (parent = commit->parents; parent; parent = parent->next) {
- if (parse_commit(parent->item) ||
+ if (repo_parse_commit(the_repository, parent->item) ||
commit_graph_position(parent->item) != COMMIT_NOT_FROM_GRAPH ||
parent->item->object.flags & SEEN)
continue;
@@ -896,12 +935,6 @@ static int fetch_remote(struct remote *remote, void *cbdata)
static int maintenance_task_prefetch(struct maintenance_run_opts *opts)
{
- git_config_set_multivar_gently("log.excludedecoration",
- "refs/prefetch/",
- "refs/prefetch/",
- CONFIG_FLAGS_FIXED_VALUE |
- CONFIG_FLAGS_MULTI_REPLACE);
-
if (for_each_remote(fetch_remote, opts)) {
error(_("failed to prefetch remotes"));
return 1;
@@ -948,9 +981,9 @@ struct write_loose_object_data {
static int loose_object_auto_limit = 100;
-static int loose_object_count(const struct object_id *oid,
- const char *path,
- void *data)
+static int loose_object_count(const struct object_id *oid UNUSED,
+ const char *path UNUSED,
+ void *data)
{
int *count = (int*)data;
if (++(*count) >= loose_object_auto_limit)
@@ -975,15 +1008,15 @@ static int loose_object_auto_condition(void)
NULL, NULL, &count);
}
-static int bail_on_loose(const struct object_id *oid,
- const char *path,
- void *data)
+static int bail_on_loose(const struct object_id *oid UNUSED,
+ const char *path UNUSED,
+ void *data UNUSED)
{
return 1;
}
static int write_loose_object_to_stdin(const struct object_id *oid,
- const char *path,
+ const char *path UNUSED,
void *data)
{
struct write_loose_object_data *d = (struct write_loose_object_data *)data;
@@ -1451,63 +1484,141 @@ static char *get_maintpath(void)
return strbuf_detach(&sb, NULL);
}
-static int maintenance_register(void)
+static char const * const builtin_maintenance_register_usage[] = {
+ "git maintenance register [--config-file <path>]",
+ NULL
+};
+
+static int maintenance_register(int argc, const char **argv, const char *prefix)
{
- int rc;
- char *config_value;
- struct child_process config_set = CHILD_PROCESS_INIT;
- struct child_process config_get = CHILD_PROCESS_INIT;
+ char *config_file = NULL;
+ struct option options[] = {
+ OPT_STRING(0, "config-file", &config_file, N_("file"), N_("use given config file")),
+ OPT_END(),
+ };
+ int found = 0;
+ const char *key = "maintenance.repo";
char *maintpath = get_maintpath();
+ struct string_list_item *item;
+ const struct string_list *list;
+
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_maintenance_register_usage, 0);
+ if (argc)
+ usage_with_options(builtin_maintenance_register_usage,
+ options);
/* Disable foreground maintenance */
git_config_set("maintenance.auto", "false");
/* Set maintenance strategy, if unset */
- if (!git_config_get_string("maintenance.strategy", &config_value))
- free(config_value);
- else
+ if (git_config_get("maintenance.strategy"))
git_config_set("maintenance.strategy", "incremental");
- config_get.git_cmd = 1;
- strvec_pushl(&config_get.args, "config", "--global", "--get",
- "--fixed-value", "maintenance.repo", maintpath, NULL);
- config_get.out = -1;
-
- if (start_command(&config_get)) {
- rc = error(_("failed to run 'git config'"));
- goto done;
- }
-
- /* We already have this value in our config! */
- if (!finish_command(&config_get)) {
- rc = 0;
- goto done;
+ if (!git_config_get_string_multi(key, &list)) {
+ for_each_string_list_item(item, list) {
+ if (!strcmp(maintpath, item->string)) {
+ found = 1;
+ break;
+ }
+ }
}
- config_set.git_cmd = 1;
- strvec_pushl(&config_set.args, "config", "--add", "--global", "maintenance.repo",
- maintpath, NULL);
+ if (!found) {
+ int rc;
+ char *user_config = NULL, *xdg_config = NULL;
- rc = run_command(&config_set);
+ if (!config_file) {
+ git_global_config(&user_config, &xdg_config);
+ config_file = user_config;
+ if (!user_config)
+ die(_("$HOME not set"));
+ }
+ rc = git_config_set_multivar_in_file_gently(
+ config_file, "maintenance.repo", maintpath,
+ CONFIG_REGEX_NONE, 0);
+ free(user_config);
+ free(xdg_config);
+
+ if (rc)
+ die(_("unable to add '%s' value of '%s'"),
+ key, maintpath);
+ }
-done:
free(maintpath);
- return rc;
+ return 0;
}
-static int maintenance_unregister(void)
+static char const * const builtin_maintenance_unregister_usage[] = {
+ "git maintenance unregister [--config-file <path>] [--force]",
+ NULL
+};
+
+static int maintenance_unregister(int argc, const char **argv, const char *prefix)
{
- int rc;
- struct child_process config_unset = CHILD_PROCESS_INIT;
+ int force = 0;
+ char *config_file = NULL;
+ struct option options[] = {
+ OPT_STRING(0, "config-file", &config_file, N_("file"), N_("use given config file")),
+ OPT__FORCE(&force,
+ N_("return success even if repository was not registered"),
+ PARSE_OPT_NOCOMPLETE),
+ OPT_END(),
+ };
+ const char *key = "maintenance.repo";
char *maintpath = get_maintpath();
+ int found = 0;
+ struct string_list_item *item;
+ const struct string_list *list;
+ struct config_set cs = { { 0 } };
+
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_maintenance_unregister_usage, 0);
+ if (argc)
+ usage_with_options(builtin_maintenance_unregister_usage,
+ options);
- config_unset.git_cmd = 1;
- strvec_pushl(&config_unset.args, "config", "--global", "--unset",
- "--fixed-value", "maintenance.repo", maintpath, NULL);
+ if (config_file) {
+ git_configset_init(&cs);
+ git_configset_add_file(&cs, config_file);
+ }
+ if (!(config_file
+ ? git_configset_get_string_multi(&cs, key, &list)
+ : git_config_get_string_multi(key, &list))) {
+ for_each_string_list_item(item, list) {
+ if (!strcmp(maintpath, item->string)) {
+ found = 1;
+ break;
+ }
+ }
+ }
- rc = run_command(&config_unset);
+ if (found) {
+ int rc;
+ char *user_config = NULL, *xdg_config = NULL;
+ if (!config_file) {
+ git_global_config(&user_config, &xdg_config);
+ config_file = user_config;
+ if (!user_config)
+ die(_("$HOME not set"));
+ }
+ rc = git_config_set_multivar_in_file_gently(
+ config_file, key, NULL, maintpath,
+ CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE);
+ free(user_config);
+ free(xdg_config);
+
+ if (rc &&
+ (!force || rc == CONFIG_NOTHING_SET))
+ die(_("unable to unset '%s' value of '%s'"),
+ key, maintpath);
+ } else if (!force) {
+ die(_("repository '%s' is not registered"), maintpath);
+ }
+
+ git_configset_clear(&cs);
free(maintpath);
- return rc;
+ return 0;
}
static const char *get_frequency(enum schedule_priority schedule)
@@ -1575,11 +1686,11 @@ static int get_schedule_cmd(const char **cmd, int *is_available)
if (is_available)
*is_available = 0;
- string_list_split_in_place(&list, testing, ',', -1);
+ string_list_split_in_place(&list, testing, ",", -1);
for_each_string_list_item(item, &list) {
struct string_list pair = STRING_LIST_INIT_NODUP;
- if (string_list_split_in_place(&pair, item->string, ':', 2) != 2)
+ if (string_list_split_in_place(&pair, item->string, ":", 2) != 2)
continue;
if (!strcmp(*cmd, pair.items[0].string)) {
@@ -1844,20 +1955,16 @@ static char *schtasks_task_name(const char *frequency)
static int schtasks_remove_task(enum schedule_priority schedule)
{
const char *cmd = "schtasks";
- int result;
- struct strvec args = STRVEC_INIT;
+ struct child_process child = CHILD_PROCESS_INIT;
const char *frequency = get_frequency(schedule);
char *name = schtasks_task_name(frequency);
get_schedule_cmd(&cmd, NULL);
- strvec_split(&args, cmd);
- strvec_pushl(&args, "/delete", "/tn", name, "/f", NULL);
-
- result = run_command_v_opt(args.v, 0);
-
- strvec_clear(&args);
+ strvec_split(&child.args, cmd);
+ strvec_pushl(&child.args, "/delete", "/tn", name, "/f", NULL);
free(name);
- return result;
+
+ return run_command(&child);
}
static int schtasks_remove_tasks(void)
@@ -2051,6 +2158,7 @@ static int crontab_update_schedule(int run_maintenance, int fd)
struct child_process crontab_edit = CHILD_PROCESS_INIT;
FILE *cron_list, *cron_in;
struct strbuf line = STRBUF_INIT;
+ struct tempfile *tmpedit = NULL;
get_schedule_cmd(&cmd, NULL);
strvec_split(&crontab_list.args, cmd);
@@ -2065,6 +2173,17 @@ static int crontab_update_schedule(int run_maintenance, int fd)
/* Ignore exit code, as an empty crontab will return error. */
finish_command(&crontab_list);
+ tmpedit = mks_tempfile_t(".git_cron_edit_tmpXXXXXX");
+ if (!tmpedit) {
+ result = error(_("failed to create crontab temporary file"));
+ goto out;
+ }
+ cron_in = fdopen_tempfile(tmpedit, "w");
+ if (!cron_in) {
+ result = error(_("failed to open temporary file"));
+ goto out;
+ }
+
/*
* Read from the .lock file, filtering out the old
* schedule while appending the new schedule.
@@ -2072,19 +2191,6 @@ static int crontab_update_schedule(int run_maintenance, int fd)
cron_list = fdopen(fd, "r");
rewind(cron_list);
- strvec_split(&crontab_edit.args, cmd);
- crontab_edit.in = -1;
- crontab_edit.git_cmd = 0;
-
- if (start_command(&crontab_edit))
- return error(_("failed to run 'crontab'; your system might not support 'cron'"));
-
- cron_in = fdopen(crontab_edit.in, "w");
- if (!cron_in) {
- result = error(_("failed to open stdin of 'crontab'"));
- goto done_editing;
- }
-
while (!strbuf_getline_lf(&line, cron_list)) {
if (!in_old_region && !strcmp(line.buf, BEGIN_LINE))
in_old_region = 1;
@@ -2118,14 +2224,22 @@ static int crontab_update_schedule(int run_maintenance, int fd)
}
fflush(cron_in);
- fclose(cron_in);
- close(crontab_edit.in);
-done_editing:
+ strvec_split(&crontab_edit.args, cmd);
+ strvec_push(&crontab_edit.args, get_tempfile_path(tmpedit));
+ crontab_edit.git_cmd = 0;
+
+ if (start_command(&crontab_edit)) {
+ result = error(_("failed to run 'crontab'; your system might not support 'cron'"));
+ goto out;
+ }
+
if (finish_command(&crontab_edit))
result = error(_("'crontab' died"));
else
fclose(cron_list);
+out:
+ delete_tempfile(&tmpedit);
return result;
}
@@ -2238,7 +2352,7 @@ static int systemd_timer_write_unit_templates(const char *exec_path)
goto error;
}
file = fopen_or_warn(filename, "w");
- if (file == NULL)
+ if (!file)
goto error;
unit = "# This file was created and is maintained by Git.\n"
@@ -2267,7 +2381,7 @@ static int systemd_timer_write_unit_templates(const char *exec_path)
filename = xdg_config_home_systemd("git-maintenance@.service");
file = fopen_or_warn(filename, "w");
- if (file == NULL)
+ if (!file)
goto error;
unit = "# This file was created and is maintained by Git.\n"
@@ -2482,6 +2596,7 @@ static int maintenance_start(int argc, const char **argv, const char *prefix)
PARSE_OPT_NONEG, maintenance_opt_scheduler),
OPT_END()
};
+ const char *register_args[] = { "register", NULL };
argc = parse_options(argc, argv, prefix, options,
builtin_maintenance_start_usage, 0);
@@ -2491,34 +2606,46 @@ static int maintenance_start(int argc, const char **argv, const char *prefix)
opts.scheduler = resolve_scheduler(opts.scheduler);
validate_scheduler(opts.scheduler);
- if (maintenance_register())
+ if (maintenance_register(ARRAY_SIZE(register_args)-1, register_args, NULL))
warning(_("failed to add repo to global config"));
return update_background_schedule(&opts, 1);
}
-static int maintenance_stop(void)
+static const char *const builtin_maintenance_stop_usage[] = {
+ "git maintenance stop",
+ NULL
+};
+
+static int maintenance_stop(int argc, const char **argv, const char *prefix)
{
+ struct option options[] = {
+ OPT_END()
+ };
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_maintenance_stop_usage, 0);
+ if (argc)
+ usage_with_options(builtin_maintenance_stop_usage, options);
return update_background_schedule(NULL, 0);
}
-static const char builtin_maintenance_usage[] = N_("git maintenance <subcommand> [<options>]");
+static const char * const builtin_maintenance_usage[] = {
+ N_("git maintenance <subcommand> [<options>]"),
+ NULL,
+};
int cmd_maintenance(int argc, const char **argv, const char *prefix)
{
- if (argc < 2 ||
- (argc == 2 && !strcmp(argv[1], "-h")))
- usage(builtin_maintenance_usage);
-
- if (!strcmp(argv[1], "run"))
- return maintenance_run(argc - 1, argv + 1, prefix);
- if (!strcmp(argv[1], "start"))
- return maintenance_start(argc - 1, argv + 1, prefix);
- if (!strcmp(argv[1], "stop"))
- return maintenance_stop();
- if (!strcmp(argv[1], "register"))
- return maintenance_register();
- if (!strcmp(argv[1], "unregister"))
- return maintenance_unregister();
-
- die(_("invalid subcommand: %s"), argv[1]);
+ parse_opt_subcommand_fn *fn = NULL;
+ struct option builtin_maintenance_options[] = {
+ OPT_SUBCOMMAND("run", &fn, maintenance_run),
+ OPT_SUBCOMMAND("start", &fn, maintenance_start),
+ OPT_SUBCOMMAND("stop", &fn, maintenance_stop),
+ OPT_SUBCOMMAND("register", &fn, maintenance_register),
+ OPT_SUBCOMMAND("unregister", &fn, maintenance_unregister),
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, prefix, builtin_maintenance_options,
+ builtin_maintenance_usage, 0);
+ return fn(argc, argv, prefix);
}
diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c
index 491af9202d..20d0dfe9cf 100644
--- a/builtin/get-tar-commit-id.c
+++ b/builtin/get-tar-commit-id.c
@@ -1,10 +1,9 @@
/*
* Copyright (c) 2005, 2006 Rene Scharfe
*/
-#include "cache.h"
+#include "builtin.h"
#include "commit.h"
#include "tar.h"
-#include "builtin.h"
#include "quote.h"
static const char builtin_get_tar_commit_id_usage[] =
@@ -14,7 +13,7 @@ static const char builtin_get_tar_commit_id_usage[] =
#define RECORDSIZE (512)
#define HEADERSIZE (2 * RECORDSIZE)
-int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
+int cmd_get_tar_commit_id(int argc, const char **argv UNUSED, const char *prefix)
{
char buffer[HEADERSIZE];
struct ustar_header *header = (struct ustar_header *)buffer;
@@ -24,6 +23,8 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
long len;
char *end;
+ BUG_ON_NON_EMPTY_PREFIX(prefix);
+
if (argc != 1)
usage(builtin_get_tar_commit_id_usage);
@@ -32,7 +33,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
die_errno("git get-tar-commit-id: read error");
if (n != HEADERSIZE)
die_errno("git get-tar-commit-id: EOF before reading tar header");
- if (header->typeflag[0] != 'g')
+ if (header->typeflag[0] != TYPEFLAG_GLOBAL_HEADER)
return 1;
len = strtol(content, &end, 10);
diff --git a/builtin/grep.c b/builtin/grep.c
index bcb07ea7f7..ce866523e2 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -3,8 +3,9 @@
*
* Copyright (c) 2006 Junio C Hamano
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#include "builtin.h"
+#include "gettext.h"
+#include "hex.h"
#include "repository.h"
#include "config.h"
#include "blob.h"
@@ -12,7 +13,6 @@
#include "commit.h"
#include "tag.h"
#include "tree-walk.h"
-#include "builtin.h"
#include "parse-options.h"
#include "string-list.h"
#include "run-command.h"
@@ -21,10 +21,17 @@
#include "quote.h"
#include "dir.h"
#include "pathspec.h"
+#include "setup.h"
#include "submodule.h"
#include "submodule-config.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
#include "packfile.h"
+#include "pager.h"
+#include "path.h"
+#include "read-cache-ll.h"
+#include "write-or-die.h"
static const char *grep_prefix;
@@ -283,14 +290,18 @@ static int wait_all(void)
return hit;
}
-static int grep_cmd_config(const char *var, const char *value, void *cb)
+static int grep_cmd_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
- int st = grep_config(var, value, cb);
- if (git_color_default_config(var, value, NULL) < 0)
+ int st = grep_config(var, value, ctx, cb);
+
+ if (git_color_config(var, value, cb) < 0)
+ st = -1;
+ else if (git_default_config(var, value, ctx, cb) < 0)
st = -1;
if (!strcmp(var, "grep.threads")) {
- num_threads = git_config_int(var, value);
+ num_threads = git_config_int(var, value, ctx->kvi);
if (num_threads < 0)
die(_("invalid number of threads specified (%d) for %s"),
num_threads, var);
@@ -458,6 +469,33 @@ static int grep_submodule(struct grep_opt *opt,
* subrepo's odbs to the in-memory alternates list.
*/
obj_read_lock();
+
+ /*
+ * NEEDSWORK: when reading a submodule, the sparsity settings in the
+ * superproject are incorrectly forgotten or misused. For example:
+ *
+ * 1. "command_requires_full_index"
+ * When this setting is turned on for `grep`, only the superproject
+ * knows it. All the submodules are read with their own configs
+ * and get prepare_repo_settings()'d. Therefore, these submodules
+ * "forget" the sparse-index feature switch. As a result, the index
+ * of these submodules are expanded unexpectedly.
+ *
+ * 2. "core_apply_sparse_checkout"
+ * When running `grep` in the superproject, this setting is
+ * populated using the superproject's configs. However, once
+ * initialized, this config is globally accessible and is read by
+ * prepare_repo_settings() for the submodules. For instance, if a
+ * submodule is using a sparse-checkout, however, the superproject
+ * is not, the result is that the config from the superproject will
+ * dictate the behavior for the submodule, making it "forget" its
+ * sparse-checkout state.
+ *
+ * 3. "core_sparse_checkout_cone"
+ * ditto.
+ *
+ * Note that this list is not exhaustive.
+ */
repo_read_gitmodules(subrepo, 0);
/*
@@ -520,8 +558,6 @@ static int grep_cache(struct grep_opt *opt,
if (repo_read_index(repo) < 0)
die(_("index file corrupt"));
- /* TODO: audit for interaction with sparse-index. */
- ensure_full_index(repo->index);
for (nr = 0; nr < repo->index->cache_nr; nr++) {
const struct cache_entry *ce = repo->index->cache[nr];
@@ -530,8 +566,21 @@ static int grep_cache(struct grep_opt *opt,
strbuf_setlen(&name, name_base_len);
strbuf_addstr(&name, ce->name);
+ if (S_ISSPARSEDIR(ce->ce_mode)) {
+ enum object_type type;
+ struct tree_desc tree;
+ void *data;
+ unsigned long size;
+
+ data = repo_read_object_file(the_repository, &ce->oid,
+ &type, &size);
+ init_tree_desc(&tree, data, size);
- if (S_ISREG(ce->ce_mode) &&
+ hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0);
+ strbuf_setlen(&name, name_base_len);
+ strbuf_addstr(&name, ce->name);
+ free(data);
+ } else if (S_ISREG(ce->ce_mode) &&
match_pathspec(repo->index, pathspec, name.buf, name.len, 0, NULL,
S_ISDIR(ce->ce_mode) ||
S_ISGITLINK(ce->ce_mode))) {
@@ -614,7 +663,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
void *data;
unsigned long size;
- data = read_object_file(&entry.oid, &type, &size);
+ data = repo_read_object_file(the_repository,
+ &entry.oid, &type, &size);
if (!data)
die(_("unable to read tree (%s)"),
oid_to_hex(&entry.oid));
@@ -961,6 +1011,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored,
N_("allow calling of grep(1) (ignored by this build)"),
PARSE_OPT_NOCOMPLETE),
+ OPT_INTEGER('m', "max-count", &opt.max_count,
+ N_("maximum number of results per file")),
OPT_END()
};
grep_prefix = prefix;
@@ -982,6 +1034,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
PARSE_OPT_KEEP_DASHDASH |
PARSE_OPT_STOP_AT_NON_OPTION);
+ if (the_repository->gitdir) {
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+ }
+
if (use_index && !startup_info->have_repository) {
int fallback = 0;
git_config_get_bool("grep.fallbacktonoindex", &fallback);
@@ -1101,6 +1158,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (recurse_submodules && untracked)
die(_("--untracked not supported with --recurse-submodules"));
+ /*
+ * Optimize out the case where the amount of matches is limited to zero.
+ * We do this to keep results consistent with GNU grep(1).
+ */
+ if (opt.max_count == 0)
+ return 1;
+
if (show_in_pager) {
if (num_threads > 1)
warning(_("invalid option combination, ignoring --threads"));
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index fbae878c2b..5ffec99dce 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -5,12 +5,19 @@
* Copyright (C) Junio C Hamano, 2005
*/
#include "builtin.h"
+#include "abspath.h"
#include "config.h"
-#include "object-store.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-file.h"
+#include "object-store-ll.h"
#include "blob.h"
#include "quote.h"
#include "parse-options.h"
#include "exec-cmd.h"
+#include "setup.h"
+#include "strbuf.h"
+#include "write-or-die.h"
/*
* This is to create corrupt objects for debugging and as such it
@@ -27,6 +34,7 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig
else
ret = write_object_file_literally(buf.buf, buf.len, type, oid,
flags);
+ close(fd);
strbuf_release(&buf);
return ret;
}
@@ -80,8 +88,9 @@ static void hash_stdin_paths(const char *type, int no_filters, unsigned flags,
int cmd_hash_object(int argc, const char **argv, const char *prefix)
{
static const char * const hash_object_usage[] = {
- N_("git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] [--] <file>..."),
- "git hash-object --stdin-paths",
+ N_("git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+ " [--stdin [--literally]] [--] <file>..."),
+ N_("git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"),
NULL
};
const char *type = blob_type;
diff --git a/builtin/help.c b/builtin/help.c
index 222f994f86..dc1fbe2b98 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -1,15 +1,18 @@
/*
* Builtin help command
*/
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
+#include "config.h"
#include "exec-cmd.h"
+#include "gettext.h"
+#include "pager.h"
#include "parse-options.h"
+#include "path.h"
#include "run-command.h"
#include "config-list.h"
#include "help.h"
#include "alias.h"
+#include "setup.h"
#ifndef DEFAULT_HELP_FORMAT
#define DEFAULT_HELP_FORMAT "man"
@@ -43,6 +46,8 @@ static enum help_action {
HELP_ACTION_ALL = 1,
HELP_ACTION_GUIDES,
HELP_ACTION_CONFIG,
+ HELP_ACTION_USER_INTERFACES,
+ HELP_ACTION_DEVELOPER_INTERFACES,
HELP_ACTION_CONFIG_FOR_COMPLETION,
HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION,
} cmd_mode;
@@ -69,6 +74,12 @@ static struct option builtin_help_options[] = {
OPT_CMDMODE('g', "guides", &cmd_mode, N_("print list of useful guides"),
HELP_ACTION_GUIDES),
+ OPT_CMDMODE(0, "user-interfaces", &cmd_mode,
+ N_("print list of user-facing repository, command and file interfaces"),
+ HELP_ACTION_USER_INTERFACES),
+ OPT_CMDMODE(0, "developer-interfaces", &cmd_mode,
+ N_("print list of file formats, protocols and other developer interfaces"),
+ HELP_ACTION_DEVELOPER_INTERFACES),
OPT_CMDMODE('c', "config", &cmd_mode, N_("print all configuration variable names"),
HELP_ACTION_CONFIG),
OPT_CMDMODE_F(0, "config-for-completion", &cmd_mode, "",
@@ -80,10 +91,12 @@ static struct option builtin_help_options[] = {
};
static const char * const builtin_help_usage[] = {
- "git help [-a|--all] [--[no-]verbose]] [--[no-]external-commands] [--[no-]aliases]",
- N_("git help [[-i|--info] [-m|--man] [-w|--web]] [<command>]"),
+ "git help [-a|--all] [--[no-]verbose] [--[no-]external-commands] [--[no-]aliases]",
+ N_("git help [[-i|--info] [-m|--man] [-w|--web]] [<command>|<doc>]"),
"git help [-g|--guides]",
"git help [-c|--config]",
+ "git help [--user-interfaces]",
+ "git help [--developer-interfaces]",
NULL
};
@@ -384,7 +397,8 @@ static int add_man_viewer_info(const char *var, const char *value)
return 0;
}
-static int git_help_config(const char *var, const char *value, void *cb)
+static int git_help_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (!strcmp(var, "help.format")) {
if (!value)
@@ -407,7 +421,7 @@ static int git_help_config(const char *var, const char *value, void *cb)
if (starts_with(var, "man."))
return add_man_viewer_info(var, value);
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
}
static struct cmdnames main_cmds, other_cmds;
@@ -430,6 +444,8 @@ static const char *cmd_to_page(const char *git_cmd)
return git_cmd;
else if (is_git_command(git_cmd))
return xstrfmt("git-%s", git_cmd);
+ else if (!strcmp("scalar", git_cmd))
+ return xstrdup(git_cmd);
else
return xstrfmt("git%s", git_cmd);
}
@@ -654,6 +670,14 @@ int cmd_help(int argc, const char **argv, const char *prefix)
opt_mode_usage(argc, "--config-for-completion", help_format);
list_config_help(SHOW_CONFIG_VARS);
return 0;
+ case HELP_ACTION_USER_INTERFACES:
+ opt_mode_usage(argc, "--user-interfaces", help_format);
+ list_user_interfaces_help();
+ return 0;
+ case HELP_ACTION_DEVELOPER_INTERFACES:
+ opt_mode_usage(argc, "--developer-interfaces", help_format);
+ list_developer_interfaces_help();
+ return 0;
case HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION:
opt_mode_usage(argc, "--config-sections-for-completion",
help_format);
diff --git a/builtin/hook.c b/builtin/hook.c
index 54e5c6ec93..09b51a6487 100644
--- a/builtin/hook.c
+++ b/builtin/hook.c
@@ -1,13 +1,13 @@
-#include "cache.h"
#include "builtin.h"
#include "config.h"
+#include "gettext.h"
#include "hook.h"
#include "parse-options.h"
#include "strbuf.h"
#include "strvec.h"
#define BUILTIN_HOOK_RUN_USAGE \
- N_("git hook run [--ignore-missing] <hook-name> [-- <hook-args>]")
+ N_("git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]")
static const char * const builtin_hook_usage[] = {
BUILTIN_HOOK_RUN_USAGE,
@@ -28,6 +28,8 @@ static int run(int argc, const char **argv, const char *prefix)
struct option run_options[] = {
OPT_BOOL(0, "ignore-missing", &ignore_missing,
N_("silently ignore missing requested <hook-name>")),
+ OPT_STRING(0, "to-stdin", &opt.path_to_stdin, N_("path"),
+ N_("file to read into hooks' stdin")),
OPT_END(),
};
int ret;
@@ -67,18 +69,14 @@ usage:
int cmd_hook(int argc, const char **argv, const char *prefix)
{
+ parse_opt_subcommand_fn *fn = NULL;
struct option builtin_hook_options[] = {
+ OPT_SUBCOMMAND("run", &fn, run),
OPT_END(),
};
argc = parse_options(argc, argv, NULL, builtin_hook_options,
- builtin_hook_usage, PARSE_OPT_STOP_AT_NON_OPTION);
- if (!argc)
- goto usage;
+ builtin_hook_usage, 0);
- if (!strcmp(argv[0], "run"))
- return run(argc, argv, prefix);
-
-usage:
- usage_with_options(builtin_hook_usage, builtin_hook_options);
+ return fn(argc, argv, prefix);
}
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 680b66b063..3da879d138 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1,6 +1,9 @@
#include "builtin.h"
#include "config.h"
#include "delta.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "pack.h"
#include "csum-file.h"
#include "blob.h"
@@ -10,11 +13,17 @@
#include "progress.h"
#include "fsck.h"
#include "exec-cmd.h"
+#include "strbuf.h"
#include "streaming.h"
#include "thread-utils.h"
#include "packfile.h"
-#include "object-store.h"
+#include "pack-revindex.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "oid-array.h"
+#include "replace-object.h"
#include "promisor-remote.h"
+#include "setup.h"
static const char index_pack_usage[] =
"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
@@ -801,7 +810,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
if (startup_info->have_repository) {
read_lock();
collision_test_needed =
- has_object_file_with_flags(oid, OBJECT_INFO_QUICK);
+ repo_has_object_file_with_flags(the_repository, oid,
+ OBJECT_INFO_QUICK);
read_unlock();
}
@@ -821,7 +831,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
die(_("cannot read existing object info %s"), oid_to_hex(oid));
if (has_type != type || has_size != size)
die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid));
- has_data = read_object_file(oid, &has_type, &has_size);
+ has_data = repo_read_object_file(the_repository, oid,
+ &has_type, &has_size);
read_unlock();
if (!data)
data = new_data = get_data_from_pack(obj_entry);
@@ -1388,7 +1399,7 @@ static void fix_unresolved_deltas(struct hashfile *f)
sorted_by_pos[i] = &ref_deltas[i];
QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
- if (has_promisor_remote()) {
+ if (repo_has_promisor_remote(the_repository)) {
/*
* Prefetch the delta bases.
*/
@@ -1414,7 +1425,8 @@ static void fix_unresolved_deltas(struct hashfile *f)
if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
continue;
- data = read_object_file(&d->oid, &type, &size);
+ data = repo_read_object_file(the_repository, &d->oid, &type,
+ &size);
if (!data)
continue;
@@ -1568,18 +1580,19 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
strbuf_release(&pack_name);
}
-static int git_index_pack_config(const char *k, const char *v, void *cb)
+static int git_index_pack_config(const char *k, const char *v,
+ const struct config_context *ctx, void *cb)
{
struct pack_idx_option *opts = cb;
if (!strcmp(k, "pack.indexversion")) {
- opts->version = git_config_int(k, v);
+ opts->version = git_config_int(k, v, ctx->kvi);
if (opts->version > 2)
- die(_("bad pack.indexversion=%"PRIu32), opts->version);
+ die(_("bad pack.indexVersion=%"PRIu32), opts->version);
return 0;
}
if (!strcmp(k, "pack.threads")) {
- nr_threads = git_config_int(k, v);
+ nr_threads = git_config_int(k, v, ctx->kvi);
if (nr_threads < 0)
die(_("invalid number of threads specified (%d)"),
nr_threads);
@@ -1595,7 +1608,7 @@ static int git_index_pack_config(const char *k, const char *v, void *cb)
else
opts->flags &= ~WRITE_REV;
}
- return git_default_config(k, v, cb);
+ return git_default_config(k, v, ctx, cb);
}
static int cmp_uint32(const void *a_, const void *b_)
@@ -1739,16 +1752,17 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(index_pack_usage);
- read_replace_refs = 0;
+ disable_replace_refs();
fsck_options.walk = mark_link;
reset_pack_idx_option(&opts);
+ opts.flags |= WRITE_REV;
git_config(git_index_pack_config, &opts);
if (prefix && chdir(prefix))
die(_("Cannot come back to cwd"));
- if (git_env_bool(GIT_TEST_WRITE_REV_INDEX, 0))
- rev_index = 1;
+ if (git_env_bool(GIT_TEST_NO_WRITE_REV_INDEX, 0))
+ rev_index = 0;
else
rev_index = !!(opts.flags & (WRITE_REV_VERIFY | WRITE_REV));
@@ -1942,11 +1956,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
free(objects);
strbuf_release(&index_name_buf);
strbuf_release(&rev_index_name_buf);
- if (pack_name == NULL)
+ if (!pack_name)
free((void *) curr_pack);
- if (index_name == NULL)
+ if (!index_name)
free((void *) curr_index);
- if (rev_index_name == NULL)
+ if (!rev_index_name)
free((void *) curr_rev_index);
/*
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 546f9c595e..cb727c826f 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -3,476 +3,16 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#include "cache.h"
-#include "config.h"
-#include "refs.h"
#include "builtin.h"
-#include "exec-cmd.h"
+#include "abspath.h"
+#include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-file.h"
#include "parse-options.h"
-#include "worktree.h"
-
-#ifndef DEFAULT_GIT_TEMPLATE_DIR
-#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
-#endif
-
-#ifdef NO_TRUSTABLE_FILEMODE
-#define TEST_FILEMODE 0
-#else
-#define TEST_FILEMODE 1
-#endif
-
-#define GIT_DEFAULT_HASH_ENVIRONMENT "GIT_DEFAULT_HASH"
-
-static int init_is_bare_repository = 0;
-static int init_shared_repository = -1;
-
-static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
- DIR *dir)
-{
- size_t path_baselen = path->len;
- size_t template_baselen = template_path->len;
- struct dirent *de;
-
- /* Note: if ".git/hooks" file exists in the repository being
- * re-initialized, /etc/core-git/templates/hooks/update would
- * cause "git init" to fail here. I think this is sane but
- * it means that the set of templates we ship by default, along
- * with the way the namespace under .git/ is organized, should
- * be really carefully chosen.
- */
- safe_create_dir(path->buf, 1);
- while ((de = readdir(dir)) != NULL) {
- struct stat st_git, st_template;
- int exists = 0;
-
- strbuf_setlen(path, path_baselen);
- strbuf_setlen(template_path, template_baselen);
-
- if (de->d_name[0] == '.')
- continue;
- strbuf_addstr(path, de->d_name);
- strbuf_addstr(template_path, de->d_name);
- if (lstat(path->buf, &st_git)) {
- if (errno != ENOENT)
- die_errno(_("cannot stat '%s'"), path->buf);
- }
- else
- exists = 1;
-
- if (lstat(template_path->buf, &st_template))
- die_errno(_("cannot stat template '%s'"), template_path->buf);
-
- if (S_ISDIR(st_template.st_mode)) {
- DIR *subdir = opendir(template_path->buf);
- if (!subdir)
- die_errno(_("cannot opendir '%s'"), template_path->buf);
- strbuf_addch(path, '/');
- strbuf_addch(template_path, '/');
- copy_templates_1(path, template_path, subdir);
- closedir(subdir);
- }
- else if (exists)
- continue;
- else if (S_ISLNK(st_template.st_mode)) {
- struct strbuf lnk = STRBUF_INIT;
- if (strbuf_readlink(&lnk, template_path->buf,
- st_template.st_size) < 0)
- die_errno(_("cannot readlink '%s'"), template_path->buf);
- if (symlink(lnk.buf, path->buf))
- die_errno(_("cannot symlink '%s' '%s'"),
- lnk.buf, path->buf);
- strbuf_release(&lnk);
- }
- else if (S_ISREG(st_template.st_mode)) {
- if (copy_file(path->buf, template_path->buf, st_template.st_mode))
- die_errno(_("cannot copy '%s' to '%s'"),
- template_path->buf, path->buf);
- }
- else
- error(_("ignoring template %s"), template_path->buf);
- }
-}
-
-static void copy_templates(const char *template_dir, const char *init_template_dir)
-{
- struct strbuf path = STRBUF_INIT;
- struct strbuf template_path = STRBUF_INIT;
- size_t template_len;
- struct repository_format template_format = REPOSITORY_FORMAT_INIT;
- struct strbuf err = STRBUF_INIT;
- DIR *dir;
- char *to_free = NULL;
-
- if (!template_dir)
- template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
- if (!template_dir)
- template_dir = init_template_dir;
- if (!template_dir)
- template_dir = to_free = system_path(DEFAULT_GIT_TEMPLATE_DIR);
- if (!template_dir[0]) {
- free(to_free);
- return;
- }
-
- strbuf_addstr(&template_path, template_dir);
- strbuf_complete(&template_path, '/');
- template_len = template_path.len;
-
- dir = opendir(template_path.buf);
- if (!dir) {
- warning(_("templates not found in %s"), template_dir);
- goto free_return;
- }
-
- /* Make sure that template is from the correct vintage */
- strbuf_addstr(&template_path, "config");
- read_repository_format(&template_format, template_path.buf);
- strbuf_setlen(&template_path, template_len);
-
- /*
- * No mention of version at all is OK, but anything else should be
- * verified.
- */
- if (template_format.version >= 0 &&
- verify_repository_format(&template_format, &err) < 0) {
- warning(_("not copying templates from '%s': %s"),
- template_dir, err.buf);
- strbuf_release(&err);
- goto close_free_return;
- }
-
- strbuf_addstr(&path, get_git_common_dir());
- strbuf_complete(&path, '/');
- copy_templates_1(&path, &template_path, dir);
-close_free_return:
- closedir(dir);
-free_return:
- free(to_free);
- strbuf_release(&path);
- strbuf_release(&template_path);
- clear_repository_format(&template_format);
-}
-
-/*
- * If the git_dir is not directly inside the working tree, then git will not
- * find it by default, and we need to set the worktree explicitly.
- */
-static int needs_work_tree_config(const char *git_dir, const char *work_tree)
-{
- if (!strcmp(work_tree, "/") && !strcmp(git_dir, "/.git"))
- return 0;
- if (skip_prefix(git_dir, work_tree, &git_dir) &&
- !strcmp(git_dir, "/.git"))
- return 0;
- return 1;
-}
-
-void initialize_repository_version(int hash_algo, int reinit)
-{
- char repo_version_string[10];
- int repo_version = GIT_REPO_VERSION;
-
- if (hash_algo != GIT_HASH_SHA1)
- repo_version = GIT_REPO_VERSION_READ;
-
- /* This forces creation of new config file */
- xsnprintf(repo_version_string, sizeof(repo_version_string),
- "%d", repo_version);
- git_config_set("core.repositoryformatversion", repo_version_string);
-
- if (hash_algo != GIT_HASH_SHA1)
- git_config_set("extensions.objectformat",
- hash_algos[hash_algo].name);
- else if (reinit)
- git_config_set_gently("extensions.objectformat", NULL);
-}
-
-static int create_default_files(const char *template_path,
- const char *original_git_dir,
- const char *initial_branch,
- const struct repository_format *fmt,
- int quiet)
-{
- struct stat st1;
- struct strbuf buf = STRBUF_INIT;
- char *path;
- char junk[2];
- int reinit;
- int filemode;
- struct strbuf err = STRBUF_INIT;
- const char *init_template_dir = NULL;
- const char *work_tree = get_git_work_tree();
-
- /*
- * First copy the templates -- we might have the default
- * config file there, in which case we would want to read
- * from it after installing.
- *
- * Before reading that config, we also need to clear out any cached
- * values (since we've just potentially changed what's available on
- * disk).
- */
- git_config_get_pathname("init.templatedir", &init_template_dir);
- copy_templates(template_path, init_template_dir);
- free((char *)init_template_dir);
- git_config_clear();
- reset_shared_repository();
- git_config(git_default_config, NULL);
-
- /*
- * We must make sure command-line options continue to override any
- * values we might have just re-read from the config.
- */
- is_bare_repository_cfg = init_is_bare_repository || !work_tree;
- if (init_shared_repository != -1)
- set_shared_repository(init_shared_repository);
-
- /*
- * We would have created the above under user's umask -- under
- * shared-repository settings, we would need to fix them up.
- */
- if (get_shared_repository()) {
- adjust_shared_perm(get_git_dir());
- }
-
- /*
- * We need to create a "refs" dir in any case so that older
- * versions of git can tell that this is a repository.
- */
- safe_create_dir(git_path("refs"), 1);
- adjust_shared_perm(git_path("refs"));
-
- if (refs_init_db(&err))
- die("failed to set up refs db: %s", err.buf);
-
- /*
- * Point the HEAD symref to the initial branch with if HEAD does
- * not yet exist.
- */
- path = git_path_buf(&buf, "HEAD");
- reinit = (!access(path, R_OK)
- || readlink(path, junk, sizeof(junk)-1) != -1);
- if (!reinit) {
- char *ref;
-
- if (!initial_branch)
- initial_branch = git_default_branch_name(quiet);
-
- ref = xstrfmt("refs/heads/%s", initial_branch);
- if (check_refname_format(ref, 0) < 0)
- die(_("invalid initial branch name: '%s'"),
- initial_branch);
-
- if (create_symref("HEAD", ref, NULL) < 0)
- exit(1);
- free(ref);
- }
-
- initialize_repository_version(fmt->hash_algo, 0);
-
- /* Check filemode trustability */
- path = git_path_buf(&buf, "config");
- filemode = TEST_FILEMODE;
- if (TEST_FILEMODE && !lstat(path, &st1)) {
- struct stat st2;
- filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
- !lstat(path, &st2) &&
- st1.st_mode != st2.st_mode &&
- !chmod(path, st1.st_mode));
- if (filemode && !reinit && (st1.st_mode & S_IXUSR))
- filemode = 0;
- }
- git_config_set("core.filemode", filemode ? "true" : "false");
-
- if (is_bare_repository())
- git_config_set("core.bare", "true");
- else {
- git_config_set("core.bare", "false");
- /* allow template config file to override the default */
- if (log_all_ref_updates == LOG_REFS_UNSET)
- git_config_set("core.logallrefupdates", "true");
- if (needs_work_tree_config(original_git_dir, work_tree))
- git_config_set("core.worktree", work_tree);
- }
-
- if (!reinit) {
- /* Check if symlink is supported in the work tree */
- path = git_path_buf(&buf, "tXXXXXX");
- if (!close(xmkstemp(path)) &&
- !unlink(path) &&
- !symlink("testing", path) &&
- !lstat(path, &st1) &&
- S_ISLNK(st1.st_mode))
- unlink(path); /* good */
- else
- git_config_set("core.symlinks", "false");
-
- /* Check if the filesystem is case-insensitive */
- path = git_path_buf(&buf, "CoNfIg");
- if (!access(path, F_OK))
- git_config_set("core.ignorecase", "true");
- probe_utf8_pathname_composition();
- }
-
- strbuf_release(&buf);
- return reinit;
-}
-
-static void create_object_directory(void)
-{
- struct strbuf path = STRBUF_INIT;
- size_t baselen;
-
- strbuf_addstr(&path, get_object_directory());
- baselen = path.len;
-
- safe_create_dir(path.buf, 1);
-
- strbuf_setlen(&path, baselen);
- strbuf_addstr(&path, "/pack");
- safe_create_dir(path.buf, 1);
-
- strbuf_setlen(&path, baselen);
- strbuf_addstr(&path, "/info");
- safe_create_dir(path.buf, 1);
-
- strbuf_release(&path);
-}
-
-static void separate_git_dir(const char *git_dir, const char *git_link)
-{
- struct stat st;
-
- if (!stat(git_link, &st)) {
- const char *src;
-
- if (S_ISREG(st.st_mode))
- src = read_gitfile(git_link);
- else if (S_ISDIR(st.st_mode))
- src = git_link;
- else
- die(_("unable to handle file type %d"), (int)st.st_mode);
-
- if (rename(src, git_dir))
- die_errno(_("unable to move %s to %s"), src, git_dir);
- repair_worktrees(NULL, NULL);
- }
-
- write_file(git_link, "gitdir: %s", git_dir);
-}
-
-static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash)
-{
- const char *env = getenv(GIT_DEFAULT_HASH_ENVIRONMENT);
- /*
- * If we already have an initialized repo, don't allow the user to
- * specify a different algorithm, as that could cause corruption.
- * Otherwise, if the user has specified one on the command line, use it.
- */
- if (repo_fmt->version >= 0 && hash != GIT_HASH_UNKNOWN && hash != repo_fmt->hash_algo)
- die(_("attempt to reinitialize repository with different hash"));
- else if (hash != GIT_HASH_UNKNOWN)
- repo_fmt->hash_algo = hash;
- else if (env) {
- int env_algo = hash_algo_by_name(env);
- if (env_algo == GIT_HASH_UNKNOWN)
- die(_("unknown hash algorithm '%s'"), env);
- repo_fmt->hash_algo = env_algo;
- }
-}
-
-int init_db(const char *git_dir, const char *real_git_dir,
- const char *template_dir, int hash, const char *initial_branch,
- unsigned int flags)
-{
- int reinit;
- int exist_ok = flags & INIT_DB_EXIST_OK;
- char *original_git_dir = real_pathdup(git_dir, 1);
- struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
-
- if (real_git_dir) {
- struct stat st;
-
- if (!exist_ok && !stat(git_dir, &st))
- die(_("%s already exists"), git_dir);
-
- if (!exist_ok && !stat(real_git_dir, &st))
- die(_("%s already exists"), real_git_dir);
-
- set_git_dir(real_git_dir, 1);
- git_dir = get_git_dir();
- separate_git_dir(git_dir, original_git_dir);
- }
- else {
- set_git_dir(git_dir, 1);
- git_dir = get_git_dir();
- }
- startup_info->have_repository = 1;
-
- /* Ensure `core.hidedotfiles` is processed */
- git_config(platform_core_config, NULL);
-
- safe_create_dir(git_dir, 0);
-
- init_is_bare_repository = is_bare_repository();
-
- /* Check to see if the repository version is right.
- * Note that a newly created repository does not have
- * config file, so this will not fail. What we are catching
- * is an attempt to reinitialize new repository with an old tool.
- */
- check_repository_format(&repo_fmt);
-
- validate_hash_algorithm(&repo_fmt, hash);
-
- reinit = create_default_files(template_dir, original_git_dir,
- initial_branch, &repo_fmt,
- flags & INIT_DB_QUIET);
- if (reinit && initial_branch)
- warning(_("re-init: ignored --initial-branch=%s"),
- initial_branch);
-
- create_object_directory();
-
- if (get_shared_repository()) {
- char buf[10];
- /* We do not spell "group" and such, so that
- * the configuration can be read by older version
- * of git. Note, we use octal numbers for new share modes,
- * and compatibility values for PERM_GROUP and
- * PERM_EVERYBODY.
- */
- if (get_shared_repository() < 0)
- /* force to the mode value */
- xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository());
- else if (get_shared_repository() == PERM_GROUP)
- xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
- else if (get_shared_repository() == PERM_EVERYBODY)
- xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
- else
- BUG("invalid value for shared_repository");
- git_config_set("core.sharedrepository", buf);
- git_config_set("receive.denyNonFastforwards", "true");
- }
-
- if (!(flags & INIT_DB_QUIET)) {
- int len = strlen(git_dir);
-
- if (reinit)
- printf(get_shared_repository()
- ? _("Reinitialized existing shared Git repository in %s%s\n")
- : _("Reinitialized existing Git repository in %s%s\n"),
- git_dir, len && git_dir[len-1] != '/' ? "/" : "");
- else
- printf(get_shared_repository()
- ? _("Initialized empty shared Git repository in %s%s\n")
- : _("Initialized empty Git repository in %s%s\n"),
- git_dir, len && git_dir[len-1] != '/' ? "/" : "");
- }
-
- free(original_git_dir);
- return 0;
-}
+#include "path.h"
+#include "setup.h"
+#include "strbuf.h"
static int guess_repository_type(const char *git_dir)
{
@@ -515,7 +55,10 @@ static int shared_callback(const struct option *opt, const char *arg, int unset)
}
static const char *const init_db_usage[] = {
- N_("git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [<directory>]"),
+ N_("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+ " [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+ " [-b <branch-name> | --initial-branch=<branch-name>]\n"
+ " [--shared[=<permissions>]] [<directory>]"),
NULL
};
@@ -535,6 +78,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *object_format = NULL;
const char *initial_branch = NULL;
int hash_algo = GIT_HASH_UNKNOWN;
+ int init_shared_repository = -1;
const struct option init_db_options[] = {
OPT_STRING(0, "template", &template_dir, N_("template-directory"),
N_("directory from which templates will be used")),
@@ -692,5 +236,5 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
flags |= INIT_DB_EXIST_OK;
return init_db(git_dir, real_git_dir, template_dir, hash_algo,
- initial_branch, flags);
+ initial_branch, init_shared_repository, flags);
}
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 84748eafc0..c5e8345265 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -5,15 +5,17 @@
*
*/
-#include "cache.h"
#include "builtin.h"
+#include "gettext.h"
#include "parse-options.h"
#include "string-list.h"
#include "trailer.h"
#include "config.h"
static const char * const git_interpret_trailers_usage[] = {
- N_("git interpret-trailers [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
+ N_("git interpret-trailers [--in-place] [--trim-empty]\n"
+ " [(--trailer <token>[(=|:)<value>])...]\n"
+ " [--parse] [<file>...]"),
NULL
};
diff --git a/builtin/log.c b/builtin/log.c
index 3ac479bec3..1b119eaf0b 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -4,11 +4,17 @@
* (C) Copyright 2006 Linus Torvalds
* 2006 Junio Hamano
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
#include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "refs.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "pager.h"
#include "color.h"
#include "commit.h"
#include "diff.h"
@@ -16,6 +22,7 @@
#include "revision.h"
#include "log-tree.h"
#include "builtin.h"
+#include "oid-array.h"
#include "tag.h"
#include "reflog-walk.h"
#include "patch-ids.h"
@@ -36,6 +43,8 @@
#include "commit-reach.h"
#include "range-diff.h"
#include "tmp-objdir.h"
+#include "tree.h"
+#include "write-or-die.h"
#define MAIL_DEFAULT_WRAP 72
#define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100
@@ -52,9 +61,12 @@ static int default_encode_email_headers = 1;
static int decoration_style;
static int decoration_given;
static int use_mailmap_config = 1;
+static unsigned int force_in_body_from;
+static int stdout_mboxrd;
static const char *fmt_patch_subject_prefix = "PATCH";
static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
static const char *fmt_pretty;
+static int format_no_prefix;
static const char * const builtin_log_usage[] = {
N_("git log [<options>] [<revision-range>] [[--] <path>...]"),
@@ -101,6 +113,20 @@ static int parse_decoration_style(const char *value)
return -1;
}
+static int use_default_decoration_filter = 1;
+static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
+static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
+static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
+
+static int clear_decorations_callback(const struct option *opt,
+ const char *arg, int unset)
+{
+ string_list_clear(&decorate_refs_include, 0);
+ string_list_clear(&decorate_refs_exclude, 0);
+ use_default_decoration_filter = 0;
+ return 0;
+}
+
static int decorate_callback(const struct option *opt, const char *arg, int unset)
{
if (unset)
@@ -162,18 +188,61 @@ static void cmd_log_init_defaults(struct rev_info *rev)
parse_date_format(default_date_mode, &rev->date_mode);
}
+static void set_default_decoration_filter(struct decoration_filter *decoration_filter)
+{
+ int i;
+ char *value = NULL;
+ struct string_list *include = decoration_filter->include_ref_pattern;
+ const struct string_list *config_exclude;
+
+ if (!git_config_get_string_multi("log.excludeDecoration",
+ &config_exclude)) {
+ struct string_list_item *item;
+ for_each_string_list_item(item, config_exclude)
+ string_list_append(decoration_filter->exclude_ref_config_pattern,
+ item->string);
+ }
+
+ /*
+ * By default, decorate_all is disabled. Enable it if
+ * log.initialDecorationSet=all. Don't ever disable it by config,
+ * since the command-line takes precedent.
+ */
+ if (use_default_decoration_filter &&
+ !git_config_get_string("log.initialdecorationset", &value) &&
+ !strcmp("all", value))
+ use_default_decoration_filter = 0;
+ free(value);
+
+ if (!use_default_decoration_filter ||
+ decoration_filter->exclude_ref_pattern->nr ||
+ decoration_filter->include_ref_pattern->nr ||
+ decoration_filter->exclude_ref_config_pattern->nr)
+ return;
+
+ /*
+ * No command-line or config options were given, so
+ * populate with sensible defaults.
+ */
+ for (i = 0; i < ARRAY_SIZE(ref_namespace); i++) {
+ if (!ref_namespace[i].decoration)
+ continue;
+
+ string_list_append(include, ref_namespace[i].ref);
+ }
+}
+
static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
struct rev_info *rev, struct setup_revision_opt *opt)
{
struct userformat_want w;
int quiet = 0, source = 0, mailmap;
static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP};
- static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
- static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
- static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
- struct decoration_filter decoration_filter = {&decorate_refs_include,
- &decorate_refs_exclude,
- &decorate_refs_exclude_config};
+ struct decoration_filter decoration_filter = {
+ .exclude_ref_pattern = &decorate_refs_exclude,
+ .include_ref_pattern = &decorate_refs_include,
+ .exclude_ref_config_pattern = &decorate_refs_exclude_config,
+ };
static struct revision_sources revision_sources;
const struct option builtin_log_options[] = {
@@ -181,6 +250,10 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
OPT_BOOL(0, "source", &source, N_("show source")),
OPT_BOOL(0, "use-mailmap", &mailmap, N_("use mail map file")),
OPT_ALIAS(0, "mailmap", "use-mailmap"),
+ OPT_CALLBACK_F(0, "clear-decorations", NULL, NULL,
+ N_("clear all previously-defined decoration filters"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+ clear_decorations_callback),
OPT_STRING_LIST(0, "decorate-refs", &decorate_refs_include,
N_("pattern"), N_("only decorate refs that match <pattern>")),
OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude,
@@ -199,7 +272,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
mailmap = use_mailmap_config;
argc = parse_options(argc, argv, prefix,
builtin_log_options, builtin_log_usage,
- PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
+ PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT |
PARSE_OPT_KEEP_DASHDASH);
if (quiet)
@@ -231,7 +304,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
}
if (mailmap) {
- rev->mailmap = xcalloc(1, sizeof(struct string_list));
+ rev->mailmap = xmalloc(sizeof(struct string_list));
+ string_list_init_nodup(rev->mailmap);
read_mailmap(rev->mailmap);
}
@@ -264,16 +338,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
}
if (decoration_style || rev->simplify_by_decoration) {
- const struct string_list *config_exclude =
- repo_config_get_value_multi(the_repository,
- "log.excludeDecoration");
-
- if (config_exclude) {
- struct string_list_item *item;
- for_each_string_list_item(item, config_exclude)
- string_list_append(&decorate_refs_exclude_config,
- item->string);
- }
+ set_default_decoration_filter(&decoration_filter);
if (decoration_style)
rev->show_decorations = 1;
@@ -294,6 +359,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
cmd_log_init_finish(argc, argv, prefix, rev, opt);
}
+static int cmd_log_deinit(int ret, struct rev_info *rev)
+{
+ release_revisions(rev);
+ return ret;
+}
+
/*
* This gives a rough estimate for how many commits we
* will print out in the list.
@@ -376,7 +447,7 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list)
setitimer(ITIMER_REAL, &early_output_timer, NULL);
}
-static void early_output(int signal)
+static void early_output(int signal UNUSED)
{
show_early_output = log_show_early;
}
@@ -492,7 +563,8 @@ static int cmd_log_walk(struct rev_info *rev)
return retval;
}
-static int git_log_config(const char *var, const char *value, void *cb)
+static int git_log_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
const char *slot_name;
@@ -501,7 +573,7 @@ static int git_log_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "format.subjectprefix"))
return git_config_string(&fmt_patch_subject_prefix, var, value);
if (!strcmp(var, "format.filenamemaxlength")) {
- fmt_patch_name_max = git_config_int(var, value);
+ fmt_patch_name_max = git_config_int(var, value, ctx->kvi);
return 0;
}
if (!strcmp(var, "format.encodeemailheaders")) {
@@ -541,9 +613,7 @@ static int git_log_config(const char *var, const char *value, void *cb)
return 0;
}
- if (git_gpg_config(var, value, cb) < 0)
- return -1;
- return git_diff_ui_config(var, value, cb);
+ return git_diff_ui_config(var, value, ctx, cb);
}
int cmd_whatchanged(int argc, const char **argv, const char *prefix)
@@ -565,7 +635,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
cmd_log_init(argc, argv, prefix, &rev, &opt);
if (!rev.diffopt.output_format)
rev.diffopt.output_format = DIFF_FORMAT_RAW;
- return cmd_log_walk(&rev);
+ return cmd_log_deinit(cmd_log_walk(&rev), &rev);
}
static void show_tagger(const char *buf, struct rev_info *rev)
@@ -615,7 +685,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)
{
unsigned long size;
enum object_type type;
- char *buf = read_object_file(oid, &type, &size);
+ char *buf = repo_read_object_file(the_repository, oid, &type, &size);
int offset = 0;
if (!buf)
@@ -638,9 +708,10 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)
return 0;
}
-static int show_tree_object(const struct object_id *oid,
- struct strbuf *base,
- const char *pathname, unsigned mode, void *context)
+static int show_tree_object(const struct object_id *oid UNUSED,
+ struct strbuf *base UNUSED,
+ const char *pathname, unsigned mode,
+ void *context)
{
FILE *file = context;
fprintf(file, "%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
@@ -661,14 +732,19 @@ static void show_setup_revisions_tweak(struct rev_info *rev,
int cmd_show(int argc, const char **argv, const char *prefix)
{
struct rev_info rev;
- struct object_array_entry *objects;
+ unsigned int i;
struct setup_revision_opt opt;
struct pathspec match_all;
- int i, count, ret = 0;
+ int ret = 0;
init_log_defaults();
git_config(git_log_config, NULL);
+ if (the_repository->gitdir) {
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+ }
+
memset(&match_all, 0, sizeof(match_all));
repo_init_revisions(the_repository, &rev, prefix);
git_config(grep_config, &rev.grep_filter);
@@ -684,14 +760,12 @@ int cmd_show(int argc, const char **argv, const char *prefix)
cmd_log_init(argc, argv, prefix, &rev, &opt);
if (!rev.no_walk)
- return cmd_log_walk(&rev);
+ return cmd_log_deinit(cmd_log_walk(&rev), &rev);
- count = rev.pending.nr;
- objects = rev.pending.objects;
rev.diffopt.no_free = 1;
- for (i = 0; i < count && !ret; i++) {
- struct object *o = objects[i].item;
- const char *name = objects[i].name;
+ for (i = 0; i < rev.pending.nr && !ret; i++) {
+ struct object *o = rev.pending.objects[i].item;
+ const char *name = rev.pending.objects[i].name;
switch (o->type) {
case OBJ_BLOB:
ret = show_blob_object(&o->oid, &rev, name);
@@ -714,7 +788,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
if (!o)
ret = error(_("could not read object %s"),
oid_to_hex(oid));
- objects[i].item = o;
+ rev.pending.objects[i].item = o;
i--;
break;
}
@@ -731,11 +805,24 @@ int cmd_show(int argc, const char **argv, const char *prefix)
rev.shown_one = 1;
break;
case OBJ_COMMIT:
- rev.pending.nr = rev.pending.alloc = 0;
- rev.pending.objects = NULL;
+ {
+ struct object_array old;
+ struct object_array blank = OBJECT_ARRAY_INIT;
+
+ memcpy(&old, &rev.pending, sizeof(old));
+ memcpy(&rev.pending, &blank, sizeof(rev.pending));
+
add_object_array(o, name, &rev.pending);
ret = cmd_log_walk_no_free(&rev);
+
+ /*
+ * No need for
+ * object_array_clear(&pending). It was
+ * cleared already in prepare_revision_walk()
+ */
+ memcpy(&rev.pending, &old, sizeof(rev.pending));
break;
+ }
default:
ret = error(_("unknown type: %d"), o->type);
}
@@ -744,8 +831,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
rev.diffopt.no_free = 0;
diff_free(&rev.diffopt);
- free(objects);
- return ret;
+ return cmd_log_deinit(ret, &rev);
}
/*
@@ -773,14 +859,14 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
rev.always_show_header = 1;
cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
- return cmd_log_walk(&rev);
+ return cmd_log_deinit(cmd_log_walk(&rev), &rev);
}
static void log_setup_revisions_tweak(struct rev_info *rev,
struct setup_revision_opt *opt)
{
if (rev->diffopt.flags.default_follow_renames &&
- rev->prune_data.nr == 1)
+ diff_check_follow_pathspec(&rev->prune_data, 0))
rev->diffopt.flags.follow_renames = 1;
if (rev->first_parent_only)
@@ -804,7 +890,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
opt.revarg_opt = REVARG_COMMITTISH;
opt.tweak = log_setup_revisions_tweak;
cmd_log_init(argc, argv, prefix, &rev, &opt);
- return cmd_log_walk(&rev);
+ return cmd_log_deinit(cmd_log_walk(&rev), &rev);
}
/* format-patch */
@@ -893,7 +979,8 @@ static enum cover_from_description parse_cover_from_description(const char *arg)
die(_("%s: invalid cover from description mode"), arg);
}
-static int git_format_config(const char *var, const char *value, void *cb)
+static int git_format_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (!strcmp(var, "format.headers")) {
if (!value)
@@ -931,6 +1018,8 @@ static int git_format_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "format.attach")) {
if (value && *value)
default_attach = xstrdup(value);
+ else if (value && !*value)
+ FREE_AND_NULL(default_attach);
else
default_attach = xstrdup(git_version_string);
return 0;
@@ -984,6 +1073,10 @@ static int git_format_config(const char *var, const char *value, void *cb)
from = NULL;
return 0;
}
+ if (!strcmp(var, "format.forceinbodyfrom")) {
+ force_in_body_from = git_config_bool(var, value);
+ return 0;
+ }
if (!strcmp(var, "format.notes")) {
int b = git_parse_maybe_bool(value);
if (b < 0)
@@ -998,8 +1091,25 @@ static int git_format_config(const char *var, const char *value, void *cb)
cover_from_description_mode = parse_cover_from_description(value);
return 0;
}
+ if (!strcmp(var, "format.mboxrd")) {
+ stdout_mboxrd = git_config_bool(var, value);
+ return 0;
+ }
+ if (!strcmp(var, "format.noprefix")) {
+ format_no_prefix = 1;
+ return 0;
+ }
- return git_log_config(var, value, cb);
+ /*
+ * ignore some porcelain config which would otherwise be parsed by
+ * git_diff_ui_config(), via git_log_config(); we can't just avoid
+ * diff_ui_config completely, because we do care about some ui options
+ * like color.
+ */
+ if (!strcmp(var, "diff.noprefix"))
+ return 0;
+
+ return git_log_config(var, value, ctx, cb);
}
static const char *output_directory = NULL;
@@ -1025,7 +1135,7 @@ static int open_next_file(struct commit *commit, const char *subject,
if (!quiet)
printf("%s\n", filename.buf + outdir_offset);
- if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL) {
+ if (!(rev->diffopt.file = fopen(filename.buf, "w"))) {
error_errno(_("cannot open patch file %s"), filename.buf);
strbuf_release(&filename);
return -1;
@@ -1118,7 +1228,8 @@ static char *find_branch_name(struct rev_info *rev)
return NULL;
ref = rev->cmdline.rev[positive].name;
tip_oid = &rev->cmdline.rev[positive].item->oid;
- if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref, 0) &&
+ if (repo_dwim_ref(the_repository, ref, strlen(ref), &branch_oid,
+ &full_ref, 0) &&
skip_prefix(full_ref, "refs/heads/", &v) &&
oideq(tip_oid, &branch_oid))
branch = xstrdup(v);
@@ -1228,10 +1339,11 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte, 0);
for (i = 0; !need_8bit_cte && i < nr; i++) {
- const char *buf = get_commit_buffer(list[i], NULL);
+ const char *buf = repo_get_commit_buffer(the_repository,
+ list[i], NULL);
if (has_non_ascii(buf))
need_8bit_cte = 1;
- unuse_commit_buffer(list[i], buf);
+ repo_unuse_commit_buffer(the_repository, list[i], buf);
}
if (!branch_name)
@@ -1254,6 +1366,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
log.in2 = 4;
log.file = rev->diffopt.file;
log.groups = SHORTLOG_GROUP_AUTHOR;
+ shortlog_finish_setup(&log);
for (i = 0; i < nr; i++)
shortlog_add_commit(&log, list[i]);
@@ -1283,7 +1396,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
.other_arg = &other_arg
};
- diff_setup(&opts);
+ repo_diff_setup(the_repository, &opts);
opts.file = rev->diffopt.file;
opts.use_color = rev->diffopt.use_color;
diff_setup_done(&opts);
@@ -1294,7 +1407,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
}
}
-static const char *clean_message_id(const char *msg_id)
+static char *clean_message_id(const char *msg_id)
{
char ch;
const char *a, *z, *m;
@@ -1312,7 +1425,7 @@ static const char *clean_message_id(const char *msg_id)
if (!z)
die(_("insane in-reply-to: %s"), msg_id);
if (++z == m)
- return a;
+ return xstrdup(a);
return xmemdupz(a, z - a);
}
@@ -1555,14 +1668,16 @@ static struct commit *get_base_commit(const char *base_commit,
struct commit *commit;
struct object_id oid;
- if (get_oid(upstream, &oid)) {
+ if (repo_get_oid(the_repository, upstream, &oid)) {
if (die_on_failure)
die(_("failed to resolve '%s' as a valid ref"), upstream);
else
return NULL;
}
commit = lookup_commit_or_die(&oid, "upstream base");
- base_list = get_merge_bases_many(commit, total, list);
+ base_list = repo_get_merge_bases_many(the_repository,
+ commit, total,
+ list);
/* There should be one and only one merge base. */
if (!base_list || base_list->next) {
if (die_on_failure) {
@@ -1596,7 +1711,9 @@ static struct commit *get_base_commit(const char *base_commit,
while (rev_nr > 1) {
for (i = 0; i < rev_nr / 2; i++) {
struct commit_list *merge_base;
- merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);
+ merge_base = repo_get_merge_bases(the_repository,
+ rev[2 * i],
+ rev[2 * i + 1]);
if (!merge_base || merge_base->next) {
if (die_on_failure) {
die(_("failed to find exact merge base"));
@@ -1614,7 +1731,7 @@ static struct commit *get_base_commit(const char *base_commit,
rev_nr = DIV_ROUND_UP(rev_nr, 2);
}
- if (!in_merge_bases(base, rev[0])) {
+ if (!repo_in_merge_bases(the_repository, base, rev[0])) {
if (die_on_failure) {
die(_("base commit should be the ancestor of revision list"));
} else {
@@ -1683,7 +1800,7 @@ static void prepare_bases(struct base_tree_info *bases,
struct object_id *patch_id;
if (*commit_base_at(&commit_base, commit))
continue;
- if (commit_patch_id(commit, &diffopt, &oid, 0, 1))
+ if (commit_patch_id(commit, &diffopt, &oid, 0))
die(_("cannot get patch id"));
ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id);
patch_id = bases->patch_id + bases->nr_patch_id;
@@ -1759,6 +1876,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
struct commit *commit;
struct commit **list = NULL;
struct rev_info rev;
+ char *to_free = NULL;
struct setup_revision_opt s_r_opt;
int nr = 0, total, i;
int use_stdout = 0;
@@ -1789,6 +1907,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
struct strbuf rdiff1 = STRBUF_INIT;
struct strbuf rdiff2 = STRBUF_INIT;
struct strbuf rdiff_title = STRBUF_INIT;
+ struct strbuf sprefix = STRBUF_INIT;
int creation_factor = -1;
const struct option builtin_format_patch_options[] = {
@@ -1874,6 +1993,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
N_("show changes against <refspec> in cover letter or single patch")),
OPT_INTEGER(0, "creation-factor", &creation_factor,
N_("percentage by which creation is weighted")),
+ OPT_BOOL(0, "force-in-body-from", &force_in_body_from,
+ N_("show in-body From: even if identical to the e-mail header")),
OPT_END()
};
@@ -1902,6 +2023,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
s_r_opt.def = "HEAD";
s_r_opt.revarg_opt = REVARG_COMMITTISH;
+ if (format_no_prefix)
+ diff_set_noprefix(&rev.diffopt);
+
if (default_attach) {
rev.mime_boundary = default_attach;
rev.no_inline = 1;
@@ -1914,9 +2038,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
*/
argc = parse_options(argc, argv, prefix, builtin_format_patch_options,
builtin_format_patch_usage,
- PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
+ PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT |
PARSE_OPT_KEEP_DASHDASH);
+ rev.force_in_body_from = force_in_body_from;
+
/* Make sure "0000-$sub.patch" gives non-negative length for $sub */
if (fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix))
fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix);
@@ -1925,12 +2051,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
if (reroll_count) {
- struct strbuf sprefix = STRBUF_INIT;
-
strbuf_addf(&sprefix, "%s v%s",
rev.subject_prefix, reroll_count);
rev.reroll_count = reroll_count;
- rev.subject_prefix = strbuf_detach(&sprefix, NULL);
+ rev.subject_prefix = sprefix.buf;
}
for (i = 0; i < extra_hdr.nr; i++) {
@@ -1960,7 +2084,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
strbuf_addch(&buf, '\n');
}
- rev.extra_headers = strbuf_detach(&buf, NULL);
+ rev.extra_headers = to_free = strbuf_detach(&buf, NULL);
if (from) {
if (split_ident_line(&rev.from_ident, from, strlen(from)))
@@ -2006,6 +2130,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
/* Always generate a patch */
rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
+ rev.always_show_header = 1;
rev.zero_commit = zero_commit;
rev.patch_name_max = fmt_patch_name_max;
@@ -2020,6 +2145,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.diffopt.close_file, "--output",
!!output_directory, "--output-directory");
+ if (use_stdout && stdout_mboxrd)
+ rev.commit_format = CMIT_FMT_MBOXRD;
+
if (use_stdout) {
setup_pager();
} else if (!rev.diffopt.close_file) {
@@ -2181,11 +2309,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
prepare_bases(&bases, base, list, nr);
}
- if (in_reply_to || thread || cover_letter)
- rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
+ if (in_reply_to || thread || cover_letter) {
+ rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));
+ string_list_init_dup(rev.ref_message_ids);
+ }
if (in_reply_to) {
- const char *msgid = clean_message_id(in_reply_to);
- string_list_append(rev.ref_message_ids, msgid);
+ char *msgid = clean_message_id(in_reply_to);
+ string_list_append_nodup(rev.ref_message_ids, msgid);
}
rev.numbered_files = just_numbers;
rev.patch_suffix = fmt_patch_suffix;
@@ -2241,8 +2371,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
&& (!cover_letter || rev.nr > 1))
free(rev.message_id);
else
- string_list_append(rev.ref_message_ids,
- rev.message_id);
+ string_list_append_nodup(rev.ref_message_ids,
+ rev.message_id);
}
gen_message_id(&rev, oid_to_hex(&commit->object.oid));
}
@@ -2289,14 +2419,19 @@ done:
strbuf_release(&rdiff1);
strbuf_release(&rdiff2);
strbuf_release(&rdiff_title);
- UNLEAK(rev);
- return 0;
+ strbuf_release(&sprefix);
+ free(to_free);
+ free(rev.message_id);
+ if (rev.ref_message_ids)
+ string_list_clear(rev.ref_message_ids, 0);
+ free(rev.ref_message_ids);
+ return cmd_log_deinit(0, &rev);
}
static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
{
struct object_id oid;
- if (get_oid(arg, &oid) == 0) {
+ if (repo_get_oid(the_repository, arg, &oid) == 0) {
struct commit *commit = lookup_commit_reference(the_repository,
&oid);
if (commit) {
@@ -2318,12 +2453,12 @@ static void print_commit(char sign, struct commit *commit, int verbose,
{
if (!verbose) {
fprintf(file, "%c %s\n", sign,
- find_unique_abbrev(&commit->object.oid, abbrev));
+ repo_find_unique_abbrev(the_repository, &commit->object.oid, abbrev));
} else {
struct strbuf buf = STRBUF_INIT;
pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
fprintf(file, "%c %s %s\n", sign,
- find_unique_abbrev(&commit->object.oid, abbrev),
+ repo_find_unique_abbrev(the_repository, &commit->object.oid, abbrev),
buf.buf);
strbuf_release(&buf);
}
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index e791b65e7e..a0229c3277 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -5,21 +5,31 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#include "cache.h"
+#include "builtin.h"
#include "repository.h"
#include "config.h"
+#include "convert.h"
#include "quote.h"
#include "dir.h"
-#include "builtin.h"
+#include "gettext.h"
+#include "object-name.h"
+#include "strbuf.h"
#include "tree.h"
#include "cache-tree.h"
#include "parse-options.h"
#include "resolve-undo.h"
#include "string-list.h"
+#include "path.h"
#include "pathspec.h"
+#include "read-cache.h"
#include "run-command.h"
+#include "setup.h"
+#include "sparse-index.h"
#include "submodule.h"
#include "submodule-config.h"
+#include "object-store.h"
+#include "hex.h"
+
static int abbrev;
static int show_deleted;
@@ -48,6 +58,7 @@ static char *ps_matched;
static const char *with_tree;
static int exc_given;
static int exclude_args;
+static const char *format;
static const char *tag_cached = "";
static const char *tag_unmerged = "";
@@ -85,6 +96,19 @@ static void write_name(const char *name)
stdout, line_terminator);
}
+static void write_name_to_buf(struct strbuf *sb, const char *name)
+{
+ struct strbuf buf = STRBUF_INIT;
+ const char *rel = relative_path(name, prefix_len ? prefix : NULL, &buf);
+
+ if (line_terminator)
+ quote_c_style(rel, sb, NULL, 0);
+ else
+ strbuf_addstr(sb, rel);
+
+ strbuf_release(&buf);
+}
+
static const char *get_tag(const struct cache_entry *ce, const char *tag)
{
static char alttag[4];
@@ -222,6 +246,80 @@ static void show_submodule(struct repository *superproject,
repo_clear(&subrepo);
}
+static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
+ const enum object_type type, unsigned int padded)
+{
+ if (type == OBJ_BLOB) {
+ unsigned long size;
+ if (oid_object_info(the_repository, oid, &size) < 0)
+ die(_("could not get object info about '%s'"),
+ oid_to_hex(oid));
+ if (padded)
+ strbuf_addf(line, "%7"PRIuMAX, (uintmax_t)size);
+ else
+ strbuf_addf(line, "%"PRIuMAX, (uintmax_t)size);
+ } else if (padded) {
+ strbuf_addf(line, "%7s", "-");
+ } else {
+ strbuf_addstr(line, "-");
+ }
+}
+
+static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce,
+ const char *format, const char *fullname) {
+ struct strbuf sb = STRBUF_INIT;
+
+ while (strbuf_expand_step(&sb, &format)) {
+ const char *end;
+ size_t len;
+ struct stat st;
+
+ if (skip_prefix(format, "%", &format))
+ strbuf_addch(&sb, '%');
+ else if ((len = strbuf_expand_literal(&sb, format)))
+ format += len;
+ else if (*format != '(')
+ die(_("bad ls-files format: element '%s' "
+ "does not start with '('"), format);
+ else if (!(end = strchr(format + 1, ')')))
+ die(_("bad ls-files format: element '%s' "
+ "does not end in ')'"), format);
+ else if (skip_prefix(format, "(objectmode)", &format))
+ strbuf_addf(&sb, "%06o", ce->ce_mode);
+ else if (skip_prefix(format, "(objectname)", &format))
+ strbuf_add_unique_abbrev(&sb, &ce->oid, abbrev);
+ else if (skip_prefix(format, "(objecttype)", &format))
+ strbuf_addstr(&sb, type_name(object_type(ce->ce_mode)));
+ else if (skip_prefix(format, "(objectsize:padded)", &format))
+ expand_objectsize(&sb, &ce->oid,
+ object_type(ce->ce_mode), 1);
+ else if (skip_prefix(format, "(objectsize)", &format))
+ expand_objectsize(&sb, &ce->oid,
+ object_type(ce->ce_mode), 0);
+ else if (skip_prefix(format, "(stage)", &format))
+ strbuf_addf(&sb, "%d", ce_stage(ce));
+ else if (skip_prefix(format, "(eolinfo:index)", &format))
+ strbuf_addstr(&sb, S_ISREG(ce->ce_mode) ?
+ get_cached_convert_stats_ascii(repo->index,
+ ce->name) : "");
+ else if (skip_prefix(format, "(eolinfo:worktree)", &format))
+ strbuf_addstr(&sb, !lstat(fullname, &st) &&
+ S_ISREG(st.st_mode) ?
+ get_wt_convert_stats_ascii(fullname) : "");
+ else if (skip_prefix(format, "(eolattr)", &format))
+ strbuf_addstr(&sb, get_convert_attr_ascii(repo->index,
+ fullname));
+ else if (skip_prefix(format, "(path)", &format))
+ write_name_to_buf(&sb, fullname);
+ else
+ die(_("bad ls-files format: %%%.*s"),
+ (int)(end - format + 1), format);
+ }
+ strbuf_addch(&sb, line_terminator);
+ fwrite(sb.buf, sb.len, 1, stdout);
+ strbuf_release(&sb);
+}
+
static void show_ce(struct repository *repo, struct dir_struct *dir,
const struct cache_entry *ce, const char *fullname,
const char *tag)
@@ -236,6 +334,12 @@ static void show_ce(struct repository *repo, struct dir_struct *dir,
max_prefix_len, ps_matched,
S_ISDIR(ce->ce_mode) ||
S_ISGITLINK(ce->ce_mode))) {
+ if (format) {
+ show_ce_fmt(repo, ce, format, fullname);
+ print_debug(ce);
+ return;
+ }
+
tag = get_tag(ce, tag);
if (!show_stage) {
@@ -275,7 +379,7 @@ static void show_ru_info(struct index_state *istate)
if (!ui->mode[i])
continue;
printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
- find_unique_abbrev(&ui->oid[i], abbrev),
+ repo_find_unique_abbrev(the_repository, &ui->oid[i], abbrev),
i + 1);
write_name(path);
}
@@ -424,142 +528,6 @@ static int get_common_prefix_len(const char *common_prefix)
return common_prefix_len;
}
-static int read_one_entry_opt(struct index_state *istate,
- const struct object_id *oid,
- struct strbuf *base,
- const char *pathname,
- unsigned mode, int opt)
-{
- int len;
- struct cache_entry *ce;
-
- if (S_ISDIR(mode))
- return READ_TREE_RECURSIVE;
-
- len = strlen(pathname);
- ce = make_empty_cache_entry(istate, base->len + len);
-
- ce->ce_mode = create_ce_mode(mode);
- ce->ce_flags = create_ce_flags(1);
- ce->ce_namelen = base->len + len;
- memcpy(ce->name, base->buf, base->len);
- memcpy(ce->name + base->len, pathname, len+1);
- oidcpy(&ce->oid, oid);
- return add_index_entry(istate, ce, opt);
-}
-
-static int read_one_entry(const struct object_id *oid, struct strbuf *base,
- const char *pathname, unsigned mode,
- void *context)
-{
- struct index_state *istate = context;
- return read_one_entry_opt(istate, oid, base, pathname,
- mode,
- ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
-}
-
-/*
- * This is used when the caller knows there is no existing entries at
- * the stage that will conflict with the entry being added.
- */
-static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,
- const char *pathname, unsigned mode,
- void *context)
-{
- struct index_state *istate = context;
- return read_one_entry_opt(istate, oid, base, pathname,
- mode, ADD_CACHE_JUST_APPEND);
-}
-
-/*
- * Read the tree specified with --with-tree option
- * (typically, HEAD) into stage #1 and then
- * squash them down to stage #0. This is used for
- * --error-unmatch to list and check the path patterns
- * that were given from the command line. We are not
- * going to write this index out.
- */
-void overlay_tree_on_index(struct index_state *istate,
- const char *tree_name, const char *prefix)
-{
- struct tree *tree;
- struct object_id oid;
- struct pathspec pathspec;
- struct cache_entry *last_stage0 = NULL;
- int i;
- read_tree_fn_t fn = NULL;
- int err;
-
- if (get_oid(tree_name, &oid))
- die("tree-ish %s not found.", tree_name);
- tree = parse_tree_indirect(&oid);
- if (!tree)
- die("bad tree-ish %s", tree_name);
-
- /* Hoist the unmerged entries up to stage #3 to make room */
- /* TODO: audit for interaction with sparse-index. */
- ensure_full_index(istate);
- for (i = 0; i < istate->cache_nr; i++) {
- struct cache_entry *ce = istate->cache[i];
- if (!ce_stage(ce))
- continue;
- ce->ce_flags |= CE_STAGEMASK;
- }
-
- if (prefix) {
- static const char *(matchbuf[1]);
- matchbuf[0] = NULL;
- parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC,
- PATHSPEC_PREFER_CWD, prefix, matchbuf);
- } else
- memset(&pathspec, 0, sizeof(pathspec));
-
- /*
- * See if we have cache entry at the stage. If so,
- * do it the original slow way, otherwise, append and then
- * sort at the end.
- */
- for (i = 0; !fn && i < istate->cache_nr; i++) {
- const struct cache_entry *ce = istate->cache[i];
- if (ce_stage(ce) == 1)
- fn = read_one_entry;
- }
-
- if (!fn)
- fn = read_one_entry_quick;
- err = read_tree(the_repository, tree, &pathspec, fn, istate);
- if (err)
- die("unable to read tree entries %s", tree_name);
-
- /*
- * Sort the cache entry -- we need to nuke the cache tree, though.
- */
- if (fn == read_one_entry_quick) {
- cache_tree_free(&istate->cache_tree);
- QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);
- }
-
- for (i = 0; i < istate->cache_nr; i++) {
- struct cache_entry *ce = istate->cache[i];
- switch (ce_stage(ce)) {
- case 0:
- last_stage0 = ce;
- /* fallthru */
- default:
- continue;
- case 1:
- /*
- * If there is stage #0 entry for this, we do not
- * need to show it. We use CE_UPDATE bit to mark
- * such an entry.
- */
- if (last_stage0 &&
- !strcmp(last_stage0->name, ce->name))
- ce->ce_flags |= CE_UPDATE;
- }
- }
-}
-
static const char * const ls_files_usage[] = {
N_("git ls-files [<options>] [<file>...]"),
NULL
@@ -675,6 +643,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
N_("suppress duplicate entries")),
OPT_BOOL(0, "sparse", &show_sparse_dirs,
N_("show sparse directories in the presence of a sparse index")),
+ OPT_STRING_F(0, "format", &format, N_("format"),
+ N_("format to use for the output"),
+ PARSE_OPT_NONEG),
OPT_END()
};
int ret = 0;
@@ -699,6 +670,13 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
for (i = 0; i < exclude_list.nr; i++) {
add_pattern(exclude_list.items[i].string, "", 0, pl, --exclude_args);
}
+
+ if (format && (show_stage || show_others || show_killed ||
+ show_resolve_undo || skipping_duplicates || show_eol || show_tag))
+ usage_msg_opt(_("--format cannot be used with -s, -o, -k, -t, "
+ "--resolve-undo, --deduplicate, --eol"),
+ ls_files_usage, builtin_ls_files_options);
+
if (show_tag || show_valid_bit || show_fsmonitor_bit) {
tag_cached = "H ";
tag_unmerged = "M ";
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index d856085e94..fc76575430 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -1,14 +1,18 @@
#include "builtin.h"
-#include "cache.h"
+#include "gettext.h"
+#include "hex.h"
#include "transport.h"
+#include "pkt-line.h"
#include "ref-filter.h"
#include "remote.h"
#include "refs.h"
+#include "parse-options.h"
+#include "wildmatch.h"
static const char * const ls_remote_usage[] = {
N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
- " [-q | --quiet] [--exit-code] [--get-url]\n"
- " [--symref] [<repository> [<refs>...]]"),
+ " [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+ " [--symref] [<repository> [<patterns>...]]"),
NULL
};
@@ -114,7 +118,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
}
transport = transport_get(remote, NULL);
- if (uploadpack != NULL)
+ if (uploadpack)
transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
if (server_options.nr)
transport->server_options = &server_options;
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index e279be8bb6..53073d64cb 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -3,48 +3,25 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
-#include "object-store.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-store-ll.h"
#include "blob.h"
#include "tree.h"
#include "commit.h"
+#include "path.h"
#include "quote.h"
-#include "builtin.h"
#include "parse-options.h"
#include "pathspec.h"
-static int line_termination = '\n';
-#define LS_RECURSIVE 1
-#define LS_TREE_ONLY (1 << 1)
-#define LS_SHOW_TREES (1 << 2)
-static int abbrev;
-static int ls_options;
-static struct pathspec pathspec;
-static int chomp_prefix;
-static const char *ls_tree_prefix;
-static const char *format;
-struct show_tree_data {
- unsigned mode;
- enum object_type type;
- const struct object_id *oid;
- const char *pathname;
- struct strbuf *base;
-};
-
-static const char * const ls_tree_usage[] = {
+static const char * const ls_tree_usage[] = {
N_("git ls-tree [<options>] <tree-ish> [<path>...]"),
NULL
};
-static enum ls_tree_cmdmode {
- MODE_DEFAULT = 0,
- MODE_LONG,
- MODE_NAME_ONLY,
- MODE_NAME_STATUS,
- MODE_OBJECT_ONLY,
-} cmdmode;
-
static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
const enum object_type type, unsigned int padded)
{
@@ -64,65 +41,32 @@ static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
}
}
-static size_t expand_show_tree(struct strbuf *sb, const char *start,
- void *context)
-{
- struct show_tree_data *data = context;
- const char *end;
- const char *p;
- unsigned int errlen;
- size_t len = strbuf_expand_literal_cb(sb, start, NULL);
-
- if (len)
- return len;
- if (*start != '(')
- die(_("bad ls-tree format: element '%s' does not start with '('"), start);
-
- end = strchr(start + 1, ')');
- if (!end)
- die(_("bad ls-tree format: element '%s' does not end in ')'"), start);
-
- len = end - start + 1;
- if (skip_prefix(start, "(objectmode)", &p)) {
- strbuf_addf(sb, "%06o", data->mode);
- } else if (skip_prefix(start, "(objecttype)", &p)) {
- strbuf_addstr(sb, type_name(data->type));
- } else if (skip_prefix(start, "(objectsize:padded)", &p)) {
- expand_objectsize(sb, data->oid, data->type, 1);
- } else if (skip_prefix(start, "(objectsize)", &p)) {
- expand_objectsize(sb, data->oid, data->type, 0);
- } else if (skip_prefix(start, "(objectname)", &p)) {
- strbuf_add_unique_abbrev(sb, data->oid, abbrev);
- } else if (skip_prefix(start, "(path)", &p)) {
- const char *name = data->base->buf;
- const char *prefix = chomp_prefix ? ls_tree_prefix : NULL;
- struct strbuf quoted = STRBUF_INIT;
- struct strbuf sbuf = STRBUF_INIT;
- strbuf_addstr(data->base, data->pathname);
- name = relative_path(data->base->buf, prefix, &sbuf);
- quote_c_style(name, &quoted, NULL, 0);
- strbuf_addbuf(sb, &quoted);
- strbuf_release(&sbuf);
- strbuf_release(&quoted);
- } else {
- errlen = (unsigned long)len;
- die(_("bad ls-tree format: %%%.*s"), errlen, start);
- }
- return len;
-}
+struct ls_tree_options {
+ unsigned null_termination:1;
+ int abbrev;
+ enum ls_tree_path_options {
+ LS_RECURSIVE = 1 << 0,
+ LS_TREE_ONLY = 1 << 1,
+ LS_SHOW_TREES = 1 << 2,
+ } ls_options;
+ struct pathspec pathspec;
+ const char *prefix;
+ const char *format;
+};
-static int show_recursive(const char *base, size_t baselen, const char *pathname)
+static int show_recursive(struct ls_tree_options *options, const char *base,
+ size_t baselen, const char *pathname)
{
int i;
- if (ls_options & LS_RECURSIVE)
+ if (options->ls_options & LS_RECURSIVE)
return 1;
- if (!pathspec.nr)
+ if (!options->pathspec.nr)
return 0;
- for (i = 0; i < pathspec.nr; i++) {
- const char *spec = pathspec.items[i].match;
+ for (i = 0; i < options->pathspec.nr; i++) {
+ const char *spec = options->pathspec.items[i].match;
size_t len, speclen;
if (strncmp(base, spec, baselen))
@@ -144,70 +88,105 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname
static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
const char *pathname, unsigned mode, void *context)
{
- size_t baselen;
+ struct ls_tree_options *options = context;
int recurse = 0;
struct strbuf sb = STRBUF_INIT;
enum object_type type = object_type(mode);
+ const char *format = options->format;
- struct show_tree_data data = {
- .mode = mode,
- .type = type,
- .oid = oid,
- .pathname = pathname,
- .base = base,
- };
-
- if (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))
+ if (type == OBJ_TREE && show_recursive(options, base->buf, base->len, pathname))
recurse = READ_TREE_RECURSIVE;
- if (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES))
+ if (type == OBJ_TREE && recurse && !(options->ls_options & LS_SHOW_TREES))
return recurse;
- if (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))
+ if (type == OBJ_BLOB && (options->ls_options & LS_TREE_ONLY))
return 0;
- baselen = base->len;
- strbuf_expand(&sb, format, expand_show_tree, &data);
- strbuf_addch(&sb, line_termination);
+ while (strbuf_expand_step(&sb, &format)) {
+ const char *end;
+ size_t len;
+
+ if (skip_prefix(format, "%", &format))
+ strbuf_addch(&sb, '%');
+ else if ((len = strbuf_expand_literal(&sb, format)))
+ format += len;
+ else if (*format != '(')
+ die(_("bad ls-tree format: element '%s' "
+ "does not start with '('"), format);
+ else if (!(end = strchr(format + 1, ')')))
+ die(_("bad ls-tree format: element '%s' "
+ "does not end in ')'"), format);
+ else if (skip_prefix(format, "(objectmode)", &format))
+ strbuf_addf(&sb, "%06o", mode);
+ else if (skip_prefix(format, "(objecttype)", &format))
+ strbuf_addstr(&sb, type_name(type));
+ else if (skip_prefix(format, "(objectsize:padded)", &format))
+ expand_objectsize(&sb, oid, type, 1);
+ else if (skip_prefix(format, "(objectsize)", &format))
+ expand_objectsize(&sb, oid, type, 0);
+ else if (skip_prefix(format, "(objectname)", &format))
+ strbuf_add_unique_abbrev(&sb, oid, options->abbrev);
+ else if (skip_prefix(format, "(path)", &format)) {
+ const char *name;
+ const char *prefix = options->prefix;
+ struct strbuf sbuf = STRBUF_INIT;
+ size_t baselen = base->len;
+
+ strbuf_addstr(base, pathname);
+ name = relative_path(base->buf, prefix, &sbuf);
+ quote_c_style(name, &sb, NULL, 0);
+ strbuf_setlen(base, baselen);
+ strbuf_release(&sbuf);
+ } else
+ die(_("bad ls-tree format: %%%.*s"),
+ (int)(end - format + 1), format);
+ }
+ strbuf_addch(&sb, options->null_termination ? '\0' : '\n');
fwrite(sb.buf, sb.len, 1, stdout);
strbuf_release(&sb);
- strbuf_setlen(base, baselen);
return recurse;
}
-static int show_tree_common(struct show_tree_data *data, int *recurse,
- const struct object_id *oid, struct strbuf *base,
- const char *pathname, unsigned mode)
+static int show_tree_common(struct ls_tree_options *options, int *recurse,
+ struct strbuf *base, const char *pathname,
+ enum object_type type)
{
- enum object_type type = object_type(mode);
int ret = -1;
-
*recurse = 0;
- data->mode = mode;
- data->type = type;
- data->oid = oid;
- data->pathname = pathname;
- data->base = base;
if (type == OBJ_BLOB) {
- if (ls_options & LS_TREE_ONLY)
+ if (options->ls_options & LS_TREE_ONLY)
ret = 0;
} else if (type == OBJ_TREE &&
- show_recursive(base->buf, base->len, pathname)) {
+ show_recursive(options, base->buf, base->len, pathname)) {
*recurse = READ_TREE_RECURSIVE;
- if (!(ls_options & LS_SHOW_TREES))
+ if (!(options->ls_options & LS_SHOW_TREES))
ret = *recurse;
}
return ret;
}
-static void show_tree_common_default_long(struct strbuf *base,
+static void show_tree_common_default_long(struct ls_tree_options *options,
+ struct strbuf *base,
const char *pathname,
const size_t baselen)
{
+ const char *prefix = options->prefix;
+
strbuf_addstr(base, pathname);
- write_name_quoted_relative(base->buf,
- chomp_prefix ? ls_tree_prefix : NULL, stdout,
- line_termination);
+
+ if (options->null_termination) {
+ struct strbuf sb = STRBUF_INIT;
+ const char *name = relative_path(base->buf, prefix, &sb);
+
+ fputs(name, stdout);
+ fputc('\0', stdout);
+
+ strbuf_release(&sb);
+ } else {
+ write_name_quoted_relative(base->buf, prefix, stdout, '\n');
+ }
+
strbuf_setlen(base, baselen);
}
@@ -215,35 +194,38 @@ static int show_tree_default(const struct object_id *oid, struct strbuf *base,
const char *pathname, unsigned mode,
void *context)
{
+ struct ls_tree_options *options = context;
int early;
int recurse;
- struct show_tree_data data = { 0 };
+ enum object_type type = object_type(mode);
- early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+ early = show_tree_common(options, &recurse, base, pathname, type);
if (early >= 0)
return early;
- printf("%06o %s %s\t", data.mode, type_name(data.type),
- find_unique_abbrev(data.oid, abbrev));
- show_tree_common_default_long(base, pathname, data.base->len);
+ printf("%06o %s %s\t", mode, type_name(object_type(mode)),
+ repo_find_unique_abbrev(the_repository, oid, options->abbrev));
+ show_tree_common_default_long(options, base, pathname, base->len);
return recurse;
}
static int show_tree_long(const struct object_id *oid, struct strbuf *base,
- const char *pathname, unsigned mode, void *context)
+ const char *pathname, unsigned mode,
+ void *context)
{
+ struct ls_tree_options *options = context;
int early;
int recurse;
- struct show_tree_data data = { 0 };
char size_text[24];
+ enum object_type type = object_type(mode);
- early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+ early = show_tree_common(options, &recurse, base, pathname, type);
if (early >= 0)
return early;
- if (data.type == OBJ_BLOB) {
+ if (type == OBJ_BLOB) {
unsigned long size;
- if (oid_object_info(the_repository, data.oid, &size) == OBJ_BAD)
+ if (oid_object_info(the_repository, oid, &size) == OBJ_BAD)
xsnprintf(size_text, sizeof(size_text), "BAD");
else
xsnprintf(size_text, sizeof(size_text),
@@ -252,47 +234,77 @@ static int show_tree_long(const struct object_id *oid, struct strbuf *base,
xsnprintf(size_text, sizeof(size_text), "-");
}
- printf("%06o %s %s %7s\t", data.mode, type_name(data.type),
- find_unique_abbrev(data.oid, abbrev), size_text);
- show_tree_common_default_long(base, pathname, data.base->len);
+ printf("%06o %s %s %7s\t", mode, type_name(type),
+ repo_find_unique_abbrev(the_repository, oid, options->abbrev),
+ size_text);
+ show_tree_common_default_long(options, base, pathname, base->len);
return recurse;
}
static int show_tree_name_only(const struct object_id *oid, struct strbuf *base,
- const char *pathname, unsigned mode, void *context)
+ const char *pathname, unsigned mode,
+ void *context)
{
+ struct ls_tree_options *options = context;
int early;
int recurse;
const size_t baselen = base->len;
- struct show_tree_data data = { 0 };
+ enum object_type type = object_type(mode);
+ const char *prefix;
- early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+ early = show_tree_common(options, &recurse, base, pathname, type);
if (early >= 0)
return early;
+ prefix = options->prefix;
strbuf_addstr(base, pathname);
- write_name_quoted_relative(base->buf,
- chomp_prefix ? ls_tree_prefix : NULL,
- stdout, line_termination);
+ if (options->null_termination) {
+ struct strbuf sb = STRBUF_INIT;
+ const char *name = relative_path(base->buf, prefix, &sb);
+
+ fputs(name, stdout);
+ fputc('\0', stdout);
+
+ strbuf_release(&sb);
+ } else {
+ write_name_quoted_relative(base->buf, prefix, stdout, '\n');
+ }
strbuf_setlen(base, baselen);
return recurse;
}
static int show_tree_object(const struct object_id *oid, struct strbuf *base,
- const char *pathname, unsigned mode, void *context)
+ const char *pathname, unsigned mode,
+ void *context)
{
+ struct ls_tree_options *options = context;
int early;
int recurse;
- struct show_tree_data data = { 0 };
+ enum object_type type = object_type(mode);
+ const char *str;
- early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+ early = show_tree_common(options, &recurse, base, pathname, type);
if (early >= 0)
return early;
- printf("%s%c", find_unique_abbrev(oid, abbrev), line_termination);
+ str = repo_find_unique_abbrev(the_repository, oid, options->abbrev);
+ if (options->null_termination) {
+ fputs(str, stdout);
+ fputc('\0', stdout);
+ } else {
+ puts(str);
+ }
return recurse;
}
+enum ls_tree_cmdmode {
+ MODE_DEFAULT = 0,
+ MODE_LONG,
+ MODE_NAME_ONLY,
+ MODE_NAME_STATUS,
+ MODE_OBJECT_ONLY,
+};
+
struct ls_tree_cmdmode_to_fmt {
enum ls_tree_cmdmode mode;
const char *const fmt;
@@ -331,16 +343,20 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
struct object_id oid;
struct tree *tree;
int i, full_tree = 0;
+ int chomp_prefix = prefix && *prefix;
read_tree_fn_t fn = NULL;
+ enum ls_tree_cmdmode cmdmode = MODE_DEFAULT;
+ int null_termination = 0;
+ struct ls_tree_options options = { 0 };
const struct option ls_tree_options[] = {
- OPT_BIT('d', NULL, &ls_options, N_("only show trees"),
+ OPT_BIT('d', NULL, &options.ls_options, N_("only show trees"),
LS_TREE_ONLY),
- OPT_BIT('r', NULL, &ls_options, N_("recurse into subtrees"),
+ OPT_BIT('r', NULL, &options.ls_options, N_("recurse into subtrees"),
LS_RECURSIVE),
- OPT_BIT('t', NULL, &ls_options, N_("show trees when recursing"),
+ OPT_BIT('t', NULL, &options.ls_options, N_("show trees when recursing"),
LS_SHOW_TREES),
- OPT_SET_INT('z', NULL, &line_termination,
- N_("terminate entries with NUL byte"), 0),
+ OPT_BOOL('z', NULL, &null_termination,
+ N_("terminate entries with NUL byte")),
OPT_CMDMODE('l', "long", &cmdmode, N_("include object size"),
MODE_LONG),
OPT_CMDMODE(0, "name-only", &cmdmode, N_("list only filenames"),
@@ -354,25 +370,25 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "full-tree", &full_tree,
N_("list entire tree; not just current directory "
"(implies --full-name)")),
- OPT_STRING_F(0, "format", &format, N_("format"),
+ OPT_STRING_F(0, "format", &options.format, N_("format"),
N_("format to use for the output"),
PARSE_OPT_NONEG),
- OPT__ABBREV(&abbrev),
+ OPT__ABBREV(&options.abbrev),
OPT_END()
};
struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;
+ int ret;
git_config(git_default_config, NULL);
- ls_tree_prefix = prefix;
- if (prefix)
- chomp_prefix = strlen(prefix);
argc = parse_options(argc, argv, prefix, ls_tree_options,
ls_tree_usage, 0);
- if (full_tree) {
- ls_tree_prefix = prefix = NULL;
- chomp_prefix = 0;
- }
+ options.null_termination = null_termination;
+
+ if (full_tree)
+ prefix = NULL;
+ options.prefix = chomp_prefix ? prefix : NULL;
+
/*
* We wanted to detect conflicts between --name-only and
* --name-status, but once we're done with that subsequent
@@ -383,16 +399,16 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
/* -d -r should imply -t, but -d by itself should not have to. */
if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
- ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
- ls_options |= LS_SHOW_TREES;
+ ((LS_TREE_ONLY|LS_RECURSIVE) & options.ls_options))
+ options.ls_options |= LS_SHOW_TREES;
- if (format && cmdmode)
+ if (options.format && cmdmode)
usage_msg_opt(
_("--format can't be combined with other format-altering options"),
ls_tree_usage, ls_tree_options);
if (argc < 1)
usage_with_options(ls_tree_usage, ls_tree_options);
- if (get_oid(argv[0], &oid))
+ if (repo_get_oid(the_repository, argv[0], &oid))
die("Not a valid object name %s", argv[0]);
/*
@@ -401,13 +417,13 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
* cannot be lifted until it is converted to use
* match_pathspec() or tree_entry_interesting()
*/
- parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC &
- ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
+ parse_pathspec(&options.pathspec, PATHSPEC_ALL_MAGIC &
+ ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
PATHSPEC_PREFER_CWD,
prefix, argv + 1);
- for (i = 0; i < pathspec.nr; i++)
- pathspec.items[i].nowildcard_len = pathspec.items[i].len;
- pathspec.has_wildcard = 0;
+ for (i = 0; i < options.pathspec.nr; i++)
+ options.pathspec.items[i].nowildcard_len = options.pathspec.items[i].len;
+ options.pathspec.has_wildcard = 0;
tree = parse_tree_indirect(&oid);
if (!tree)
die("not a tree object");
@@ -417,11 +433,11 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
*/
while (m2f) {
if (!m2f->fmt) {
- fn = format ? show_tree_fmt : show_tree_default;
- } else if (format && !strcmp(format, m2f->fmt)) {
+ fn = options.format ? show_tree_fmt : show_tree_default;
+ } else if (options.format && !strcmp(options.format, m2f->fmt)) {
cmdmode = m2f->mode;
fn = m2f->fn;
- } else if (!format && cmdmode == m2f->mode) {
+ } else if (!options.format && cmdmode == m2f->mode) {
fn = m2f->fn;
} else {
m2f++;
@@ -430,5 +446,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
break;
}
- return !!read_tree(the_repository, tree, &pathspec, fn, NULL);
+ ret = !!read_tree(the_repository, tree, &options.pathspec, fn, &options);
+ clear_pathspec(&options.pathspec);
+ return ret;
}
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 01d16ef9e5..53b55dd71c 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -2,8 +2,10 @@
* Another stupid program, this one parsing the headers of an
* email to figure out authorship and subject
*/
-#include "cache.h"
#include "builtin.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
#include "utf8.h"
#include "strbuf.h"
#include "mailinfo.h"
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 30952353a3..3af9ddb8ae 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -4,8 +4,8 @@
* It just splits a mbox into a list of files: "0001" "0002" ..
* so you can process them further from there.
*/
-#include "cache.h"
#include "builtin.h"
+#include "gettext.h"
#include "string-list.h"
#include "strbuf.h"
@@ -120,7 +120,7 @@ static int populate_maildir_list(struct string_list *list, const char *path)
for (sub = subs; *sub; ++sub) {
free(name);
name = xstrfmt("%s/%s", path, *sub);
- if ((dir = opendir(name)) == NULL) {
+ if (!(dir = opendir(name))) {
if (errno == ENOENT)
continue;
error_errno("cannot opendir %s", name);
@@ -277,6 +277,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
const char **argp;
static const char *stdin_only[] = { "-", NULL };
+ BUG_ON_NON_EMPTY_PREFIX(prefix);
+
for (argp = argv+1; *argp; argp++) {
const char *arg = *argp;
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index a11f8c6e4b..e68b7fe45d 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -1,10 +1,12 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "commit.h"
+#include "gettext.h"
+#include "hex.h"
#include "refs.h"
#include "diff.h"
#include "revision.h"
+#include "object-name.h"
#include "parse-options.h"
#include "repository.h"
#include "commit-reach.h"
@@ -13,7 +15,8 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
{
struct commit_list *result, *r;
- result = get_merge_bases_many_dirty(rev[0], rev_nr - 1, rev + 1);
+ result = repo_get_merge_bases_many_dirty(the_repository, rev[0],
+ rev_nr - 1, rev + 1);
if (!result)
return 1;
@@ -31,8 +34,8 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
static const char * const merge_base_usage[] = {
N_("git merge-base [-a | --all] <commit> <commit>..."),
N_("git merge-base [-a | --all] --octopus <commit>..."),
- N_("git merge-base --independent <commit>..."),
N_("git merge-base --is-ancestor <commit> <commit>"),
+ N_("git merge-base --independent <commit>..."),
N_("git merge-base --fork-point <ref> [<commit>]"),
NULL
};
@@ -42,7 +45,7 @@ static struct commit *get_commit_reference(const char *arg)
struct object_id revkey;
struct commit *r;
- if (get_oid(arg, &revkey))
+ if (repo_get_oid(the_repository, arg, &revkey))
die("Not a valid object name %s", arg);
r = lookup_commit_reference(the_repository, &revkey);
if (!r)
@@ -105,7 +108,7 @@ static int handle_is_ancestor(int argc, const char **argv)
die("--is-ancestor takes exactly two commits");
one = get_commit_reference(argv[0]);
two = get_commit_reference(argv[1]);
- if (in_merge_bases(one, two))
+ if (repo_in_merge_bases(the_repository, one, two))
return 0;
else
return 1;
@@ -118,7 +121,7 @@ static int handle_fork_point(int argc, const char **argv)
const char *commitname;
commitname = (argc == 2) ? argv[1] : "HEAD";
- if (get_oid(commitname, &oid))
+ if (repo_get_oid(the_repository, commitname, &oid))
die("Not a valid object name: '%s'", commitname);
derived = lookup_commit_reference(the_repository, &oid);
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index e695867ee5..d7eb4c6540 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -1,6 +1,8 @@
#include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
#include "config.h"
+#include "gettext.h"
+#include "setup.h"
#include "xdiff/xdiff.h"
#include "xdiff-interface.h"
#include "parse-options.h"
@@ -25,10 +27,10 @@ static int label_cb(const struct option *opt, const char *arg, int unset)
int cmd_merge_file(int argc, const char **argv, const char *prefix)
{
- const char *names[3] = { NULL, NULL, NULL };
- mmfile_t mmfs[3];
- mmbuffer_t result = {NULL, 0};
- xmparam_t xmp = {{0}};
+ const char *names[3] = { 0 };
+ mmfile_t mmfs[3] = { 0 };
+ mmbuffer_t result = { 0 };
+ xmparam_t xmp = { 0 };
int ret = 0, i = 0, to_stdout = 0;
int quiet = 0;
struct option options[] = {
@@ -71,21 +73,24 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
for (i = 0; i < 3; i++) {
char *fname;
- int ret;
+ mmfile_t *mmf = mmfs + i;
if (!names[i])
names[i] = argv[i];
fname = prefix_filename(prefix, argv[i]);
- ret = read_mmfile(mmfs + i, fname);
+
+ if (read_mmfile(mmf, fname))
+ ret = -1;
+ else if (mmf->size > MAX_XDIFF_SIZE ||
+ buffer_is_binary(mmf->ptr, mmf->size))
+ ret = error("Cannot merge binary files: %s",
+ argv[i]);
+
free(fname);
if (ret)
- return -1;
+ goto cleanup;
- if (mmfs[i].size > MAX_XDIFF_SIZE ||
- buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
- return error("Cannot merge binary files: %s",
- argv[i]);
}
xmp.ancestor = names[1];
@@ -93,9 +98,6 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
xmp.file2 = names[2];
ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result);
- for (i = 0; i < 3; i++)
- free(mmfs[i].ptr);
-
if (ret >= 0) {
const char *filename = argv[0];
char *fpath = prefix_filename(prefix, argv[0]);
@@ -116,5 +118,9 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
if (ret > 127)
ret = 127;
+cleanup:
+ for (i = 0; i < 3; i++)
+ free(mmfs[i].ptr);
+
return ret;
}
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
index c0383fe9df..270d5f644a 100644
--- a/builtin/merge-index.c
+++ b/builtin/merge-index.c
@@ -1,6 +1,10 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
+#include "hex.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "run-command.h"
+#include "sparse-index.h"
static const char *pgm;
static int one_shot, quiet;
@@ -12,12 +16,13 @@ static int merge_entry(int pos, const char *path)
const char *arguments[] = { pgm, "", "", "", path, "", "", "", NULL };
char hexbuf[4][GIT_MAX_HEXSZ + 1];
char ownbuf[4][60];
+ struct child_process cmd = CHILD_PROCESS_INIT;
- if (pos >= active_nr)
+ if (pos >= the_index.cache_nr)
die("git merge-index: %s not in the cache", path);
found = 0;
do {
- const struct cache_entry *ce = active_cache[pos];
+ const struct cache_entry *ce = the_index.cache[pos];
int stage = ce_stage(ce);
if (strcmp(ce->name, path))
@@ -27,11 +32,12 @@ static int merge_entry(int pos, const char *path)
xsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), "%o", ce->ce_mode);
arguments[stage] = hexbuf[stage];
arguments[stage + 4] = ownbuf[stage];
- } while (++pos < active_nr);
+ } while (++pos < the_index.cache_nr);
if (!found)
die("git merge-index: %s not in the cache", path);
- if (run_command_v_opt(arguments, 0)) {
+ strvec_pushv(&cmd.args, arguments);
+ if (run_command(&cmd)) {
if (one_shot)
err++;
else {
@@ -45,7 +51,7 @@ static int merge_entry(int pos, const char *path)
static void merge_one_path(const char *path)
{
- int pos = cache_name_pos(path, strlen(path));
+ int pos = index_name_pos(&the_index, path, strlen(path));
/*
* If it already exists in the cache as stage0, it's
@@ -60,15 +66,15 @@ static void merge_all(void)
int i;
/* TODO: audit for interaction with sparse-index. */
ensure_full_index(&the_index);
- for (i = 0; i < active_nr; i++) {
- const struct cache_entry *ce = active_cache[i];
+ for (i = 0; i < the_index.cache_nr; i++) {
+ const struct cache_entry *ce = the_index.cache[i];
if (!ce_stage(ce))
continue;
i += merge_entry(i, ce->name)-1;
}
}
-int cmd_merge_index(int argc, const char **argv, const char *prefix)
+int cmd_merge_index(int argc, const char **argv, const char *prefix UNUSED)
{
int i, force_file = 0;
@@ -80,7 +86,7 @@ int cmd_merge_index(int argc, const char **argv, const char *prefix)
if (argc < 3)
usage("git merge-index [-o] [-q] <merge-program> (-a | [--] [<filename>...])");
- read_cache();
+ repo_read_index(the_repository);
/* TODO: audit for interaction with sparse-index. */
ensure_full_index(&the_index);
diff --git a/builtin/merge-ours.c b/builtin/merge-ours.c
index 3583cff71c..932924e5d0 100644
--- a/builtin/merge-ours.c
+++ b/builtin/merge-ours.c
@@ -7,15 +7,15 @@
*
* Pretend we resolved the heads, but declare our tree trumps everybody else.
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
#include "git-compat-util.h"
#include "builtin.h"
#include "diff.h"
+#include "repository.h"
static const char builtin_merge_ours_usage[] =
"git merge-ours <base>... -- HEAD <remote>...";
-int cmd_merge_ours(int argc, const char **argv, const char *prefix)
+int cmd_merge_ours(int argc, const char **argv, const char *prefix UNUSED)
{
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(builtin_merge_ours_usage);
@@ -25,7 +25,7 @@ int cmd_merge_ours(int argc, const char **argv, const char *prefix)
* commit. The index must match HEAD, or this merge cannot go
* through.
*/
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
die_errno("read_cache failed");
if (index_differs_from(the_repository, "HEAD", NULL, 0))
return 2;
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index b9acbf5d34..3366699657 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -1,8 +1,12 @@
-#include "cache.h"
#include "builtin.h"
+#include "advice.h"
#include "commit.h"
+#include "gettext.h"
+#include "hash.h"
#include "tag.h"
#include "merge-recursive.h"
+#include "object-name.h"
+#include "repository.h"
#include "xdiff-interface.h"
static const char builtin_merge_recursive_usage[] =
@@ -20,7 +24,7 @@ static char *better_branch_name(const char *branch)
return xstrdup(name ? name : branch);
}
-int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
+int cmd_merge_recursive(int argc, const char **argv, const char *prefix UNUSED)
{
const struct object_id *bases[21];
unsigned bases_count = 0;
@@ -49,7 +53,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
}
if (bases_count < ARRAY_SIZE(bases)-1) {
struct object_id *oid = xmalloc(sizeof(struct object_id));
- if (get_oid(argv[i], oid))
+ if (repo_get_oid(the_repository, argv[i], oid))
die(_("could not parse object '%s'"), argv[i]);
bases[bases_count++] = oid;
}
@@ -70,9 +74,9 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
o.branch1 = argv[++i];
o.branch2 = argv[++i];
- if (get_oid(o.branch1, &h1))
+ if (repo_get_oid(the_repository, o.branch1, &h1))
die(_("could not resolve ref '%s'"), o.branch1);
- if (get_oid(o.branch2, &h2))
+ if (repo_get_oid(the_repository, o.branch2, &h2))
die(_("could not resolve ref '%s'"), o.branch2);
o.branch1 = better1 = better_branch_name(o.branch1);
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 5dc94d6f88..6f7db436d2 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -1,14 +1,25 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "tree-walk.h"
#include "xdiff-interface.h"
-#include "object-store.h"
+#include "help.h"
+#include "gettext.h"
+#include "hex.h"
+#include "commit.h"
+#include "commit-reach.h"
+#include "merge-ort.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "parse-options.h"
#include "repository.h"
#include "blob.h"
#include "exec-cmd.h"
#include "merge-blobs.h"
+#include "quote.h"
+#include "tree.h"
+#include "config.h"
-static const char merge_tree_usage[] = "git merge-tree <base-tree> <branch1> <branch2>";
+static int line_termination = '\n';
struct merge_list {
struct merge_list *next;
@@ -28,7 +39,7 @@ static void add_merge_entry(struct merge_list *entry)
merge_result_end = &entry->next;
}
-static void merge_trees(struct tree_desc t[3], const char *base);
+static void trivial_merge_trees(struct tree_desc t[3], const char *base);
static const char *explanation(struct merge_list *entry)
{
@@ -63,7 +74,9 @@ static void *result(struct merge_list *entry, unsigned long *size)
const char *path = entry->path;
if (!entry->stage)
- return read_object_file(&entry->blob->object.oid, &type, size);
+ return repo_read_object_file(the_repository,
+ &entry->blob->object.oid, &type,
+ size);
base = NULL;
if (entry->stage == 1) {
base = entry->blob;
@@ -86,14 +99,15 @@ static void *origin(struct merge_list *entry, unsigned long *size)
enum object_type type;
while (entry) {
if (entry->stage == 2)
- return read_object_file(&entry->blob->object.oid,
- &type, size);
+ return repo_read_object_file(the_repository,
+ &entry->blob->object.oid,
+ &type, size);
entry = entry->link;
}
return NULL;
}
-static int show_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+static int show_outf(void *priv UNUSED, mmbuffer_t *mb, int nbuf)
{
int i;
for (i = 0; i < nbuf; i++)
@@ -225,7 +239,7 @@ static void unresolved_directory(const struct traverse_info *info,
buf2 = fill_tree_descriptor(r, t + 2, ENTRY_OID(n + 2));
#undef ENTRY_OID
- merge_trees(t, newbase);
+ trivial_merge_trees(t, newbase);
free(buf0);
free(buf1);
@@ -342,7 +356,7 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s
return mask;
}
-static void merge_trees(struct tree_desc t[3], const char *base)
+static void trivial_merge_trees(struct tree_desc t[3], const char *base)
{
struct traverse_info info;
@@ -366,19 +380,18 @@ static void *get_tree_descriptor(struct repository *r,
return buf;
}
-int cmd_merge_tree(int argc, const char **argv, const char *prefix)
+static int trivial_merge(const char *base,
+ const char *branch1,
+ const char *branch2)
{
struct repository *r = the_repository;
struct tree_desc t[3];
void *buf1, *buf2, *buf3;
- if (argc != 4)
- usage(merge_tree_usage);
-
- buf1 = get_tree_descriptor(r, t+0, argv[1]);
- buf2 = get_tree_descriptor(r, t+1, argv[2]);
- buf3 = get_tree_descriptor(r, t+2, argv[3]);
- merge_trees(t, "");
+ buf1 = get_tree_descriptor(r, t+0, base);
+ buf2 = get_tree_descriptor(r, t+1, branch1);
+ buf3 = get_tree_descriptor(r, t+2, branch2);
+ trivial_merge_trees(t, "");
free(buf1);
free(buf2);
free(buf3);
@@ -386,3 +399,241 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
show_result();
return 0;
}
+
+enum mode {
+ MODE_UNKNOWN,
+ MODE_TRIVIAL,
+ MODE_REAL,
+};
+
+struct merge_tree_options {
+ int mode;
+ int allow_unrelated_histories;
+ int show_messages;
+ int name_only;
+ int use_stdin;
+};
+
+static int real_merge(struct merge_tree_options *o,
+ const char *merge_base,
+ const char *branch1, const char *branch2,
+ const char *prefix)
+{
+ struct commit *parent1, *parent2;
+ struct commit_list *merge_bases = NULL;
+ struct merge_options opt;
+ struct merge_result result = { 0 };
+ int show_messages = o->show_messages;
+
+ parent1 = get_merge_parent(branch1);
+ if (!parent1)
+ help_unknown_ref(branch1, "merge-tree",
+ _("not something we can merge"));
+
+ parent2 = get_merge_parent(branch2);
+ if (!parent2)
+ help_unknown_ref(branch2, "merge-tree",
+ _("not something we can merge"));
+
+ init_merge_options(&opt, the_repository);
+
+ opt.show_rename_progress = 0;
+
+ opt.branch1 = branch1;
+ opt.branch2 = branch2;
+
+ if (merge_base) {
+ struct commit *base_commit;
+ struct tree *base_tree, *parent1_tree, *parent2_tree;
+
+ base_commit = lookup_commit_reference_by_name(merge_base);
+ if (!base_commit)
+ die(_("could not lookup commit '%s'"), merge_base);
+
+ opt.ancestor = merge_base;
+ base_tree = repo_get_commit_tree(the_repository, base_commit);
+ parent1_tree = repo_get_commit_tree(the_repository, parent1);
+ parent2_tree = repo_get_commit_tree(the_repository, parent2);
+ merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result);
+ } else {
+ /*
+ * Get the merge bases, in reverse order; see comment above
+ * merge_incore_recursive in merge-ort.h
+ */
+ merge_bases = repo_get_merge_bases(the_repository, parent1,
+ parent2);
+ if (!merge_bases && !o->allow_unrelated_histories)
+ die(_("refusing to merge unrelated histories"));
+ merge_bases = reverse_commit_list(merge_bases);
+ merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result);
+ }
+
+ if (result.clean < 0)
+ die(_("failure to merge"));
+
+ if (show_messages == -1)
+ show_messages = !result.clean;
+
+ if (o->use_stdin)
+ printf("%d%c", result.clean, line_termination);
+ printf("%s%c", oid_to_hex(&result.tree->object.oid), line_termination);
+ if (!result.clean) {
+ struct string_list conflicted_files = STRING_LIST_INIT_NODUP;
+ const char *last = NULL;
+ int i;
+
+ merge_get_conflicted_files(&result, &conflicted_files);
+ for (i = 0; i < conflicted_files.nr; i++) {
+ const char *name = conflicted_files.items[i].string;
+ struct stage_info *c = conflicted_files.items[i].util;
+ if (!o->name_only)
+ printf("%06o %s %d\t",
+ c->mode, oid_to_hex(&c->oid), c->stage);
+ else if (last && !strcmp(last, name))
+ continue;
+ write_name_quoted_relative(
+ name, prefix, stdout, line_termination);
+ last = name;
+ }
+ string_list_clear(&conflicted_files, 1);
+ }
+ if (show_messages) {
+ putchar(line_termination);
+ merge_display_update_messages(&opt, line_termination == '\0',
+ &result);
+ }
+ if (o->use_stdin)
+ putchar(line_termination);
+ merge_finalize(&opt, &result);
+ return !result.clean; /* result.clean < 0 handled above */
+}
+
+int cmd_merge_tree(int argc, const char **argv, const char *prefix)
+{
+ struct merge_tree_options o = { .show_messages = -1 };
+ int expected_remaining_argc;
+ int original_argc;
+ const char *merge_base = NULL;
+
+ const char * const merge_tree_usage[] = {
+ N_("git merge-tree [--write-tree] [<options>] <branch1> <branch2>"),
+ N_("git merge-tree [--trivial-merge] <base-tree> <branch1> <branch2>"),
+ NULL
+ };
+ struct option mt_options[] = {
+ OPT_CMDMODE(0, "write-tree", &o.mode,
+ N_("do a real merge instead of a trivial merge"),
+ MODE_REAL),
+ OPT_CMDMODE(0, "trivial-merge", &o.mode,
+ N_("do a trivial merge only"), MODE_TRIVIAL),
+ OPT_BOOL(0, "messages", &o.show_messages,
+ N_("also show informational/conflict messages")),
+ OPT_SET_INT('z', NULL, &line_termination,
+ N_("separate paths with the NUL character"), '\0'),
+ OPT_BOOL_F(0, "name-only",
+ &o.name_only,
+ N_("list filenames without modes/oids/stages"),
+ PARSE_OPT_NONEG),
+ OPT_BOOL_F(0, "allow-unrelated-histories",
+ &o.allow_unrelated_histories,
+ N_("allow merging unrelated histories"),
+ PARSE_OPT_NONEG),
+ OPT_BOOL_F(0, "stdin",
+ &o.use_stdin,
+ N_("perform multiple merges, one per line of input"),
+ PARSE_OPT_NONEG),
+ OPT_STRING(0, "merge-base",
+ &merge_base,
+ N_("commit"),
+ N_("specify a merge-base for the merge")),
+ OPT_END()
+ };
+
+ /* Parse arguments */
+ original_argc = argc - 1; /* ignoring argv[0] */
+ argc = parse_options(argc, argv, prefix, mt_options,
+ merge_tree_usage, PARSE_OPT_STOP_AT_NON_OPTION);
+
+ /* Handle --stdin */
+ if (o.use_stdin) {
+ struct strbuf buf = STRBUF_INIT;
+
+ if (o.mode == MODE_TRIVIAL)
+ die(_("--trivial-merge is incompatible with all other options"));
+ if (merge_base)
+ die(_("--merge-base is incompatible with --stdin"));
+ line_termination = '\0';
+ while (strbuf_getline_lf(&buf, stdin) != EOF) {
+ struct strbuf **split;
+ int result;
+ const char *input_merge_base = NULL;
+
+ split = strbuf_split(&buf, ' ');
+ if (!split[0] || !split[1])
+ die(_("malformed input line: '%s'."), buf.buf);
+ strbuf_rtrim(split[0]);
+ strbuf_rtrim(split[1]);
+
+ /* parse the merge-base */
+ if (!strcmp(split[1]->buf, "--")) {
+ input_merge_base = split[0]->buf;
+ }
+
+ if (input_merge_base && split[2] && split[3] && !split[4]) {
+ strbuf_rtrim(split[2]);
+ strbuf_rtrim(split[3]);
+ result = real_merge(&o, input_merge_base, split[2]->buf, split[3]->buf, prefix);
+ } else if (!input_merge_base && !split[2]) {
+ result = real_merge(&o, NULL, split[0]->buf, split[1]->buf, prefix);
+ } else {
+ die(_("malformed input line: '%s'."), buf.buf);
+ }
+
+ if (result < 0)
+ die(_("merging cannot continue; got unclean result of %d"), result);
+ strbuf_list_free(split);
+ }
+ strbuf_release(&buf);
+ return 0;
+ }
+
+ /* Figure out which mode to use */
+ switch (o.mode) {
+ default:
+ BUG("unexpected command mode %d", o.mode);
+ case MODE_UNKNOWN:
+ switch (argc) {
+ default:
+ usage_with_options(merge_tree_usage, mt_options);
+ case 2:
+ o.mode = MODE_REAL;
+ break;
+ case 3:
+ o.mode = MODE_TRIVIAL;
+ break;
+ }
+ expected_remaining_argc = argc;
+ break;
+ case MODE_REAL:
+ expected_remaining_argc = 2;
+ break;
+ case MODE_TRIVIAL:
+ expected_remaining_argc = 3;
+ /* Removal of `--trivial-merge` is expected */
+ original_argc--;
+ break;
+ }
+ if (o.mode == MODE_TRIVIAL && argc < original_argc)
+ die(_("--trivial-merge is incompatible with all other options"));
+
+ if (argc != expected_remaining_argc)
+ usage_with_options(merge_tree_usage, mt_options);
+
+ git_config(git_default_config, NULL);
+
+ /* Do the relevant type of merge */
+ if (o.mode == MODE_REAL)
+ return real_merge(&o, merge_base, argv[0], argv[1], prefix);
+ else
+ return trivial_merge(argv[0], argv[1], argv[2]);
+}
diff --git a/builtin/merge.c b/builtin/merge.c
index f178f5a3ee..de68910177 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -6,11 +6,17 @@
* Based on git-merge.sh by Junio C Hamano.
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
+#include "abspath.h"
+#include "advice.h"
#include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
#include "parse-options.h"
-#include "builtin.h"
#include "lockfile.h"
#include "run-command.h"
#include "hook.h"
@@ -20,6 +26,7 @@
#include "refspec.h"
#include "commit.h"
#include "diffcore.h"
+#include "path.h"
#include "revision.h"
#include "unpack-trees.h"
#include "cache-tree.h"
@@ -29,6 +36,7 @@
#include "color.h"
#include "rerere.h"
#include "help.h"
+#include "merge.h"
#include "merge-recursive.h"
#include "merge-ort-wrappers.h"
#include "resolve-undo.h"
@@ -188,7 +196,7 @@ static struct strategy *get_strategy(const char *name)
for (i = 0; i < main_cmds.cnt; i++) {
int j, found = 0;
struct cmdname *ent = main_cmds.names[i];
- for (j = 0; j < ARRAY_SIZE(all_strategy); j++)
+ for (j = 0; !found && j < ARRAY_SIZE(all_strategy); j++)
if (!strncmp(ent->name, all_strategy[j].name, ent->len)
&& !all_strategy[j].name[ent->len])
found = 1;
@@ -313,8 +321,16 @@ static int save_state(struct object_id *stash)
int len;
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf buffer = STRBUF_INIT;
+ struct lock_file lock_file = LOCK_INIT;
+ int fd;
int rc = -1;
+ fd = repo_hold_locked_index(the_repository, &lock_file, 0);
+ refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+ if (0 <= fd)
+ repo_update_index_if_able(the_repository, &lock_file);
+ rollback_lock_file(&lock_file);
+
strvec_pushl(&cp.args, "stash", "create", NULL);
cp.out = -1;
cp.git_cmd = 1;
@@ -329,7 +345,7 @@ static int save_state(struct object_id *stash)
else if (!len) /* no changes */
goto out;
strbuf_setlen(&buffer, buffer.len-1);
- if (get_oid(buffer.buf, stash))
+ if (repo_get_oid(the_repository, buffer.buf, stash))
die(_("not a valid object: %s"), buffer.buf);
rc = 0;
out:
@@ -337,62 +353,54 @@ out:
return rc;
}
-static void read_empty(const struct object_id *oid, int verbose)
+static void read_empty(const struct object_id *oid)
{
- int i = 0;
- const char *args[7];
-
- args[i++] = "read-tree";
- if (verbose)
- args[i++] = "-v";
- args[i++] = "-m";
- args[i++] = "-u";
- args[i++] = empty_tree_oid_hex();
- args[i++] = oid_to_hex(oid);
- args[i] = NULL;
+ struct child_process cmd = CHILD_PROCESS_INIT;
- if (run_command_v_opt(args, RUN_GIT_CMD))
+ strvec_pushl(&cmd.args, "read-tree", "-m", "-u", empty_tree_oid_hex(),
+ oid_to_hex(oid), NULL);
+ cmd.git_cmd = 1;
+
+ if (run_command(&cmd))
die(_("read-tree failed"));
}
-static void reset_hard(const struct object_id *oid, int verbose)
+static void reset_hard(const struct object_id *oid)
{
- int i = 0;
- const char *args[6];
-
- args[i++] = "read-tree";
- if (verbose)
- args[i++] = "-v";
- args[i++] = "--reset";
- args[i++] = "-u";
- args[i++] = oid_to_hex(oid);
- args[i] = NULL;
+ struct child_process cmd = CHILD_PROCESS_INIT;
+
+ strvec_pushl(&cmd.args, "read-tree", "-v", "--reset", "-u",
+ oid_to_hex(oid), NULL);
+ cmd.git_cmd = 1;
- if (run_command_v_opt(args, RUN_GIT_CMD))
+ if (run_command(&cmd))
die(_("read-tree failed"));
}
static void restore_state(const struct object_id *head,
const struct object_id *stash)
{
- struct strbuf sb = STRBUF_INIT;
- const char *args[] = { "stash", "apply", NULL, NULL };
+ struct child_process cmd = CHILD_PROCESS_INIT;
- if (is_null_oid(stash))
- return;
+ reset_hard(head);
- reset_hard(head, 1);
+ if (is_null_oid(stash))
+ goto refresh_cache;
- args[2] = oid_to_hex(stash);
+ strvec_pushl(&cmd.args, "stash", "apply", "--index", "--quiet", NULL);
+ strvec_push(&cmd.args, oid_to_hex(stash));
/*
* It is OK to ignore error here, for example when there was
* nothing to restore.
*/
- run_command_v_opt(args, RUN_GIT_CMD);
+ cmd.git_cmd = 1;
+ run_command(&cmd);
- strbuf_release(&sb);
- refresh_cache(REFRESH_QUIET);
+refresh_cache:
+ discard_index(&the_index);
+ if (repo_read_index(the_repository) < 0)
+ die(_("could not read index"));
}
/* This is called when no merge was necessary. */
@@ -443,6 +451,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
}
write_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);
strbuf_release(&out);
+ release_revisions(&rev);
}
static void finish(struct commit *head_commit,
@@ -492,7 +501,8 @@ static void finish(struct commit *head_commit,
/* Run a post-merge hook */
run_hooks_l("post-merge", squash ? "1" : "0", NULL);
- apply_autostash(git_path_merge_autostash(the_repository));
+ if (new_head)
+ apply_autostash(git_path_merge_autostash(the_repository));
strbuf_release(&reflog_message);
}
@@ -501,7 +511,6 @@ static void merge_name(const char *remote, struct strbuf *msg)
{
struct commit *remote_head;
struct object_id branch_head;
- struct strbuf buf = STRBUF_INIT;
struct strbuf bname = STRBUF_INIT;
struct merge_remote_desc *desc;
const char *ptr;
@@ -516,7 +525,8 @@ static void merge_name(const char *remote, struct strbuf *msg)
if (!remote_head)
die(_("'%s' does not point to a commit"), remote);
- if (dwim_ref(remote, strlen(remote), &branch_head, &found_ref, 0) > 0) {
+ if (repo_dwim_ref(the_repository, remote, strlen(remote), &branch_head,
+ &found_ref, 0) > 0) {
if (starts_with(found_ref, "refs/heads/")) {
strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
oid_to_hex(&branch_head), remote);
@@ -589,7 +599,6 @@ static void merge_name(const char *remote, struct strbuf *msg)
oid_to_hex(&remote_head->object.oid), remote);
cleanup:
free(found_ref);
- strbuf_release(&buf);
strbuf_release(&bname);
}
@@ -613,7 +622,8 @@ static void parse_branch_merge_options(char *bmo)
free(argv);
}
-static int git_merge_config(const char *k, const char *v, void *cb)
+static int git_merge_config(const char *k, const char *v,
+ const struct config_context *ctx, void *cb)
{
int status;
const char *str;
@@ -658,13 +668,10 @@ static int git_merge_config(const char *k, const char *v, void *cb)
return 0;
}
- status = fmt_merge_msg_config(k, v, cb);
+ status = fmt_merge_msg_config(k, v, ctx, cb);
if (status)
return status;
- status = git_gpg_config(k, v, NULL);
- if (status)
- return status;
- return git_diff_ui_config(k, v, cb);
+ return git_diff_ui_config(k, v, ctx, cb);
}
static int read_tree_trivial(struct object_id *common, struct object_id *head,
@@ -694,7 +701,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
if (!trees[nr_trees++])
return -1;
opts.fn = threeway_merge;
- cache_tree_free(&active_cache_tree);
+ cache_tree_free(&the_index.cache_tree);
for (i = 0; i < nr_trees; i++) {
parse_tree(trees[i]);
init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
@@ -706,7 +713,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
static void write_tree_trivial(struct object_id *oid)
{
- if (write_cache_as_tree(oid, 0, NULL))
+ if (write_index_as_tree(oid, &the_index, get_index_file(), 0, NULL))
die(_("git write-tree failed to write a tree"));
}
@@ -716,7 +723,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
{
const char *head_arg = "HEAD";
- if (refresh_and_write_cache(REFRESH_QUIET, SKIP_IF_UNCHANGED, 0) < 0)
+ if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET,
+ SKIP_IF_UNCHANGED, 0, NULL, NULL,
+ NULL) < 0)
return error(_("Unable to write index."));
if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree") ||
@@ -750,15 +759,18 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
for (j = common; j; j = j->next)
commit_list_insert(j->item, &reversed);
- hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &lock,
+ LOCK_DIE_ON_ERROR);
if (!strcmp(strategy, "ort"))
clean = merge_ort_recursive(&o, head, remoteheads->item,
reversed, &result);
else
clean = merge_recursive(&o, head, remoteheads->item,
reversed, &result);
- if (clean < 0)
- exit(128);
+ if (clean < 0) {
+ rollback_lock_file(&lock);
+ return 2;
+ }
if (write_locked_index(&the_index, &lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write %s"), get_index_file());
@@ -771,7 +783,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
}
static void count_diff_files(struct diff_queue_struct *q,
- struct diff_options *opt, void *data)
+ struct diff_options *opt UNUSED, void *data)
{
int *count = data;
@@ -782,8 +794,8 @@ static int count_unmerged_entries(void)
{
int i, ret = 0;
- for (i = 0; i < active_nr; i++)
- if (ce_stage(active_cache[i]))
+ for (i = 0; i < the_index.cache_nr; i++)
+ if (ce_stage(the_index.cache[i]))
ret++;
return ret;
@@ -857,9 +869,9 @@ static void prepare_to_commit(struct commit_list *remoteheads)
* the editor and after we invoke run_status above.
*/
if (invoked_hook)
- discard_cache();
+ discard_index(&the_index);
}
- read_cache_from(index_file);
+ read_index_from(&the_index, index_file, get_git_dir());
strbuf_addbuf(&msg, &merge_msg);
if (squash)
BUG("the control must not reach here under --squash");
@@ -867,13 +879,15 @@ static void prepare_to_commit(struct commit_list *remoteheads)
strbuf_addch(&msg, '\n');
if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
wt_status_append_cut_line(&msg);
- strbuf_commented_addf(&msg, "\n");
+ strbuf_commented_addf(&msg, comment_line_char, "\n");
}
- strbuf_commented_addf(&msg, _(merge_editor_comment));
+ strbuf_commented_addf(&msg, comment_line_char,
+ _(merge_editor_comment));
if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
- strbuf_commented_addf(&msg, _(scissors_editor_comment));
+ strbuf_commented_addf(&msg, comment_line_char,
+ _(scissors_editor_comment));
else
- strbuf_commented_addf(&msg,
+ strbuf_commented_addf(&msg, comment_line_char,
_(no_scissors_editor_comment), comment_line_char);
}
if (signoff)
@@ -908,7 +922,9 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
struct object_id result_tree, result_commit;
struct commit_list *parents, **pptr = &parents;
- if (refresh_and_write_cache(REFRESH_QUIET, SKIP_IF_UNCHANGED, 0) < 0)
+ if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET,
+ SKIP_IF_UNCHANGED, 0, NULL, NULL,
+ NULL) < 0)
return error(_("Unable to write index."));
write_tree_trivial(&result_tree);
@@ -998,6 +1014,7 @@ static int evaluate_result(void)
*/
cnt += count_unmerged_entries();
+ release_revisions(&rev);
return cnt;
}
@@ -1373,7 +1390,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
goto done;
}
- if (read_cache_unmerged())
+ if (repo_read_index_unmerged(the_repository))
die_resolve_conflict("merge");
if (file_exists(git_path_merge_head(the_repository))) {
@@ -1394,7 +1411,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
}
- resolve_undo_clear();
+ resolve_undo_clear_index(&the_index);
if (option_edit < 0)
option_edit = default_edit_option();
@@ -1457,7 +1474,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
check_trust_level);
remote_head_oid = &remoteheads->item->object.oid;
- read_empty(remote_head_oid, 0);
+ read_empty(remote_head_oid);
update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0,
UPDATE_REFS_DIE_ON_ERR);
goto done;
@@ -1523,7 +1540,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (!remoteheads)
; /* already up-to-date */
else if (!remoteheads->next)
- common = get_merge_bases(head_commit, remoteheads->item);
+ common = repo_get_merge_bases(the_repository, head_commit,
+ remoteheads->item);
else {
struct commit_list *list = remoteheads;
commit_list_insert(head_commit, &list);
@@ -1552,20 +1570,18 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
!common->next &&
oideq(&common->item->object.oid, &head_commit->object.oid)) {
/* Again the most common case of merging one remote. */
- struct strbuf msg = STRBUF_INIT;
+ const char *msg = have_message ?
+ "Fast-forward (no commit created; -m option ignored)" :
+ "Fast-forward";
struct commit *commit;
if (verbosity >= 0) {
printf(_("Updating %s..%s\n"),
- find_unique_abbrev(&head_commit->object.oid,
- DEFAULT_ABBREV),
- find_unique_abbrev(&remoteheads->item->object.oid,
- DEFAULT_ABBREV));
+ repo_find_unique_abbrev(the_repository, &head_commit->object.oid,
+ DEFAULT_ABBREV),
+ repo_find_unique_abbrev(the_repository, &remoteheads->item->object.oid,
+ DEFAULT_ABBREV));
}
- strbuf_addstr(&msg, "Fast-forward");
- if (have_message)
- strbuf_addstr(&msg,
- " (no commit created; -m option ignored)");
commit = remoteheads->item;
if (!commit) {
ret = 1;
@@ -1584,9 +1600,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
goto done;
}
- finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
+ finish(head_commit, remoteheads, &commit->object.oid, msg);
remove_merge_branch_state(the_repository);
- strbuf_release(&msg);
goto done;
} else if (!remoteheads->next && common->next)
;
@@ -1599,8 +1614,25 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* We are not doing octopus, not fast-forward, and have
* only one common.
*/
- refresh_cache(REFRESH_QUIET);
+ refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
if (allow_trivial && fast_forward != FF_ONLY) {
+ /*
+ * Must first ensure that index matches HEAD before
+ * attempting a trivial merge.
+ */
+ struct tree *head_tree = repo_get_commit_tree(the_repository,
+ head_commit);
+ struct strbuf sb = STRBUF_INIT;
+
+ if (repo_index_has_changes(the_repository, head_tree,
+ &sb)) {
+ error(_("Your local changes to the following files would be overwritten by merge:\n %s"),
+ sb.buf);
+ strbuf_release(&sb);
+ ret = 2;
+ goto done;
+ }
+
/* See if it is really trivial. */
git_committer_info(IDENT_STRICT);
printf(_("Trying really trivial in-index merge...\n"));
@@ -1628,7 +1660,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* merge_bases again, otherwise "git merge HEAD^
* HEAD^^" would be missed.
*/
- common_one = get_merge_bases(head_commit, j->item);
+ common_one = repo_get_merge_bases(the_repository,
+ head_commit,
+ j->item);
if (!oideq(&common_one->item->object.oid, &j->item->object.oid)) {
up_to_date = 0;
break;
@@ -1657,15 +1691,15 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* tree in the index -- this means that the index must be in
* sync with the head commit. The strategies are responsible
* to ensure this.
+ *
+ * Stash away the local changes so that we can try more than one
+ * and/or recover from merge strategies bailing while leaving the
+ * index and working tree polluted.
*/
- if (use_strategies_nr == 1 ||
- /*
- * Stash away the local changes so that we can try more than one.
- */
- save_state(&stash))
+ if (save_state(&stash))
oidclr(&stash);
- for (i = 0; !merge_was_ok && i < use_strategies_nr; i++) {
+ for (i = 0; i < use_strategies_nr; i++) {
int ret, cnt;
if (i) {
printf(_("Rewinding the tree to pristine...\n"));
@@ -1680,7 +1714,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
*/
wt_strategy = use_strategies[i]->name;
- ret = try_merge_strategy(use_strategies[i]->name,
+ ret = try_merge_strategy(wt_strategy,
common, remoteheads,
head_commit);
/*
@@ -1690,16 +1724,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
*/
if (ret < 2) {
if (!ret) {
- if (option_commit) {
- /* Automerge succeeded. */
- automerge_was_ok = 1;
- break;
- }
+ /*
+ * This strategy worked; no point in trying
+ * another.
+ */
merge_was_ok = 1;
+ best_strategy = wt_strategy;
+ break;
}
cnt = (use_strategies_nr > 1) ? evaluate_result() : 0;
if (best_cnt <= 0 || cnt <= best_cnt) {
- best_strategy = use_strategies[i]->name;
+ best_strategy = wt_strategy;
best_cnt = cnt;
}
}
@@ -1709,7 +1744,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* If we have a resulting tree, that means the strategy module
* auto resolved the merge cleanly.
*/
- if (automerge_was_ok) {
+ if (merge_was_ok && option_commit) {
+ automerge_was_ok = 1;
ret = finish_automerge(head_commit, head_subsumed,
common, remoteheads,
&result_tree, wt_strategy);
@@ -1754,6 +1790,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
"stopped before committing as requested\n"));
else
ret = suggest_conflicts();
+ if (autostash)
+ printf(_("When finished, apply stashed changes with `git stash pop`\n"));
done:
if (!automerge_was_ok) {
@@ -1762,5 +1800,6 @@ done:
}
strbuf_release(&buf);
free(branch_to_free);
+ discard_index(&the_index);
return ret;
}
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 5d22909122..43e2766db4 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -1,8 +1,12 @@
#include "builtin.h"
+#include "gettext.h"
+#include "hex.h"
#include "parse-options.h"
+#include "strbuf.h"
#include "tag.h"
#include "replace-object.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
#include "fsck.h"
#include "config.h"
@@ -51,7 +55,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
void *buffer;
const struct object_id *repl;
- buffer = read_object_file(tagged_oid, &type, &size);
+ buffer = repo_read_object_file(the_repository, tagged_oid, &type,
+ &size);
if (!buffer)
die(_("could not read tagged object '%s'"),
oid_to_hex(tagged_oid));
@@ -80,7 +85,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)
int tagged_type;
struct object_id result;
- argc = parse_options(argc, argv, NULL,
+ argc = parse_options(argc, argv, prefix,
builtin_mktag_options,
builtin_mktag_usage, 0);
diff --git a/builtin/mktree.c b/builtin/mktree.c
index 902edba6d2..9a22d4e277 100644
--- a/builtin/mktree.c
+++ b/builtin/mktree.c
@@ -4,10 +4,13 @@
* Copyright (c) Junio C Hamano, 2006, 2009
*/
#include "builtin.h"
+#include "gettext.h"
+#include "hex.h"
#include "quote.h"
+#include "strbuf.h"
#include "tree.h"
#include "parse-options.h"
-#include "object-store.h"
+#include "object-store-ll.h"
static struct treeent {
unsigned mode;
@@ -74,6 +77,7 @@ static void mktree_line(char *buf, int nul_term_line, int allow_missing)
unsigned mode;
enum object_type mode_type; /* object type derived from mode */
enum object_type obj_type; /* object type derived from sha */
+ struct object_info oi = OBJECT_INFO_INIT;
char *path, *to_free = NULL;
struct object_id oid;
@@ -116,8 +120,14 @@ static void mktree_line(char *buf, int nul_term_line, int allow_missing)
path, ptr, type_name(mode_type));
}
- /* Check the type of object identified by sha1 */
- obj_type = oid_object_info(the_repository, &oid, NULL);
+ /* Check the type of object identified by oid without fetching objects */
+ oi.typep = &obj_type;
+ if (oid_object_info_extended(the_repository, &oid, &oi,
+ OBJECT_INFO_LOOKUP_REPLACE |
+ OBJECT_INFO_QUICK |
+ OBJECT_INFO_SKIP_FETCH_OBJECT) < 0)
+ obj_type = -1;
+
if (obj_type < 0) {
if (allow_missing) {
; /* no problem - missing objects are presumed to be of the right type */
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index 5edbb7fe86..a72aebecaa 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -1,10 +1,13 @@
#include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
#include "config.h"
+#include "environment.h"
+#include "gettext.h"
#include "parse-options.h"
#include "midx.h"
+#include "strbuf.h"
#include "trace2.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#define BUILTIN_MIDX_WRITE_USAGE \
N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \
@@ -56,11 +59,12 @@ static struct opts_multi_pack_index {
static int parse_object_dir(const struct option *opt, const char *arg,
int unset)
{
- free(opts.object_dir);
+ char **value = opt->value;
+ free(*value);
if (unset)
- opts.object_dir = xstrdup(get_object_directory());
+ *value = xstrdup(get_object_directory());
else
- opts.object_dir = real_pathdup(arg, 1);
+ *value = real_pathdup(arg, 1);
return 0;
}
@@ -78,7 +82,8 @@ static struct option *add_common_options(struct option *prev)
}
static int git_multi_pack_index_write_config(const char *var, const char *value,
- void *cb)
+ const struct config_context *ctx UNUSED,
+ void *cb UNUSED)
{
if (!strcmp(var, "pack.writebitmaphashcache")) {
if (git_config_bool(var, value))
@@ -87,6 +92,13 @@ static int git_multi_pack_index_write_config(const char *var, const char *value,
opts.flags &= ~MIDX_WRITE_BITMAP_HASH_CACHE;
}
+ if (!strcmp(var, "pack.writebitmaplookuptable")) {
+ if (git_config_bool(var, value))
+ opts.flags |= MIDX_WRITE_BITMAP_LOOKUP_TABLE;
+ else
+ opts.flags &= ~MIDX_WRITE_BITMAP_LOOKUP_TABLE;
+ }
+
/*
* We should never make a fall-back call to 'git_default_config', since
* this was already called in 'cmd_multi_pack_index()'.
@@ -104,7 +116,8 @@ static void read_packs_from_stdin(struct string_list *to)
strbuf_release(&buf);
}
-static int cmd_multi_pack_index_write(int argc, const char **argv)
+static int cmd_multi_pack_index_write(int argc, const char **argv,
+ const char *prefix)
{
struct option *options;
static struct option builtin_multi_pack_index_write_options[] = {
@@ -132,9 +145,9 @@ static int cmd_multi_pack_index_write(int argc, const char **argv)
if (isatty(2))
opts.flags |= MIDX_PROGRESS;
- argc = parse_options(argc, argv, NULL,
+ argc = parse_options(argc, argv, prefix,
options, builtin_multi_pack_index_write_usage,
- PARSE_OPT_KEEP_UNKNOWN);
+ 0);
if (argc)
usage_with_options(builtin_multi_pack_index_write_usage,
options);
@@ -160,7 +173,8 @@ static int cmd_multi_pack_index_write(int argc, const char **argv)
opts.refs_snapshot, opts.flags);
}
-static int cmd_multi_pack_index_verify(int argc, const char **argv)
+static int cmd_multi_pack_index_verify(int argc, const char **argv,
+ const char *prefix)
{
struct option *options;
static struct option builtin_multi_pack_index_verify_options[] = {
@@ -174,9 +188,9 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv)
if (isatty(2))
opts.flags |= MIDX_PROGRESS;
- argc = parse_options(argc, argv, NULL,
+ argc = parse_options(argc, argv, prefix,
options, builtin_multi_pack_index_verify_usage,
- PARSE_OPT_KEEP_UNKNOWN);
+ 0);
if (argc)
usage_with_options(builtin_multi_pack_index_verify_usage,
options);
@@ -186,7 +200,8 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv)
return verify_midx_file(the_repository, opts.object_dir, opts.flags);
}
-static int cmd_multi_pack_index_expire(int argc, const char **argv)
+static int cmd_multi_pack_index_expire(int argc, const char **argv,
+ const char *prefix)
{
struct option *options;
static struct option builtin_multi_pack_index_expire_options[] = {
@@ -200,9 +215,9 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv)
if (isatty(2))
opts.flags |= MIDX_PROGRESS;
- argc = parse_options(argc, argv, NULL,
+ argc = parse_options(argc, argv, prefix,
options, builtin_multi_pack_index_expire_usage,
- PARSE_OPT_KEEP_UNKNOWN);
+ 0);
if (argc)
usage_with_options(builtin_multi_pack_index_expire_usage,
options);
@@ -212,7 +227,8 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv)
return expire_midx_packs(the_repository, opts.object_dir, opts.flags);
}
-static int cmd_multi_pack_index_repack(int argc, const char **argv)
+static int cmd_multi_pack_index_repack(int argc, const char **argv,
+ const char *prefix)
{
struct option *options;
static struct option builtin_multi_pack_index_repack_options[] = {
@@ -229,10 +245,10 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv)
if (isatty(2))
opts.flags |= MIDX_PROGRESS;
- argc = parse_options(argc, argv, NULL,
+ argc = parse_options(argc, argv, prefix,
options,
builtin_multi_pack_index_repack_usage,
- PARSE_OPT_KEEP_UNKNOWN);
+ 0);
if (argc)
usage_with_options(builtin_multi_pack_index_repack_usage,
options);
@@ -247,7 +263,15 @@ int cmd_multi_pack_index(int argc, const char **argv,
const char *prefix)
{
int res;
- struct option *builtin_multi_pack_index_options = common_opts;
+ parse_opt_subcommand_fn *fn = NULL;
+ struct option builtin_multi_pack_index_options[] = {
+ OPT_SUBCOMMAND("repack", &fn, cmd_multi_pack_index_repack),
+ OPT_SUBCOMMAND("write", &fn, cmd_multi_pack_index_write),
+ OPT_SUBCOMMAND("verify", &fn, cmd_multi_pack_index_verify),
+ OPT_SUBCOMMAND("expire", &fn, cmd_multi_pack_index_expire),
+ OPT_END(),
+ };
+ struct option *options = parse_options_concat(builtin_multi_pack_index_options, common_opts);
git_config(git_default_config, NULL);
@@ -256,31 +280,12 @@ int cmd_multi_pack_index(int argc, const char **argv,
the_repository->objects->odb)
opts.object_dir = xstrdup(the_repository->objects->odb->path);
- argc = parse_options(argc, argv, prefix,
- builtin_multi_pack_index_options,
- builtin_multi_pack_index_usage,
- PARSE_OPT_STOP_AT_NON_OPTION);
-
- if (!argc)
- goto usage;
-
- if (!strcmp(argv[0], "repack"))
- res = cmd_multi_pack_index_repack(argc, argv);
- else if (!strcmp(argv[0], "write"))
- res = cmd_multi_pack_index_write(argc, argv);
- else if (!strcmp(argv[0], "verify"))
- res = cmd_multi_pack_index_verify(argc, argv);
- else if (!strcmp(argv[0], "expire"))
- res = cmd_multi_pack_index_expire(argc, argv);
- else {
- error(_("unrecognized subcommand: %s"), argv[0]);
- goto usage;
- }
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_multi_pack_index_usage, 0);
+ FREE_AND_NULL(options);
+
+ res = fn(argc, argv, prefix);
free(opts.object_dir);
return res;
-
-usage:
- usage_with_options(builtin_multi_pack_index_usage,
- builtin_multi_pack_index_options);
}
diff --git a/builtin/mv.c b/builtin/mv.c
index 83a465ba83..fa84fcb20d 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -3,22 +3,39 @@
*
* Copyright (C) 2006 Johannes Schindelin
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
+#include "abspath.h"
+#include "advice.h"
#include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "name-hash.h"
+#include "object-file.h"
#include "pathspec.h"
#include "lockfile.h"
#include "dir.h"
#include "cache-tree.h"
#include "string-list.h"
#include "parse-options.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
#include "submodule.h"
+#include "entry.h"
static const char * const builtin_mv_usage[] = {
N_("git mv [<options>] <source>... <destination>"),
NULL
};
+enum update_mode {
+ WORKING_DIRECTORY = (1 << 1),
+ INDEX = (1 << 2),
+ SPARSE = (1 << 3),
+ SKIP_WORKTREE_DIR = (1 << 4),
+};
+
#define DUP_BASENAME 1
#define KEEP_TRAILING_SLASH 2
@@ -63,7 +80,7 @@ static const char **internal_prefix_pathspec(const char *prefix,
static const char *add_slash(const char *path)
{
size_t len = strlen(path);
- if (path[len - 1] != '/') {
+ if (len && path[len - 1] != '/') {
char *with_slash = xmalloc(st_add(len, 2));
memcpy(with_slash, path, len);
with_slash[len++] = '/';
@@ -79,7 +96,7 @@ static void prepare_move_submodule(const char *src, int first,
const char **submodule_gitfile)
{
struct strbuf submodule_dotgit = STRBUF_INIT;
- if (!S_ISGITLINK(active_cache[first]->ce_mode))
+ if (!S_ISGITLINK(the_index.cache[first]->ce_mode))
die(_("Directory %s is in index and no submodule?"), src);
if (!is_staging_gitmodules_ok(&the_index))
die(_("Please stage your changes to .gitmodules or stash them to proceed"));
@@ -98,13 +115,13 @@ static int index_range_of_same_dir(const char *src, int length,
const char *src_w_slash = add_slash(src);
int first, last, len_w_slash = length + 1;
- first = cache_name_pos(src_w_slash, len_w_slash);
+ first = index_name_pos(&the_index, src_w_slash, len_w_slash);
if (first >= 0)
die(_("%.*s is in index"), len_w_slash, src_w_slash);
first = -1 - first;
- for (last = first; last < active_nr; last++) {
- const char *path = active_cache[last]->name;
+ for (last = first; last < the_index.cache_nr; last++) {
+ const char *path = the_index.cache[last]->name;
if (strncmp(path, src_w_slash, len_w_slash))
break;
}
@@ -115,6 +132,39 @@ static int index_range_of_same_dir(const char *src, int length,
return last - first;
}
+/*
+ * Given the path of a directory that does not exist on-disk, check whether the
+ * directory contains any entries in the index with the SKIP_WORKTREE flag
+ * enabled.
+ * Return 1 if such index entries exist.
+ * Return 0 otherwise.
+ */
+static int empty_dir_has_sparse_contents(const char *name)
+{
+ int ret = 0;
+ const char *with_slash = add_slash(name);
+ int length = strlen(with_slash);
+
+ int pos = index_name_pos(&the_index, with_slash, length);
+ const struct cache_entry *ce;
+
+ if (pos < 0) {
+ pos = -pos - 1;
+ if (pos >= the_index.cache_nr)
+ goto free_return;
+ ce = the_index.cache[pos];
+ if (strncmp(with_slash, ce->name, length))
+ goto free_return;
+ if (ce_skip_worktree(ce))
+ ret = 1;
+ }
+
+free_return:
+ if (with_slash != name)
+ free((char *)with_slash);
+ return ret;
+}
+
int cmd_mv(int argc, const char **argv, const char *prefix)
{
int i, flags, gitmodules_modified = 0;
@@ -129,12 +179,17 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
OPT_END(),
};
const char **source, **destination, **dest_path, **submodule_gitfile;
- enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX, SPARSE } *modes;
+ const char *dst_w_slash;
+ const char **src_dir = NULL;
+ int src_dir_nr = 0, src_dir_alloc = 0;
+ struct strbuf a_src_dir = STRBUF_INIT;
+ enum update_mode *modes, dst_mode = 0;
struct stat st;
struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
struct lock_file lock_file = LOCK_INIT;
struct cache_entry *ce;
struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP;
+ struct string_list dirty_paths = STRING_LIST_INIT_NODUP;
git_config(git_default_config, NULL);
@@ -143,12 +198,13 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
if (--argc < 1)
usage_with_options(builtin_mv_usage, builtin_mv_options);
- hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
- if (read_cache() < 0)
+ repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
+ if (repo_read_index(the_repository) < 0)
die(_("index file corrupt"));
source = internal_prefix_pathspec(prefix, argv, argc, 0);
- modes = xcalloc(argc, sizeof(enum update_mode));
+ CALLOC_ARRAY(modes, argc);
+
/*
* Keep trailing slash, needed to let
* "git mv file no-such-dir/" error out, except in the case
@@ -158,6 +214,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1]))
flags = 0;
dest_path = internal_prefix_pathspec(prefix, argv + argc, 1, flags);
+ dst_w_slash = add_slash(dest_path[0]);
submodule_gitfile = xcalloc(argc, sizeof(char *));
if (dest_path[0][0] == '\0')
@@ -165,18 +222,37 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
else if (!lstat(dest_path[0], &st) &&
S_ISDIR(st.st_mode)) {
- dest_path[0] = add_slash(dest_path[0]);
- destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
+ destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME);
} else {
- if (argc != 1)
+ if (!path_in_sparse_checkout(dst_w_slash, &the_index) &&
+ empty_dir_has_sparse_contents(dst_w_slash)) {
+ destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME);
+ dst_mode = SKIP_WORKTREE_DIR;
+ } else if (argc != 1) {
die(_("destination '%s' is not a directory"), dest_path[0]);
- destination = dest_path;
+ } else {
+ destination = dest_path;
+ /*
+ * <destination> is a file outside of sparse-checkout
+ * cone. Insist on cone mode here for backward
+ * compatibility. We don't want dst_mode to be assigned
+ * for a file when the repo is using no-cone mode (which
+ * is deprecated at this point) sparse-checkout. As
+ * SPARSE here is only considering cone-mode situation.
+ */
+ if (!path_in_cone_mode_sparse_checkout(destination[0], &the_index))
+ dst_mode = SPARSE;
+ }
+ }
+ if (dst_w_slash != dest_path[0]) {
+ free((char *)dst_w_slash);
+ dst_w_slash = NULL;
}
/* Checking */
for (i = 0; i < argc; i++) {
const char *src = source[i], *dst = destination[i];
- int length, src_is_dir;
+ int length;
const char *bad = NULL;
int skip_sparse = 0;
@@ -185,54 +261,107 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
length = strlen(src);
if (lstat(src, &st) < 0) {
- /* only error if existence is expected. */
- if (modes[i] != SPARSE)
+ int pos;
+ const struct cache_entry *ce;
+
+ pos = index_name_pos(&the_index, src, length);
+ if (pos < 0) {
+ const char *src_w_slash = add_slash(src);
+ if (!path_in_sparse_checkout(src_w_slash, &the_index) &&
+ empty_dir_has_sparse_contents(src)) {
+ modes[i] |= SKIP_WORKTREE_DIR;
+ goto dir_check;
+ }
+ /* only error if existence is expected. */
+ if (!(modes[i] & SPARSE))
+ bad = _("bad source");
+ goto act_on_entry;
+ }
+ ce = the_index.cache[pos];
+ if (!ce_skip_worktree(ce)) {
bad = _("bad source");
- } else if (!strncmp(src, dst, length) &&
- (dst[length] == 0 || dst[length] == '/')) {
+ goto act_on_entry;
+ }
+ if (!ignore_sparse) {
+ string_list_append(&only_match_skip_worktree, src);
+ goto act_on_entry;
+ }
+ /* Check if dst exists in index */
+ if (index_name_pos(&the_index, dst, strlen(dst)) < 0) {
+ modes[i] |= SPARSE;
+ goto act_on_entry;
+ }
+ if (!force) {
+ bad = _("destination exists");
+ goto act_on_entry;
+ }
+ modes[i] |= SPARSE;
+ goto act_on_entry;
+ }
+ if (!strncmp(src, dst, length) &&
+ (dst[length] == 0 || dst[length] == '/')) {
bad = _("can not move directory into itself");
- } else if ((src_is_dir = S_ISDIR(st.st_mode))
- && lstat(dst, &st) == 0)
+ goto act_on_entry;
+ }
+ if (S_ISDIR(st.st_mode)
+ && lstat(dst, &st) == 0) {
bad = _("cannot move directory over file");
- else if (src_is_dir) {
- int first = cache_name_pos(src, length), last;
+ goto act_on_entry;
+ }
- if (first >= 0)
+dir_check:
+ if (S_ISDIR(st.st_mode)) {
+ int j, dst_len, n;
+ int first = index_name_pos(&the_index, src, length), last;
+
+ if (first >= 0) {
prepare_move_submodule(src, first,
submodule_gitfile + i);
- else if (index_range_of_same_dir(src, length,
- &first, &last) < 1)
+ goto act_on_entry;
+ } else if (index_range_of_same_dir(src, length,
+ &first, &last) < 1) {
bad = _("source directory is empty");
- else { /* last - first >= 1 */
- int j, dst_len, n;
-
- modes[i] = WORKING_DIRECTORY;
- n = argc + last - first;
- REALLOC_ARRAY(source, n);
- REALLOC_ARRAY(destination, n);
- REALLOC_ARRAY(modes, n);
- REALLOC_ARRAY(submodule_gitfile, n);
-
- dst = add_slash(dst);
- dst_len = strlen(dst);
-
- for (j = 0; j < last - first; j++) {
- const struct cache_entry *ce = active_cache[first + j];
- const char *path = ce->name;
- source[argc + j] = path;
- destination[argc + j] =
- prefix_path(dst, dst_len, path + length + 1);
- modes[argc + j] = ce_skip_worktree(ce) ? SPARSE : INDEX;
- submodule_gitfile[argc + j] = NULL;
- }
- argc += last - first;
+ goto act_on_entry;
}
- } else if (!(ce = cache_file_exists(src, length, 0))) {
+
+ /* last - first >= 1 */
+ modes[i] |= WORKING_DIRECTORY;
+
+ ALLOC_GROW(src_dir, src_dir_nr + 1, src_dir_alloc);
+ src_dir[src_dir_nr++] = src;
+
+ n = argc + last - first;
+ REALLOC_ARRAY(source, n);
+ REALLOC_ARRAY(destination, n);
+ REALLOC_ARRAY(modes, n);
+ REALLOC_ARRAY(submodule_gitfile, n);
+
+ dst = add_slash(dst);
+ dst_len = strlen(dst);
+
+ for (j = 0; j < last - first; j++) {
+ const struct cache_entry *ce = the_index.cache[first + j];
+ const char *path = ce->name;
+ source[argc + j] = path;
+ destination[argc + j] =
+ prefix_path(dst, dst_len, path + length + 1);
+ memset(modes + argc + j, 0, sizeof(enum update_mode));
+ modes[argc + j] |= ce_skip_worktree(ce) ? SPARSE : INDEX;
+ submodule_gitfile[argc + j] = NULL;
+ }
+ argc += last - first;
+ goto act_on_entry;
+ }
+ if (!(ce = index_file_exists(&the_index, src, length, 0))) {
bad = _("not under version control");
- } else if (ce_stage(ce)) {
+ goto act_on_entry;
+ }
+ if (ce_stage(ce)) {
bad = _("conflicted");
- } else if (lstat(dst, &st) == 0 &&
- (!ignore_case || strcasecmp(src, dst))) {
+ goto act_on_entry;
+ }
+ if (lstat(dst, &st) == 0 &&
+ (!ignore_case || strcasecmp(src, dst))) {
bad = _("destination exists");
if (force) {
/*
@@ -246,34 +375,52 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
} else
bad = _("Cannot overwrite");
}
- } else if (string_list_has_string(&src_for_dst, dst))
+ goto act_on_entry;
+ }
+ if (string_list_has_string(&src_for_dst, dst)) {
bad = _("multiple sources for the same target");
- else if (is_dir_sep(dst[strlen(dst) - 1]))
+ goto act_on_entry;
+ }
+ if (is_dir_sep(dst[strlen(dst) - 1])) {
bad = _("destination directory does not exist");
- else {
- /*
- * We check if the paths are in the sparse-checkout
- * definition as a very final check, since that
- * allows us to point the user to the --sparse
- * option as a way to have a successful run.
- */
- if (!ignore_sparse &&
- !path_in_sparse_checkout(src, &the_index)) {
- string_list_append(&only_match_skip_worktree, src);
- skip_sparse = 1;
- }
- if (!ignore_sparse &&
- !path_in_sparse_checkout(dst, &the_index)) {
- string_list_append(&only_match_skip_worktree, dst);
- skip_sparse = 1;
+ goto act_on_entry;
+ }
+
+ if (ignore_sparse &&
+ (dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
+ index_entry_exists(&the_index, dst, strlen(dst))) {
+ bad = _("destination exists in the index");
+ if (force) {
+ if (verbose)
+ warning(_("overwriting '%s'"), dst);
+ bad = NULL;
+ } else {
+ goto act_on_entry;
}
+ }
+ /*
+ * We check if the paths are in the sparse-checkout
+ * definition as a very final check, since that
+ * allows us to point the user to the --sparse
+ * option as a way to have a successful run.
+ */
+ if (!ignore_sparse &&
+ !path_in_sparse_checkout(src, &the_index)) {
+ string_list_append(&only_match_skip_worktree, src);
+ skip_sparse = 1;
+ }
+ if (!ignore_sparse &&
+ !path_in_sparse_checkout(dst, &the_index)) {
+ string_list_append(&only_match_skip_worktree, dst);
+ skip_sparse = 1;
+ }
- if (skip_sparse)
- goto remove_entry;
+ if (skip_sparse)
+ goto remove_entry;
- string_list_insert(&src_for_dst, dst);
- }
+ string_list_insert(&src_for_dst, dst);
+act_on_entry:
if (!bad)
continue;
if (!ignore_errors)
@@ -282,14 +429,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
remove_entry:
if (--argc > 0) {
int n = argc - i;
- memmove(source + i, source + i + 1,
- n * sizeof(char *));
- memmove(destination + i, destination + i + 1,
- n * sizeof(char *));
- memmove(modes + i, modes + i + 1,
- n * sizeof(enum update_mode));
- memmove(submodule_gitfile + i, submodule_gitfile + i + 1,
- n * sizeof(char *));
+ MOVE_ARRAY(source + i, source + i + 1, n);
+ MOVE_ARRAY(destination + i, destination + i + 1, n);
+ MOVE_ARRAY(modes + i, modes + i + 1, n);
+ MOVE_ARRAY(submodule_gitfile + i,
+ submodule_gitfile + i + 1, n);
i--;
}
}
@@ -304,11 +448,19 @@ remove_entry:
const char *src = source[i], *dst = destination[i];
enum update_mode mode = modes[i];
int pos;
+ int sparse_and_dirty = 0;
+ struct checkout state = CHECKOUT_INIT;
+ state.istate = &the_index;
+
+ if (force)
+ state.force = 1;
if (show_only || verbose)
printf(_("Renaming %s to %s\n"), src, dst);
if (show_only)
continue;
- if (mode != INDEX && mode != SPARSE && rename(src, dst) < 0) {
+ if (!(mode & (INDEX | SPARSE | SKIP_WORKTREE_DIR)) &&
+ !(dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
+ rename(src, dst) < 0) {
if (ignore_errors)
continue;
die_errno(_("renaming '%s' failed"), src);
@@ -322,14 +474,91 @@ remove_entry:
1);
}
- if (mode == WORKING_DIRECTORY)
+ if (mode & (WORKING_DIRECTORY | SKIP_WORKTREE_DIR))
continue;
- pos = cache_name_pos(src, strlen(src));
+ pos = index_name_pos(&the_index, src, strlen(src));
assert(pos >= 0);
- rename_cache_entry_at(pos, dst);
+ if (!(mode & SPARSE) && !lstat(src, &st))
+ sparse_and_dirty = ie_modified(&the_index,
+ the_index.cache[pos],
+ &st,
+ 0);
+ rename_index_entry_at(&the_index, pos, dst);
+
+ if (ignore_sparse &&
+ core_apply_sparse_checkout &&
+ core_sparse_checkout_cone) {
+ /*
+ * NEEDSWORK: we are *not* paying attention to
+ * "out-to-out" move (<source> is out-of-cone and
+ * <destination> is out-of-cone) at this point. It
+ * should be added in a future patch.
+ */
+ if ((mode & SPARSE) &&
+ path_in_sparse_checkout(dst, &the_index)) {
+ /* from out-of-cone to in-cone */
+ int dst_pos = index_name_pos(&the_index, dst,
+ strlen(dst));
+ struct cache_entry *dst_ce = the_index.cache[dst_pos];
+
+ dst_ce->ce_flags &= ~CE_SKIP_WORKTREE;
+
+ if (checkout_entry(dst_ce, &state, NULL, NULL))
+ die(_("cannot checkout %s"), dst_ce->name);
+ } else if ((dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
+ !(mode & SPARSE) &&
+ !path_in_sparse_checkout(dst, &the_index)) {
+ /* from in-cone to out-of-cone */
+ int dst_pos = index_name_pos(&the_index, dst,
+ strlen(dst));
+ struct cache_entry *dst_ce = the_index.cache[dst_pos];
+
+ /*
+ * if src is clean, it will suffice to remove it
+ */
+ if (!sparse_and_dirty) {
+ dst_ce->ce_flags |= CE_SKIP_WORKTREE;
+ unlink_or_warn(src);
+ } else {
+ /*
+ * if src is dirty, move it to the
+ * destination and create leading
+ * dirs if necessary
+ */
+ char *dst_dup = xstrdup(dst);
+ string_list_append(&dirty_paths, dst);
+ safe_create_leading_directories(dst_dup);
+ FREE_AND_NULL(dst_dup);
+ rename(src, dst);
+ }
+ }
+ }
}
+ /*
+ * cleanup the empty src_dirs
+ */
+ for (i = 0; i < src_dir_nr; i++) {
+ int dummy;
+ strbuf_addstr(&a_src_dir, src_dir[i]);
+ /*
+ * if entries under a_src_dir are all moved away,
+ * recursively remove a_src_dir to cleanup
+ */
+ if (index_range_of_same_dir(a_src_dir.buf, a_src_dir.len,
+ &dummy, &dummy) < 1) {
+ remove_dir_recursively(&a_src_dir, 0);
+ }
+ strbuf_reset(&a_src_dir);
+ }
+
+ strbuf_release(&a_src_dir);
+ free(src_dir);
+
+ if (dirty_paths.nr)
+ advise_on_moving_dirty_path(&dirty_paths);
+
if (gitmodules_modified)
stage_updated_gitmodules(&the_index);
@@ -338,6 +567,7 @@ remove_entry:
die(_("Unable to write new index file"));
string_list_clear(&src_for_dst, 0);
+ string_list_clear(&dirty_paths, 0);
UNLEAK(source);
UNLEAK(dest_path);
free(submodule_gitfile);
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 02ea9d1633..c706fa3720 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -1,15 +1,20 @@
#include "builtin.h"
-#include "cache.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "repository.h"
#include "config.h"
#include "commit.h"
#include "tag.h"
#include "refs.h"
+#include "object-name.h"
+#include "pager.h"
#include "parse-options.h"
#include "prio-queue.h"
#include "hash-lookup.h"
#include "commit-slab.h"
#include "commit-graph.h"
+#include "wildmatch.h"
/*
* One day. See the 'name a rev shortly after epoch' test in t6120 when
@@ -108,19 +113,11 @@ static int is_better_name(struct rev_name *name,
int name_distance = effective_distance(name->distance, name->generation);
int new_distance = effective_distance(distance, generation);
- /*
- * When comparing names based on tags, prefer names
- * based on the older tag, even if it is farther away.
- */
+ /* If both are tags, we prefer the nearer one. */
if (from_tag && name->from_tag)
- return (name->taggerdate > taggerdate ||
- (name->taggerdate == taggerdate &&
- name_distance > new_distance));
+ return name_distance > new_distance;
- /*
- * We know that at least one of them is a non-tag at this point.
- * favor a tag over a non-tag.
- */
+ /* Favor a tag over a non-tag. */
if (name->from_tag != from_tag)
return from_tag;
@@ -189,7 +186,7 @@ static void name_rev(struct commit *start_commit,
size_t parents_to_queue_nr, parents_to_queue_alloc = 0;
struct rev_name *start_name;
- parse_commit(start_commit);
+ repo_parse_commit(the_repository, start_commit);
if (commit_is_before_cutoff(start_commit))
return;
@@ -219,7 +216,7 @@ static void name_rev(struct commit *start_commit,
struct rev_name *parent_name;
int generation, distance;
- parse_commit(parent);
+ repo_parse_commit(the_repository, parent);
if (commit_is_before_cutoff(parent))
continue;
@@ -273,17 +270,6 @@ static int subpath_matches(const char *path, const char *filter)
return -1;
}
-static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
-{
- if (shorten_unambiguous)
- refname = shorten_unambiguous_ref(refname, 0);
- else if (skip_prefix(refname, "refs/heads/", &refname))
- ; /* refname already advanced */
- else
- skip_prefix(refname, "refs/", &refname);
- return refname;
-}
-
struct name_ref_data {
int tags_only;
int name_only;
@@ -309,11 +295,19 @@ static void add_to_tip_table(const struct object_id *oid, const char *refname,
int shorten_unambiguous, struct commit *commit,
timestamp_t taggerdate, int from_tag, int deref)
{
- refname = name_ref_abbrev(refname, shorten_unambiguous);
+ char *short_refname = NULL;
+
+ if (shorten_unambiguous)
+ short_refname = shorten_unambiguous_ref(refname, 0);
+ else if (skip_prefix(refname, "refs/heads/", &refname))
+ ; /* refname already advanced */
+ else
+ skip_prefix(refname, "refs/", &refname);
ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
oidcpy(&tip_table.table[tip_table.nr].oid, oid);
- tip_table.table[tip_table.nr].refname = xstrdup(refname);
+ tip_table.table[tip_table.nr].refname = short_refname ?
+ short_refname : xstrdup(refname);
tip_table.table[tip_table.nr].commit = commit;
tip_table.table[tip_table.nr].taggerdate = taggerdate;
tip_table.table[tip_table.nr].from_tag = from_tag;
@@ -344,7 +338,8 @@ static int cmp_by_tag_and_age(const void *a_, const void *b_)
return a->taggerdate != b->taggerdate;
}
-static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data)
+static int name_ref(const char *path, const struct object_id *oid,
+ int flags UNUSED, void *cb_data)
{
struct object *o = parse_object(the_repository, oid);
struct name_ref_data *data = cb_data;
@@ -503,7 +498,8 @@ static void show_name(const struct object *obj,
else if (allow_undefined)
printf("undefined\n");
else if (always)
- printf("%s\n", find_unique_abbrev(oid, DEFAULT_ABBREV));
+ printf("%s\n",
+ repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV));
else
die("cannot describe '%s'", oid_to_hex(oid));
strbuf_release(&buf);
@@ -537,7 +533,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
counter = 0;
*(p+1) = 0;
- if (!get_oid(p - (hexsz - 1), &oid)) {
+ if (!repo_get_oid(the_repository, p - (hexsz - 1), &oid)) {
struct object *o =
lookup_object(the_repository, &oid);
if (o)
@@ -577,7 +573,11 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
N_("ignore refs matching <pattern>")),
OPT_GROUP(""),
OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
- OPT_BOOL(0, "stdin", &transform_stdin, N_("deprecated: use annotate-stdin instead")),
+ OPT_BOOL_F(0,
+ "stdin",
+ &transform_stdin,
+ N_("deprecated: use --annotate-stdin instead"),
+ PARSE_OPT_HIDDEN),
OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")),
OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
OPT_BOOL(0, "always", &always,
@@ -614,7 +614,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
struct object *object;
struct commit *commit;
- if (get_oid(*argv, &oid)) {
+ if (repo_get_oid(the_repository, *argv, &oid)) {
fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
*argv);
continue;
diff --git a/builtin/notes.c b/builtin/notes.c
index a3d0d15a22..9f38863dd5 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -7,11 +7,17 @@
* and builtin/tag.c by Kristian Høgsberg and Carlos Rica.
*/
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
+#include "config.h"
+#include "alloc.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "notes.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "repository.h"
#include "blob.h"
#include "pretty.h"
@@ -23,12 +29,14 @@
#include "notes-merge.h"
#include "notes-utils.h"
#include "worktree.h"
+#include "write-or-die.h"
+static const char *separator = "\n";
static const char * const git_notes_usage[] = {
N_("git notes [--ref <notes-ref>] [list [<object>]]"),
- N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+ N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
N_("git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"),
- N_("git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+ N_("git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
N_("git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"),
N_("git notes [--ref <notes-ref>] show [<object>]"),
N_("git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"),
@@ -96,11 +104,26 @@ static const char * const git_notes_get_ref_usage[] = {
static const char note_template[] =
N_("Write/edit the notes for the following object:");
+enum notes_stripspace {
+ UNSPECIFIED = -1,
+ NO_STRIPSPACE = 0,
+ STRIPSPACE = 1,
+};
+
+struct note_msg {
+ enum notes_stripspace stripspace;
+ struct strbuf buf;
+};
+
struct note_data {
int given;
int use_editor;
+ int stripspace;
char *edit_path;
struct strbuf buf;
+ struct note_msg **messages;
+ size_t msg_nr;
+ size_t msg_alloc;
};
static void free_note_data(struct note_data *d)
@@ -110,11 +133,18 @@ static void free_note_data(struct note_data *d)
free(d->edit_path);
}
strbuf_release(&d->buf);
+
+ while (d->msg_nr--) {
+ strbuf_release(&d->messages[d->msg_nr]->buf);
+ free(d->messages[d->msg_nr]);
+ }
+ free(d->messages);
}
static int list_each_note(const struct object_id *object_oid,
- const struct object_id *note_oid, char *note_path,
- void *cb_data)
+ const struct object_id *note_oid,
+ char *note_path UNUSED,
+ void *cb_data UNUSED)
{
printf("%s %s\n", oid_to_hex(note_oid), oid_to_hex(object_oid));
return 0;
@@ -124,7 +154,7 @@ static void copy_obj_to_fd(int fd, const struct object_id *oid)
{
unsigned long size;
enum object_type type;
- char *buf = read_object_file(oid, &type, &size);
+ char *buf = repo_read_object_file(the_repository, oid, &type, &size);
if (buf) {
if (size)
write_or_die(fd, buf, size);
@@ -151,7 +181,7 @@ static void write_commented_object(int fd, const struct object_id *object)
if (strbuf_read(&buf, show.out, 0) < 0)
die_errno(_("could not read 'show' output"));
- strbuf_add_commented_lines(&cbuf, buf.buf, buf.len);
+ strbuf_add_commented_lines(&cbuf, buf.buf, buf.len, comment_line_char);
write_or_die(fd, cbuf.buf, cbuf.len);
strbuf_release(&cbuf);
@@ -179,9 +209,10 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
copy_obj_to_fd(fd, old_note);
strbuf_addch(&buf, '\n');
- strbuf_add_commented_lines(&buf, "\n", strlen("\n"));
- strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)));
- strbuf_addch(&buf, '\n');
+ strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char);
+ strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)),
+ comment_line_char);
+ strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char);
write_or_die(fd, buf.buf, buf.len);
write_commented_object(fd, object);
@@ -193,7 +224,8 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
if (launch_editor(d->edit_path, &d->buf, NULL)) {
die(_("please supply the note contents using either -m or -F option"));
}
- strbuf_stripspace(&d->buf, 1);
+ if (d->stripspace)
+ strbuf_stripspace(&d->buf, comment_line_char);
}
}
@@ -209,66 +241,102 @@ static void write_note_data(struct note_data *d, struct object_id *oid)
}
}
+static void append_separator(struct strbuf *message)
+{
+ size_t sep_len = 0;
+
+ if (!separator)
+ return;
+ else if ((sep_len = strlen(separator)) && separator[sep_len - 1] == '\n')
+ strbuf_addstr(message, separator);
+ else
+ strbuf_addf(message, "%s%s", separator, "\n");
+}
+
+static void concat_messages(struct note_data *d)
+{
+ struct strbuf msg = STRBUF_INIT;
+ size_t i;
+
+ for (i = 0; i < d->msg_nr ; i++) {
+ if (d->buf.len)
+ append_separator(&d->buf);
+ strbuf_add(&msg, d->messages[i]->buf.buf, d->messages[i]->buf.len);
+ strbuf_addbuf(&d->buf, &msg);
+ if ((d->stripspace == UNSPECIFIED &&
+ d->messages[i]->stripspace == STRIPSPACE) ||
+ d->stripspace == STRIPSPACE)
+ strbuf_stripspace(&d->buf, 0);
+ strbuf_reset(&msg);
+ }
+ strbuf_release(&msg);
+}
+
static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
{
struct note_data *d = opt->value;
+ struct note_msg *msg = xmalloc(sizeof(*msg));
BUG_ON_OPT_NEG(unset);
- strbuf_grow(&d->buf, strlen(arg) + 2);
- if (d->buf.len)
- strbuf_addch(&d->buf, '\n');
- strbuf_addstr(&d->buf, arg);
- strbuf_stripspace(&d->buf, 0);
-
- d->given = 1;
+ strbuf_init(&msg->buf, strlen(arg));
+ strbuf_addstr(&msg->buf, arg);
+ ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
+ d->messages[d->msg_nr - 1] = msg;
+ msg->stripspace = STRIPSPACE;
return 0;
}
static int parse_file_arg(const struct option *opt, const char *arg, int unset)
{
struct note_data *d = opt->value;
+ struct note_msg *msg = xmalloc(sizeof(*msg));
BUG_ON_OPT_NEG(unset);
- if (d->buf.len)
- strbuf_addch(&d->buf, '\n');
+ strbuf_init(&msg->buf , 0);
if (!strcmp(arg, "-")) {
- if (strbuf_read(&d->buf, 0, 1024) < 0)
+ if (strbuf_read(&msg->buf, 0, 1024) < 0)
die_errno(_("cannot read '%s'"), arg);
- } else if (strbuf_read_file(&d->buf, arg, 1024) < 0)
+ } else if (strbuf_read_file(&msg->buf, arg, 1024) < 0)
die_errno(_("could not open or read '%s'"), arg);
- strbuf_stripspace(&d->buf, 0);
- d->given = 1;
+ ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
+ d->messages[d->msg_nr - 1] = msg;
+ msg->stripspace = STRIPSPACE;
return 0;
}
static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
{
struct note_data *d = opt->value;
- char *buf;
+ struct note_msg *msg = xmalloc(sizeof(*msg));
+ char *value;
struct object_id object;
enum object_type type;
unsigned long len;
BUG_ON_OPT_NEG(unset);
- if (d->buf.len)
- strbuf_addch(&d->buf, '\n');
-
- if (get_oid(arg, &object))
+ strbuf_init(&msg->buf, 0);
+ if (repo_get_oid(the_repository, arg, &object))
die(_("failed to resolve '%s' as a valid ref."), arg);
- if (!(buf = read_object_file(&object, &type, &len)))
+ if (!(value = repo_read_object_file(the_repository, &object, &type, &len)))
die(_("failed to read object '%s'."), arg);
if (type != OBJ_BLOB) {
- free(buf);
+ strbuf_release(&msg->buf);
+ free(value);
+ free(msg);
die(_("cannot read note data from non-blob object '%s'."), arg);
}
- strbuf_add(&d->buf, buf, len);
- free(buf);
- d->given = 1;
+ strbuf_add(&msg->buf, value, len);
+ free(value);
+
+ msg->buf.len = len;
+ ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
+ d->messages[d->msg_nr - 1] = msg;
+ msg->stripspace = NO_STRIPSPACE;
return 0;
}
@@ -280,6 +348,16 @@ static int parse_reedit_arg(const struct option *opt, const char *arg, int unset
return parse_reuse_arg(opt, arg, unset);
}
+static int parse_separator_arg(const struct option *opt, const char *arg,
+ int unset)
+{
+ if (unset)
+ *(const char **)opt->value = NULL;
+ else
+ *(const char **)opt->value = arg ? arg : "\n";
+ return 0;
+}
+
static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
{
struct strbuf buf = STRBUF_INIT;
@@ -307,9 +385,9 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
die(_("malformed input line: '%s'."), buf.buf);
strbuf_rtrim(split[0]);
strbuf_rtrim(split[1]);
- if (get_oid(split[0]->buf, &from_obj))
+ if (repo_get_oid(the_repository, split[0]->buf, &from_obj))
die(_("failed to resolve '%s' as a valid ref."), split[0]->buf);
- if (get_oid(split[1]->buf, &to_obj))
+ if (repo_get_oid(the_repository, split[1]->buf, &to_obj))
die(_("failed to resolve '%s' as a valid ref."), split[1]->buf);
if (rewrite_cmd)
@@ -377,7 +455,7 @@ static int list(int argc, const char **argv, const char *prefix)
t = init_notes_check("list", 0);
if (argc) {
- if (get_oid(argv[0], &object))
+ if (repo_get_oid(the_repository, argv[0], &object))
die(_("failed to resolve '%s' as a valid ref."), argv[0]);
note = get_note(t, &object);
if (note) {
@@ -402,7 +480,8 @@ static int add(int argc, const char **argv, const char *prefix)
struct notes_tree *t;
struct object_id object, new_note;
const struct object_id *note;
- struct note_data d = { 0, 0, NULL, STRBUF_INIT };
+ struct note_data d = { .buf = STRBUF_INIT, .stripspace = UNSPECIFIED };
+
struct option options[] = {
OPT_CALLBACK_F('m', "message", &d, N_("message"),
N_("note contents as a string"), PARSE_OPT_NONEG,
@@ -419,6 +498,12 @@ static int add(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "allow-empty", &allow_empty,
N_("allow storing empty note")),
OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
+ OPT_CALLBACK_F(0, "separator", &separator,
+ N_("<paragraph-break>"),
+ N_("insert <paragraph-break> between paragraphs"),
+ PARSE_OPT_OPTARG, parse_separator_arg),
+ OPT_BOOL(0, "stripspace", &d.stripspace,
+ N_("remove unnecessary whitespace")),
OPT_END()
};
@@ -430,9 +515,13 @@ static int add(int argc, const char **argv, const char *prefix)
usage_with_options(git_notes_add_usage, options);
}
+ if (d.msg_nr)
+ concat_messages(&d);
+ d.given = !!d.buf.len;
+
object_ref = argc > 1 ? argv[1] : "HEAD";
- if (get_oid(object_ref, &object))
+ if (repo_get_oid(the_repository, object_ref, &object))
die(_("failed to resolve '%s' as a valid ref."), object_ref);
t = init_notes_check("add", NOTES_INIT_WRITABLE);
@@ -520,12 +609,12 @@ static int copy(int argc, const char **argv, const char *prefix)
usage_with_options(git_notes_copy_usage, options);
}
- if (get_oid(argv[0], &from_obj))
+ if (repo_get_oid(the_repository, argv[0], &from_obj))
die(_("failed to resolve '%s' as a valid ref."), argv[0]);
object_ref = 1 < argc ? argv[1] : "HEAD";
- if (get_oid(object_ref, &object))
+ if (repo_get_oid(the_repository, object_ref, &object))
die(_("failed to resolve '%s' as a valid ref."), object_ref);
t = init_notes_check("copy", NOTES_INIT_WRITABLE);
@@ -568,7 +657,7 @@ static int append_edit(int argc, const char **argv, const char *prefix)
const struct object_id *note;
char *logmsg;
const char * const *usage;
- struct note_data d = { 0, 0, NULL, STRBUF_INIT };
+ struct note_data d = { .buf = STRBUF_INIT, .stripspace = UNSPECIFIED };
struct option options[] = {
OPT_CALLBACK_F('m', "message", &d, N_("message"),
N_("note contents as a string"), PARSE_OPT_NONEG,
@@ -584,6 +673,12 @@ static int append_edit(int argc, const char **argv, const char *prefix)
parse_reuse_arg),
OPT_BOOL(0, "allow-empty", &allow_empty,
N_("allow storing empty note")),
+ OPT_CALLBACK_F(0, "separator", &separator,
+ N_("<paragraph-break>"),
+ N_("insert <paragraph-break> between paragraphs"),
+ PARSE_OPT_OPTARG, parse_separator_arg),
+ OPT_BOOL(0, "stripspace", &d.stripspace,
+ N_("remove unnecessary whitespace")),
OPT_END()
};
int edit = !strcmp(argv[0], "edit");
@@ -597,6 +692,10 @@ static int append_edit(int argc, const char **argv, const char *prefix)
usage_with_options(usage, options);
}
+ if (d.msg_nr)
+ concat_messages(&d);
+ d.given = !!d.buf.len;
+
if (d.given && edit)
fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated "
"for the 'edit' subcommand.\n"
@@ -604,7 +703,7 @@ static int append_edit(int argc, const char **argv, const char *prefix)
object_ref = 1 < argc ? argv[1] : "HEAD";
- if (get_oid(object_ref, &object))
+ if (repo_get_oid(the_repository, object_ref, &object))
die(_("failed to resolve '%s' as a valid ref."), object_ref);
t = init_notes_check(argv[0], NOTES_INIT_WRITABLE);
@@ -616,14 +715,17 @@ static int append_edit(int argc, const char **argv, const char *prefix)
/* Append buf to previous note contents */
unsigned long size;
enum object_type type;
- char *prev_buf = read_object_file(note, &type, &size);
+ struct strbuf buf = STRBUF_INIT;
+ char *prev_buf = repo_read_object_file(the_repository, note, &type, &size);
- strbuf_grow(&d.buf, size + 1);
- if (d.buf.len && prev_buf && size)
- strbuf_insertstr(&d.buf, 0, "\n");
if (prev_buf && size)
- strbuf_insert(&d.buf, 0, prev_buf, size);
+ strbuf_add(&buf, prev_buf, size);
+ if (d.buf.len && prev_buf && size)
+ append_separator(&buf);
+ strbuf_insert(&d.buf, 0, buf.buf, buf.len);
+
free(prev_buf);
+ strbuf_release(&buf);
}
if (d.buf.len || allow_empty) {
@@ -666,7 +768,7 @@ static int show(int argc, const char **argv, const char *prefix)
object_ref = argc ? argv[0] : "HEAD";
- if (get_oid(object_ref, &object))
+ if (repo_get_oid(the_repository, object_ref, &object))
die(_("failed to resolve '%s' as a valid ref."), object_ref);
t = init_notes_check("show", 0);
@@ -716,11 +818,11 @@ static int merge_commit(struct notes_merge_options *o)
* and target notes ref from .git/NOTES_MERGE_REF.
*/
- if (get_oid("NOTES_MERGE_PARTIAL", &oid))
+ if (repo_get_oid(the_repository, "NOTES_MERGE_PARTIAL", &oid))
die(_("failed to read ref NOTES_MERGE_PARTIAL"));
else if (!(partial = lookup_commit_reference(the_repository, &oid)))
die(_("could not find commit from NOTES_MERGE_PARTIAL."));
- else if (parse_commit(partial))
+ else if (repo_parse_commit(the_repository, partial))
die(_("could not parse commit from NOTES_MERGE_PARTIAL."));
if (partial->parents)
@@ -741,7 +843,8 @@ static int merge_commit(struct notes_merge_options *o)
/* Reuse existing commit message in reflog message */
memset(&pretty_ctx, 0, sizeof(pretty_ctx));
- format_commit_message(partial, "%s", &msg, &pretty_ctx);
+ repo_format_commit_message(the_repository, partial, "%s", &msg,
+ &pretty_ctx);
strbuf_trim(&msg);
strbuf_insertstr(&msg, 0, "notes: ");
update_ref(msg.buf, o->local_ref, &oid,
@@ -895,7 +998,7 @@ static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag
{
int status;
struct object_id oid;
- if (get_oid(name, &oid))
+ if (repo_get_oid(the_repository, name, &oid))
return error(_("Failed to resolve '%s' as a valid ref."), name);
status = remove_note(t, oid.hash);
if (status)
@@ -994,17 +1097,34 @@ static int get_ref(int argc, const char **argv, const char *prefix)
int cmd_notes(int argc, const char **argv, const char *prefix)
{
- int result;
const char *override_notes_ref = NULL;
+ parse_opt_subcommand_fn *fn = NULL;
struct option options[] = {
OPT_STRING(0, "ref", &override_notes_ref, N_("notes-ref"),
N_("use notes from <notes-ref>")),
+ OPT_SUBCOMMAND("list", &fn, list),
+ OPT_SUBCOMMAND("add", &fn, add),
+ OPT_SUBCOMMAND("copy", &fn, copy),
+ OPT_SUBCOMMAND("append", &fn, append_edit),
+ OPT_SUBCOMMAND("edit", &fn, append_edit),
+ OPT_SUBCOMMAND("show", &fn, show),
+ OPT_SUBCOMMAND("merge", &fn, merge),
+ OPT_SUBCOMMAND("remove", &fn, remove_cmd),
+ OPT_SUBCOMMAND("prune", &fn, prune),
+ OPT_SUBCOMMAND("get-ref", &fn, get_ref),
OPT_END()
};
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, options, git_notes_usage,
- PARSE_OPT_STOP_AT_NON_OPTION);
+ PARSE_OPT_SUBCOMMAND_OPTIONAL);
+ if (!fn) {
+ if (argc) {
+ error(_("unknown subcommand: `%s'"), argv[0]);
+ usage_with_options(git_notes_usage, options);
+ }
+ fn = list;
+ }
if (override_notes_ref) {
struct strbuf sb = STRBUF_INIT;
@@ -1014,28 +1134,5 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
strbuf_release(&sb);
}
- if (argc < 1 || !strcmp(argv[0], "list"))
- result = list(argc, argv, prefix);
- else if (!strcmp(argv[0], "add"))
- result = add(argc, argv, prefix);
- else if (!strcmp(argv[0], "copy"))
- result = copy(argc, argv, prefix);
- else if (!strcmp(argv[0], "append") || !strcmp(argv[0], "edit"))
- result = append_edit(argc, argv, prefix);
- else if (!strcmp(argv[0], "show"))
- result = show(argc, argv, prefix);
- else if (!strcmp(argv[0], "merge"))
- result = merge(argc, argv, prefix);
- else if (!strcmp(argv[0], "remove"))
- result = remove_cmd(argc, argv, prefix);
- else if (!strcmp(argv[0], "prune"))
- result = prune(argc, argv, prefix);
- else if (!strcmp(argv[0], "get-ref"))
- result = get_ref(argc, argv, prefix);
- else {
- result = error(_("unknown subcommand: %s"), argv[0]);
- usage_with_options(git_notes_usage, options);
- }
-
- return result ? 1 : 0;
+ return !!fn(argc, argv, prefix);
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 014dcd4bc9..06b33d49e9 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -1,5 +1,7 @@
#include "builtin.h"
-#include "cache.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "repository.h"
#include "config.h"
#include "attr.h"
@@ -30,12 +32,16 @@
#include "strvec.h"
#include "list.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "replace-object.h"
#include "dir.h"
#include "midx.h"
#include "trace2.h"
#include "shallow.h"
#include "promisor-remote.h"
+#include "pack-mtimes.h"
+#include "parse-options.h"
/*
* Objects we are going to pack are collected in the `to_pack` structure.
@@ -179,8 +185,8 @@ static inline void oe_set_delta_size(struct packing_data *pack,
#define SET_DELTA_SIBLING(obj, val) oe_set_delta_sibling(&to_pack, obj, val)
static const char *pack_usage[] = {
- N_("git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"),
- N_("git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"),
+ N_("git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"),
+ N_("git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"),
NULL
};
@@ -194,6 +200,8 @@ static int reuse_delta = 1, reuse_object = 1;
static int keep_unreachable, unpack_unreachable, include_tag;
static timestamp_t unpack_unreachable_expiration;
static int pack_loose_unreachable;
+static int cruft;
+static timestamp_t cruft_expiration;
static int local;
static int have_non_local_packs;
static int incremental;
@@ -285,11 +293,13 @@ static void *get_delta(struct object_entry *entry)
void *buf, *base_buf, *delta_buf;
enum object_type type;
- buf = read_object_file(&entry->idx.oid, &type, &size);
+ buf = repo_read_object_file(the_repository, &entry->idx.oid, &type,
+ &size);
if (!buf)
die(_("unable to read %s"), oid_to_hex(&entry->idx.oid));
- base_buf = read_object_file(&DELTA(entry)->idx.oid, &type,
- &base_size);
+ base_buf = repo_read_object_file(the_repository,
+ &DELTA(entry)->idx.oid, &type,
+ &base_size);
if (!base_buf)
die("unable to read %s",
oid_to_hex(&DELTA(entry)->idx.oid));
@@ -451,7 +461,9 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent
&size, NULL)) != NULL)
buf = NULL;
else {
- buf = read_object_file(&entry->idx.oid, &type, &size);
+ buf = repo_read_object_file(the_repository,
+ &entry->idx.oid, &type,
+ &size);
if (!buf)
die(_("unable to read %s"),
oid_to_hex(&entry->idx.oid));
@@ -756,8 +768,8 @@ static enum write_one_status write_one(struct hashfile *f,
return WRITE_ONE_WRITTEN;
}
-static int mark_tagged(const char *path, const struct object_id *oid, int flag,
- void *cb_data)
+static int mark_tagged(const char *path UNUSED, const struct object_id *oid,
+ int flag UNUSED, void *cb_data UNUSED)
{
struct object_id peeled;
struct object_entry *entry = packlist_find(&to_pack, oid);
@@ -926,8 +938,10 @@ static struct object_entry **compute_write_order(void)
*/
for_each_tag_ref(mark_tagged, NULL);
- if (use_delta_islands)
+ if (use_delta_islands) {
max_layers = compute_pack_layers(&to_pack);
+ free_island_marks();
+ }
ALLOC_ARRAY(wo, to_pack.nr_objects);
wo_end = 0;
@@ -1260,9 +1274,13 @@ static void write_pack_file(void)
&to_pack, written_list, nr_written);
}
+ if (cruft)
+ pack_idx_opts.flags |= WRITE_MTIMES;
+
stage_tmp_packfiles(&tmpname, pack_tmp_name,
written_list, nr_written,
- &pack_idx_opts, hash, &idx_tmp_name);
+ &to_pack, &pack_idx_opts, hash,
+ &idx_tmp_name);
if (write_bitmap_index) {
size_t tmpname_len = tmpname.len;
@@ -1357,6 +1375,9 @@ static int want_found_object(const struct object_id *oid, int exclude,
if (incremental)
return 0;
+ if (!is_pack_valid(p))
+ return -1;
+
/*
* When asked to do --local (do not include an object that appears in a
* pack we borrow from elsewhere) or --honor-pack-keep (do not include
@@ -1472,6 +1493,9 @@ static int want_object_in_pack(const struct object_id *oid,
want = want_found_object(oid, exclude, *found_pack);
if (want != -1)
return want;
+
+ *found_pack = NULL;
+ *found_offset = 0;
}
for (m = get_multi_pack_index(the_repository); m; m = m->next) {
@@ -1515,13 +1539,13 @@ static int want_object_in_pack(const struct object_id *oid,
return 1;
}
-static void create_object_entry(const struct object_id *oid,
- enum object_type type,
- uint32_t hash,
- int exclude,
- int no_try_delta,
- struct packed_git *found_pack,
- off_t found_offset)
+static struct object_entry *create_object_entry(const struct object_id *oid,
+ enum object_type type,
+ uint32_t hash,
+ int exclude,
+ int no_try_delta,
+ struct packed_git *found_pack,
+ off_t found_offset)
{
struct object_entry *entry;
@@ -1538,6 +1562,8 @@ static void create_object_entry(const struct object_id *oid,
}
entry->no_try_delta = no_try_delta;
+
+ return entry;
}
static const char no_closure_warning[] = N_(
@@ -1573,7 +1599,7 @@ static int add_object_entry(const struct object_id *oid, enum object_type type,
static int add_object_entry_from_bitmap(const struct object_id *oid,
enum object_type type,
- int flags, uint32_t name_hash,
+ int flags UNUSED, uint32_t name_hash,
struct packed_git *pack, off_t offset)
{
display_progress(progress_state, ++nr_seen);
@@ -1648,7 +1674,7 @@ static struct pbase_tree_cache *pbase_tree_get(const struct object_id *oid)
/* Did not find one. Either we got a bogus request or
* we need to read and perhaps cache.
*/
- data = read_object_file(oid, &type, &size);
+ data = repo_read_object_file(the_repository, oid, &type, &size);
if (!data)
return NULL;
if (type != OBJ_TREE) {
@@ -1693,17 +1719,14 @@ static void pbase_tree_put(struct pbase_tree_cache *cache)
free(cache);
}
-static int name_cmp_len(const char *name)
+static size_t name_cmp_len(const char *name)
{
- int i;
- for (i = 0; name[i] && name[i] != '\n' && name[i] != '/'; i++)
- ;
- return i;
+ return strcspn(name, "\n/");
}
static void add_pbase_object(struct tree_desc *tree,
const char *name,
- int cmplen,
+ size_t cmplen,
const char *fullname)
{
struct name_entry entry;
@@ -1728,7 +1751,7 @@ static void add_pbase_object(struct tree_desc *tree,
struct tree_desc sub;
struct pbase_tree_cache *tree;
const char *down = name+cmplen+1;
- int downlen = name_cmp_len(down);
+ size_t downlen = name_cmp_len(down);
tree = pbase_tree_get(&entry.oid);
if (!tree)
@@ -1780,7 +1803,7 @@ static int check_pbase_path(unsigned hash)
static void add_preferred_base_object(const char *name)
{
struct pbase_tree *it;
- int cmplen;
+ size_t cmplen;
unsigned hash = pack_name_hash(name);
if (!num_preferred_base || check_pbase_path(hash))
@@ -2060,7 +2083,7 @@ static void check_object(struct object_entry *entry, uint32_t object_index)
if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi,
OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0) {
- if (has_promisor_remote()) {
+ if (repo_has_promisor_remote(the_repository)) {
prefetch_to_pack(object_index);
if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi,
OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0)
@@ -2511,7 +2534,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
/* Load data if not already done */
if (!trg->data) {
packing_data_lock(&to_pack);
- trg->data = read_object_file(&trg_entry->idx.oid, &type, &sz);
+ trg->data = repo_read_object_file(the_repository,
+ &trg_entry->idx.oid, &type,
+ &sz);
packing_data_unlock(&to_pack);
if (!trg->data)
die(_("object %s cannot be read"),
@@ -2524,7 +2549,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
}
if (!src->data) {
packing_data_lock(&to_pack);
- src->data = read_object_file(&src_entry->idx.oid, &type, &sz);
+ src->data = repo_read_object_file(the_repository,
+ &src_entry->idx.oid, &type,
+ &sz);
packing_data_unlock(&to_pack);
if (!src->data) {
if (src_entry->preferred_base) {
@@ -3020,7 +3047,8 @@ static void add_tag_chain(const struct object_id *oid)
}
}
-static int add_ref_tag(const char *tag, const struct object_id *oid, int flag, void *cb_data)
+static int add_ref_tag(const char *tag UNUSED, const struct object_id *oid,
+ int flag UNUSED, void *cb_data UNUSED)
{
struct object_id peeled;
@@ -3105,26 +3133,27 @@ static void prepare_pack(int window, int depth)
free(delta_list);
}
-static int git_pack_config(const char *k, const char *v, void *cb)
+static int git_pack_config(const char *k, const char *v,
+ const struct config_context *ctx, void *cb)
{
if (!strcmp(k, "pack.window")) {
- window = git_config_int(k, v);
+ window = git_config_int(k, v, ctx->kvi);
return 0;
}
if (!strcmp(k, "pack.windowmemory")) {
- window_memory_limit = git_config_ulong(k, v);
+ window_memory_limit = git_config_ulong(k, v, ctx->kvi);
return 0;
}
if (!strcmp(k, "pack.depth")) {
- depth = git_config_int(k, v);
+ depth = git_config_int(k, v, ctx->kvi);
return 0;
}
if (!strcmp(k, "pack.deltacachesize")) {
- max_delta_cache_size = git_config_int(k, v);
+ max_delta_cache_size = git_config_int(k, v, ctx->kvi);
return 0;
}
if (!strcmp(k, "pack.deltacachelimit")) {
- cache_max_small_delta_size = git_config_int(k, v);
+ cache_max_small_delta_size = git_config_int(k, v, ctx->kvi);
return 0;
}
if (!strcmp(k, "pack.writebitmaphashcache")) {
@@ -3133,6 +3162,14 @@ static int git_pack_config(const char *k, const char *v, void *cb)
else
write_bitmap_options &= ~BITMAP_OPT_HASH_CACHE;
}
+
+ if (!strcmp(k, "pack.writebitmaplookuptable")) {
+ if (git_config_bool(k, v))
+ write_bitmap_options |= BITMAP_OPT_LOOKUP_TABLE;
+ else
+ write_bitmap_options &= ~BITMAP_OPT_LOOKUP_TABLE;
+ }
+
if (!strcmp(k, "pack.usebitmaps")) {
use_bitmap_index_default = git_config_bool(k, v);
return 0;
@@ -3142,7 +3179,7 @@ static int git_pack_config(const char *k, const char *v, void *cb)
return 0;
}
if (!strcmp(k, "pack.threads")) {
- delta_search_threads = git_config_int(k, v);
+ delta_search_threads = git_config_int(k, v, ctx->kvi);
if (delta_search_threads < 0)
die(_("invalid number of threads specified (%d)"),
delta_search_threads);
@@ -3153,9 +3190,9 @@ static int git_pack_config(const char *k, const char *v, void *cb)
return 0;
}
if (!strcmp(k, "pack.indexversion")) {
- pack_idx_opts.version = git_config_int(k, v);
+ pack_idx_opts.version = git_config_int(k, v, ctx->kvi);
if (pack_idx_opts.version > 2)
- die(_("bad pack.indexversion=%"PRIu32),
+ die(_("bad pack.indexVersion=%"PRIu32),
pack_idx_opts.version);
return 0;
}
@@ -3189,7 +3226,7 @@ static int git_pack_config(const char *k, const char *v, void *cb)
ex->uri = xstrdup(pack_end + 1);
oidmap_put(&configured_exclusions, ex);
}
- return git_default_config(k, v, cb);
+ return git_default_config(k, v, ctx, cb);
}
/* Counters for trace2 output when in --stdin-packs mode. */
@@ -3201,10 +3238,8 @@ static int add_object_entry_from_pack(const struct object_id *oid,
uint32_t pos,
void *_data)
{
- struct rev_info *revs = _data;
- struct object_info oi = OBJECT_INFO_INIT;
off_t ofs;
- enum object_type type;
+ enum object_type type = OBJ_NONE;
display_progress(progress_state, ++nr_seen);
@@ -3215,32 +3250,38 @@ static int add_object_entry_from_pack(const struct object_id *oid,
if (!want_object_in_pack(oid, 0, &p, &ofs))
return 0;
- oi.typep = &type;
- if (packed_object_info(the_repository, p, ofs, &oi) < 0)
- die(_("could not get type of object %s in pack %s"),
- oid_to_hex(oid), p->pack_name);
- else if (type == OBJ_COMMIT) {
- /*
- * commits in included packs are used as starting points for the
- * subsequent revision walk
- */
- add_pending_oid(revs, NULL, oid, 0);
- }
+ if (p) {
+ struct rev_info *revs = _data;
+ struct object_info oi = OBJECT_INFO_INIT;
+
+ oi.typep = &type;
+ if (packed_object_info(the_repository, p, ofs, &oi) < 0) {
+ die(_("could not get type of object %s in pack %s"),
+ oid_to_hex(oid), p->pack_name);
+ } else if (type == OBJ_COMMIT) {
+ /*
+ * commits in included packs are used as starting points for the
+ * subsequent revision walk
+ */
+ add_pending_oid(revs, NULL, oid, 0);
+ }
- stdin_packs_found_nr++;
+ stdin_packs_found_nr++;
+ }
create_object_entry(oid, type, 0, 0, 0, p, ofs);
return 0;
}
-static void show_commit_pack_hint(struct commit *commit, void *_data)
+static void show_commit_pack_hint(struct commit *commit UNUSED,
+ void *data UNUSED)
{
/* nothing to do; commits don't have a namehash */
}
static void show_object_pack_hint(struct object *object, const char *name,
- void *_data)
+ void *data UNUSED)
{
struct object_entry *oe = packlist_find(&to_pack, &object->oid);
if (!oe)
@@ -3318,16 +3359,16 @@ static void read_packs_list_from_stdin(void)
}
string_list_sort(&include_packs);
+ string_list_remove_duplicates(&include_packs, 0);
string_list_sort(&exclude_packs);
+ string_list_remove_duplicates(&exclude_packs, 0);
for (p = get_all_packs(the_repository); p; p = p->next) {
const char *pack_name = pack_basename(p);
- item = string_list_lookup(&include_packs, pack_name);
- if (!item)
- item = string_list_lookup(&exclude_packs, pack_name);
-
- if (item)
+ if ((item = string_list_lookup(&include_packs, pack_name)))
+ item->util = p;
+ if ((item = string_list_lookup(&exclude_packs, pack_name)))
item->util = p;
}
@@ -3346,6 +3387,8 @@ static void read_packs_list_from_stdin(void)
struct packed_git *p = item->util;
if (!p)
die(_("could not find pack '%s'"), item->string);
+ if (!is_pack_valid(p))
+ die(_("packfile %s cannot be accessed"), p->pack_name);
}
/*
@@ -3369,8 +3412,6 @@ static void read_packs_list_from_stdin(void)
for_each_string_list_item(item, &include_packs) {
struct packed_git *p = item->util;
- if (!p)
- die(_("could not find pack '%s'"), item->string);
for_each_object_in_pack(p,
add_object_entry_from_pack,
&revs,
@@ -3394,6 +3435,217 @@ static void read_packs_list_from_stdin(void)
string_list_clear(&exclude_packs, 0);
}
+static void add_cruft_object_entry(const struct object_id *oid, enum object_type type,
+ struct packed_git *pack, off_t offset,
+ const char *name, uint32_t mtime)
+{
+ struct object_entry *entry;
+
+ display_progress(progress_state, ++nr_seen);
+
+ entry = packlist_find(&to_pack, oid);
+ if (entry) {
+ if (name) {
+ entry->hash = pack_name_hash(name);
+ entry->no_try_delta = no_try_delta(name);
+ }
+ } else {
+ if (!want_object_in_pack(oid, 0, &pack, &offset))
+ return;
+ if (!pack && type == OBJ_BLOB && !has_loose_object(oid)) {
+ /*
+ * If a traversed tree has a missing blob then we want
+ * to avoid adding that missing object to our pack.
+ *
+ * This only applies to missing blobs, not trees,
+ * because the traversal needs to parse sub-trees but
+ * not blobs.
+ *
+ * Note we only perform this check when we couldn't
+ * already find the object in a pack, so we're really
+ * limited to "ensure non-tip blobs which don't exist in
+ * packs do exist via loose objects". Confused?
+ */
+ return;
+ }
+
+ entry = create_object_entry(oid, type, pack_name_hash(name),
+ 0, name && no_try_delta(name),
+ pack, offset);
+ }
+
+ if (mtime > oe_cruft_mtime(&to_pack, entry))
+ oe_set_cruft_mtime(&to_pack, entry, mtime);
+ return;
+}
+
+static void show_cruft_object(struct object *obj, const char *name, void *data UNUSED)
+{
+ /*
+ * if we did not record it earlier, it's at least as old as our
+ * expiration value. Rather than find it exactly, just use that
+ * value. This may bump it forward from its real mtime, but it
+ * will still be "too old" next time we run with the same
+ * expiration.
+ *
+ * if obj does appear in the packing list, this call is a noop (or may
+ * set the namehash).
+ */
+ add_cruft_object_entry(&obj->oid, obj->type, NULL, 0, name, cruft_expiration);
+}
+
+static void show_cruft_commit(struct commit *commit, void *data)
+{
+ show_cruft_object((struct object*)commit, NULL, data);
+}
+
+static int cruft_include_check_obj(struct object *obj, void *data UNUSED)
+{
+ return !has_object_kept_pack(&obj->oid, IN_CORE_KEEP_PACKS);
+}
+
+static int cruft_include_check(struct commit *commit, void *data)
+{
+ return cruft_include_check_obj((struct object*)commit, data);
+}
+
+static void set_cruft_mtime(const struct object *object,
+ struct packed_git *pack,
+ off_t offset, time_t mtime)
+{
+ add_cruft_object_entry(&object->oid, object->type, pack, offset, NULL,
+ mtime);
+}
+
+static void mark_pack_kept_in_core(struct string_list *packs, unsigned keep)
+{
+ struct string_list_item *item = NULL;
+ for_each_string_list_item(item, packs) {
+ struct packed_git *p = item->util;
+ if (!p)
+ die(_("could not find pack '%s'"), item->string);
+ p->pack_keep_in_core = keep;
+ }
+}
+
+static void add_unreachable_loose_objects(void);
+static void add_objects_in_unpacked_packs(void);
+
+static void enumerate_cruft_objects(void)
+{
+ if (progress)
+ progress_state = start_progress(_("Enumerating cruft objects"), 0);
+
+ add_objects_in_unpacked_packs();
+ add_unreachable_loose_objects();
+
+ stop_progress(&progress_state);
+}
+
+static void enumerate_and_traverse_cruft_objects(struct string_list *fresh_packs)
+{
+ struct packed_git *p;
+ struct rev_info revs;
+ int ret;
+
+ repo_init_revisions(the_repository, &revs, NULL);
+
+ revs.tag_objects = 1;
+ revs.tree_objects = 1;
+ revs.blob_objects = 1;
+
+ revs.include_check = cruft_include_check;
+ revs.include_check_obj = cruft_include_check_obj;
+
+ revs.ignore_missing_links = 1;
+
+ if (progress)
+ progress_state = start_progress(_("Enumerating cruft objects"), 0);
+ ret = add_unseen_recent_objects_to_traversal(&revs, cruft_expiration,
+ set_cruft_mtime, 1);
+ stop_progress(&progress_state);
+
+ if (ret)
+ die(_("unable to add cruft objects"));
+
+ /*
+ * Re-mark only the fresh packs as kept so that objects in
+ * unknown packs do not halt the reachability traversal early.
+ */
+ for (p = get_all_packs(the_repository); p; p = p->next)
+ p->pack_keep_in_core = 0;
+ mark_pack_kept_in_core(fresh_packs, 1);
+
+ if (prepare_revision_walk(&revs))
+ die(_("revision walk setup failed"));
+ if (progress)
+ progress_state = start_progress(_("Traversing cruft objects"), 0);
+ nr_seen = 0;
+ traverse_commit_list(&revs, show_cruft_commit, show_cruft_object, NULL);
+
+ stop_progress(&progress_state);
+}
+
+static void read_cruft_objects(void)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct string_list discard_packs = STRING_LIST_INIT_DUP;
+ struct string_list fresh_packs = STRING_LIST_INIT_DUP;
+ struct packed_git *p;
+
+ ignore_packed_keep_in_core = 1;
+
+ while (strbuf_getline(&buf, stdin) != EOF) {
+ if (!buf.len)
+ continue;
+
+ if (*buf.buf == '-')
+ string_list_append(&discard_packs, buf.buf + 1);
+ else
+ string_list_append(&fresh_packs, buf.buf);
+ strbuf_reset(&buf);
+ }
+
+ string_list_sort(&discard_packs);
+ string_list_sort(&fresh_packs);
+
+ for (p = get_all_packs(the_repository); p; p = p->next) {
+ const char *pack_name = pack_basename(p);
+ struct string_list_item *item;
+
+ item = string_list_lookup(&fresh_packs, pack_name);
+ if (!item)
+ item = string_list_lookup(&discard_packs, pack_name);
+
+ if (item) {
+ item->util = p;
+ } else {
+ /*
+ * This pack wasn't mentioned in either the "fresh" or
+ * "discard" list, so the caller didn't know about it.
+ *
+ * Mark it as kept so that its objects are ignored by
+ * add_unseen_recent_objects_to_traversal(). We'll
+ * unmark it before starting the traversal so it doesn't
+ * halt the traversal early.
+ */
+ p->pack_keep_in_core = 1;
+ }
+ }
+
+ mark_pack_kept_in_core(&fresh_packs, 1);
+ mark_pack_kept_in_core(&discard_packs, 0);
+
+ if (cruft_expiration)
+ enumerate_and_traverse_cruft_objects(&fresh_packs);
+ else
+ enumerate_cruft_objects();
+
+ strbuf_release(&buf);
+ string_list_clear(&discard_packs, 0);
+ string_list_clear(&fresh_packs, 0);
+}
+
static void read_object_list_from_stdin(void)
{
char line[GIT_MAX_HEXSZ + 1 + PATH_MAX + 2];
@@ -3426,7 +3678,7 @@ static void read_object_list_from_stdin(void)
}
}
-static void show_commit(struct commit *commit, void *data)
+static void show_commit(struct commit *commit, void *data UNUSED)
{
add_object_entry(&commit->object.oid, OBJ_COMMIT, NULL, 0);
@@ -3437,7 +3689,8 @@ static void show_commit(struct commit *commit, void *data)
propagate_island_marks(commit);
}
-static void show_object(struct object *obj, const char *name, void *data)
+static void show_object(struct object *obj, const char *name,
+ void *data UNUSED)
{
add_preferred_base_object(name);
add_object_entry(&obj->oid, obj->type, name, 0);
@@ -3524,9 +3777,26 @@ static void show_edge(struct commit *commit)
static int add_object_in_unpacked_pack(const struct object_id *oid,
struct packed_git *pack,
uint32_t pos,
- void *_data)
+ void *data UNUSED)
{
- add_object_entry(oid, OBJ_NONE, "", 0);
+ if (cruft) {
+ off_t offset;
+ time_t mtime;
+
+ if (pack->is_cruft) {
+ if (load_pack_mtimes(pack) < 0)
+ die(_("could not load cruft pack .mtimes"));
+ mtime = nth_packed_mtime(pack, pos);
+ } else {
+ mtime = pack->mtime;
+ }
+ offset = nth_packed_object_offset(pack, pos);
+
+ add_cruft_object_entry(oid, OBJ_NONE, pack, offset,
+ NULL, mtime);
+ } else {
+ add_object_entry(oid, OBJ_NONE, "", 0);
+ }
return 0;
}
@@ -3541,7 +3811,7 @@ static void add_objects_in_unpacked_packs(void)
}
static int add_loose_object(const struct object_id *oid, const char *path,
- void *data)
+ void *data UNUSED)
{
enum object_type type = oid_object_info(the_repository, oid, NULL);
@@ -3550,7 +3820,19 @@ static int add_loose_object(const struct object_id *oid, const char *path,
return 0;
}
- add_object_entry(oid, type, "", 0);
+ if (cruft) {
+ struct stat st;
+ if (stat(path, &st) < 0) {
+ if (errno == ENOENT)
+ return 0;
+ return error_errno("unable to stat %s", oid_to_hex(oid));
+ }
+
+ add_cruft_object_entry(oid, type, NULL, 0, NULL,
+ st.st_mtime);
+ } else {
+ add_object_entry(oid, type, "", 0);
+ }
return 0;
}
@@ -3680,20 +3962,21 @@ static int get_object_list_from_bitmap(struct rev_info *revs)
}
static void record_recent_object(struct object *obj,
- const char *name,
- void *data)
+ const char *name UNUSED,
+ void *data UNUSED)
{
oid_array_append(&recent_objects, &obj->oid);
}
-static void record_recent_commit(struct commit *commit, void *data)
+static void record_recent_commit(struct commit *commit, void *data UNUSED)
{
oid_array_append(&recent_objects, &commit->object.oid);
}
static int mark_bitmap_preferred_tip(const char *refname,
- const struct object_id *oid, int flags,
- void *_data)
+ const struct object_id *oid,
+ int flags UNUSED,
+ void *data UNUSED)
{
struct object_id peeled;
struct object *object;
@@ -3790,7 +4073,7 @@ static void get_object_list(struct rev_info *revs, int ac, const char **av)
if (unpack_unreachable_expiration) {
revs->ignore_missing_links = 1;
if (add_unseen_recent_objects_to_traversal(revs,
- unpack_unreachable_expiration))
+ unpack_unreachable_expiration, NULL, 0))
die(_("unable to add recent objects"));
if (prepare_revision_walk(revs))
die(_("revision walk setup failed"));
@@ -3867,19 +4150,18 @@ static int option_parse_unpack_unreachable(const struct option *opt,
return 0;
}
-struct po_filter_data {
- unsigned have_revs:1;
- struct rev_info revs;
-};
-
-static struct list_objects_filter_options *po_filter_revs_init(void *value)
+static int option_parse_cruft_expiration(const struct option *opt,
+ const char *arg, int unset)
{
- struct po_filter_data *data = value;
-
- repo_init_revisions(the_repository, &data->revs, NULL);
- data->have_revs = 1;
-
- return &data->revs.filter;
+ if (unset) {
+ cruft = 0;
+ cruft_expiration = 0;
+ } else {
+ cruft = 1;
+ if (arg)
+ cruft_expiration = approxidate(arg);
+ }
+ return 0;
}
int cmd_pack_objects(int argc, const char **argv, const char *prefix)
@@ -3892,7 +4174,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
int rev_list_index = 0;
int stdin_packs = 0;
struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
- struct po_filter_data pfd = { .have_revs = 0 };
+ struct list_objects_filter_options filter_options =
+ LIST_OBJECTS_FILTER_INIT;
struct option pack_objects_options[] = {
OPT_SET_INT('q', "quiet", &progress,
@@ -3956,6 +4239,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
OPT_CALLBACK_F(0, "unpack-unreachable", NULL, N_("time"),
N_("unpack unreachable objects newer than <time>"),
PARSE_OPT_OPTARG, option_parse_unpack_unreachable),
+ OPT_BOOL(0, "cruft", &cruft, N_("create a cruft pack")),
+ OPT_CALLBACK_F(0, "cruft-expiration", NULL, N_("time"),
+ N_("expire cruft objects older than <time>"),
+ PARSE_OPT_OPTARG, option_parse_cruft_expiration),
OPT_BOOL(0, "sparse", &sparse,
N_("use the sparse reachability algorithm")),
OPT_BOOL(0, "thin", &thin,
@@ -3979,7 +4266,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
&write_bitmap_index,
N_("write a bitmap index if possible"),
WRITE_BITMAP_QUIET, PARSE_OPT_HIDDEN),
- OPT_PARSE_LIST_OBJECTS_FILTER_INIT(&pfd, po_filter_revs_init),
+ OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
OPT_CALLBACK_F(0, "missing", NULL, N_("action"),
N_("handling for missing objects"), PARSE_OPT_NONEG,
option_parse_missing_action),
@@ -3996,7 +4283,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (DFS_NUM_STATES > (1 << OE_DFS_STATE_BITS))
BUG("too many dfs states, increase OE_DFS_STATE_BITS");
- read_replace_refs = 0;
+ disable_replace_refs();
sparse = git_env_bool("GIT_TEST_PACK_SPARSE", -1);
if (the_repository->gitdir) {
@@ -4006,9 +4293,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
}
reset_pack_idx_option(&pack_idx_opts);
+ pack_idx_opts.flags |= WRITE_REV;
git_config(git_pack_config, NULL);
- if (git_env_bool(GIT_TEST_WRITE_REV_INDEX, 0))
- pack_idx_opts.flags |= WRITE_REV;
+ if (git_env_bool(GIT_TEST_NO_WRITE_REV_INDEX, 0))
+ pack_idx_opts.flags &= ~WRITE_REV;
progress = isatty(2);
argc = parse_options(argc, argv, prefix, pack_objects_options,
@@ -4082,7 +4370,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (!HAVE_THREADS && delta_search_threads != 1)
warning(_("no threads support, ignoring --threads"));
- if (!pack_to_stdout && !pack_size_limit)
+ if (!pack_to_stdout && !pack_size_limit && !cruft)
pack_size_limit = pack_size_limit_cfg;
if (pack_to_stdout && pack_size_limit)
die(_("--max-pack-size cannot be used to build a pack for transfer"));
@@ -4099,7 +4387,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (!rev_list_all || !rev_list_reflog || !rev_list_index)
unpack_unreachable_expiration = 0;
- if (pfd.have_revs && pfd.revs.filter.choice) {
+ if (filter_options.choice) {
if (!pack_to_stdout)
die(_("cannot use --filter without --stdout"));
if (stdin_packs)
@@ -4109,6 +4397,15 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (stdin_packs && use_internal_rev_list)
die(_("cannot use internal rev list with --stdin-packs"));
+ if (cruft) {
+ if (use_internal_rev_list)
+ die(_("cannot use internal rev list with --cruft"));
+ if (stdin_packs)
+ die(_("cannot use --stdin-packs with --cruft"));
+ if (pack_size_limit)
+ die(_("cannot use --max-pack-size with --cruft"));
+ }
+
/*
* "soft" reasons not to use bitmaps - for on-disk repack by default we want
*
@@ -4165,7 +4462,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
the_repository);
prepare_packing_data(the_repository, &to_pack);
- if (progress)
+ if (progress && !cruft)
progress_state = start_progress(_("Enumerating objects"), 0);
if (stdin_packs) {
/* avoids adding objects in excluded packs */
@@ -4173,15 +4470,17 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
read_packs_list_from_stdin();
if (rev_list_unpacked)
add_unreachable_loose_objects();
+ } else if (cruft) {
+ read_cruft_objects();
} else if (!use_internal_rev_list) {
read_object_list_from_stdin();
- } else if (pfd.have_revs) {
- get_object_list(&pfd.revs, rp.nr, rp.v);
} else {
struct rev_info revs;
repo_init_revisions(the_repository, &revs, NULL);
+ list_objects_filter_copy(&revs.filter, &filter_options);
get_object_list(&revs, rp.nr, rp.v);
+ release_revisions(&revs);
}
cleanup_preferred_base();
if (include_tag && nr_result)
@@ -4214,6 +4513,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
reuse_packfile_objects);
cleanup:
+ list_objects_filter_release(&filter_options);
strvec_clear(&rp);
return 0;
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
index 8bf5c0acad..4c735ba069 100644
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@ -7,14 +7,16 @@
*/
#include "builtin.h"
+#include "gettext.h"
+#include "hex.h"
#include "repository.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#define BLKSIZE 512
static const char pack_redundant_usage[] =
-"git pack-redundant [--verbose] [--alt-odb] (--all | <filename.pack>...)";
+"git pack-redundant [--verbose] [--alt-odb] (--all | <pack-filename>...)";
static int load_all_packs, verbose, alt_odb;
@@ -101,7 +103,7 @@ static inline struct llist_item *llist_insert(struct llist *list,
oidread(&new_item->oid, oid);
new_item->next = NULL;
- if (after != NULL) {
+ if (after) {
new_item->next = after->next;
after->next = new_item;
if (after == list->back)
@@ -157,7 +159,7 @@ redo_from_start:
if (cmp > 0) /* not in list, since sorted */
return prev;
if (!cmp) { /* found */
- if (prev == NULL) {
+ if (!prev) {
if (hint != NULL && hint != list->front) {
/* we don't know the previous element */
hint = NULL;
@@ -219,7 +221,7 @@ static struct pack_list * pack_list_difference(const struct pack_list *A,
struct pack_list *ret;
const struct pack_list *pl;
- if (A == NULL)
+ if (!A)
return NULL;
pl = B;
@@ -317,7 +319,7 @@ static size_t get_pack_redundancy(struct pack_list *pl)
struct pack_list *subset;
size_t ret = 0;
- if (pl == NULL)
+ if (!pl)
return 0;
while ((subset = pl->next)) {
@@ -557,7 +559,7 @@ static void load_all(void)
}
}
-int cmd_pack_redundant(int argc, const char **argv, const char *prefix)
+int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED)
{
int i;
int i_still_use_this = 0;
@@ -603,6 +605,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix)
"option, '--i-still-use-this', on the command line\n"
"and let us know you still use it by sending an e-mail\n"
"to <git@vger.kernel.org>. Thanks.\n"), stderr);
+ die(_("refusing to run without --i-still-use-this"));
}
if (load_all_packs)
@@ -611,7 +614,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix)
while (*(argv + i) != NULL)
add_pack_file(*(argv + i++));
- if (local_packs == NULL)
+ if (!local_packs)
die("Zero packs found!");
load_all_objects();
diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c
index cfbd5c36c7..bcf383cac9 100644
--- a/builtin/pack-refs.c
+++ b/builtin/pack-refs.c
@@ -1,24 +1,48 @@
#include "builtin.h"
#include "config.h"
+#include "gettext.h"
#include "parse-options.h"
#include "refs.h"
#include "repository.h"
+#include "revision.h"
static char const * const pack_refs_usage[] = {
- N_("git pack-refs [<options>]"),
+ N_("git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude <pattern>]"),
NULL
};
int cmd_pack_refs(int argc, const char **argv, const char *prefix)
{
unsigned int flags = PACK_REFS_PRUNE;
+ static struct ref_exclusions excludes = REF_EXCLUSIONS_INIT;
+ static struct string_list included_refs = STRING_LIST_INIT_NODUP;
+ struct pack_refs_opts pack_refs_opts = { .exclusions = &excludes,
+ .includes = &included_refs,
+ .flags = flags };
+ static struct string_list option_excluded_refs = STRING_LIST_INIT_NODUP;
+ struct string_list_item *item;
+
struct option opts[] = {
- OPT_BIT(0, "all", &flags, N_("pack everything"), PACK_REFS_ALL),
- OPT_BIT(0, "prune", &flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE),
+ OPT_BIT(0, "all", &pack_refs_opts.flags, N_("pack everything"), PACK_REFS_ALL),
+ OPT_BIT(0, "prune", &pack_refs_opts.flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE),
+ OPT_STRING_LIST(0, "include", pack_refs_opts.includes, N_("pattern"),
+ N_("references to include")),
+ OPT_STRING_LIST(0, "exclude", &option_excluded_refs, N_("pattern"),
+ N_("references to exclude")),
OPT_END(),
};
git_config(git_default_config, NULL);
if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))
usage_with_options(pack_refs_usage, opts);
- return refs_pack_refs(get_main_ref_store(the_repository), flags);
+
+ for_each_string_list_item(item, &option_excluded_refs)
+ add_ref_exclusion(pack_refs_opts.exclusions, item->string);
+
+ if (pack_refs_opts.flags & PACK_REFS_ALL)
+ string_list_append(pack_refs_opts.includes, "*");
+
+ if (!pack_refs_opts.includes->nr)
+ string_list_append(pack_refs_opts.includes, "refs/tags/*");
+
+ return refs_pack_refs(get_main_ref_store(the_repository), &pack_refs_opts);
}
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index 881fcf3273..3894d2b970 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -1,7 +1,10 @@
-#include "cache.h"
#include "builtin.h"
#include "config.h"
#include "diff.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
+#include "parse-options.h"
static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
{
@@ -57,10 +60,12 @@ static int scan_hunk_header(const char *p, int *p_before, int *p_after)
}
static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
- struct strbuf *line_buf, int stable)
+ struct strbuf *line_buf, int stable, int verbatim)
{
int patchlen = 0, found_next = 0;
int before = -1, after = -1;
+ int diff_is_binary = 0;
+ char pre_oid_str[GIT_MAX_HEXSZ + 1], post_oid_str[GIT_MAX_HEXSZ + 1];
git_hash_ctx ctx;
the_hash_algo->init_fn(&ctx);
@@ -71,11 +76,14 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
const char *p = line;
int len;
- if (!skip_prefix(line, "diff-tree ", &p) &&
- !skip_prefix(line, "commit ", &p) &&
+ /* Possibly skip over the prefix added by "log" or "format-patch" */
+ if (!skip_prefix(line, "commit ", &p) &&
!skip_prefix(line, "From ", &p) &&
- starts_with(line, "\\ ") && 12 < strlen(line))
+ starts_with(line, "\\ ") && 12 < strlen(line)) {
+ if (verbatim)
+ the_hash_algo->update_fn(&ctx, line, strlen(line));
continue;
+ }
if (!get_oid_hex(p, next_oid)) {
found_next = 1;
@@ -88,14 +96,44 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
/* Parsing diff header? */
if (before == -1) {
- if (starts_with(line, "index "))
+ if (starts_with(line, "GIT binary patch") ||
+ starts_with(line, "Binary files")) {
+ diff_is_binary = 1;
+ before = 0;
+ the_hash_algo->update_fn(&ctx, pre_oid_str,
+ strlen(pre_oid_str));
+ the_hash_algo->update_fn(&ctx, post_oid_str,
+ strlen(post_oid_str));
+ if (stable)
+ flush_one_hunk(result, &ctx);
+ continue;
+ } else if (skip_prefix(line, "index ", &p)) {
+ char *oid1_end = strstr(line, "..");
+ char *oid2_end = NULL;
+ if (oid1_end)
+ oid2_end = strstr(oid1_end, " ");
+ if (!oid2_end)
+ oid2_end = line + strlen(line) - 1;
+ if (oid1_end != NULL && oid2_end != NULL) {
+ *oid1_end = *oid2_end = '\0';
+ strlcpy(pre_oid_str, p, GIT_MAX_HEXSZ + 1);
+ strlcpy(post_oid_str, oid1_end + 2, GIT_MAX_HEXSZ + 1);
+ }
continue;
- else if (starts_with(line, "--- "))
+ } else if (starts_with(line, "--- "))
before = after = 1;
else if (!isalpha(line[0]))
break;
}
+ if (diff_is_binary) {
+ if (starts_with(line, "diff ")) {
+ diff_is_binary = 0;
+ before = -1;
+ }
+ continue;
+ }
+
/* Looking for a valid hunk header? */
if (before == 0 && after == 0) {
if (starts_with(line, "@@ -")) {
@@ -120,8 +158,8 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
if (line[0] == '+' || line[0] == ' ')
after--;
- /* Compute the sha without whitespace */
- len = remove_space(line);
+ /* Add line to hash algo (possibly removing whitespace) */
+ len = verbatim ? strlen(line) : remove_space(line);
patchlen += len;
the_hash_algo->update_fn(&ctx, line, len);
}
@@ -134,7 +172,7 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
return patchlen;
}
-static void generate_id_list(int stable)
+static void generate_id_list(int stable, int verbatim)
{
struct object_id oid, n, result;
int patchlen;
@@ -142,44 +180,64 @@ static void generate_id_list(int stable)
oidclr(&oid);
while (!feof(stdin)) {
- patchlen = get_one_patchid(&n, &result, &line_buf, stable);
+ patchlen = get_one_patchid(&n, &result, &line_buf, stable, verbatim);
flush_current_id(patchlen, &oid, &result);
oidcpy(&oid, &n);
}
strbuf_release(&line_buf);
}
-static const char patch_id_usage[] = "git patch-id [--stable | --unstable]";
+static const char *const patch_id_usage[] = {
+ N_("git patch-id [--stable | --unstable | --verbatim]"), NULL
+};
+
+struct patch_id_opts {
+ int stable;
+ int verbatim;
+};
-static int git_patch_id_config(const char *var, const char *value, void *cb)
+static int git_patch_id_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
- int *stable = cb;
+ struct patch_id_opts *opts = cb;
if (!strcmp(var, "patchid.stable")) {
- *stable = git_config_bool(var, value);
+ opts->stable = git_config_bool(var, value);
+ return 0;
+ }
+ if (!strcmp(var, "patchid.verbatim")) {
+ opts->verbatim = git_config_bool(var, value);
return 0;
}
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
}
int cmd_patch_id(int argc, const char **argv, const char *prefix)
{
- int stable = -1;
-
- git_config(git_patch_id_config, &stable);
-
- /* If nothing is set, default to unstable. */
- if (stable < 0)
- stable = 0;
-
- if (argc == 2 && !strcmp(argv[1], "--stable"))
- stable = 1;
- else if (argc == 2 && !strcmp(argv[1], "--unstable"))
- stable = 0;
- else if (argc != 1)
- usage(patch_id_usage);
-
- generate_id_list(stable);
+ /* if nothing is set, default to unstable */
+ struct patch_id_opts config = {0, 0};
+ int opts = 0;
+ struct option builtin_patch_id_options[] = {
+ OPT_CMDMODE(0, "unstable", &opts,
+ N_("use the unstable patch-id algorithm"), 1),
+ OPT_CMDMODE(0, "stable", &opts,
+ N_("use the stable patch-id algorithm"), 2),
+ OPT_CMDMODE(0, "verbatim", &opts,
+ N_("don't strip whitespace from the patch"), 3),
+ OPT_END()
+ };
+
+ git_config(git_patch_id_config, &config);
+
+ /* verbatim implies stable */
+ if (config.verbatim)
+ config.stable = 1;
+
+ argc = parse_options(argc, argv, prefix, builtin_patch_id_options,
+ patch_id_usage, 0);
+
+ generate_id_list(opts ? opts > 1 : config.stable,
+ opts ? opts == 3 : config.verbatim);
return 0;
}
diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c
index da3273a268..ca3578e158 100644
--- a/builtin/prune-packed.c
+++ b/builtin/prune-packed.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "gettext.h"
#include "parse-options.h"
#include "prune-packed.h"
diff --git a/builtin/prune.c b/builtin/prune.c
index c2bcdc07db..57fe31467f 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -1,13 +1,20 @@
-#include "cache.h"
+#include "builtin.h"
#include "commit.h"
#include "diff.h"
+#include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "revision.h"
-#include "builtin.h"
#include "reachable.h"
#include "parse-options.h"
+#include "path.h"
#include "progress.h"
#include "prune-packed.h"
-#include "object-store.h"
+#include "replace-object.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
#include "shallow.h"
static const char * const prune_usage[] = {
@@ -98,7 +105,8 @@ static int prune_object(const struct object_id *oid, const char *fullpath,
return 0;
}
-static int prune_cruft(const char *basename, const char *path, void *data)
+static int prune_cruft(const char *basename, const char *path,
+ void *data UNUSED)
{
if (starts_with(basename, "tmp_obj_"))
prune_tmp_file(path);
@@ -107,7 +115,8 @@ static int prune_cruft(const char *basename, const char *path, void *data)
return 0;
}
-static int prune_subdir(unsigned int nr, const char *path, void *data)
+static int prune_subdir(unsigned int nr UNUSED, const char *path,
+ void *data UNUSED)
{
if (!show_only)
rmdir(path);
@@ -127,7 +136,9 @@ static void remove_temporary_files(const char *path)
dir = opendir(path);
if (!dir) {
- fprintf(stderr, "Unable to open directory %s\n", path);
+ if (errno != ENOENT)
+ fprintf(stderr, "Unable to open directory %s: %s\n",
+ path, strerror(errno));
return;
}
while ((de = readdir(dir)) != NULL)
@@ -154,7 +165,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
expire = TIME_MAX;
save_commit_buffer = 0;
- read_replace_refs = 0;
+ disable_replace_refs();
repo_init_revisions(the_repository, &revs, prefix);
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
@@ -166,7 +177,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
struct object_id oid;
const char *name = *argv++;
- if (!get_oid(name, &oid)) {
+ if (!repo_get_oid(the_repository, name, &oid)) {
struct object *object = parse_object_or_die(&oid,
name);
add_pending_object(&revs, object, "");
@@ -196,5 +207,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);
}
+ release_revisions(&revs);
return 0;
}
diff --git a/builtin/pull.c b/builtin/pull.c
index 4d667abc19..be2b2c9ebc 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -5,16 +5,22 @@
*
* Fetch one or more remote refs and merge it/them into the current HEAD.
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
-#include "config.h"
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
+#include "advice.h"
+#include "config.h"
+#include "gettext.h"
+#include "hex.h"
+#include "merge.h"
+#include "object-name.h"
#include "parse-options.h"
#include "exec-cmd.h"
#include "run-command.h"
#include "oid-array.h"
#include "remote.h"
#include "dir.h"
+#include "path.h"
+#include "read-cache-ll.h"
#include "rebase.h"
#include "refs.h"
#include "refspec.h"
@@ -72,6 +78,7 @@ static const char * const pull_usage[] = {
static int opt_verbosity;
static char *opt_progress;
static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
/* Options passed to git-merge or git-rebase */
static enum rebase_type opt_rebase = -1;
@@ -120,7 +127,7 @@ static struct option pull_options[] = {
N_("force progress reporting"),
PARSE_OPT_NOARG),
OPT_CALLBACK_F(0, "recurse-submodules",
- &recurse_submodules, N_("on-demand"),
+ &recurse_submodules_cli, N_("on-demand"),
N_("control for recursive fetching of submodules"),
PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
@@ -356,10 +363,9 @@ static enum rebase_type config_get_rebase(int *rebase_unspecified)
/**
* Read config variables.
*/
-static int git_pull_config(const char *var, const char *value, void *cb)
+static int git_pull_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
- int status;
-
if (!strcmp(var, "rebase.autostash")) {
config_autostash = git_config_bool(var, value);
return 0;
@@ -371,11 +377,7 @@ static int git_pull_config(const char *var, const char *value, void *cb)
check_trust_level = 0;
}
- status = git_gpg_config(var, value, cb);
- if (status)
- return status;
-
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
}
/**
@@ -514,76 +516,75 @@ static void parse_repo_refspecs(int argc, const char **argv, const char **repo,
*/
static int run_fetch(const char *repo, const char **refspecs)
{
- struct strvec args = STRVEC_INIT;
- int ret;
+ struct child_process cmd = CHILD_PROCESS_INIT;
- strvec_pushl(&args, "fetch", "--update-head-ok", NULL);
+ strvec_pushl(&cmd.args, "fetch", "--update-head-ok", NULL);
/* Shared options */
- argv_push_verbosity(&args);
+ argv_push_verbosity(&cmd.args);
if (opt_progress)
- strvec_push(&args, opt_progress);
+ strvec_push(&cmd.args, opt_progress);
/* Options passed to git-fetch */
if (opt_all)
- strvec_push(&args, opt_all);
+ strvec_push(&cmd.args, opt_all);
if (opt_append)
- strvec_push(&args, opt_append);
+ strvec_push(&cmd.args, opt_append);
if (opt_upload_pack)
- strvec_push(&args, opt_upload_pack);
- argv_push_force(&args);
+ strvec_push(&cmd.args, opt_upload_pack);
+ argv_push_force(&cmd.args);
if (opt_tags)
- strvec_push(&args, opt_tags);
+ strvec_push(&cmd.args, opt_tags);
if (opt_prune)
- strvec_push(&args, opt_prune);
- if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
- switch (recurse_submodules) {
+ strvec_push(&cmd.args, opt_prune);
+ if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT)
+ switch (recurse_submodules_cli) {
case RECURSE_SUBMODULES_ON:
- strvec_push(&args, "--recurse-submodules=on");
+ strvec_push(&cmd.args, "--recurse-submodules=on");
break;
case RECURSE_SUBMODULES_OFF:
- strvec_push(&args, "--recurse-submodules=no");
+ strvec_push(&cmd.args, "--recurse-submodules=no");
break;
case RECURSE_SUBMODULES_ON_DEMAND:
- strvec_push(&args, "--recurse-submodules=on-demand");
+ strvec_push(&cmd.args, "--recurse-submodules=on-demand");
break;
default:
BUG("submodule recursion option not understood");
}
if (max_children)
- strvec_push(&args, max_children);
+ strvec_push(&cmd.args, max_children);
if (opt_dry_run)
- strvec_push(&args, "--dry-run");
+ strvec_push(&cmd.args, "--dry-run");
if (opt_keep)
- strvec_push(&args, opt_keep);
+ strvec_push(&cmd.args, opt_keep);
if (opt_depth)
- strvec_push(&args, opt_depth);
+ strvec_push(&cmd.args, opt_depth);
if (opt_unshallow)
- strvec_push(&args, opt_unshallow);
+ strvec_push(&cmd.args, opt_unshallow);
if (opt_update_shallow)
- strvec_push(&args, opt_update_shallow);
+ strvec_push(&cmd.args, opt_update_shallow);
if (opt_refmap)
- strvec_push(&args, opt_refmap);
+ strvec_push(&cmd.args, opt_refmap);
if (opt_ipv4)
- strvec_push(&args, opt_ipv4);
+ strvec_push(&cmd.args, opt_ipv4);
if (opt_ipv6)
- strvec_push(&args, opt_ipv6);
+ strvec_push(&cmd.args, opt_ipv6);
if (opt_show_forced_updates > 0)
- strvec_push(&args, "--show-forced-updates");
+ strvec_push(&cmd.args, "--show-forced-updates");
else if (opt_show_forced_updates == 0)
- strvec_push(&args, "--no-show-forced-updates");
+ strvec_push(&cmd.args, "--no-show-forced-updates");
if (set_upstream)
- strvec_push(&args, set_upstream);
- strvec_pushv(&args, opt_fetch.v);
+ strvec_push(&cmd.args, set_upstream);
+ strvec_pushv(&cmd.args, opt_fetch.v);
if (repo) {
- strvec_push(&args, repo);
- strvec_pushv(&args, refspecs);
+ strvec_push(&cmd.args, repo);
+ strvec_pushv(&cmd.args, refspecs);
} else if (*refspecs)
BUG("refspecs without repo?");
- ret = run_command_v_opt(args.v, RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE);
- strvec_clear(&args);
- return ret;
+ cmd.git_cmd = 1;
+ cmd.close_object_store = 1;
+ return run_command(&cmd);
}
/**
@@ -652,52 +653,50 @@ static int update_submodules(void)
*/
static int run_merge(void)
{
- int ret;
- struct strvec args = STRVEC_INIT;
+ struct child_process cmd = CHILD_PROCESS_INIT;
- strvec_pushl(&args, "merge", NULL);
+ strvec_pushl(&cmd.args, "merge", NULL);
/* Shared options */
- argv_push_verbosity(&args);
+ argv_push_verbosity(&cmd.args);
if (opt_progress)
- strvec_push(&args, opt_progress);
+ strvec_push(&cmd.args, opt_progress);
/* Options passed to git-merge */
if (opt_diffstat)
- strvec_push(&args, opt_diffstat);
+ strvec_push(&cmd.args, opt_diffstat);
if (opt_log)
- strvec_push(&args, opt_log);
+ strvec_push(&cmd.args, opt_log);
if (opt_signoff)
- strvec_push(&args, opt_signoff);
+ strvec_push(&cmd.args, opt_signoff);
if (opt_squash)
- strvec_push(&args, opt_squash);
+ strvec_push(&cmd.args, opt_squash);
if (opt_commit)
- strvec_push(&args, opt_commit);
+ strvec_push(&cmd.args, opt_commit);
if (opt_edit)
- strvec_push(&args, opt_edit);
+ strvec_push(&cmd.args, opt_edit);
if (cleanup_arg)
- strvec_pushf(&args, "--cleanup=%s", cleanup_arg);
+ strvec_pushf(&cmd.args, "--cleanup=%s", cleanup_arg);
if (opt_ff)
- strvec_push(&args, opt_ff);
+ strvec_push(&cmd.args, opt_ff);
if (opt_verify)
- strvec_push(&args, opt_verify);
+ strvec_push(&cmd.args, opt_verify);
if (opt_verify_signatures)
- strvec_push(&args, opt_verify_signatures);
- strvec_pushv(&args, opt_strategies.v);
- strvec_pushv(&args, opt_strategy_opts.v);
+ strvec_push(&cmd.args, opt_verify_signatures);
+ strvec_pushv(&cmd.args, opt_strategies.v);
+ strvec_pushv(&cmd.args, opt_strategy_opts.v);
if (opt_gpg_sign)
- strvec_push(&args, opt_gpg_sign);
+ strvec_push(&cmd.args, opt_gpg_sign);
if (opt_autostash == 0)
- strvec_push(&args, "--no-autostash");
+ strvec_push(&cmd.args, "--no-autostash");
else if (opt_autostash == 1)
- strvec_push(&args, "--autostash");
+ strvec_push(&cmd.args, "--autostash");
if (opt_allow_unrelated_histories > 0)
- strvec_push(&args, "--allow-unrelated-histories");
+ strvec_push(&cmd.args, "--allow-unrelated-histories");
- strvec_push(&args, "FETCH_HEAD");
- ret = run_command_v_opt(args.v, RUN_GIT_CMD);
- strvec_clear(&args);
- return ret;
+ strvec_push(&cmd.args, "FETCH_HEAD");
+ cmd.git_cmd = 1;
+ return run_command(&cmd);
}
/**
@@ -878,43 +877,41 @@ static int get_rebase_newbase_and_upstream(struct object_id *newbase,
static int run_rebase(const struct object_id *newbase,
const struct object_id *upstream)
{
- int ret;
- struct strvec args = STRVEC_INIT;
+ struct child_process cmd = CHILD_PROCESS_INIT;
- strvec_push(&args, "rebase");
+ strvec_push(&cmd.args, "rebase");
/* Shared options */
- argv_push_verbosity(&args);
+ argv_push_verbosity(&cmd.args);
/* Options passed to git-rebase */
if (opt_rebase == REBASE_MERGES)
- strvec_push(&args, "--rebase-merges");
+ strvec_push(&cmd.args, "--rebase-merges");
else if (opt_rebase == REBASE_INTERACTIVE)
- strvec_push(&args, "--interactive");
+ strvec_push(&cmd.args, "--interactive");
if (opt_diffstat)
- strvec_push(&args, opt_diffstat);
- strvec_pushv(&args, opt_strategies.v);
- strvec_pushv(&args, opt_strategy_opts.v);
+ strvec_push(&cmd.args, opt_diffstat);
+ strvec_pushv(&cmd.args, opt_strategies.v);
+ strvec_pushv(&cmd.args, opt_strategy_opts.v);
if (opt_gpg_sign)
- strvec_push(&args, opt_gpg_sign);
+ strvec_push(&cmd.args, opt_gpg_sign);
if (opt_signoff)
- strvec_push(&args, opt_signoff);
+ strvec_push(&cmd.args, opt_signoff);
if (opt_autostash == 0)
- strvec_push(&args, "--no-autostash");
+ strvec_push(&cmd.args, "--no-autostash");
else if (opt_autostash == 1)
- strvec_push(&args, "--autostash");
+ strvec_push(&cmd.args, "--autostash");
if (opt_verify_signatures &&
!strcmp(opt_verify_signatures, "--verify-signatures"))
warning(_("ignoring --verify-signatures for rebase"));
- strvec_push(&args, "--onto");
- strvec_push(&args, oid_to_hex(newbase));
+ strvec_push(&cmd.args, "--onto");
+ strvec_push(&cmd.args, oid_to_hex(newbase));
- strvec_push(&args, oid_to_hex(upstream));
+ strvec_push(&cmd.args, oid_to_hex(upstream));
- ret = run_command_v_opt(args.v, RUN_GIT_CMD);
- strvec_clear(&args);
- return ret;
+ cmd.git_cmd = 1;
+ return run_command(&cmd);
}
static int get_can_ff(struct object_id *orig_head,
@@ -989,6 +986,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
int rebase_unspecified = 0;
int can_ff;
int divergent;
+ int ret;
if (!getenv("GIT_REFLOG_ACTION"))
set_reflog_message(argc, argv);
@@ -1001,6 +999,9 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);
+ if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT)
+ recurse_submodules = recurse_submodules_cli;
+
if (cleanup_arg)
/*
* this only checks the validity of cleanup_arg; we don't need
@@ -1030,26 +1031,26 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
if (opt_rebase < 0)
opt_rebase = config_get_rebase(&rebase_unspecified);
- if (read_cache_unmerged())
+ if (repo_read_index_unmerged(the_repository))
die_resolve_conflict("pull");
if (file_exists(git_path_merge_head(the_repository)))
die_conclude_merge();
- if (get_oid("HEAD", &orig_head))
+ if (repo_get_oid(the_repository, "HEAD", &orig_head))
oidclr(&orig_head);
if (opt_rebase) {
if (opt_autostash == -1)
opt_autostash = config_autostash;
- if (is_null_oid(&orig_head) && !is_cache_unborn())
+ if (is_null_oid(&orig_head) && !is_index_unborn(&the_index))
die(_("Updating an unborn branch with changes added to the index."));
if (!opt_autostash)
require_clean_work_tree(the_repository,
N_("pull with rebase"),
- _("please commit or stash them."), 1, 0);
+ _("Please commit or stash them."), 1, 0);
if (get_rebase_fork_point(&rebase_fork_point, repo, *refspecs))
oidclr(&rebase_fork_point);
@@ -1061,7 +1062,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
if (opt_dry_run)
return 0;
- if (get_oid("HEAD", &curr_head))
+ if (repo_get_oid(the_repository, "HEAD", &curr_head))
oidclr(&curr_head);
if (!is_null_oid(&orig_head) && !is_null_oid(&curr_head) &&
@@ -1096,7 +1097,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
if (is_null_oid(&orig_head)) {
if (merge_heads.nr > 1)
die(_("Cannot merge multiple branches into empty head."));
- return pull_into_void(merge_heads.oid, &curr_head);
+ ret = pull_into_void(merge_heads.oid, &curr_head);
+ goto cleanup;
}
if (merge_heads.nr > 1) {
if (opt_rebase)
@@ -1121,8 +1123,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
}
if (opt_rebase) {
- int ret = 0;
-
struct object_id newbase;
struct object_id upstream;
get_rebase_newbase_and_upstream(&newbase, &upstream, &curr_head,
@@ -1145,12 +1145,16 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND))
ret = rebase_submodules();
- return ret;
+ goto cleanup;
} else {
- int ret = run_merge();
+ ret = run_merge();
if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON ||
recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND))
ret = update_submodules();
- return ret;
+ goto cleanup;
}
+
+cleanup:
+ oid_array_clear(&merge_heads);
+ return ret;
}
diff --git a/builtin/push.c b/builtin/push.c
index cad997965a..f05d2e29bc 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -1,18 +1,24 @@
/*
* "git push"
*/
-#include "cache.h"
+#include "builtin.h"
+#include "advice.h"
+#include "branch.h"
#include "config.h"
+#include "environment.h"
+#include "gettext.h"
#include "refs.h"
#include "refspec.h"
#include "run-command.h"
-#include "builtin.h"
#include "remote.h"
#include "transport.h"
#include "parse-options.h"
+#include "pkt-line.h"
+#include "repository.h"
#include "submodule.h"
#include "submodule-config.h"
#include "send-pack.h"
+#include "trace2.h"
#include "color.h"
static const char * const push_usage[] = {
@@ -62,16 +68,9 @@ static struct refspec rs = REFSPEC_INIT_PUSH;
static struct string_list push_options_config = STRING_LIST_INIT_DUP;
static void refspec_append_mapped(struct refspec *refspec, const char *ref,
- struct remote *remote, struct ref *local_refs)
+ struct remote *remote, struct ref *matched)
{
const char *branch_name;
- struct ref *matched = NULL;
-
- /* Does "ref" uniquely name our ref? */
- if (count_refspec_match(ref, local_refs, &matched) != 1) {
- refspec_append(refspec, ref);
- return;
- }
if (remote->push.nr) {
struct refspec_item query;
@@ -119,15 +118,28 @@ static void set_refspecs(const char **refs, int nr, const char *repo)
die(_("--delete only accepts plain target ref names"));
refspec_appendf(&rs, ":%s", ref);
} else if (!strchr(ref, ':')) {
- if (!remote) {
- /* lazily grab remote and local_refs */
- remote = remote_get(repo);
+ struct ref *matched = NULL;
+
+ /* lazily grab local_refs */
+ if (!local_refs)
local_refs = get_local_heads();
+
+ /* Does "ref" uniquely name our ref? */
+ if (count_refspec_match(ref, local_refs, &matched) != 1) {
+ refspec_append(&rs, ref);
+ } else {
+ /* lazily grab remote */
+ if (!remote)
+ remote = remote_get(repo);
+ if (!remote)
+ BUG("must get a remote for repo '%s'", repo);
+
+ refspec_append_mapped(&rs, ref, remote, matched);
}
- refspec_append_mapped(&rs, ref, remote, local_refs);
} else
refspec_append(&rs, ref);
}
+ free_refs(local_refs);
}
static int push_url_of_remote(struct remote *remote, const char ***url_p)
@@ -151,7 +163,8 @@ static NORETURN void die_push_simple(struct branch *branch,
* upstream to a non-branch, we should probably be showing
* them the big ugly fully qualified ref.
*/
- const char *advice_maybe = "";
+ const char *advice_pushdefault_maybe = "";
+ const char *advice_automergesimple_maybe = "";
const char *short_upstream = branch->merge[0]->src;
skip_prefix(short_upstream, "refs/heads/", &short_upstream);
@@ -161,9 +174,16 @@ static NORETURN void die_push_simple(struct branch *branch,
* push.default.
*/
if (push_default == PUSH_DEFAULT_UNSPECIFIED)
- advice_maybe = _("\n"
+ advice_pushdefault_maybe = _("\n"
"To choose either option permanently, "
- "see push.default in 'git help config'.");
+ "see push.default in 'git help config'.\n");
+ if (git_branch_track != BRANCH_TRACK_SIMPLE)
+ advice_automergesimple_maybe = _("\n"
+ "To avoid automatically configuring "
+ "an upstream branch when its name\n"
+ "won't match the local branch, see option "
+ "'simple' of branch.autoSetupMerge\n"
+ "in 'git help config'.\n");
die(_("The upstream branch of your current branch does not match\n"
"the name of your current branch. To push to the upstream branch\n"
"on the remote, use\n"
@@ -173,9 +193,10 @@ static NORETURN void die_push_simple(struct branch *branch,
"To push to the branch of the same name on the remote, use\n"
"\n"
" git push %s HEAD\n"
- "%s"),
+ "%s%s"),
remote->name, short_upstream,
- remote->name, advice_maybe);
+ remote->name, advice_pushdefault_maybe,
+ advice_automergesimple_maybe);
}
static const char message_detached_head_die[] =
@@ -185,16 +206,32 @@ static const char message_detached_head_die[] =
"\n"
" git push %s HEAD:<name-of-remote-branch>\n");
-static const char *get_upstream_ref(struct branch *branch, const char *remote_name)
+static const char *get_upstream_ref(int flags, struct branch *branch, const char *remote_name)
{
- if (!branch->merge_nr || !branch->merge || !branch->remote_name)
+ if (branch->merge_nr == 0 && (flags & TRANSPORT_PUSH_AUTO_UPSTREAM)) {
+ /* if missing, assume same; set_upstream will be defined later */
+ return branch->refname;
+ }
+
+ if (!branch->merge_nr || !branch->merge || !branch->remote_name) {
+ const char *advice_autosetup_maybe = "";
+ if (!(flags & TRANSPORT_PUSH_AUTO_UPSTREAM)) {
+ advice_autosetup_maybe = _("\n"
+ "To have this happen automatically for "
+ "branches without a tracking\n"
+ "upstream, see 'push.autoSetupRemote' "
+ "in 'git help config'.\n");
+ }
die(_("The current branch %s has no upstream branch.\n"
"To push the current branch and set the remote as upstream, use\n"
"\n"
- " git push --set-upstream %s %s\n"),
+ " git push --set-upstream %s %s\n"
+ "%s"),
branch->name,
remote_name,
- branch->name);
+ branch->name,
+ advice_autosetup_maybe);
+ }
if (branch->merge_nr != 1)
die(_("The current branch %s has multiple upstream branches, "
"refusing to push."), branch->name);
@@ -202,7 +239,7 @@ static const char *get_upstream_ref(struct branch *branch, const char *remote_na
return branch->merge[0]->src;
}
-static void setup_default_push_refspecs(struct remote *remote)
+static void setup_default_push_refspecs(int *flags, struct remote *remote)
{
struct branch *branch;
const char *dst;
@@ -234,7 +271,7 @@ static void setup_default_push_refspecs(struct remote *remote)
case PUSH_DEFAULT_SIMPLE:
if (!same_remote)
break;
- if (strcmp(branch->refname, get_upstream_ref(branch, remote->name)))
+ if (strcmp(branch->refname, get_upstream_ref(*flags, branch, remote->name)))
die_push_simple(branch, remote);
break;
@@ -244,33 +281,41 @@ static void setup_default_push_refspecs(struct remote *remote)
"your current branch '%s', without telling me what to push\n"
"to update which remote branch."),
remote->name, branch->name);
- dst = get_upstream_ref(branch, remote->name);
+ dst = get_upstream_ref(*flags, branch, remote->name);
break;
case PUSH_DEFAULT_CURRENT:
break;
}
+ /*
+ * this is a default push - if auto-upstream is enabled and there is
+ * no upstream defined, then set it (with options 'simple', 'upstream',
+ * and 'current').
+ */
+ if ((*flags & TRANSPORT_PUSH_AUTO_UPSTREAM) && branch->merge_nr == 0)
+ *flags |= TRANSPORT_PUSH_SET_UPSTREAM;
+
refspec_appendf(&rs, "%s:%s", branch->refname, dst);
}
static const char message_advice_pull_before_push[] =
N_("Updates were rejected because the tip of your current branch is behind\n"
- "its remote counterpart. Integrate the remote changes (e.g.\n"
- "'git pull ...') before pushing again.\n"
+ "its remote counterpart. If you want to integrate the remote changes,\n"
+ "use 'git pull' before pushing again.\n"
"See the 'Note about fast-forwards' in 'git push --help' for details.");
static const char message_advice_checkout_pull_push[] =
N_("Updates were rejected because a pushed branch tip is behind its remote\n"
- "counterpart. Check out this branch and integrate the remote changes\n"
- "(e.g. 'git pull ...') before pushing again.\n"
+ "counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+ "before pushing again.\n"
"See the 'Note about fast-forwards' in 'git push --help' for details.");
static const char message_advice_ref_fetch_first[] =
- N_("Updates were rejected because the remote contains work that you do\n"
- "not have locally. This is usually caused by another repository pushing\n"
- "to the same ref. You may want to first integrate the remote changes\n"
- "(e.g., 'git pull ...') before pushing again.\n"
+ N_("Updates were rejected because the remote contains work that you do not\n"
+ "have locally. This is usually caused by another repository pushing to\n"
+ "the same ref. If you want to integrate the remote changes, use\n"
+ "'git pull' before pushing again.\n"
"See the 'Note about fast-forwards' in 'git push --help' for details.");
static const char message_advice_ref_already_exists[] =
@@ -282,10 +327,10 @@ static const char message_advice_ref_needs_force[] =
"without using the '--force' option.\n");
static const char message_advice_ref_needs_update[] =
- N_("Updates were rejected because the tip of the remote-tracking\n"
- "branch has been updated since the last checkout. You may want\n"
- "to integrate those changes locally (e.g., 'git pull ...')\n"
- "before forcing an update.\n");
+ N_("Updates were rejected because the tip of the remote-tracking branch has\n"
+ "been updated since the last checkout. If you want to integrate the\n"
+ "remote changes, use 'git pull' before pushing again.\n"
+ "See the 'Note about fast-forwards' in 'git push --help' for details.");
static void advise_pull_before_push(void)
{
@@ -401,7 +446,7 @@ static int do_push(int flags,
if (remote->push.nr) {
push_refspec = &remote->push;
} else if (!(flags & TRANSPORT_PUSH_MIRROR))
- setup_default_push_refspecs(remote);
+ setup_default_push_refspecs(&flags, remote);
}
errs = 0;
url_nr = push_url_of_remote(remote, &url);
@@ -432,8 +477,16 @@ static int option_parse_recurse_submodules(const struct option *opt,
if (unset)
*recurse_submodules = RECURSE_SUBMODULES_OFF;
- else
- *recurse_submodules = parse_push_recurse_submodules_arg(opt->long_name, arg);
+ else {
+ if (!strcmp(arg, "only-is-on-demand")) {
+ if (*recurse_submodules == RECURSE_SUBMODULES_ONLY) {
+ warning(_("recursing into submodule with push.recurseSubmodules=only; using on-demand instead"));
+ *recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
+ }
+ } else {
+ *recurse_submodules = parse_push_recurse_submodules_arg(opt->long_name, arg);
+ }
+ }
return 0;
}
@@ -456,15 +509,11 @@ static void set_push_cert_flags(int *flags, int v)
}
-static int git_push_config(const char *k, const char *v, void *cb)
+static int git_push_config(const char *k, const char *v,
+ const struct config_context *ctx, void *cb)
{
const char *slot_name;
int *flags = cb;
- int status;
-
- status = git_gpg_config(k, v, NULL);
- if (status)
- return status;
if (!strcmp(k, "push.followtags")) {
if (git_config_bool(k, v))
@@ -472,6 +521,10 @@ static int git_push_config(const char *k, const char *v, void *cb)
else
*flags &= ~TRANSPORT_PUSH_FOLLOW_TAGS;
return 0;
+ } else if (!strcmp(k, "push.autosetupremote")) {
+ if (git_config_bool(k, v))
+ *flags |= TRANSPORT_PUSH_AUTO_UPSTREAM;
+ return 0;
} else if (!strcmp(k, "push.gpgsign")) {
const char *value;
if (!git_config_get_value("push.gpgsign", &value)) {
@@ -524,7 +577,7 @@ static int git_push_config(const char *k, const char *v, void *cb)
return 0;
}
- return git_default_config(k, v, NULL);
+ return git_default_config(k, v, ctx, NULL);
}
int cmd_push(int argc, const char **argv, const char *prefix)
@@ -542,11 +595,12 @@ int cmd_push(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT__VERBOSITY(&verbosity),
OPT_STRING( 0 , "repo", &repo, N_("repository"), N_("repository")),
- OPT_BIT( 0 , "all", &flags, N_("push all refs"), TRANSPORT_PUSH_ALL),
+ OPT_BIT( 0 , "all", &flags, N_("push all branches"), TRANSPORT_PUSH_ALL),
+ OPT_ALIAS( 0 , "branches", "all"),
OPT_BIT( 0 , "mirror", &flags, N_("mirror all refs"),
(TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
OPT_BOOL('d', "delete", &deleterefs, N_("delete refs")),
- OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --mirror)")),
+ OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --branches or --mirror)")),
OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN),
OPT_BIT( 0, "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN),
OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE),
@@ -589,7 +643,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
set_push_cert_flags(&flags, push_cert);
if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
- die(_("options '%s' and '%s' cannot be used together"), "--delete", "--all/--mirror/--tags");
+ die(_("options '%s' and '%s' cannot be used together"), "--delete", "--all/--branches/--mirror/--tags");
if (deleterefs && argc < 2)
die(_("--delete doesn't make sense without any refs"));
diff --git a/builtin/range-diff.c b/builtin/range-diff.c
index 50318849d6..e455a4795c 100644
--- a/builtin/range-diff.c
+++ b/builtin/range-diff.c
@@ -1,8 +1,10 @@
-#include "cache.h"
#include "builtin.h"
+#include "gettext.h"
+#include "object-name.h"
#include "parse-options.h"
#include "range-diff.h"
#include "config.h"
+#include "repository.h"
#include "revision.h"
static const char * const builtin_range_diff_usage[] = {
@@ -38,16 +40,18 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
OPT_END()
};
struct option *options;
- int res = 0;
+ int i, dash_dash = -1, res = 0;
struct strbuf range1 = STRBUF_INIT, range2 = STRBUF_INIT;
+ struct object_id oid;
+ const char *three_dots = NULL;
git_config(git_diff_ui_config, NULL);
repo_diff_setup(the_repository, &diffopt);
- options = parse_options_concat(range_diff_options, diffopt.parseopts);
+ options = add_diff_options(range_diff_options, &diffopt);
argc = parse_options(argc, argv, prefix, options,
- builtin_range_diff_usage, 0);
+ builtin_range_diff_usage, PARSE_OPT_KEEP_DASHDASH);
diff_setup_done(&diffopt);
@@ -55,40 +59,91 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
if (!simple_color)
diffopt.use_color = 1;
- if (argc == 2) {
- if (!is_range_diff_range(argv[0]))
- die(_("not a commit range: '%s'"), argv[0]);
- strbuf_addstr(&range1, argv[0]);
+ for (i = 0; i < argc; i++)
+ if (!strcmp(argv[i], "--")) {
+ dash_dash = i;
+ break;
+ }
+
+ if (dash_dash == 3 ||
+ (dash_dash < 0 && argc > 2 &&
+ !repo_get_oid_committish(the_repository, argv[0], &oid) &&
+ !repo_get_oid_committish(the_repository, argv[1], &oid) &&
+ !repo_get_oid_committish(the_repository, argv[2], &oid))) {
+ if (dash_dash < 0)
+ ; /* already validated arguments */
+ else if (repo_get_oid_committish(the_repository, argv[0], &oid))
+ usage_msg_optf(_("not a revision: '%s'"),
+ builtin_range_diff_usage, options,
+ argv[0]);
+ else if (repo_get_oid_committish(the_repository, argv[1], &oid))
+ usage_msg_optf(_("not a revision: '%s'"),
+ builtin_range_diff_usage, options,
+ argv[1]);
+ else if (repo_get_oid_committish(the_repository, argv[2], &oid))
+ usage_msg_optf(_("not a revision: '%s'"),
+ builtin_range_diff_usage, options,
+ argv[2]);
- if (!is_range_diff_range(argv[1]))
- die(_("not a commit range: '%s'"), argv[1]);
- strbuf_addstr(&range2, argv[1]);
- } else if (argc == 3) {
strbuf_addf(&range1, "%s..%s", argv[0], argv[1]);
strbuf_addf(&range2, "%s..%s", argv[0], argv[2]);
- } else if (argc == 1) {
- const char *b = strstr(argv[0], "..."), *a = argv[0];
+
+ strvec_pushv(&other_arg, argv +
+ (dash_dash < 0 ? 3 : dash_dash));
+ } else if (dash_dash == 2 ||
+ (dash_dash < 0 && argc > 1 &&
+ is_range_diff_range(argv[0]) &&
+ is_range_diff_range(argv[1]))) {
+ if (dash_dash < 0)
+ ; /* already validated arguments */
+ else if (!is_range_diff_range(argv[0]))
+ usage_msg_optf(_("not a commit range: '%s'"),
+ builtin_range_diff_usage, options,
+ argv[0]);
+ else if (!is_range_diff_range(argv[1]))
+ usage_msg_optf(_("not a commit range: '%s'"),
+ builtin_range_diff_usage, options,
+ argv[1]);
+
+ strbuf_addstr(&range1, argv[0]);
+ strbuf_addstr(&range2, argv[1]);
+
+ strvec_pushv(&other_arg, argv +
+ (dash_dash < 0 ? 2 : dash_dash));
+ } else if (dash_dash == 1 ||
+ (dash_dash < 0 && argc > 0 &&
+ (three_dots = strstr(argv[0], "...")))) {
+ const char *a, *b;
int a_len;
- if (!b) {
- error(_("single arg format must be symmetric range"));
- usage_with_options(builtin_range_diff_usage, options);
- }
+ if (dash_dash < 0)
+ ; /* already validated arguments */
+ else if (!(three_dots = strstr(argv[0], "...")))
+ usage_msg_optf(_("not a symmetric range: '%s'"),
+ builtin_range_diff_usage, options,
+ argv[0]);
- a_len = (int)(b - a);
- if (!a_len) {
+ if (three_dots == argv[0]) {
a = "HEAD";
a_len = strlen(a);
+ } else {
+ a = argv[0];
+ a_len = (int)(three_dots - a);
}
- b += 3;
- if (!*b)
+
+ if (three_dots[3])
+ b = three_dots + 3;
+ else
b = "HEAD";
+
strbuf_addf(&range1, "%s..%.*s", b, a_len, a);
strbuf_addf(&range2, "%.*s..%s", a_len, a, b);
- } else {
- error(_("need two commit ranges"));
- usage_with_options(builtin_range_diff_usage, options);
- }
+
+ strvec_pushv(&other_arg, argv +
+ (dash_dash < 0 ? 1 : dash_dash));
+ } else
+ usage_msg_opt(_("need two commit ranges"),
+ builtin_range_diff_usage, options);
FREE_AND_NULL(options);
range_diff_opts.dual_color = simple_color < 1;
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 9f1f33e954..1fec702a04 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -4,19 +4,24 @@
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
#include "config.h"
+#include "gettext.h"
+#include "hex.h"
#include "lockfile.h"
#include "object.h"
+#include "object-name.h"
#include "tree.h"
#include "tree-walk.h"
#include "cache-tree.h"
#include "unpack-trees.h"
#include "dir.h"
-#include "builtin.h"
#include "parse-options.h"
+#include "repository.h"
#include "resolve-undo.h"
+#include "setup.h"
+#include "sparse-index.h"
#include "submodule.h"
#include "submodule-config.h"
@@ -38,7 +43,9 @@ static int list_tree(struct object_id *oid)
}
static const char * const read_tree_usage[] = {
- N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) [-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"),
+ N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>)\n"
+ " [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+ " (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"),
NULL
};
@@ -85,9 +92,9 @@ static int debug_merge(const struct cache_entry * const *stages,
{
int i;
- printf("* %d-way merge\n", o->merge_size);
+ printf("* %d-way merge\n", o->internal.merge_size);
debug_stage("index", stages[0], o);
- for (i = 1; i <= o->merge_size; i++) {
+ for (i = 1; i <= o->internal.merge_size; i++) {
char buf[24];
xsnprintf(buf, sizeof(buf), "ent#%d", i);
debug_stage(buf, stages[i], o);
@@ -95,12 +102,13 @@ static int debug_merge(const struct cache_entry * const *stages,
return 0;
}
-static int git_read_tree_config(const char *var, const char *value, void *cb)
+static int git_read_tree_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (!strcmp(var, "submodule.recurse"))
return git_default_submodule_config(var, value, cb);
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
}
int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
@@ -112,6 +120,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
int prefix_set = 0;
struct lock_file lock_file = LOCK_INIT;
const struct option read_tree_options[] = {
+ OPT__SUPER_PREFIX(&opts.super_prefix),
OPT_CALLBACK_F(0, "index-output", NULL, N_("file"),
N_("write resulting index to <file>"),
PARSE_OPT_NONEG, index_output_cb),
@@ -141,7 +150,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
OPT__DRY_RUN(&opts.dry_run, N_("don't update the index or the work tree")),
OPT_BOOL(0, "no-sparse-checkout", &opts.skip_sparse_checkout,
N_("skip applying sparse checkout filter")),
- OPT_BOOL(0, "debug-unpack", &opts.debug_unpack,
+ OPT_BOOL(0, "debug-unpack", &opts.internal.debug_unpack,
N_("debug unpack-trees")),
OPT_CALLBACK_F(0, "recurse-submodules", NULL,
"checkout", "control recursive updating of submodules",
@@ -174,7 +183,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
- hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
/*
* NEEDSWORK
@@ -186,16 +195,16 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
*/
if (opts.reset || opts.merge || opts.prefix) {
- if (read_cache_unmerged() && (opts.prefix || opts.merge))
+ if (repo_read_index_unmerged(the_repository) && (opts.prefix || opts.merge))
die(_("You need to resolve your current index first"));
stage = opts.merge = 1;
}
- resolve_undo_clear();
+ resolve_undo_clear_index(&the_index);
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
- if (get_oid(arg, &oid))
+ if (repo_get_oid(the_repository, arg, &oid))
die("Not a valid object name %s", arg);
if (list_tree(&oid) < 0)
die("failed to unpack tree object %s", arg);
@@ -230,7 +239,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
break;
case 2:
opts.fn = twoway_merge;
- opts.initial_checkout = is_cache_unborn();
+ opts.initial_checkout = is_index_unborn(&the_index);
break;
case 3:
default:
@@ -244,10 +253,14 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
opts.head_idx = 1;
}
- if (opts.debug_unpack)
+ if (opts.internal.debug_unpack)
opts.fn = debug_merge;
- cache_tree_free(&active_cache_tree);
+ /* If we're going to prime_cache_tree later, skip cache tree update */
+ if (nr_trees == 1 && !opts.prefix)
+ opts.skip_cache_tree_update = 1;
+
+ cache_tree_free(&the_index.cache_tree);
for (i = 0; i < nr_trees; i++) {
struct tree *tree = trees[i];
parse_tree(tree);
@@ -256,7 +269,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
if (unpack_trees(nr_trees, t, &opts))
return 128;
- if (opts.debug_unpack || opts.dry_run)
+ if (opts.internal.debug_unpack || opts.dry_run)
return 0; /* do not write the index out */
/*
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 7f3bffc0a2..50cb85751f 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -4,8 +4,12 @@
* Copyright (c) 2018 Pratik Karki
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "run-command.h"
#include "exec-cmd.h"
#include "strvec.h"
@@ -17,7 +21,10 @@
#include "cache-tree.h"
#include "unpack-trees.h"
#include "lockfile.h"
+#include "object-file.h"
+#include "object-name.h"
#include "parse-options.h"
+#include "path.h"
#include "commit.h"
#include "diff.h"
#include "wt-status.h"
@@ -28,10 +35,9 @@
#include "sequencer.h"
#include "rebase-interactive.h"
#include "reset.h"
+#include "trace2.h"
#include "hook.h"
-#define DEFAULT_REFLOG_ACTION "rebase"
-
static char const * const builtin_rebase_usage[] = {
N_("git rebase [-i] [options] [--exec <cmd>] "
"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
@@ -59,6 +65,26 @@ enum empty_type {
EMPTY_ASK
};
+enum action {
+ ACTION_NONE = 0,
+ ACTION_CONTINUE,
+ ACTION_SKIP,
+ ACTION_ABORT,
+ ACTION_QUIT,
+ ACTION_EDIT_TODO,
+ ACTION_SHOW_CURRENT_PATCH
+};
+
+static const char *action_names[] = {
+ "undefined",
+ "continue",
+ "skip",
+ "abort",
+ "quit",
+ "edit_todo",
+ "show_current_patch"
+};
+
struct rebase_options {
enum rebase_type type;
enum empty_type empty;
@@ -68,7 +94,7 @@ struct rebase_options {
const char *upstream_name;
const char *upstream_arg;
char *head_name;
- struct object_id orig_head;
+ struct commit *orig_head;
struct commit *onto;
const char *onto_name;
const char *revisions;
@@ -85,7 +111,8 @@ struct rebase_options {
REBASE_INTERACTIVE_EXPLICIT = 1<<4,
} flags;
struct strvec git_am_opts;
- const char *action;
+ enum action action;
+ char *reflog_action;
int signoff;
int allow_rerere_autoupdate;
int keep_empty;
@@ -94,14 +121,19 @@ struct rebase_options {
int autostash;
int committer_date_is_author_date;
int ignore_date;
- char *cmd;
+ struct string_list exec;
int allow_empty_message;
int rebase_merges, rebase_cousins;
- char *strategy, *strategy_opts;
+ char *strategy;
+ struct string_list strategy_opts;
struct strbuf git_format_patch_opt;
int reschedule_failed_exec;
int reapply_cherry_picks;
int fork_point;
+ int update_refs;
+ int config_autosquash;
+ int config_rebase_merges;
+ int config_update_refs;
};
#define REBASE_OPTIONS_INIT { \
@@ -111,8 +143,18 @@ struct rebase_options {
.default_backend = "merge", \
.flags = REBASE_NO_QUIET, \
.git_am_opts = STRVEC_INIT, \
+ .exec = STRING_LIST_INIT_NODUP, \
.git_format_patch_opt = STRBUF_INIT, \
.fork_point = -1, \
+ .reapply_cherry_picks = -1, \
+ .allow_empty_message = 1, \
+ .autosquash = -1, \
+ .config_autosquash = -1, \
+ .rebase_merges = -1, \
+ .config_rebase_merges = -1, \
+ .update_refs = -1, \
+ .config_update_refs = -1, \
+ .strategy_opts = STRING_LIST_INIT_NODUP,\
}
static struct replay_opts get_replay_opts(const struct rebase_options *opts)
@@ -138,6 +180,7 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
opts->committer_date_is_author_date;
replay.ignore_date = opts->ignore_date;
replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
+ replay.reflog_action = xstrdup(opts->reflog_action);
if (opts->strategy)
replay.strategy = xstrdup_or_null(opts->strategy);
else if (!replay.strategy && replay.default_strategy) {
@@ -145,8 +188,8 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
replay.default_strategy = NULL;
}
- if (opts->strategy_opts)
- parse_strategy_opts(&replay, opts->strategy_opts);
+ for (size_t i = 0; i < opts->strategy_opts.nr; i++)
+ strvec_push(&replay.xopts, opts->strategy_opts.items[i].string);
if (opts->squash_onto) {
oidcpy(&replay.squash_onto, opts->squash_onto);
@@ -156,24 +199,6 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
return replay;
}
-enum action {
- ACTION_NONE = 0,
- ACTION_CONTINUE,
- ACTION_SKIP,
- ACTION_ABORT,
- ACTION_QUIT,
- ACTION_EDIT_TODO,
- ACTION_SHOW_CURRENT_PATCH
-};
-
-static const char *action_names[] = { "undefined",
- "continue",
- "skip",
- "abort",
- "quit",
- "edit_todo",
- "show_current_patch" };
-
static int edit_todo_file(unsigned flags)
{
const char *todo_file = rebase_path_todo();
@@ -184,7 +209,7 @@ static int edit_todo_file(unsigned flags)
if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
return error_errno(_("could not read '%s'."), todo_file);
- strbuf_stripspace(&todo_list.buf, 1);
+ strbuf_stripspace(&todo_list.buf, comment_line_char);
res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
@@ -206,13 +231,15 @@ static int get_revision_ranges(struct commit *upstream, struct commit *onto,
*revisions = xstrfmt("%s...%s", oid_to_hex(&base_rev->object.oid),
oid_to_hex(orig_head));
- shorthead = find_unique_abbrev(orig_head, DEFAULT_ABBREV);
+ shorthead = repo_find_unique_abbrev(the_repository, orig_head,
+ DEFAULT_ABBREV);
if (upstream) {
const char *shortrev;
- shortrev = find_unique_abbrev(&base_rev->object.oid,
- DEFAULT_ABBREV);
+ shortrev = repo_find_unique_abbrev(the_repository,
+ &base_rev->object.oid,
+ DEFAULT_ABBREV);
*shortrevisions = xstrfmt("%s..%s", shortrev, shorthead);
} else
@@ -240,38 +267,22 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name,
return write_basic_state(opts, head_name, onto, orig_head);
}
-static void split_exec_commands(const char *cmd, struct string_list *commands)
-{
- if (cmd && *cmd) {
- string_list_split(commands, cmd, '\n', -1);
-
- /* rebase.c adds a new line to cmd after every command,
- * so here the last command is always empty */
- string_list_remove_empty_items(commands, 0);
- }
-}
-
static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
{
- int ret;
+ int ret = -1;
char *revisions = NULL, *shortrevisions = NULL;
struct strvec make_script_args = STRVEC_INIT;
struct todo_list todo_list = TODO_LIST_INIT;
struct replay_opts replay = get_replay_opts(opts);
- struct string_list commands = STRING_LIST_INIT_DUP;
- if (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head,
+ if (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head->object.oid,
&revisions, &shortrevisions))
- return -1;
+ goto cleanup;
if (init_basic_state(&replay,
opts->head_name ? opts->head_name : "detached HEAD",
- opts->onto, &opts->orig_head)) {
- free(revisions);
- free(shortrevisions);
-
- return -1;
- }
+ opts->onto, &opts->orig_head->object.oid))
+ goto cleanup;
if (!opts->upstream && opts->squash_onto)
write_file(path_squash_onto(), "%s\n",
@@ -289,19 +300,19 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
if (ret)
error(_("could not generate todo list"));
else {
- discard_cache();
+ discard_index(&the_index);
if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
&todo_list))
BUG("unusable todo list");
- split_exec_commands(opts->cmd, &commands);
ret = complete_action(the_repository, &replay, flags,
shortrevisions, opts->onto_name, opts->onto,
- &opts->orig_head, &commands, opts->autosquash,
- &todo_list);
+ &opts->orig_head->object.oid, &opts->exec,
+ opts->autosquash, opts->update_refs, &todo_list);
}
- string_list_clear(&commands, 0);
+cleanup:
+ replay_opts_release(&replay);
free(revisions);
free(shortrevisions);
todo_list_release(&todo_list);
@@ -310,8 +321,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
return ret;
}
-static int run_sequencer_rebase(struct rebase_options *opts,
- enum action command)
+static int run_sequencer_rebase(struct rebase_options *opts)
{
unsigned flags = 0;
int abbreviate_commands = 0, ret = 0;
@@ -326,7 +336,7 @@ static int run_sequencer_rebase(struct rebase_options *opts,
flags |= opts->reapply_cherry_picks ? TODO_LIST_REAPPLY_CHERRY_PICKS : 0;
flags |= opts->flags & REBASE_NO_QUIET ? TODO_LIST_WARN_SKIPPED_CHERRY_PICKS : 0;
- switch (command) {
+ switch (opts->action) {
case ACTION_NONE: {
if (!opts->onto && !opts->upstream)
die(_("a base commit must be provided with --upstream or --onto"));
@@ -344,6 +354,7 @@ static int run_sequencer_rebase(struct rebase_options *opts,
struct replay_opts replay_opts = get_replay_opts(opts);
ret = sequencer_continue(the_repository, &replay_opts);
+ replay_opts_release(&replay_opts);
break;
}
case ACTION_EDIT_TODO:
@@ -359,7 +370,7 @@ static int run_sequencer_rebase(struct rebase_options *opts,
break;
}
default:
- BUG("invalid command '%d'", command);
+ BUG("invalid command '%d'", opts->action);
}
return ret;
@@ -429,9 +440,9 @@ static int read_basic_state(struct rebase_options *opts)
opts->head_name = starts_with(head_name.buf, "refs/") ?
xstrdup(head_name.buf) : NULL;
strbuf_release(&head_name);
- if (get_oid(buf.buf, &oid))
- return error(_("could not get 'onto': '%s'"), buf.buf);
- opts->onto = lookup_commit_or_die(&oid, buf.buf);
+ if (get_oid_hex(buf.buf, &oid) ||
+ !(opts->onto = lookup_commit_object(the_repository, &oid)))
+ return error(_("invalid onto: '%s'"), buf.buf);
/*
* We always write to orig-head, but interactive rebase used to write to
@@ -446,7 +457,8 @@ static int read_basic_state(struct rebase_options *opts)
} else if (!read_oneliner(&buf, state_dir_path("head", opts),
READ_ONELINER_WARN_MISSING))
return -1;
- if (get_oid(buf.buf, &opts->orig_head))
+ if (get_oid_hex(buf.buf, &oid) ||
+ !(opts->orig_head = lookup_commit_object(the_repository, &oid)))
return error(_("invalid orig-head: '%s'"), buf.buf);
if (file_exists(state_dir_path("quiet", opts)))
@@ -485,24 +497,6 @@ static int read_basic_state(struct rebase_options *opts)
opts->gpg_sign_opt = xstrdup(buf.buf);
}
- if (file_exists(state_dir_path("strategy", opts))) {
- strbuf_reset(&buf);
- if (!read_oneliner(&buf, state_dir_path("strategy", opts),
- READ_ONELINER_WARN_MISSING))
- return -1;
- free(opts->strategy);
- opts->strategy = xstrdup(buf.buf);
- }
-
- if (file_exists(state_dir_path("strategy_opts", opts))) {
- strbuf_reset(&buf);
- if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
- READ_ONELINER_WARN_MISSING))
- return -1;
- free(opts->strategy_opts);
- opts->strategy_opts = xstrdup(buf.buf);
- }
-
strbuf_release(&buf);
return 0;
@@ -515,17 +509,11 @@ static int rebase_write_basic_state(struct rebase_options *opts)
write_file(state_dir_path("onto", opts), "%s",
opts->onto ? oid_to_hex(&opts->onto->object.oid) : "");
write_file(state_dir_path("orig-head", opts), "%s",
- oid_to_hex(&opts->orig_head));
+ oid_to_hex(&opts->orig_head->object.oid));
if (!(opts->flags & REBASE_NO_QUIET))
write_file(state_dir_path("quiet", opts), "%s", "");
if (opts->flags & REBASE_VERBOSE)
write_file(state_dir_path("verbose", opts), "%s", "");
- if (opts->strategy)
- write_file(state_dir_path("strategy", opts), "%s",
- opts->strategy);
- if (opts->strategy_opts)
- write_file(state_dir_path("strategy_opts", opts), "%s",
- opts->strategy_opts);
if (opts->allow_rerere_autoupdate > 0)
write_file(state_dir_path("allow_rerere_autoupdate", opts),
"-%s-rerere-autoupdate",
@@ -558,6 +546,7 @@ static int finish_rebase(struct rebase_options *opts)
replay.action = REPLAY_INTERACTIVE_REBASE;
ret = sequencer_remove_state(&replay);
+ replay_opts_release(&replay);
} else {
strbuf_addstr(&dir, opts->state_dir);
if (remove_dir_recursively(&dir, 0))
@@ -581,10 +570,11 @@ static int move_to_original_branch(struct rebase_options *opts)
if (!opts->onto)
BUG("move_to_original_branch without onto");
- strbuf_addf(&branch_reflog, "rebase finished: %s onto %s",
+ strbuf_addf(&branch_reflog, "%s (finish): %s onto %s",
+ opts->reflog_action,
opts->head_name, oid_to_hex(&opts->onto->object.oid));
- strbuf_addf(&head_reflog, "rebase finished: returning to %s",
- opts->head_name);
+ strbuf_addf(&head_reflog, "%s (finish): returning to %s",
+ opts->reflog_action, opts->head_name);
ropts.branch = opts->head_name;
ropts.flags = RESET_HEAD_REFS_ONLY;
ropts.branch_msg = branch_reflog.buf;
@@ -613,8 +603,9 @@ static int run_am(struct rebase_options *opts)
am.git_cmd = 1;
strvec_push(&am.args, "am");
-
- if (opts->action && !strcmp("continue", opts->action)) {
+ strvec_pushf(&am.env, GIT_REFLOG_ACTION_ENVIRONMENT "=%s (pick)",
+ opts->reflog_action);
+ if (opts->action == ACTION_CONTINUE) {
strvec_push(&am.args, "--resolved");
strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
if (opts->gpg_sign_opt)
@@ -625,7 +616,7 @@ static int run_am(struct rebase_options *opts)
return move_to_original_branch(opts);
}
- if (opts->action && !strcmp("skip", opts->action)) {
+ if (opts->action == ACTION_SKIP) {
strvec_push(&am.args, "--skip");
strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
status = run_command(&am);
@@ -634,7 +625,7 @@ static int run_am(struct rebase_options *opts)
return move_to_original_branch(opts);
}
- if (opts->action && !strcmp("show-current-patch", opts->action)) {
+ if (opts->action == ACTION_SHOW_CURRENT_PATCH) {
strvec_push(&am.args, "--show-current-patch");
return run_command(&am);
}
@@ -644,7 +635,7 @@ static int run_am(struct rebase_options *opts)
/* this is now equivalent to !opts->upstream */
&opts->onto->object.oid :
&opts->upstream->object.oid),
- oid_to_hex(&opts->orig_head));
+ oid_to_hex(&opts->orig_head->object.oid));
rebased_patches = xstrdup(git_path("rebased-patches"));
format_patch.out = open(rebased_patches,
@@ -660,7 +651,7 @@ static int run_am(struct rebase_options *opts)
format_patch.git_cmd = 1;
strvec_pushl(&format_patch.args, "format-patch", "-k", "--stdout",
"--full-index", "--cherry-pick", "--right-only",
- "--src-prefix=a/", "--dst-prefix=b/", "--no-renames",
+ "--default-prefix", "--no-renames",
"--no-cover-letter", "--pretty=mboxrd", "--topo-order",
"--no-base", NULL);
if (opts->git_format_patch_opt.len)
@@ -678,9 +669,9 @@ static int run_am(struct rebase_options *opts)
free(rebased_patches);
strvec_clear(&am.args);
- ropts.oid = &opts->orig_head;
+ ropts.oid = &opts->orig_head->object.oid;
ropts.branch = opts->head_name;
- ropts.default_reflog_action = DEFAULT_REFLOG_ACTION;
+ ropts.default_reflog_action = opts->reflog_action;
reset_head(the_repository, &ropts);
error(_("\ngit encountered an error while preparing the "
"patches to replay\n"
@@ -727,7 +718,7 @@ static int run_am(struct rebase_options *opts)
return status;
}
-static int run_specific_rebase(struct rebase_options *opts, enum action action)
+static int run_specific_rebase(struct rebase_options *opts)
{
int status;
@@ -745,7 +736,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
opts->gpg_sign_opt = tmp;
}
- status = run_sequencer_rebase(opts, action);
+ status = run_sequencer_rebase(opts);
} else if (opts->type == REBASE_APPLY)
status = run_am(opts);
else
@@ -771,7 +762,18 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
return status ? -1 : 0;
}
-static int rebase_config(const char *var, const char *value, void *data)
+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
+{
+ if (!strcmp("no-rebase-cousins", value))
+ options->rebase_cousins = 0;
+ else if (!strcmp("rebase-cousins", value))
+ options->rebase_cousins = 1;
+ else
+ die(_("Unknown rebase-merges mode: %s"), value);
+}
+
+static int rebase_config(const char *var, const char *value,
+ const struct config_context *ctx, void *data)
{
struct rebase_options *opts = data;
@@ -784,7 +786,7 @@ static int rebase_config(const char *var, const char *value, void *data)
}
if (!strcmp(var, "rebase.autosquash")) {
- opts->autosquash = git_config_bool(var, value);
+ opts->config_autosquash = git_config_bool(var, value);
return 0;
}
@@ -800,6 +802,22 @@ static int rebase_config(const char *var, const char *value, void *data)
return 0;
}
+ if (!strcmp(var, "rebase.rebasemerges")) {
+ opts->config_rebase_merges = git_parse_maybe_bool(value);
+ if (opts->config_rebase_merges < 0) {
+ opts->config_rebase_merges = 1;
+ parse_rebase_merges_value(opts, value);
+ } else {
+ opts->rebase_cousins = 0;
+ }
+ return 0;
+ }
+
+ if (!strcmp(var, "rebase.updaterefs")) {
+ opts->config_update_refs = git_config_bool(var, value);
+ return 0;
+ }
+
if (!strcmp(var, "rebase.reschedulefailedexec")) {
opts->reschedule_failed_exec = git_config_bool(var, value);
return 0;
@@ -814,7 +832,7 @@ static int rebase_config(const char *var, const char *value, void *data)
return git_config_string(&opts->default_backend, var, value);
}
- return git_default_config(var, value, data);
+ return git_default_config(var, value, ctx, data);
}
static int checkout_up_to_date(struct rebase_options *options)
@@ -824,9 +842,8 @@ static int checkout_up_to_date(struct rebase_options *options)
int ret = 0;
strbuf_addf(&buf, "%s: checkout %s",
- getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
- options->switch_to);
- ropts.oid = &options->orig_head;
+ options->reflog_action, options->switch_to);
+ ropts.oid = &options->orig_head->object.oid;
ropts.branch = options->head_name;
ropts.flags = RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
if (!ropts.branch)
@@ -847,7 +864,7 @@ static int checkout_up_to_date(struct rebase_options *options)
static int is_linear_history(struct commit *from, struct commit *to)
{
while (to && to != from) {
- parse_commit(to);
+ repo_parse_commit(the_repository, to);
if (!to->parents)
return 1;
if (to->parents->next)
@@ -859,33 +876,24 @@ static int is_linear_history(struct commit *from, struct commit *to)
static int can_fast_forward(struct commit *onto, struct commit *upstream,
struct commit *restrict_revision,
- struct object_id *head_oid, struct object_id *merge_base)
+ struct commit *head, struct object_id *branch_base)
{
- struct commit *head = lookup_commit(the_repository, head_oid);
struct commit_list *merge_bases = NULL;
int res = 0;
- if (!head)
- goto done;
+ if (is_null_oid(branch_base))
+ goto done; /* fill_branch_base() found multiple merge bases */
- merge_bases = get_merge_bases(onto, head);
- if (!merge_bases || merge_bases->next) {
- oidcpy(merge_base, null_oid());
- goto done;
- }
-
- oidcpy(merge_base, &merge_bases->item->object.oid);
- if (!oideq(merge_base, &onto->object.oid))
+ if (!oideq(branch_base, &onto->object.oid))
goto done;
- if (restrict_revision && !oideq(&restrict_revision->object.oid, merge_base))
+ if (restrict_revision && !oideq(&restrict_revision->object.oid, branch_base))
goto done;
if (!upstream)
goto done;
- free_commit_list(merge_bases);
- merge_bases = get_merge_bases(upstream, head);
+ merge_bases = repo_get_merge_bases(the_repository, upstream, head);
if (!merge_bases || merge_bases->next)
goto done;
@@ -899,6 +907,21 @@ done:
return res && is_linear_history(onto, head);
}
+static void fill_branch_base(struct rebase_options *options,
+ struct object_id *branch_base)
+{
+ struct commit_list *merge_bases = NULL;
+
+ merge_bases = repo_get_merge_bases(the_repository, options->onto,
+ options->orig_head);
+ if (!merge_bases || merge_bases->next)
+ oidcpy(branch_base, null_oid());
+ else
+ oidcpy(branch_base, &merge_bases->item->object.oid);
+
+ free_commit_list(merge_bases);
+}
+
static int parse_opt_am(const struct option *opt, const char *arg, int unset)
{
struct rebase_options *opts = opt->value;
@@ -906,6 +929,9 @@ static int parse_opt_am(const struct option *opt, const char *arg, int unset)
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
+ if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_APPLY)
+ die(_("apply options and merge options cannot be used together"));
+
opts->type = REBASE_APPLY;
return 0;
@@ -919,8 +945,10 @@ static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
- if (!is_merge(opts))
- opts->type = REBASE_MERGE;
+ if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE)
+ die(_("apply options and merge options cannot be used together"));
+
+ opts->type = REBASE_MERGE;
return 0;
}
@@ -934,6 +962,9 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
+ if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE)
+ die(_("apply options and merge options cannot be used together"));
+
opts->type = REBASE_MERGE;
opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
@@ -963,6 +994,28 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
return 0;
}
+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
+{
+ struct rebase_options *options = opt->value;
+
+ options->rebase_merges = !unset;
+ options->rebase_cousins = 0;
+
+ if (arg) {
+ if (!*arg) {
+ warning(_("--rebase-merges with an empty string "
+ "argument is deprecated and will stop "
+ "working in a future version of Git. Use "
+ "--rebase-merges without an argument "
+ "instead, which does the same thing."));
+ return 0;
+ }
+ parse_rebase_merges_value(options, arg);
+ }
+
+ return 0;
+}
+
static void NORETURN error_on_missing_default_upstream(void)
{
struct branch *current_branch = branch_get(NULL);
@@ -993,23 +1046,6 @@ static void NORETURN error_on_missing_default_upstream(void)
exit(1);
}
-static void set_reflog_action(struct rebase_options *options)
-{
- const char *env;
- struct strbuf buf = STRBUF_INIT;
-
- if (!is_merge(options))
- return;
-
- env = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
- if (env && strcmp("rebase", env))
- return; /* only override it if it is "rebase" */
-
- strbuf_addf(&buf, "rebase (%s)", options->action);
- setenv(GIT_REFLOG_ACTION_ENVIRONMENT, buf.buf, 1);
- strbuf_release(&buf);
-}
-
static int check_exec_cmd(const char *cmd)
{
if (strchr(cmd, '\n'))
@@ -1032,15 +1068,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct strbuf msg = STRBUF_INIT;
struct strbuf revisions = STRBUF_INIT;
struct strbuf buf = STRBUF_INIT;
- struct object_id merge_base;
+ struct object_id branch_base;
int ignore_whitespace = 0;
- enum action action = ACTION_NONE;
const char *gpg_sign = NULL;
- struct string_list exec = STRING_LIST_INIT_NODUP;
- const char *rebase_merges = NULL;
- struct string_list strategy_options = STRING_LIST_INIT_NODUP;
struct object_id squash_onto;
char *squash_onto_name = NULL;
+ char *keep_base_onto_name = NULL;
int reschedule_failed_exec = -1;
int allow_preemptive_ff = 1;
int preserve_merges_selected = 0;
@@ -1083,18 +1116,18 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
OPT_BIT(0, "no-ff", &options.flags,
N_("cherry-pick all commits, even if unchanged"),
REBASE_FORCE),
- OPT_CMDMODE(0, "continue", &action, N_("continue"),
+ OPT_CMDMODE(0, "continue", &options.action, N_("continue"),
ACTION_CONTINUE),
- OPT_CMDMODE(0, "skip", &action,
+ OPT_CMDMODE(0, "skip", &options.action,
N_("skip current patch and continue"), ACTION_SKIP),
- OPT_CMDMODE(0, "abort", &action,
+ OPT_CMDMODE(0, "abort", &options.action,
N_("abort and check out the original branch"),
ACTION_ABORT),
- OPT_CMDMODE(0, "quit", &action,
+ OPT_CMDMODE(0, "quit", &options.action,
N_("abort but keep HEAD where it is"), ACTION_QUIT),
- OPT_CMDMODE(0, "edit-todo", &action, N_("edit the todo list "
+ OPT_CMDMODE(0, "edit-todo", &options.action, N_("edit the todo list "
"during an interactive rebase"), ACTION_EDIT_TODO),
- OPT_CMDMODE(0, "show-current-patch", &action,
+ OPT_CMDMODE(0, "show-current-patch", &options.action,
N_("show the patch file being applied or merged"),
ACTION_SHOW_CURRENT_PATCH),
OPT_CALLBACK_F(0, "apply", &options, NULL,
@@ -1110,8 +1143,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
parse_opt_interactive),
OPT_SET_INT_F('p', "preserve-merges", &preserve_merges_selected,
- N_("(DEPRECATED) try to recreate merges instead of "
- "ignoring them"),
+ N_("(REMOVED) was: try to recreate merges "
+ "instead of ignoring them"),
1, PARSE_OPT_HIDDEN),
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}",
@@ -1124,26 +1157,28 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "autosquash", &options.autosquash,
N_("move commits that begin with "
"squash!/fixup! under -i")),
+ OPT_BOOL(0, "update-refs", &options.update_refs,
+ N_("update branches that point to commits "
+ "that are being rebased")),
{ OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
N_("GPG-sign commits"),
PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_AUTOSTASH(&options.autostash),
- OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
+ OPT_STRING_LIST('x', "exec", &options.exec, N_("exec"),
N_("add exec lines after each commit of the "
"editable list")),
OPT_BOOL_F(0, "allow-empty-message",
&options.allow_empty_message,
N_("allow rebasing commits with empty messages"),
PARSE_OPT_HIDDEN),
- {OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
- N_("mode"),
+ OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
N_("try to rebase merges instead of skipping them"),
- PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+ PARSE_OPT_OPTARG, parse_opt_rebase_merges),
OPT_BOOL(0, "fork-point", &options.fork_point,
N_("use 'merge-base --fork-point' to refine upstream")),
OPT_STRING('s', "strategy", &options.strategy,
N_("strategy"), N_("use the given merge strategy")),
- OPT_STRING_LIST('X', "strategy-option", &strategy_options,
+ OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts,
N_("option"),
N_("pass the argument through to the merge "
"strategy")),
@@ -1165,7 +1200,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
- options.allow_empty_message = 1;
git_config(rebase_config, &options);
/* options.gpg_sign_opt will be either "-S" or NULL */
gpg_sign = options.gpg_sign_opt ? "" : NULL;
@@ -1182,16 +1216,16 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
} else if (is_directory(merge_dir())) {
strbuf_reset(&buf);
strbuf_addf(&buf, "%s/rewritten", merge_dir());
- if (is_directory(buf.buf)) {
- die("`rebase -p` is no longer supported");
+ if (!(options.action == ACTION_ABORT) && is_directory(buf.buf)) {
+ die(_("`rebase --preserve-merges` (-p) is no longer supported.\n"
+ "Use `git rebase --abort` to terminate current rebase.\n"
+ "Or downgrade to v2.33, or earlier, to complete the rebase."));
} else {
strbuf_reset(&buf);
strbuf_addf(&buf, "%s/interactive", merge_dir());
- if(file_exists(buf.buf)) {
- options.type = REBASE_MERGE;
+ options.type = REBASE_MERGE;
+ if (file_exists(buf.buf))
options.flags |= REBASE_INTERACTIVE_EXPLICIT;
- } else
- options.type = REBASE_MERGE;
}
options.state_dir = merge_dir();
}
@@ -1205,9 +1239,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
builtin_rebase_usage, 0);
if (preserve_merges_selected)
- die(_("--preserve-merges was replaced by --rebase-merges"));
+ die(_("--preserve-merges was replaced by --rebase-merges\n"
+ "Note: Your `pull.rebase` configuration may also be set to 'preserve',\n"
+ "which is no longer supported; use 'merges' instead"));
- if (action != ACTION_NONE && total_argc != 2) {
+ if (options.action != ACTION_NONE && total_argc != 2) {
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
}
@@ -1221,42 +1257,47 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--onto");
if (options.root)
die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--root");
+ /*
+ * --keep-base defaults to --no-fork-point to keep the
+ * base the same.
+ */
+ if (options.fork_point < 0)
+ options.fork_point = 0;
}
-
if (options.root && options.fork_point > 0)
die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");
- if (action != ACTION_NONE && !in_progress)
+ if (options.action != ACTION_NONE && !in_progress)
die(_("No rebase in progress?"));
- setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
- if (action == ACTION_EDIT_TODO && !is_merge(&options))
+ if (options.action == ACTION_EDIT_TODO && !is_merge(&options))
die(_("The --edit-todo action can only be used during "
"interactive rebase."));
if (trace2_is_enabled()) {
if (is_merge(&options))
trace2_cmd_mode("interactive");
- else if (exec.nr)
+ else if (options.exec.nr)
trace2_cmd_mode("interactive-exec");
else
- trace2_cmd_mode(action_names[action]);
+ trace2_cmd_mode(action_names[options.action]);
}
- switch (action) {
+ options.reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
+ options.reflog_action =
+ xstrdup(options.reflog_action ? options.reflog_action : "rebase");
+
+ switch (options.action) {
case ACTION_CONTINUE: {
struct object_id head;
struct lock_file lock_file = LOCK_INIT;
int fd;
- options.action = "continue";
- set_reflog_action(&options);
-
/* Sanity check */
- if (get_oid("HEAD", &head))
+ if (repo_get_oid(the_repository, "HEAD", &head))
die(_("Cannot read HEAD"));
- fd = hold_locked_index(&lock_file, 0);
+ fd = repo_hold_locked_index(the_repository, &lock_file, 0);
if (repo_read_index(the_repository) < 0)
die(_("could not read index"));
refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL,
@@ -1277,9 +1318,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
case ACTION_SKIP: {
struct string_list merge_rr = STRING_LIST_INIT_DUP;
- options.action = "skip";
- set_reflog_action(&options);
-
rerere_clear(the_repository, &merge_rr);
string_list_clear(&merge_rr, 1);
ropts.flags = RESET_HEAD_HARD;
@@ -1292,21 +1330,26 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
}
case ACTION_ABORT: {
struct string_list merge_rr = STRING_LIST_INIT_DUP;
- options.action = "abort";
- set_reflog_action(&options);
+ struct strbuf head_msg = STRBUF_INIT;
rerere_clear(the_repository, &merge_rr);
string_list_clear(&merge_rr, 1);
if (read_basic_state(&options))
exit(1);
- ropts.oid = &options.orig_head;
+
+ strbuf_addf(&head_msg, "%s (abort): returning to %s",
+ options.reflog_action,
+ options.head_name ? options.head_name
+ : oid_to_hex(&options.orig_head->object.oid));
+ ropts.oid = &options.orig_head->object.oid;
+ ropts.head_msg = head_msg.buf;
ropts.branch = options.head_name;
ropts.flags = RESET_HEAD_HARD;
- ropts.default_reflog_action = DEFAULT_REFLOG_ACTION;
if (reset_head(the_repository, &ropts) < 0)
die(_("could not move back to %s"),
- oid_to_hex(&options.orig_head));
+ oid_to_hex(&options.orig_head->object.oid));
+ strbuf_release(&head_msg);
remove_branch_state(the_repository, 0);
ret = finish_rebase(&options);
goto cleanup;
@@ -1318,6 +1361,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
replay.action = REPLAY_INTERACTIVE_REBASE;
ret = sequencer_remove_state(&replay);
+ replay_opts_release(&replay);
} else {
strbuf_reset(&buf);
strbuf_addstr(&buf, options.state_dir);
@@ -1329,17 +1373,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
goto cleanup;
}
case ACTION_EDIT_TODO:
- options.action = "edit-todo";
options.dont_finish_rebase = 1;
goto run_rebase;
case ACTION_SHOW_CURRENT_PATCH:
- options.action = "show-current-patch";
options.dont_finish_rebase = 1;
goto run_rebase;
case ACTION_NONE:
break;
default:
- BUG("action: %d", action);
+ BUG("action: %d", options.action);
}
/* Make sure no rebase is in progress */
@@ -1363,9 +1405,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
}
if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
- (action != ACTION_NONE) ||
- (exec.nr > 0) ||
- options.autosquash) {
+ (options.action != ACTION_NONE) ||
+ (options.exec.nr > 0) ||
+ (options.autosquash == -1 && options.config_autosquash == 1) ||
+ options.autosquash == 1) {
allow_preemptive_ff = 0;
}
if (options.committer_date_is_author_date || options.ignore_date)
@@ -1388,8 +1431,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
}
}
- for (i = 0; i < exec.nr; i++)
- if (check_exec_cmd(exec.items[i].string))
+ for (i = 0; i < options.exec.nr; i++)
+ if (check_exec_cmd(options.exec.items[i].string))
exit(1);
if (!(options.flags & REBASE_NO_QUIET))
@@ -1398,34 +1441,34 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.empty != EMPTY_UNSPECIFIED)
imply_merge(&options, "--empty");
- if (options.reapply_cherry_picks)
- imply_merge(&options, "--reapply-cherry-picks");
+ if (options.reapply_cherry_picks < 0)
+ /*
+ * We default to --no-reapply-cherry-picks unless
+ * --keep-base is given; when --keep-base is given, we want
+ * to default to --reapply-cherry-picks.
+ */
+ options.reapply_cherry_picks = keep_base;
+ else if (!keep_base)
+ /*
+ * The apply backend always searches for and drops cherry
+ * picks. This is often not wanted with --keep-base, so
+ * --keep-base allows --reapply-cherry-picks to be
+ * simulated by altering the upstream such that
+ * cherry-picks cannot be detected and thus all commits are
+ * reapplied. Thus, --[no-]reapply-cherry-picks is
+ * supported when --keep-base is specified, but not when
+ * --keep-base is left out.
+ */
+ imply_merge(&options, options.reapply_cherry_picks ?
+ "--reapply-cherry-picks" :
+ "--no-reapply-cherry-picks");
if (gpg_sign)
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
- if (exec.nr) {
- int i;
-
+ if (options.exec.nr)
imply_merge(&options, "--exec");
- strbuf_reset(&buf);
- for (i = 0; i < exec.nr; i++)
- strbuf_addf(&buf, "exec %s\n", exec.items[i].string);
- options.cmd = xstrdup(buf.buf);
- }
-
- if (rebase_merges) {
- if (!*rebase_merges)
- ; /* default mode; do nothing */
- else if (!strcmp("rebase-cousins", rebase_merges))
- options.rebase_cousins = 1;
- else if (strcmp("no-rebase-cousins", rebase_merges))
- die(_("Unknown mode: %s"), rebase_merges);
- options.rebase_merges = 1;
- imply_merge(&options, "--rebase-merges");
- }
-
if (options.type == REBASE_APPLY) {
if (ignore_whitespace)
strvec_push(&options.git_am_opts,
@@ -1438,23 +1481,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
} else {
/* REBASE_MERGE */
if (ignore_whitespace) {
- string_list_append(&strategy_options,
+ string_list_append(&options.strategy_opts,
"ignore-space-change");
}
}
- if (strategy_options.nr) {
- int i;
-
- if (!options.strategy)
- options.strategy = "ort";
-
- strbuf_reset(&buf);
- for (i = 0; i < strategy_options.nr; i++)
- strbuf_addf(&buf, " --%s",
- strategy_options.items[i].string);
- options.strategy_opts = xstrdup(buf.buf);
- }
+ if (options.strategy_opts.nr && !options.strategy)
+ options.strategy = "ort";
if (options.strategy) {
options.strategy = xstrdup(options.strategy);
@@ -1487,15 +1520,36 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (strcmp(options.git_am_opts.v[i], "-q"))
break;
- if (i >= 0) {
+ if (i >= 0 || options.type == REBASE_APPLY) {
if (is_merge(&options))
die(_("apply options and merge options "
"cannot be used together"));
+ else if (options.autosquash == -1 && options.config_autosquash == 1)
+ die(_("apply options are incompatible with rebase.autoSquash. Consider adding --no-autosquash"));
+ else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
+ die(_("apply options are incompatible with rebase.rebaseMerges. Consider adding --no-rebase-merges"));
+ else if (options.update_refs == -1 && options.config_update_refs == 1)
+ die(_("apply options are incompatible with rebase.updateRefs. Consider adding --no-update-refs"));
else
options.type = REBASE_APPLY;
}
}
+ if (options.update_refs == 1)
+ imply_merge(&options, "--update-refs");
+ options.update_refs = (options.update_refs >= 0) ? options.update_refs :
+ ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
+
+ if (options.rebase_merges == 1)
+ imply_merge(&options, "--rebase-merges");
+ options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
+ ((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
+
+ if (options.autosquash == 1)
+ imply_merge(&options, "--autosquash");
+ options.autosquash = (options.autosquash >= 0) ? options.autosquash :
+ ((options.config_autosquash >= 0) ? options.config_autosquash : 0);
+
if (options.type == REBASE_UNSPECIFIED) {
if (!strcmp(options.default_backend, "merge"))
imply_merge(&options, "--merge");
@@ -1525,7 +1579,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.empty == EMPTY_UNSPECIFIED) {
if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
options.empty = EMPTY_ASK;
- else if (exec.nr > 0)
+ else if (options.exec.nr > 0)
options.empty = EMPTY_KEEP;
else
options.empty = EMPTY_DROP;
@@ -1592,25 +1646,27 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
*/
if (argc == 1) {
/* Is it "rebase other branchname" or "rebase other commit"? */
+ struct object_id branch_oid;
branch_name = argv[0];
options.switch_to = argv[0];
/* Is it a local branch? */
strbuf_reset(&buf);
strbuf_addf(&buf, "refs/heads/%s", branch_name);
- if (!read_ref(buf.buf, &options.orig_head)) {
+ if (!read_ref(buf.buf, &branch_oid)) {
die_if_checked_out(buf.buf, 1);
options.head_name = xstrdup(buf.buf);
+ options.orig_head =
+ lookup_commit_object(the_repository,
+ &branch_oid);
/* If not is it a valid ref (branch or commit)? */
} else {
- struct commit *commit =
+ options.orig_head =
lookup_commit_reference_by_name(branch_name);
- if (!commit)
- die(_("no such branch/commit '%s'"),
- branch_name);
- oidcpy(&options.orig_head, &commit->object.oid);
options.head_name = NULL;
}
+ if (!options.orig_head)
+ die(_("no such branch/commit '%s'"), branch_name);
} else if (argc == 0) {
/* Do not need to switch branches, we are already on it. */
options.head_name =
@@ -1627,8 +1683,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
FREE_AND_NULL(options.head_name);
branch_name = "HEAD";
}
- if (get_oid("HEAD", &options.orig_head))
- die(_("Could not resolve HEAD to a revision"));
+ options.orig_head = lookup_commit_reference_by_name("HEAD");
+ if (!options.orig_head)
+ die(_("Could not resolve HEAD to a commit"));
} else
BUG("unexpected number of arguments left to parse");
@@ -1638,11 +1695,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
strbuf_addstr(&buf, options.upstream_name);
strbuf_addstr(&buf, "...");
strbuf_addstr(&buf, branch_name);
- options.onto_name = xstrdup(buf.buf);
+ options.onto_name = keep_base_onto_name = xstrdup(buf.buf);
} else if (!options.onto_name)
options.onto_name = options.upstream_name;
if (strstr(options.onto_name, "...")) {
- if (get_oid_mb(options.onto_name, &merge_base) < 0) {
+ if (repo_get_oid_mb(the_repository, options.onto_name, &branch_base) < 0) {
if (keep_base)
die(_("'%s': need exactly one merge base with branch"),
options.upstream_name);
@@ -1650,7 +1707,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
die(_("'%s': need exactly one merge base"),
options.onto_name);
}
- options.onto = lookup_commit_or_die(&merge_base,
+ options.onto = lookup_commit_or_die(&branch_base,
options.onto_name);
} else {
options.onto =
@@ -1658,15 +1715,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (!options.onto)
die(_("Does not point to a valid commit '%s'"),
options.onto_name);
+ fill_branch_base(&options, &branch_base);
}
- if (options.fork_point > 0) {
- struct commit *head =
- lookup_commit_reference(the_repository,
- &options.orig_head);
+ if (keep_base && options.reapply_cherry_picks)
+ options.upstream = options.onto;
+
+ if (options.fork_point > 0)
options.restrict_revision =
- get_fork_point(options.upstream_name, head);
- }
+ get_fork_point(options.upstream_name, options.orig_head);
if (repo_read_index(the_repository) < 0)
die(_("could not read index"));
@@ -1691,13 +1748,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
* Check if we are already based on onto with linear history,
* in which case we could fast-forward without replacing the commits
* with new commits recreated by replaying their changes.
- *
- * Note that can_fast_forward() initializes merge_base, so we have to
- * call it before checking allow_preemptive_ff.
*/
- if (can_fast_forward(options.onto, options.upstream, options.restrict_revision,
- &options.orig_head, &merge_base) &&
- allow_preemptive_ff) {
+ if (allow_preemptive_ff &&
+ can_fast_forward(options.onto, options.upstream, options.restrict_revision,
+ options.orig_head, &branch_base)) {
int flag;
if (!(options.flags & REBASE_FORCE)) {
@@ -1738,25 +1792,25 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct diff_options opts;
if (options.flags & REBASE_VERBOSE) {
- if (is_null_oid(&merge_base))
+ if (is_null_oid(&branch_base))
printf(_("Changes to %s:\n"),
oid_to_hex(&options.onto->object.oid));
else
printf(_("Changes from %s to %s:\n"),
- oid_to_hex(&merge_base),
+ oid_to_hex(&branch_base),
oid_to_hex(&options.onto->object.oid));
}
/* We want color (if set), but no pager */
- diff_setup(&opts);
+ repo_diff_setup(the_repository, &opts);
opts.stat_width = -1; /* use full terminal width */
opts.stat_graph_width = -1; /* respect statGraphWidth config */
opts.output_format |=
DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
opts.detect_rename = DIFF_DETECT_RENAME;
diff_setup_done(&opts);
- diff_tree_oid(is_null_oid(&merge_base) ?
- the_hash_algo->empty_tree : &merge_base,
+ diff_tree_oid(is_null_oid(&branch_base) ?
+ the_hash_algo->empty_tree : &branch_base,
&options.onto->object.oid, "", &opts);
diffcore_std(&opts);
diff_flush(&opts);
@@ -1770,14 +1824,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
printf(_("First, rewinding head to replay your work on top of "
"it...\n"));
- strbuf_addf(&msg, "%s: checkout %s",
- getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
+ strbuf_addf(&msg, "%s (start): checkout %s",
+ options.reflog_action, options.onto_name);
ropts.oid = &options.onto->object.oid;
- ropts.orig_head = &options.orig_head,
+ ropts.orig_head = &options.orig_head->object.oid,
ropts.flags = RESET_HEAD_DETACH | RESET_ORIG_HEAD |
RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
ropts.head_msg = msg.buf;
- ropts.default_reflog_action = DEFAULT_REFLOG_ACTION;
+ ropts.default_reflog_action = options.reflog_action;
if (reset_head(the_repository, &ropts))
die(_("Could not detach HEAD"));
strbuf_release(&msg);
@@ -1786,19 +1840,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
* If the onto is a proper descendant of the tip of the branch, then
* we just fast-forwarded.
*/
- strbuf_reset(&msg);
- if (oideq(&merge_base, &options.orig_head)) {
+ if (oideq(&branch_base, &options.orig_head->object.oid)) {
printf(_("Fast-forwarded %s to %s.\n"),
branch_name, options.onto_name);
- strbuf_addf(&msg, "rebase finished: %s onto %s",
- options.head_name ? options.head_name : "detached HEAD",
- oid_to_hex(&options.onto->object.oid));
- memset(&ropts, 0, sizeof(ropts));
- ropts.branch = options.head_name;
- ropts.flags = RESET_HEAD_REFS_ONLY;
- ropts.head_msg = msg.buf;
- reset_head(the_repository, &ropts);
- strbuf_release(&msg);
+ move_to_original_branch(&options);
ret = finish_rebase(&options);
goto cleanup;
}
@@ -1808,21 +1853,25 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
(options.restrict_revision ?
oid_to_hex(&options.restrict_revision->object.oid) :
oid_to_hex(&options.upstream->object.oid)),
- oid_to_hex(&options.orig_head));
+ oid_to_hex(&options.orig_head->object.oid));
options.revisions = revisions.buf;
run_rebase:
- ret = run_specific_rebase(&options, action);
+ ret = run_specific_rebase(&options);
cleanup:
strbuf_release(&buf);
strbuf_release(&revisions);
+ free(options.reflog_action);
free(options.head_name);
+ strvec_clear(&options.git_am_opts);
free(options.gpg_sign_opt);
- free(options.cmd);
+ string_list_clear(&options.exec, 0);
free(options.strategy);
+ string_list_clear(&options.strategy_opts, 0);
strbuf_release(&options.git_format_patch_opt);
free(squash_onto_name);
+ free(keep_base_onto_name);
return !!ret;
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 9aabffa1af..fb8e1549d1 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1,6 +1,10 @@
#include "builtin.h"
+#include "abspath.h"
#include "repository.h"
#include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "lockfile.h"
#include "pack.h"
#include "refs.h"
@@ -25,11 +29,17 @@
#include "tmp-objdir.h"
#include "oidset.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "protocol.h"
#include "commit-reach.h"
+#include "server-info.h"
+#include "trace.h"
+#include "trace2.h"
#include "worktree.h"
#include "shallow.h"
+#include "parse-options.h"
static const char * const receive_pack_usage[] = {
N_("git receive-pack <git-dir>"),
@@ -80,6 +90,7 @@ static struct object_id push_cert_oid;
static struct signature_check sigcheck;
static const char *push_cert_nonce;
static const char *cert_nonce_seed;
+static struct strvec hidden_refs = STRVEC_INIT;
static const char *NONCE_UNSOLICITED = "UNSOLICITED";
static const char *NONCE_BAD = "BAD";
@@ -128,17 +139,14 @@ static enum deny_action parse_deny_action(const char *var, const char *value)
return DENY_IGNORE;
}
-static int receive_pack_config(const char *var, const char *value, void *cb)
+static int receive_pack_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
- int status = parse_hide_refs_config(var, value, "receive");
+ int status = parse_hide_refs_config(var, value, "receive", &hidden_refs);
if (status)
return status;
- status = git_gpg_config(var, value, NULL);
- if (status)
- return status;
-
if (strcmp(var, "receive.denydeletes") == 0) {
deny_deletes = git_config_bool(var, value);
return 0;
@@ -150,12 +158,12 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
}
if (strcmp(var, "receive.unpacklimit") == 0) {
- receive_unpack_limit = git_config_int(var, value);
+ receive_unpack_limit = git_config_int(var, value, ctx->kvi);
return 0;
}
if (strcmp(var, "transfer.unpacklimit") == 0) {
- transfer_unpack_limit = git_config_int(var, value);
+ transfer_unpack_limit = git_config_int(var, value, ctx->kvi);
return 0;
}
@@ -223,7 +231,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
return git_config_string(&cert_nonce_seed, var, value);
if (strcmp(var, "receive.certnonceslop") == 0) {
- nonce_stamp_slop_limit = git_config_ulong(var, value);
+ nonce_stamp_slop_limit = git_config_ulong(var, value, ctx->kvi);
return 0;
}
@@ -238,12 +246,12 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
}
if (strcmp(var, "receive.keepalive") == 0) {
- keepalive_in_sec = git_config_int(var, value);
+ keepalive_in_sec = git_config_int(var, value, ctx->kvi);
return 0;
}
if (strcmp(var, "receive.maxinputsize") == 0) {
- max_input_size = git_config_int64(var, value);
+ max_input_size = git_config_int64(var, value, ctx->kvi);
return 0;
}
@@ -259,7 +267,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
return 0;
}
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
}
static void show_ref(const char *path, const struct object_id *oid)
@@ -291,12 +299,12 @@ static void show_ref(const char *path, const struct object_id *oid)
}
static int show_ref_cb(const char *path_full, const struct object_id *oid,
- int flag, void *data)
+ int flag UNUSED, void *data)
{
struct oidset *seen = data;
const char *path = strip_namespace(path_full);
- if (ref_is_hidden(path, path_full))
+ if (ref_is_hidden(path, path_full, &hidden_refs))
return 0;
/*
@@ -330,7 +338,9 @@ static void write_head_info(void)
{
static struct oidset seen = OIDSET_INIT;
- for_each_ref(show_ref_cb, &seen);
+ refs_for_each_fullref_in(get_main_ref_store(the_repository), "",
+ hidden_refs_to_excludes(&hidden_refs),
+ show_ref_cb, &seen);
for_each_alternate_ref(show_one_alternate_ref, &seen);
oidset_clear(&seen);
if (!sent_capabilities)
@@ -465,7 +475,7 @@ static void rp_error(const char *err, ...)
va_end(params);
}
-static int copy_to_sideband(int in, int out, void *arg)
+static int copy_to_sideband(int in, int out UNUSED, void *arg UNUSED)
{
char data[128];
int keepalive_active = 0;
@@ -764,23 +774,23 @@ static void prepare_push_cert_sha1(struct child_process *proc)
nonce_status = check_nonce(push_cert.buf, bogs);
}
if (!is_null_oid(&push_cert_oid)) {
- strvec_pushf(&proc->env_array, "GIT_PUSH_CERT=%s",
+ strvec_pushf(&proc->env, "GIT_PUSH_CERT=%s",
oid_to_hex(&push_cert_oid));
- strvec_pushf(&proc->env_array, "GIT_PUSH_CERT_SIGNER=%s",
+ strvec_pushf(&proc->env, "GIT_PUSH_CERT_SIGNER=%s",
sigcheck.signer ? sigcheck.signer : "");
- strvec_pushf(&proc->env_array, "GIT_PUSH_CERT_KEY=%s",
+ strvec_pushf(&proc->env, "GIT_PUSH_CERT_KEY=%s",
sigcheck.key ? sigcheck.key : "");
- strvec_pushf(&proc->env_array, "GIT_PUSH_CERT_STATUS=%c",
+ strvec_pushf(&proc->env, "GIT_PUSH_CERT_STATUS=%c",
sigcheck.result);
if (push_cert_nonce) {
- strvec_pushf(&proc->env_array,
+ strvec_pushf(&proc->env,
"GIT_PUSH_CERT_NONCE=%s",
push_cert_nonce);
- strvec_pushf(&proc->env_array,
+ strvec_pushf(&proc->env,
"GIT_PUSH_CERT_NONCE_STATUS=%s",
nonce_status);
if (nonce_status == NONCE_SLOP)
- strvec_pushf(&proc->env_array,
+ strvec_pushf(&proc->env,
"GIT_PUSH_CERT_NONCE_SLOP=%ld",
nonce_stamp_slop);
}
@@ -815,17 +825,17 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed,
if (feed_state->push_options) {
size_t i;
for (i = 0; i < feed_state->push_options->nr; i++)
- strvec_pushf(&proc.env_array,
+ strvec_pushf(&proc.env,
"GIT_PUSH_OPTION_%"PRIuMAX"=%s",
(uintmax_t)i,
feed_state->push_options->items[i].string);
- strvec_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT=%"PRIuMAX"",
+ strvec_pushf(&proc.env, "GIT_PUSH_OPTION_COUNT=%"PRIuMAX"",
(uintmax_t)feed_state->push_options->nr);
} else
- strvec_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT");
+ strvec_pushf(&proc.env, "GIT_PUSH_OPTION_COUNT");
if (tmp_objdir)
- strvec_pushv(&proc.env_array, tmp_objdir_env(tmp_objdir));
+ strvec_pushv(&proc.env, tmp_objdir_env(tmp_objdir));
if (use_sideband) {
memset(&muxer, 0, sizeof(muxer));
@@ -1346,7 +1356,7 @@ static int head_has_history(void)
{
struct object_id oid;
- return !get_oid("HEAD", &oid);
+ return !repo_get_oid(the_repository, "HEAD", &oid);
}
static const char *push_to_deploy(unsigned char *sha1,
@@ -1357,7 +1367,7 @@ static const char *push_to_deploy(unsigned char *sha1,
strvec_pushl(&child.args, "update-index", "-q", "--ignore-submodules",
"--refresh", NULL);
- strvec_pushv(&child.env_array, env->v);
+ strvec_pushv(&child.env, env->v);
child.dir = work_tree;
child.no_stdin = 1;
child.stdout_to_stderr = 1;
@@ -1369,7 +1379,7 @@ static const char *push_to_deploy(unsigned char *sha1,
child_process_init(&child);
strvec_pushl(&child.args, "diff-files", "--quiet",
"--ignore-submodules", "--", NULL);
- strvec_pushv(&child.env_array, env->v);
+ strvec_pushv(&child.env, env->v);
child.dir = work_tree;
child.no_stdin = 1;
child.stdout_to_stderr = 1;
@@ -1383,7 +1393,7 @@ static const char *push_to_deploy(unsigned char *sha1,
/* diff-index with either HEAD or an empty tree */
head_has_history() ? "HEAD" : empty_tree_oid_hex(),
"--", NULL);
- strvec_pushv(&child.env_array, env->v);
+ strvec_pushv(&child.env, env->v);
child.no_stdin = 1;
child.no_stdout = 1;
child.stdout_to_stderr = 0;
@@ -1394,7 +1404,7 @@ static const char *push_to_deploy(unsigned char *sha1,
child_process_init(&child);
strvec_pushl(&child.args, "read-tree", "-u", "-m", hash_to_hex(sha1),
NULL);
- strvec_pushv(&child.env_array, env->v);
+ strvec_pushv(&child.env, env->v);
child.dir = work_tree;
child.no_stdin = 1;
child.no_stdout = 1;
@@ -1462,8 +1472,10 @@ static const char *update(struct command *cmd, struct shallow_info *si)
find_shared_symref(worktrees, "HEAD", name);
/* only refs/... are allowed */
- if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
- rp_error("refusing to create funny ref '%s' remotely", name);
+ if (!starts_with(name, "refs/") ||
+ check_refname_format(name + 5, is_null_oid(new_oid) ?
+ REFNAME_ALLOW_ONELEVEL : 0)) {
+ rp_error("refusing to update funny ref '%s' remotely", name);
ret = "funny refname";
goto out;
}
@@ -1493,7 +1505,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
}
}
- if (!is_null_oid(new_oid) && !has_object_file(new_oid)) {
+ if (!is_null_oid(new_oid) && !repo_has_object_file(the_repository, new_oid)) {
error("unpack should have generated %s, "
"but I can't find it!", oid_to_hex(new_oid));
ret = "bad pack";
@@ -1547,7 +1559,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
}
old_commit = (struct commit *)old_object;
new_commit = (struct commit *)new_object;
- if (!in_merge_bases(old_commit, new_commit)) {
+ if (!repo_in_merge_bases(the_repository, old_commit, new_commit)) {
rp_error("denying non-fast-forward %s"
" (you should pull first)", name);
ret = "non-fast-forward";
@@ -1664,7 +1676,7 @@ static void check_aliased_update_internal(struct command *cmd,
}
dst_name = strip_namespace(dst_name);
- if ((item = string_list_lookup(list, dst_name)) == NULL)
+ if (!(item = string_list_lookup(list, dst_name)))
return;
cmd->skip_update = 1;
@@ -1680,11 +1692,11 @@ static void check_aliased_update_internal(struct command *cmd,
rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
" its target '%s' (%s..%s)",
cmd->ref_name,
- find_unique_abbrev(&cmd->old_oid, DEFAULT_ABBREV),
- find_unique_abbrev(&cmd->new_oid, DEFAULT_ABBREV),
+ repo_find_unique_abbrev(the_repository, &cmd->old_oid, DEFAULT_ABBREV),
+ repo_find_unique_abbrev(the_repository, &cmd->new_oid, DEFAULT_ABBREV),
dst_cmd->ref_name,
- find_unique_abbrev(&dst_cmd->old_oid, DEFAULT_ABBREV),
- find_unique_abbrev(&dst_cmd->new_oid, DEFAULT_ABBREV));
+ repo_find_unique_abbrev(the_repository, &dst_cmd->old_oid, DEFAULT_ABBREV),
+ repo_find_unique_abbrev(the_repository, &dst_cmd->new_oid, DEFAULT_ABBREV));
cmd->error_string = dst_cmd->error_string =
"inconsistent aliased update";
@@ -1794,7 +1806,7 @@ static void reject_updates_to_hidden(struct command *commands)
strbuf_setlen(&refname_full, prefix_len);
strbuf_addstr(&refname_full, cmd->ref_name);
- if (!ref_is_hidden(cmd->ref_name, refname_full.buf))
+ if (!ref_is_hidden(cmd->ref_name, refname_full.buf, &hidden_refs))
continue;
if (is_null_oid(&cmd->new_oid))
cmd->error_string = "deny deleting a hidden ref";
@@ -1810,21 +1822,17 @@ static int should_process_cmd(struct command *cmd)
return !cmd->error_string && !cmd->skip_update;
}
-static void warn_if_skipped_connectivity_check(struct command *commands,
+static void BUG_if_skipped_connectivity_check(struct command *commands,
struct shallow_info *si)
{
struct command *cmd;
- int checked_connectivity = 1;
for (cmd = commands; cmd; cmd = cmd->next) {
- if (should_process_cmd(cmd) && si->shallow_ref[cmd->index]) {
- error("BUG: connectivity check has not been run on ref %s",
- cmd->ref_name);
- checked_connectivity = 0;
- }
+ if (should_process_cmd(cmd) && si->shallow_ref[cmd->index])
+ bug("connectivity check has not been run on ref %s",
+ cmd->ref_name);
}
- if (!checked_connectivity)
- BUG("connectivity check skipped???");
+ BUG_if_bug("connectivity check skipped???");
}
static void execute_commands_non_atomic(struct command *commands,
@@ -1932,6 +1940,8 @@ static void execute_commands(struct command *commands,
opt.err_fd = err_fd;
opt.progress = err_fd && !quiet;
opt.env = tmp_objdir_env(tmp_objdir);
+ opt.exclude_hidden_refs_section = "receive";
+
if (check_connected(iterate_receive_command_list, &data, &opt))
set_connectivity_errors(commands, si);
@@ -2005,7 +2015,7 @@ static void execute_commands(struct command *commands,
execute_commands_non_atomic(commands, si);
if (shallow_update)
- warn_if_skipped_connectivity_check(commands, si);
+ BUG_if_skipped_connectivity_check(commands, si);
}
static struct command **queue_command(struct command **tail,
@@ -2033,6 +2043,16 @@ static struct command **queue_command(struct command **tail,
return &cmd->next;
}
+static void free_commands(struct command *commands)
+{
+ while (commands) {
+ struct command *next = commands->next;
+
+ free(commands);
+ commands = next;
+ }
+}
+
static void queue_commands_from_cert(struct command **tail,
struct strbuf *push_cert)
{
@@ -2080,7 +2100,7 @@ static struct command *read_head_info(struct packet_reader *reader,
const char *feature_list = reader->line + linelen + 1;
const char *hash = NULL;
const char *client_sid;
- int len = 0;
+ size_t len = 0;
if (parse_feature_request(feature_list, "report-status"))
report_status = 1;
if (parse_feature_request(feature_list, "report-status-v2"))
@@ -2175,7 +2195,7 @@ static const char *parse_pack_header(struct pack_header *hdr)
}
}
-static const char *pack_lockfile;
+static struct tempfile *pack_lockfile;
static void push_header_arg(struct strvec *args, struct pack_header *hdr)
{
@@ -2214,8 +2234,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)
close(err_fd);
return "unable to create temporary object directory";
}
- if (tmp_objdir)
- strvec_pushv(&child.env_array, tmp_objdir_env(tmp_objdir));
+ strvec_pushv(&child.env, tmp_objdir_env(tmp_objdir));
/*
* Normally we just pass the tmp_objdir environment to the child
@@ -2243,6 +2262,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)
return "unpack-objects abnormal exit";
} else {
char hostname[HOST_NAME_MAX + 1];
+ char *lockfile;
strvec_pushl(&child.args, "index-pack", "--stdin", NULL);
push_header_arg(&child.args, &hdr);
@@ -2272,8 +2292,14 @@ static const char *unpack(int err_fd, struct shallow_info *si)
status = start_command(&child);
if (status)
return "index-pack fork failed";
- pack_lockfile = index_pack_lockfile(child.out, NULL);
+
+ lockfile = index_pack_lockfile(child.out, NULL);
+ if (lockfile) {
+ pack_lockfile = register_tempfile(lockfile);
+ free(lockfile);
+ }
close(child.out);
+
status = finish_command(&child);
if (status)
return "index-pack abnormal exit";
@@ -2538,7 +2564,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_DIE_ON_ERR_PACKET);
- if ((commands = read_head_info(&reader, &shallow)) != NULL) {
+ if ((commands = read_head_info(&reader, &shallow))) {
const char *unpack_status = NULL;
struct string_list push_options = STRING_LIST_INIT_DUP;
@@ -2560,8 +2586,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
use_keepalive = KEEPALIVE_ALWAYS;
execute_commands(commands, unpack_status, &si,
&push_options);
- if (pack_lockfile)
- unlink_or_warn(pack_lockfile);
+ delete_tempfile(&pack_lockfile);
sigchain_push(SIGPIPE, SIG_IGN);
if (report_status_v2)
report_v2(commands, unpack_status);
@@ -2571,6 +2596,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
run_receive_hook(commands, "post-receive", 1,
&push_options);
run_update_post_hook(commands);
+ free_commands(commands);
string_list_clear(&push_options, 0);
if (auto_gc) {
struct child_process proc = CHILD_PROCESS_INIT;
@@ -2596,6 +2622,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
packet_flush(1);
oid_array_clear(&shallow);
oid_array_clear(&ref);
+ strvec_clear(&hidden_refs);
free((void *)push_cert_nonce);
return 0;
}
diff --git a/builtin/reflog.c b/builtin/reflog.c
index c943c2aabe..df63a5892e 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -1,9 +1,13 @@
#include "builtin.h"
#include "config.h"
+#include "gettext.h"
+#include "repository.h"
#include "revision.h"
#include "reachable.h"
+#include "wildmatch.h"
#include "worktree.h"
#include "reflog.h"
+#include "parse-options.h"
#define BUILTIN_REFLOG_SHOW_USAGE \
N_("git reflog [show] [<log-options>] [<ref>]")
@@ -56,7 +60,8 @@ struct worktree_reflogs {
struct string_list reflogs;
};
-static int collect_reflog(const char *ref, const struct object_id *oid, int unused, void *cb_data)
+static int collect_reflog(const char *ref, const struct object_id *oid UNUSED,
+ int flags UNUSED, void *cb_data)
{
struct worktree_reflogs *cb = cb_data;
struct worktree *worktree = cb->worktree;
@@ -66,7 +71,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus
* Avoid collecting the same shared ref multiple times because
* they are available via all worktrees.
*/
- if (!worktree->is_current && ref_type(ref) == REF_TYPE_NORMAL)
+ if (!worktree->is_current &&
+ parse_worktree_ref(ref, NULL, NULL, NULL) == REF_WORKTREE_SHARED)
return 0;
strbuf_worktree_ref(worktree, &newref, ref);
@@ -104,7 +110,8 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
#define EXPIRE_TOTAL 01
#define EXPIRE_UNREACH 02
-static int reflog_expire_config(const char *var, const char *value, void *cb)
+static int reflog_expire_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
const char *pattern, *key;
size_t pattern_len;
@@ -113,7 +120,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
struct reflog_expire_cfg *ent;
if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
if (!strcmp(key, "reflogexpire")) {
slot = EXPIRE_TOTAL;
@@ -124,7 +131,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
if (git_config_expiry_date(&expire, var, value))
return -1;
} else
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
if (!pattern) {
switch (slot) {
@@ -193,6 +200,8 @@ static int expire_unreachable_callback(const struct option *opt,
{
struct cmd_reflog_expire_cb *cmd = opt->value;
+ BUG_ON_OPT_NEG(unset);
+
if (parse_expiry_date(arg, &cmd->expire_unreachable))
die(_("invalid timestamp '%s' given to '--%s'"),
arg, opt->long_name);
@@ -207,6 +216,8 @@ static int expire_total_callback(const struct option *opt,
{
struct cmd_reflog_expire_cb *cmd = opt->value;
+ BUG_ON_OPT_NEG(unset);
+
if (parse_expiry_date(arg, &cmd->expire_total))
die(_("invalid timestamp '%s' given to '--%s'"),
arg, opt->long_name);
@@ -223,7 +234,7 @@ static int cmd_reflog_show(int argc, const char **argv, const char *prefix)
parse_options(argc, argv, prefix, options, reflog_show_usage,
PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 |
- PARSE_OPT_KEEP_UNKNOWN);
+ PARSE_OPT_KEEP_UNKNOWN_OPT);
return cmd_log_reflog(argc, argv, prefix);
}
@@ -293,6 +304,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
if (verbose)
printf(_("Marking reachable objects..."));
mark_reachable_objects(&revs, 0, 0, NULL);
+ release_revisions(&revs);
if (verbose)
putchar('\n');
}
@@ -403,40 +415,21 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)
int cmd_reflog(int argc, const char **argv, const char *prefix)
{
+ parse_opt_subcommand_fn *fn = NULL;
struct option options[] = {
+ OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
+ OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
+ OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
+ OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
OPT_END()
};
argc = parse_options(argc, argv, prefix, options, reflog_usage,
+ PARSE_OPT_SUBCOMMAND_OPTIONAL |
PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 |
- PARSE_OPT_KEEP_UNKNOWN |
- PARSE_OPT_NO_INTERNAL_HELP);
-
- /*
- * With "git reflog" we default to showing it. !argc is
- * impossible with PARSE_OPT_KEEP_ARGV0.
- */
- if (argc == 1)
- goto log_reflog;
-
- if (!strcmp(argv[1], "-h"))
- usage_with_options(reflog_usage, options);
- else if (*argv[1] == '-')
- goto log_reflog;
-
- if (!strcmp(argv[1], "show"))
- return cmd_reflog_show(argc - 1, argv + 1, prefix);
- else if (!strcmp(argv[1], "expire"))
- return cmd_reflog_expire(argc - 1, argv + 1, prefix);
- else if (!strcmp(argv[1], "delete"))
- return cmd_reflog_delete(argc - 1, argv + 1, prefix);
- else if (!strcmp(argv[1], "exists"))
- return cmd_reflog_exists(argc - 1, argv + 1, prefix);
-
- /*
- * Fall-through for e.g. "git reflog -1", "git reflog master",
- * as well as the plain "git reflog" above goto above.
- */
-log_reflog:
- return cmd_log_reflog(argc, argv, prefix);
+ PARSE_OPT_KEEP_UNKNOWN_OPT);
+ if (fn)
+ return fn(argc - 1, argv + 1, prefix);
+ else
+ return cmd_log_reflog(argc, argv, prefix);
}
diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c
index fd3538d4f0..282782eccd 100644
--- a/builtin/remote-ext.c
+++ b/builtin/remote-ext.c
@@ -169,6 +169,8 @@ static int command_loop(const char *child)
while (1) {
size_t i;
+ const char *arg;
+
if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
if (ferror(stdin))
die("Command input error");
@@ -182,10 +184,10 @@ static int command_loop(const char *child)
if (!strcmp(buffer, "capabilities")) {
printf("*connect\n\n");
fflush(stdout);
- } else if (!strncmp(buffer, "connect ", 8)) {
+ } else if (skip_prefix(buffer, "connect ", &arg)) {
printf("\n");
fflush(stdout);
- return run_child(child, buffer + 8);
+ return run_child(child, arg);
} else {
fprintf(stderr, "Bad command");
return 1;
@@ -195,6 +197,8 @@ static int command_loop(const char *child)
int cmd_remote_ext(int argc, const char **argv, const char *prefix)
{
+ BUG_ON_NON_EMPTY_PREFIX(prefix);
+
if (argc != 3)
usage(usage_msg);
diff --git a/builtin/remote-fd.c b/builtin/remote-fd.c
index 91dfe07e06..9020fab9c5 100644
--- a/builtin/remote-fd.c
+++ b/builtin/remote-fd.c
@@ -40,7 +40,7 @@ static void command_loop(int input_fd, int output_fd)
if (!strcmp(buffer, "capabilities")) {
printf("*connect\n\n");
fflush(stdout);
- } else if (!strncmp(buffer, "connect ", 8)) {
+ } else if (starts_with(buffer, "connect ")) {
printf("\n");
fflush(stdout);
if (bidirectional_transfer_loop(input_fd,
@@ -59,6 +59,8 @@ int cmd_remote_fd(int argc, const char **argv, const char *prefix)
int output_fd = -1;
char *end;
+ BUG_ON_NON_EMPTY_PREFIX(prefix);
+
if (argc != 3)
usage(usage_msg);
diff --git a/builtin/remote.c b/builtin/remote.c
index 5f4cde9d78..479a5191d4 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -1,6 +1,8 @@
#include "builtin.h"
#include "config.h"
+#include "gettext.h"
#include "parse-options.h"
+#include "path.h"
#include "transport.h"
#include "remote.h"
#include "string-list.h"
@@ -9,7 +11,7 @@
#include "rebase.h"
#include "refs.h"
#include "refspec.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "strvec.h"
#include "commit-reach.h"
#include "progress.h"
@@ -92,13 +94,15 @@ static int verbose;
static int fetch_remote(const char *name)
{
- const char *argv[] = { "fetch", name, NULL, NULL };
- if (verbose) {
- argv[1] = "-v";
- argv[2] = name;
- }
+ struct child_process cmd = CHILD_PROCESS_INIT;
+
+ strvec_push(&cmd.args, "fetch");
+ if (verbose)
+ strvec_push(&cmd.args, "-v");
+ strvec_push(&cmd.args, name);
+ cmd.git_cmd = 1;
printf_ln(_("Updating %s"), name);
- if (run_command_v_opt(argv, RUN_GIT_CMD))
+ if (run_command(&cmd))
return error(_("Could not fetch %s"), name);
return 0;
}
@@ -150,7 +154,7 @@ static int parse_mirror_opt(const struct option *opt, const char *arg, int not)
return 0;
}
-static int add(int argc, const char **argv)
+static int add(int argc, const char **argv, const char *prefix)
{
int fetch = 0, fetch_tags = TAGS_DEFAULT;
unsigned mirror = MIRROR_NONE;
@@ -177,8 +181,8 @@ static int add(int argc, const char **argv)
OPT_END()
};
- argc = parse_options(argc, argv, NULL, options, builtin_remote_add_usage,
- 0);
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_remote_add_usage, 0);
if (argc != 2)
usage_with_options(builtin_remote_add_usage, options);
@@ -264,7 +268,9 @@ static const char *abbrev_ref(const char *name, const char *prefix)
}
#define abbrev_branch(name) abbrev_ref((name), "refs/heads/")
-static int config_read_branches(const char *key, const char *value, void *cb)
+static int config_read_branches(const char *key, const char *value,
+ const struct config_context *ctx UNUSED,
+ void *data UNUSED)
{
const char *orig_key = key;
char *name;
@@ -344,12 +350,13 @@ static void read_branches(void)
struct ref_states {
struct remote *remote;
- struct string_list new_refs, stale, tracked, heads, push;
+ struct string_list new_refs, skipped, stale, tracked, heads, push;
int queried;
};
#define REF_STATES_INIT { \
.new_refs = STRING_LIST_INIT_DUP, \
+ .skipped = STRING_LIST_INIT_DUP, \
.stale = STRING_LIST_INIT_DUP, \
.tracked = STRING_LIST_INIT_DUP, \
.heads = STRING_LIST_INIT_DUP, \
@@ -368,7 +375,9 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
states->remote->fetch.raw[i]);
for (ref = fetch_map; ref; ref = ref->next) {
- if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
+ if (omit_name_by_refspec(ref->name, &states->remote->fetch))
+ string_list_append(&states->skipped, abbrev_branch(ref->name));
+ else if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
string_list_append(&states->new_refs, abbrev_branch(ref->name));
else
string_list_append(&states->tracked, abbrev_branch(ref->name));
@@ -383,6 +392,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
free_refs(fetch_map);
string_list_sort(&states->new_refs);
+ string_list_sort(&states->skipped);
string_list_sort(&states->tracked);
string_list_sort(&states->stale);
@@ -436,7 +446,7 @@ static int get_push_ref_states(const struct ref *remote_refs,
info->status = PUSH_STATUS_UPTODATE;
else if (is_null_oid(&ref->old_oid))
info->status = PUSH_STATUS_CREATE;
- else if (has_object_file(&ref->old_oid) &&
+ else if (repo_has_object_file(the_repository, &ref->old_oid) &&
ref_newer(&ref->new_oid, &ref->old_oid))
info->status = PUSH_STATUS_FASTFORWARD;
else
@@ -534,7 +544,8 @@ struct branches_for_remote {
};
static int add_branch_for_removal(const char *refname,
- const struct object_id *oid, int flags, void *cb_data)
+ const struct object_id *oid UNUSED,
+ int flags UNUSED, void *cb_data)
{
struct branches_for_remote *branches = cb_data;
struct refspec_item refspec;
@@ -576,7 +587,8 @@ struct rename_info {
};
static int read_remote_branches(const char *refname,
- const struct object_id *oid, int flags, void *cb_data)
+ const struct object_id *oid UNUSED,
+ int flags UNUSED, void *cb_data)
{
struct rename_info *rename = cb_data;
struct strbuf buf = STRBUF_INIT;
@@ -635,17 +647,19 @@ struct push_default_info
};
static int config_read_push_default(const char *key, const char *value,
- void *cb)
+ const struct config_context *ctx, void *cb)
{
+ const struct key_value_info *kvi = ctx->kvi;
+
struct push_default_info* info = cb;
if (strcmp(key, "remote.pushdefault") ||
!value || strcmp(value, info->old_name))
return 0;
- info->scope = current_config_scope();
+ info->scope = kvi->scope;
strbuf_reset(&info->origin);
- strbuf_addstr(&info->origin, current_config_name());
- info->linenr = current_config_line();
+ strbuf_addstr(&info->origin, config_origin_type_name(kvi->origin_type));
+ info->linenr = kvi->linenr;
return 0;
}
@@ -676,7 +690,7 @@ static void handle_push_default(const char* old_name, const char* new_name)
}
-static int mv(int argc, const char **argv)
+static int mv(int argc, const char **argv, const char *prefix)
{
int show_progress = isatty(2);
struct option options[] = {
@@ -691,7 +705,7 @@ static int mv(int argc, const char **argv)
int i, refs_renamed_nr = 0, refspec_updated = 0;
struct progress *progress = NULL;
- argc = parse_options(argc, argv, NULL, options,
+ argc = parse_options(argc, argv, prefix, options,
builtin_remote_rename_usage, 0);
if (argc != 2)
@@ -726,29 +740,31 @@ static int mv(int argc, const char **argv)
return error(_("Could not rename config section '%s' to '%s'"),
buf.buf, buf2.buf);
- strbuf_reset(&buf);
- strbuf_addf(&buf, "remote.%s.fetch", rename.new_name);
- git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);
- strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name);
- for (i = 0; i < oldremote->fetch.raw_nr; i++) {
- char *ptr;
-
- strbuf_reset(&buf2);
- strbuf_addstr(&buf2, oldremote->fetch.raw[i]);
- ptr = strstr(buf2.buf, old_remote_context.buf);
- if (ptr) {
- refspec_updated = 1;
- strbuf_splice(&buf2,
- ptr-buf2.buf + strlen(":refs/remotes/"),
- strlen(rename.old_name), rename.new_name,
- strlen(rename.new_name));
- } else
- warning(_("Not updating non-default fetch refspec\n"
- "\t%s\n"
- "\tPlease update the configuration manually if necessary."),
- buf2.buf);
-
- git_config_set_multivar(buf.buf, buf2.buf, "^$", 0);
+ if (oldremote->fetch.raw_nr) {
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "remote.%s.fetch", rename.new_name);
+ git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);
+ strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name);
+ for (i = 0; i < oldremote->fetch.raw_nr; i++) {
+ char *ptr;
+
+ strbuf_reset(&buf2);
+ strbuf_addstr(&buf2, oldremote->fetch.raw[i]);
+ ptr = strstr(buf2.buf, old_remote_context.buf);
+ if (ptr) {
+ refspec_updated = 1;
+ strbuf_splice(&buf2,
+ ptr-buf2.buf + strlen(":refs/remotes/"),
+ strlen(rename.old_name), rename.new_name,
+ strlen(rename.new_name));
+ } else
+ warning(_("Not updating non-default fetch refspec\n"
+ "\t%s\n"
+ "\tPlease update the configuration manually if necessary."),
+ buf2.buf);
+
+ git_config_set_multivar(buf.buf, buf2.buf, "^$", 0);
+ }
}
read_branches();
@@ -840,7 +856,7 @@ static int mv(int argc, const char **argv)
return 0;
}
-static int rm(int argc, const char **argv)
+static int rm(int argc, const char **argv, const char *prefix)
{
struct option options[] = {
OPT_END()
@@ -858,12 +874,14 @@ static int rm(int argc, const char **argv)
cb_data.skipped = &skipped;
cb_data.keep = &known_remotes;
- if (argc != 2)
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_remote_rm_usage, 0);
+ if (argc != 1)
usage_with_options(builtin_remote_rm_usage, options);
- remote = remote_get(argv[1]);
+ remote = remote_get(argv[0]);
if (!remote_is_configured(remote, 1)) {
- error(_("No such remote: '%s'"), argv[1]);
+ error(_("No such remote: '%s'"), argv[0]);
exit(2);
}
@@ -931,7 +949,7 @@ static int rm(int argc, const char **argv)
return result;
}
-static void clear_push_info(void *util, const char *string)
+static void clear_push_info(void *util, const char *string UNUSED)
{
struct push_info *info = util;
free(info->dest);
@@ -941,6 +959,7 @@ static void clear_push_info(void *util, const char *string)
static void free_remote_ref_states(struct ref_states *states)
{
string_list_clear(&states->new_refs, 0);
+ string_list_clear(&states->skipped, 0);
string_list_clear(&states->stale, 1);
string_list_clear(&states->tracked, 0);
string_list_clear(&states->heads, 0);
@@ -948,7 +967,8 @@ static void free_remote_ref_states(struct ref_states *states)
}
static int append_ref_to_tracked_list(const char *refname,
- const struct object_id *oid, int flags, void *cb_data)
+ const struct object_id *oid UNUSED,
+ int flags, void *cb_data)
{
struct ref_states *states = cb_data;
struct refspec_item refspec;
@@ -1035,6 +1055,8 @@ static int show_remote_info_item(struct string_list_item *item, void *cb_data)
arg = states->remote->name;
} else if (string_list_has_string(&states->tracked, name))
arg = _(" tracked");
+ else if (string_list_has_string(&states->skipped, name))
+ arg = _(" skipped");
else if (string_list_has_string(&states->stale, name))
arg = _(" stale (use 'git remote prune' to remove)");
else
@@ -1185,14 +1207,22 @@ static int show_push_info_item(struct string_list_item *item, void *cb_data)
static int get_one_entry(struct remote *remote, void *priv)
{
struct string_list *list = priv;
- struct strbuf url_buf = STRBUF_INIT;
+ struct strbuf remote_info_buf = STRBUF_INIT;
const char **url;
int i, url_nr;
if (remote->url_nr > 0) {
- strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
+ struct strbuf promisor_config = STRBUF_INIT;
+ const char *partial_clone_filter = NULL;
+
+ strbuf_addf(&promisor_config, "remote.%s.partialclonefilter", remote->name);
+ strbuf_addf(&remote_info_buf, "%s (fetch)", remote->url[0]);
+ if (!git_config_get_string_tmp(promisor_config.buf, &partial_clone_filter))
+ strbuf_addf(&remote_info_buf, " [%s]", partial_clone_filter);
+
+ strbuf_release(&promisor_config);
string_list_append(list, remote->name)->util =
- strbuf_detach(&url_buf, NULL);
+ strbuf_detach(&remote_info_buf, NULL);
} else
string_list_append(list, remote->name)->util = NULL;
if (remote->pushurl_nr) {
@@ -1204,9 +1234,9 @@ static int get_one_entry(struct remote *remote, void *priv)
}
for (i = 0; i < url_nr; i++)
{
- strbuf_addf(&url_buf, "%s (push)", url[i]);
+ strbuf_addf(&remote_info_buf, "%s (push)", url[i]);
string_list_append(list, remote->name)->util =
- strbuf_detach(&url_buf, NULL);
+ strbuf_detach(&remote_info_buf, NULL);
}
return 0;
@@ -1214,10 +1244,9 @@ static int get_one_entry(struct remote *remote, void *priv)
static int show_all(void)
{
- struct string_list list = STRING_LIST_INIT_NODUP;
+ struct string_list list = STRING_LIST_INIT_DUP;
int result;
- list.strdup_strings = 1;
result = for_each_remote(get_one_entry, &list);
if (!result) {
@@ -1240,7 +1269,7 @@ static int show_all(void)
return result;
}
-static int show(int argc, const char **argv)
+static int show(int argc, const char **argv, const char *prefix)
{
int no_query = 0, result = 0, query_flag = 0;
struct option options[] = {
@@ -1249,7 +1278,8 @@ static int show(int argc, const char **argv)
};
struct show_info info = SHOW_INFO_INIT;
- argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage,
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_remote_show_usage,
0);
if (argc < 1)
@@ -1300,6 +1330,7 @@ static int show(int argc, const char **argv)
/* remote branch info */
info.width = 0;
for_each_string_list(&info.states.new_refs, add_remote_to_show_info, &info);
+ for_each_string_list(&info.states.skipped, add_remote_to_show_info, &info);
for_each_string_list(&info.states.tracked, add_remote_to_show_info, &info);
for_each_string_list(&info.states.stale, add_remote_to_show_info, &info);
if (info.list.nr)
@@ -1342,7 +1373,7 @@ static int show(int argc, const char **argv)
return result;
}
-static int set_head(int argc, const char **argv)
+static int set_head(int argc, const char **argv, const char *prefix)
{
int i, opt_a = 0, opt_d = 0, result = 0;
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
@@ -1355,8 +1386,8 @@ static int set_head(int argc, const char **argv)
N_("delete refs/remotes/<name>/HEAD")),
OPT_END()
};
- argc = parse_options(argc, argv, NULL, options, builtin_remote_sethead_usage,
- 0);
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_remote_sethead_usage, 0);
if (argc)
strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
@@ -1447,7 +1478,7 @@ static int prune_remote(const char *remote, int dry_run)
return result;
}
-static int prune(int argc, const char **argv)
+static int prune(int argc, const char **argv, const char *prefix)
{
int dry_run = 0, result = 0;
struct option options[] = {
@@ -1455,8 +1486,8 @@ static int prune(int argc, const char **argv)
OPT_END()
};
- argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage,
- 0);
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_remote_prune_usage, 0);
if (argc < 1)
usage_with_options(builtin_remote_prune_usage, options);
@@ -1467,7 +1498,9 @@ static int prune(int argc, const char **argv)
return result;
}
-static int get_remote_default(const char *key, const char *value, void *priv)
+static int get_remote_default(const char *key, const char *value UNUSED,
+ const struct config_context *ctx UNUSED,
+ void *priv)
{
if (strcmp(key, "remotes.default") == 0) {
int *found = priv;
@@ -1476,7 +1509,7 @@ static int get_remote_default(const char *key, const char *value, void *priv)
return 0;
}
-static int update(int argc, const char **argv)
+static int update(int argc, const char **argv, const char *prefix)
{
int i, prune = -1;
struct option options[] = {
@@ -1484,36 +1517,35 @@ static int update(int argc, const char **argv)
N_("prune remotes after fetching")),
OPT_END()
};
- struct strvec fetch_argv = STRVEC_INIT;
+ struct child_process cmd = CHILD_PROCESS_INIT;
int default_defined = 0;
- int retval;
- argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_remote_update_usage,
PARSE_OPT_KEEP_ARGV0);
- strvec_push(&fetch_argv, "fetch");
+ strvec_push(&cmd.args, "fetch");
if (prune != -1)
- strvec_push(&fetch_argv, prune ? "--prune" : "--no-prune");
+ strvec_push(&cmd.args, prune ? "--prune" : "--no-prune");
if (verbose)
- strvec_push(&fetch_argv, "-v");
- strvec_push(&fetch_argv, "--multiple");
+ strvec_push(&cmd.args, "-v");
+ strvec_push(&cmd.args, "--multiple");
if (argc < 2)
- strvec_push(&fetch_argv, "default");
+ strvec_push(&cmd.args, "default");
for (i = 1; i < argc; i++)
- strvec_push(&fetch_argv, argv[i]);
+ strvec_push(&cmd.args, argv[i]);
- if (strcmp(fetch_argv.v[fetch_argv.nr-1], "default") == 0) {
+ if (strcmp(cmd.args.v[cmd.args.nr-1], "default") == 0) {
git_config(get_remote_default, &default_defined);
if (!default_defined) {
- strvec_pop(&fetch_argv);
- strvec_push(&fetch_argv, "--all");
+ strvec_pop(&cmd.args);
+ strvec_push(&cmd.args, "--all");
}
}
- retval = run_command_v_opt(fetch_argv.v, RUN_GIT_CMD);
- strvec_clear(&fetch_argv);
- return retval;
+ cmd.git_cmd = 1;
+ return run_command(&cmd);
}
static int remove_all_fetch_refspecs(const char *key)
@@ -1559,7 +1591,7 @@ static int set_remote_branches(const char *remotename, const char **branches,
return 0;
}
-static int set_branches(int argc, const char **argv)
+static int set_branches(int argc, const char **argv, const char *prefix)
{
int add_mode = 0;
struct option options[] = {
@@ -1567,7 +1599,7 @@ static int set_branches(int argc, const char **argv)
OPT_END()
};
- argc = parse_options(argc, argv, NULL, options,
+ argc = parse_options(argc, argv, prefix, options,
builtin_remote_setbranches_usage, 0);
if (argc == 0) {
error(_("no remote specified"));
@@ -1578,7 +1610,7 @@ static int set_branches(int argc, const char **argv)
return set_remote_branches(argv[0], argv + 1, add_mode);
}
-static int get_url(int argc, const char **argv)
+static int get_url(int argc, const char **argv, const char *prefix)
{
int i, push_mode = 0, all_mode = 0;
const char *remotename = NULL;
@@ -1592,7 +1624,8 @@ static int get_url(int argc, const char **argv)
N_("return all URLs")),
OPT_END()
};
- argc = parse_options(argc, argv, NULL, options, builtin_remote_geturl_usage, 0);
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_remote_geturl_usage, 0);
if (argc != 1)
usage_with_options(builtin_remote_geturl_usage, options);
@@ -1631,7 +1664,7 @@ static int get_url(int argc, const char **argv)
return 0;
}
-static int set_url(int argc, const char **argv)
+static int set_url(int argc, const char **argv, const char *prefix)
{
int i, push_mode = 0, add_mode = 0, delete_mode = 0;
int matches = 0, negative_matches = 0;
@@ -1652,7 +1685,8 @@ static int set_url(int argc, const char **argv)
N_("delete URLs")),
OPT_END()
};
- argc = parse_options(argc, argv, NULL, options, builtin_remote_seturl_usage,
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_remote_seturl_usage,
PARSE_OPT_KEEP_ARGV0);
if (add_mode && delete_mode)
@@ -1723,41 +1757,33 @@ out:
int cmd_remote(int argc, const char **argv, const char *prefix)
{
+ parse_opt_subcommand_fn *fn = NULL;
struct option options[] = {
OPT__VERBOSE(&verbose, N_("be verbose; must be placed before a subcommand")),
+ OPT_SUBCOMMAND("add", &fn, add),
+ OPT_SUBCOMMAND("rename", &fn, mv),
+ OPT_SUBCOMMAND_F("rm", &fn, rm, PARSE_OPT_NOCOMPLETE),
+ OPT_SUBCOMMAND("remove", &fn, rm),
+ OPT_SUBCOMMAND("set-head", &fn, set_head),
+ OPT_SUBCOMMAND("set-branches", &fn, set_branches),
+ OPT_SUBCOMMAND("get-url", &fn, get_url),
+ OPT_SUBCOMMAND("set-url", &fn, set_url),
+ OPT_SUBCOMMAND("show", &fn, show),
+ OPT_SUBCOMMAND("prune", &fn, prune),
+ OPT_SUBCOMMAND("update", &fn, update),
OPT_END()
};
- int result;
argc = parse_options(argc, argv, prefix, options, builtin_remote_usage,
- PARSE_OPT_STOP_AT_NON_OPTION);
+ PARSE_OPT_SUBCOMMAND_OPTIONAL);
- if (argc < 1)
- result = show_all();
- else if (!strcmp(argv[0], "add"))
- result = add(argc, argv);
- else if (!strcmp(argv[0], "rename"))
- result = mv(argc, argv);
- else if (!strcmp(argv[0], "rm") || !strcmp(argv[0], "remove"))
- result = rm(argc, argv);
- else if (!strcmp(argv[0], "set-head"))
- result = set_head(argc, argv);
- else if (!strcmp(argv[0], "set-branches"))
- result = set_branches(argc, argv);
- else if (!strcmp(argv[0], "get-url"))
- result = get_url(argc, argv);
- else if (!strcmp(argv[0], "set-url"))
- result = set_url(argc, argv);
- else if (!strcmp(argv[0], "show"))
- result = show(argc, argv);
- else if (!strcmp(argv[0], "prune"))
- result = prune(argc, argv);
- else if (!strcmp(argv[0], "update"))
- result = update(argc, argv);
- else {
- error(_("Unknown subcommand: %s"), argv[0]);
- usage_with_options(builtin_remote_usage, options);
+ if (fn) {
+ return !!fn(argc, argv, prefix);
+ } else {
+ if (argc) {
+ error(_("unknown subcommand: `%s'"), argv[0]);
+ usage_with_options(builtin_remote_usage, options);
+ }
+ return !!show_all();
}
-
- return result ? 1 : 0;
}
diff --git a/builtin/repack.c b/builtin/repack.c
index d1a563d5b6..aea5ca9d44 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -1,9 +1,13 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "parse-options.h"
+#include "path.h"
#include "run-command.h"
+#include "server-info.h"
#include "sigchain.h"
#include "strbuf.h"
#include "string-list.h"
@@ -11,13 +15,21 @@
#include "midx.h"
#include "packfile.h"
#include "prune-packed.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "promisor-remote.h"
#include "shallow.h"
#include "pack.h"
#include "pack-bitmap.h"
#include "refs.h"
+#define ALL_INTO_ONE 1
+#define LOOSEN_UNREACHABLE 2
+#define PACK_CRUFT 4
+
+#define DELETE_PACK 1
+#define CRUFT_PACK 2
+
+static int pack_everything;
static int delta_base_offset = 1;
static int pack_kept_objects = -1;
static int write_bitmaps = -1;
@@ -32,12 +44,25 @@ static const char *const git_repack_usage[] = {
static const char incremental_bitmap_conflict_error[] = N_(
"Incremental repacks are incompatible with bitmap indexes. Use\n"
-"--no-write-bitmap-index or disable the pack.writebitmaps configuration."
+"--no-write-bitmap-index or disable the pack.writeBitmaps configuration."
);
+struct pack_objects_args {
+ const char *window;
+ const char *window_memory;
+ const char *depth;
+ const char *threads;
+ const char *max_pack_size;
+ int no_reuse_delta;
+ int no_reuse_object;
+ int quiet;
+ int local;
+};
-static int repack_config(const char *var, const char *value, void *cb)
+static int repack_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
+ struct pack_objects_args *cruft_po_args = cb;
if (!strcmp(var, "repack.usedeltabaseoffset")) {
delta_base_offset = git_config_bool(var, value);
return 0;
@@ -59,50 +84,20 @@ static int repack_config(const char *var, const char *value, void *cb)
run_update_server_info = git_config_bool(var, value);
return 0;
}
- return git_default_config(var, value, cb);
-}
-
-/*
- * Remove temporary $GIT_OBJECT_DIRECTORY/pack/.tmp-$$-pack-* files.
- */
-static void remove_temporary_files(void)
-{
- struct strbuf buf = STRBUF_INIT;
- size_t dirlen, prefixlen;
- DIR *dir;
- struct dirent *e;
-
- dir = opendir(packdir);
- if (!dir)
- return;
-
- /* Point at the slash at the end of ".../objects/pack/" */
- dirlen = strlen(packdir) + 1;
- strbuf_addstr(&buf, packtmp);
- /* Hold the length of ".tmp-%d-pack-" */
- prefixlen = buf.len - dirlen;
-
- while ((e = readdir(dir))) {
- if (strncmp(e->d_name, buf.buf + dirlen, prefixlen))
- continue;
- strbuf_setlen(&buf, dirlen);
- strbuf_addstr(&buf, e->d_name);
- unlink(buf.buf);
- }
- closedir(dir);
- strbuf_release(&buf);
-}
-
-static void remove_pack_on_signal(int signo)
-{
- remove_temporary_files();
- sigchain_pop(signo);
- raise(signo);
+ if (!strcmp(var, "repack.cruftwindow"))
+ return git_config_string(&cruft_po_args->window, var, value);
+ if (!strcmp(var, "repack.cruftwindowmemory"))
+ return git_config_string(&cruft_po_args->window_memory, var, value);
+ if (!strcmp(var, "repack.cruftdepth"))
+ return git_config_string(&cruft_po_args->depth, var, value);
+ if (!strcmp(var, "repack.cruftthreads"))
+ return git_config_string(&cruft_po_args->threads, var, value);
+ return git_default_config(var, value, ctx, cb);
}
/*
- * Adds all packs hex strings to either fname_nonkept_list or
- * fname_kept_list based on whether each pack has a corresponding
+ * Adds all packs hex strings (pack-$HASH) to either fname_nonkept_list
+ * or fname_kept_list based on whether each pack has a corresponding
* .keep file or not. Packs without a .keep file are not to be kept
* if we are going to pack everything into one file.
*/
@@ -110,33 +105,38 @@ static void collect_pack_filenames(struct string_list *fname_nonkept_list,
struct string_list *fname_kept_list,
const struct string_list *extra_keep)
{
- DIR *dir;
- struct dirent *e;
- char *fname;
-
- if (!(dir = opendir(packdir)))
- return;
+ struct packed_git *p;
+ struct strbuf buf = STRBUF_INIT;
- while ((e = readdir(dir)) != NULL) {
- size_t len;
+ for (p = get_all_packs(the_repository); p; p = p->next) {
int i;
+ const char *base;
- if (!strip_suffix(e->d_name, ".pack", &len))
+ if (!p->pack_local)
continue;
+ base = pack_basename(p);
+
for (i = 0; i < extra_keep->nr; i++)
- if (!fspathcmp(e->d_name, extra_keep->items[i].string))
+ if (!fspathcmp(base, extra_keep->items[i].string))
break;
- fname = xmemdupz(e->d_name, len);
-
- if ((extra_keep->nr > 0 && i < extra_keep->nr) ||
- (file_exists(mkpath("%s/%s.keep", packdir, fname))))
- string_list_append_nodup(fname_kept_list, fname);
- else
- string_list_append_nodup(fname_nonkept_list, fname);
+ strbuf_reset(&buf);
+ strbuf_addstr(&buf, base);
+ strbuf_strip_suffix(&buf, ".pack");
+
+ if ((extra_keep->nr > 0 && i < extra_keep->nr) || p->pack_keep)
+ string_list_append(fname_kept_list, buf.buf);
+ else {
+ struct string_list_item *item;
+ item = string_list_append(fname_nonkept_list, buf.buf);
+ if (p->is_cruft)
+ item->util = (void*)(uintptr_t)CRUFT_PACK;
+ }
}
- closedir(dir);
+
+ string_list_sort(fname_kept_list);
+ strbuf_release(&buf);
}
static void remove_redundant_pack(const char *dir_name, const char *base_name)
@@ -151,20 +151,9 @@ static void remove_redundant_pack(const char *dir_name, const char *base_name)
strbuf_release(&buf);
}
-struct pack_objects_args {
- const char *window;
- const char *window_memory;
- const char *depth;
- const char *threads;
- const char *max_pack_size;
- int no_reuse_delta;
- int no_reuse_object;
- int quiet;
- int local;
-};
-
static void prepare_pack_objects(struct child_process *cmd,
- const struct pack_objects_args *args)
+ const struct pack_objects_args *args,
+ const char *out)
{
strvec_push(&cmd->args, "pack-objects");
if (args->window)
@@ -187,7 +176,7 @@ static void prepare_pack_objects(struct child_process *cmd,
strvec_push(&cmd->args, "--quiet");
if (delta_base_offset)
strvec_push(&cmd->args, "--delta-base-offset");
- strvec_push(&cmd->args, packtmp);
+ strvec_push(&cmd->args, out);
cmd->git_cmd = 1;
cmd->out = -1;
}
@@ -196,8 +185,9 @@ static void prepare_pack_objects(struct child_process *cmd,
* Write oid to the given struct child_process's stdin, starting it first if
* necessary.
*/
-static int write_oid(const struct object_id *oid, struct packed_git *pack,
- uint32_t pos, void *data)
+static int write_oid(const struct object_id *oid,
+ struct packed_git *pack UNUSED,
+ uint32_t pos UNUSED, void *data)
{
struct child_process *cmd = data;
@@ -217,16 +207,21 @@ static struct {
} exts[] = {
{".pack"},
{".rev", 1},
+ {".mtimes", 1},
{".bitmap", 1},
{".promisor", 1},
{".idx"},
};
-static unsigned populate_pack_exts(char *name)
+struct generated_pack_data {
+ struct tempfile *tempfiles[ARRAY_SIZE(exts)];
+};
+
+static struct generated_pack_data *populate_pack_exts(const char *name)
{
struct stat statbuf;
struct strbuf path = STRBUF_INIT;
- unsigned ret = 0;
+ struct generated_pack_data *data = xcalloc(1, sizeof(*data));
int i;
for (i = 0; i < ARRAY_SIZE(exts); i++) {
@@ -236,11 +231,11 @@ static unsigned populate_pack_exts(char *name)
if (stat(path.buf, &statbuf))
continue;
- ret |= (1 << i);
+ data->tempfiles[i] = register_tempfile(path.buf);
}
strbuf_release(&path);
- return ret;
+ return data;
}
static void repack_promisor_objects(const struct pack_objects_args *args,
@@ -250,7 +245,7 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
FILE *out;
struct strbuf line = STRBUF_INIT;
- prepare_pack_objects(&cmd, args);
+ prepare_pack_objects(&cmd, args, packtmp);
cmd.in = -1;
/*
@@ -295,7 +290,7 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
line.buf);
write_promisor_file(promisor_name, NULL, 0);
- item->util = (void *)(uintptr_t)populate_pack_exts(item->string);
+ item->util = populate_pack_exts(item->string);
free(promisor_name);
}
@@ -304,9 +299,6 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
die(_("could not finish pack-objects to repack promisor objects"));
}
-#define ALL_INTO_ONE 1
-#define LOOSEN_UNREACHABLE 2
-
struct pack_geometry {
struct packed_git **pack;
uint32_t pack_nr, pack_alloc;
@@ -332,16 +324,48 @@ static int geometry_cmp(const void *va, const void *vb)
return 0;
}
-static void init_pack_geometry(struct pack_geometry **geometry_p)
+static void init_pack_geometry(struct pack_geometry **geometry_p,
+ struct string_list *existing_kept_packs,
+ const struct pack_objects_args *args)
{
struct packed_git *p;
struct pack_geometry *geometry;
+ struct strbuf buf = STRBUF_INIT;
*geometry_p = xcalloc(1, sizeof(struct pack_geometry));
geometry = *geometry_p;
for (p = get_all_packs(the_repository); p; p = p->next) {
- if (!pack_kept_objects && p->pack_keep)
+ if (args->local && !p->pack_local)
+ /*
+ * When asked to only repack local packfiles we skip
+ * over any packfiles that are borrowed from alternate
+ * object directories.
+ */
+ continue;
+
+ if (!pack_kept_objects) {
+ /*
+ * Any pack that has its pack_keep bit set will appear
+ * in existing_kept_packs below, but this saves us from
+ * doing a more expensive check.
+ */
+ if (p->pack_keep)
+ continue;
+
+ /*
+ * The pack may be kept via the --keep-pack option;
+ * check 'existing_kept_packs' to determine whether to
+ * ignore it.
+ */
+ strbuf_reset(&buf);
+ strbuf_addstr(&buf, pack_basename(p));
+ strbuf_strip_suffix(&buf, ".pack");
+
+ if (string_list_has_string(existing_kept_packs, buf.buf))
+ continue;
+ }
+ if (p->is_cruft)
continue;
ALLOC_GROW(geometry->pack,
@@ -353,6 +377,7 @@ static void init_pack_geometry(struct pack_geometry **geometry_p)
}
QSORT(geometry->pack, geometry->pack_nr, geometry_cmp);
+ strbuf_release(&buf);
}
static void split_pack_geometry(struct pack_geometry *geometry, int factor)
@@ -432,8 +457,10 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor)
geometry->split = split;
}
-static struct packed_git *get_largest_active_pack(struct pack_geometry *geometry)
+static struct packed_git *get_preferred_pack(struct pack_geometry *geometry)
{
+ uint32_t i;
+
if (!geometry) {
/*
* No geometry means either an all-into-one repack (in which
@@ -448,7 +475,21 @@ static struct packed_git *get_largest_active_pack(struct pack_geometry *geometry
}
if (geometry->split == geometry->pack_nr)
return NULL;
- return geometry->pack[geometry->pack_nr - 1];
+
+ /*
+ * The preferred pack is the largest pack above the split line. In
+ * other words, it is the largest pack that does not get rolled up in
+ * the geometric repack.
+ */
+ for (i = geometry->pack_nr; i > geometry->split; i--)
+ /*
+ * A pack that is not local would never be included in a
+ * multi-pack index. We thus skip over any non-local packs.
+ */
+ if (geometry->pack[i - 1]->pack_local)
+ return geometry->pack[i - 1];
+
+ return NULL;
}
static void clear_pack_geometry(struct pack_geometry *geometry)
@@ -468,9 +509,9 @@ struct midx_snapshot_ref_data {
int preferred;
};
-static int midx_snapshot_ref_one(const char *refname,
+static int midx_snapshot_ref_one(const char *refname UNUSED,
const struct object_id *oid,
- int flag, void *_data)
+ int flag UNUSED, void *_data)
{
struct midx_snapshot_ref_data *data = _data;
struct object_id peeled;
@@ -542,15 +583,37 @@ static void midx_included_packs(struct string_list *include,
for (i = geometry->split; i < geometry->pack_nr; i++) {
struct packed_git *p = geometry->pack[i];
+ /*
+ * The multi-pack index never refers to packfiles part
+ * of an alternate object database, so we skip these.
+ * While git-multi-pack-index(1) would silently ignore
+ * them anyway, this allows us to skip executing the
+ * command completely when we have only non-local
+ * packfiles.
+ */
+ if (!p->pack_local)
+ continue;
+
strbuf_addstr(&buf, pack_basename(p));
strbuf_strip_suffix(&buf, ".pack");
strbuf_addstr(&buf, ".idx");
string_list_insert(include, strbuf_detach(&buf, NULL));
}
+
+ for_each_string_list_item(item, existing_nonkept_packs) {
+ if (!((uintptr_t)item->util & CRUFT_PACK)) {
+ /*
+ * no need to check DELETE_PACK, since we're not
+ * doing an ALL_INTO_ONE repack
+ */
+ continue;
+ }
+ string_list_insert(include, xstrfmt("%s.idx", item->string));
+ }
} else {
for_each_string_list_item(item, existing_nonkept_packs) {
- if (item->util)
+ if ((uintptr_t)item->util & DELETE_PACK)
continue;
string_list_insert(include, xstrfmt("%s.idx", item->string));
}
@@ -564,7 +627,7 @@ static int write_midx_included_packs(struct string_list *include,
{
struct child_process cmd = CHILD_PROCESS_INIT;
struct string_list_item *item;
- struct packed_git *largest = get_largest_active_pack(geometry);
+ struct packed_git *preferred = get_preferred_pack(geometry);
FILE *in;
int ret;
@@ -585,9 +648,9 @@ static int write_midx_included_packs(struct string_list *include,
if (write_bitmaps)
strvec_push(&cmd.args, "--bitmap");
- if (largest)
+ if (preferred)
strvec_pushf(&cmd.args, "--preferred-pack=%s",
- pack_basename(largest));
+ pack_basename(preferred));
if (refs_snapshot)
strvec_pushf(&cmd.args, "--refs-snapshot=%s", refs_snapshot);
@@ -604,12 +667,118 @@ static int write_midx_included_packs(struct string_list *include,
return finish_command(&cmd);
}
+static void remove_redundant_bitmaps(struct string_list *include,
+ const char *packdir)
+{
+ struct strbuf path = STRBUF_INIT;
+ struct string_list_item *item;
+ size_t packdir_len;
+
+ strbuf_addstr(&path, packdir);
+ strbuf_addch(&path, '/');
+ packdir_len = path.len;
+
+ /*
+ * Remove any pack bitmaps corresponding to packs which are now
+ * included in the MIDX.
+ */
+ for_each_string_list_item(item, include) {
+ strbuf_addstr(&path, item->string);
+ strbuf_strip_suffix(&path, ".idx");
+ strbuf_addstr(&path, ".bitmap");
+
+ if (unlink(path.buf) && errno != ENOENT)
+ warning_errno(_("could not remove stale bitmap: %s"),
+ path.buf);
+
+ strbuf_setlen(&path, packdir_len);
+ }
+ strbuf_release(&path);
+}
+
+static int write_cruft_pack(const struct pack_objects_args *args,
+ const char *destination,
+ const char *pack_prefix,
+ const char *cruft_expiration,
+ struct string_list *names,
+ struct string_list *existing_packs,
+ struct string_list *existing_kept_packs)
+{
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ struct strbuf line = STRBUF_INIT;
+ struct string_list_item *item;
+ FILE *in, *out;
+ int ret;
+ const char *scratch;
+ int local = skip_prefix(destination, packdir, &scratch);
+
+ prepare_pack_objects(&cmd, args, destination);
+
+ strvec_push(&cmd.args, "--cruft");
+ if (cruft_expiration)
+ strvec_pushf(&cmd.args, "--cruft-expiration=%s",
+ cruft_expiration);
+
+ strvec_push(&cmd.args, "--honor-pack-keep");
+ strvec_push(&cmd.args, "--non-empty");
+ strvec_push(&cmd.args, "--max-pack-size=0");
+
+ cmd.in = -1;
+
+ ret = start_command(&cmd);
+ if (ret)
+ return ret;
+
+ /*
+ * names has a confusing double use: it both provides the list
+ * of just-written new packs, and accepts the name of the cruft
+ * pack we are writing.
+ *
+ * By the time it is read here, it contains only the pack(s)
+ * that were just written, which is exactly the set of packs we
+ * want to consider kept.
+ *
+ * If `--expire-to` is given, the double-use served by `names`
+ * ensures that the pack written to `--expire-to` excludes any
+ * objects contained in the cruft pack.
+ */
+ in = xfdopen(cmd.in, "w");
+ for_each_string_list_item(item, names)
+ fprintf(in, "%s-%s.pack\n", pack_prefix, item->string);
+ for_each_string_list_item(item, existing_packs)
+ fprintf(in, "-%s.pack\n", item->string);
+ for_each_string_list_item(item, existing_kept_packs)
+ fprintf(in, "%s.pack\n", item->string);
+ fclose(in);
+
+ out = xfdopen(cmd.out, "r");
+ while (strbuf_getline_lf(&line, out) != EOF) {
+ struct string_list_item *item;
+
+ if (line.len != the_hash_algo->hexsz)
+ die(_("repack: Expecting full hex object ID lines only "
+ "from pack-objects."));
+ /*
+ * avoid putting packs written outside of the repository in the
+ * list of names
+ */
+ if (local) {
+ item = string_list_append(names, line.buf);
+ item->util = populate_pack_exts(line.buf);
+ }
+ }
+ fclose(out);
+
+ strbuf_release(&line);
+
+ return finish_command(&cmd);
+}
+
int cmd_repack(int argc, const char **argv, const char *prefix)
{
struct child_process cmd = CHILD_PROCESS_INIT;
struct string_list_item *item;
struct string_list names = STRING_LIST_INIT_DUP;
- struct string_list rollback = STRING_LIST_INIT_NODUP;
struct string_list existing_nonkept_packs = STRING_LIST_INIT_DUP;
struct string_list existing_kept_packs = STRING_LIST_INIT_DUP;
struct pack_geometry *geometry = NULL;
@@ -620,14 +789,16 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
int show_progress;
/* variables to be filled by option parsing */
- int pack_everything = 0;
int delete_redundant = 0;
const char *unpack_unreachable = NULL;
int keep_unreachable = 0;
struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
struct pack_objects_args po_args = {NULL};
+ struct pack_objects_args cruft_po_args = {NULL};
int geometric_factor = 0;
int write_midx = 0;
+ const char *cruft_expiration = NULL;
+ const char *expire_to = NULL;
struct option builtin_repack_options[] = {
OPT_BIT('a', NULL, &pack_everything,
@@ -635,6 +806,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
OPT_BIT('A', NULL, &pack_everything,
N_("same as -a, and turn unreachable objects loose"),
LOOSEN_UNREACHABLE | ALL_INTO_ONE),
+ OPT_BIT(0, "cruft", &pack_everything,
+ N_("same as -a, pack unreachable cruft objects separately"),
+ PACK_CRUFT),
+ OPT_STRING(0, "cruft-expiration", &cruft_expiration, N_("approxidate"),
+ N_("with --cruft, expire objects older than this")),
OPT_BOOL('d', NULL, &delete_redundant,
N_("remove redundant packs, and run git-prune-packed")),
OPT_BOOL('f', NULL, &po_args.no_reuse_delta,
@@ -672,10 +848,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
N_("find a geometric progression with factor <N>")),
OPT_BOOL('m', "write-midx", &write_midx,
N_("write a multi-pack index of the resulting packs")),
+ OPT_STRING(0, "expire-to", &expire_to, N_("dir"),
+ N_("pack prefix to store a pack containing pruned objects")),
OPT_END()
};
- git_config(repack_config, NULL);
+ git_config(repack_config, &cruft_po_args);
argc = parse_options(argc, argv, prefix, builtin_repack_options,
git_repack_usage, 0);
@@ -687,6 +865,15 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "-A");
+ if (pack_everything & PACK_CRUFT) {
+ pack_everything |= ALL_INTO_ONE;
+
+ if (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE))
+ die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-A");
+ if (keep_unreachable)
+ die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-k");
+ }
+
if (write_bitmaps < 0) {
if (!write_midx &&
(!(pack_everything & ALL_INTO_ONE) || !is_bare_repository()))
@@ -702,6 +889,18 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx)
die(_(incremental_bitmap_conflict_error));
+ if (write_bitmaps && po_args.local && has_alt_odb(the_repository)) {
+ /*
+ * When asked to do a local repack, but we have
+ * packfiles that are inherited from an alternate, then
+ * we cannot guarantee that the multi-pack-index would
+ * have full coverage of all objects. We thus disable
+ * writing bitmaps in that case.
+ */
+ warning(_("disabling bitmap writing, as some objects are not being packed"));
+ write_bitmaps = 0;
+ }
+
if (write_midx && write_bitmaps) {
struct strbuf path = STRBUF_INIT;
@@ -714,20 +913,21 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
strbuf_release(&path);
}
+ packdir = mkpathdup("%s/pack", get_object_directory());
+ packtmp_name = xstrfmt(".tmp-%d-pack", (int)getpid());
+ packtmp = mkpathdup("%s/%s", packdir, packtmp_name);
+
+ collect_pack_filenames(&existing_nonkept_packs, &existing_kept_packs,
+ &keep_pack_list);
+
if (geometric_factor) {
if (pack_everything)
die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a");
- init_pack_geometry(&geometry);
+ init_pack_geometry(&geometry, &existing_kept_packs, &po_args);
split_pack_geometry(geometry, geometric_factor);
}
- packdir = mkpathdup("%s/pack", get_object_directory());
- packtmp_name = xstrfmt(".tmp-%d-pack", (int)getpid());
- packtmp = mkpathdup("%s/%s", packdir, packtmp_name);
-
- sigchain_push_common(remove_pack_on_signal);
-
- prepare_pack_objects(&cmd, &po_args);
+ prepare_pack_objects(&cmd, &po_args, packtmp);
show_progress = !po_args.quiet && isatty(2);
@@ -753,7 +953,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
strvec_push(&cmd.args, "--reflog");
strvec_push(&cmd.args, "--indexed-objects");
}
- if (has_promisor_remote())
+ if (repo_has_promisor_remote(the_repository))
strvec_push(&cmd.args, "--exclude-promisor-objects");
if (!write_midx) {
if (write_bitmaps > 0)
@@ -764,13 +964,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (use_delta_islands)
strvec_push(&cmd.args, "--delta-islands");
- collect_pack_filenames(&existing_nonkept_packs, &existing_kept_packs,
- &keep_pack_list);
-
if (pack_everything & ALL_INTO_ONE) {
repack_promisor_objects(&po_args, &names);
- if (existing_nonkept_packs.nr && delete_redundant) {
+ if (existing_nonkept_packs.nr && delete_redundant &&
+ !(pack_everything & PACK_CRUFT)) {
for_each_string_list_item(item, &names) {
strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack",
packtmp_name, item->string);
@@ -802,7 +1000,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
ret = start_command(&cmd);
if (ret)
- return ret;
+ goto cleanup;
if (geometry) {
FILE *in = xfdopen(cmd.in, "w");
@@ -820,59 +1018,123 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
out = xfdopen(cmd.out, "r");
while (strbuf_getline_lf(&line, out) != EOF) {
+ struct string_list_item *item;
+
if (line.len != the_hash_algo->hexsz)
die(_("repack: Expecting full hex object ID lines only from pack-objects."));
- string_list_append(&names, line.buf);
+ item = string_list_append(&names, line.buf);
+ item->util = populate_pack_exts(item->string);
}
+ strbuf_release(&line);
fclose(out);
ret = finish_command(&cmd);
if (ret)
- return ret;
+ goto cleanup;
if (!names.nr && !po_args.quiet)
printf_ln(_("Nothing new to pack."));
- for_each_string_list_item(item, &names) {
- item->util = (void *)(uintptr_t)populate_pack_exts(item->string);
+ if (pack_everything & PACK_CRUFT) {
+ const char *pack_prefix;
+ if (!skip_prefix(packtmp, packdir, &pack_prefix))
+ die(_("pack prefix %s does not begin with objdir %s"),
+ packtmp, packdir);
+ if (*pack_prefix == '/')
+ pack_prefix++;
+
+ if (!cruft_po_args.window)
+ cruft_po_args.window = po_args.window;
+ if (!cruft_po_args.window_memory)
+ cruft_po_args.window_memory = po_args.window_memory;
+ if (!cruft_po_args.depth)
+ cruft_po_args.depth = po_args.depth;
+ if (!cruft_po_args.threads)
+ cruft_po_args.threads = po_args.threads;
+
+ cruft_po_args.local = po_args.local;
+ cruft_po_args.quiet = po_args.quiet;
+
+ ret = write_cruft_pack(&cruft_po_args, packtmp, pack_prefix,
+ cruft_expiration, &names,
+ &existing_nonkept_packs,
+ &existing_kept_packs);
+ if (ret)
+ goto cleanup;
+
+ if (delete_redundant && expire_to) {
+ /*
+ * If `--expire-to` is given with `-d`, it's possible
+ * that we're about to prune some objects. With cruft
+ * packs, pruning is implicit: any objects from existing
+ * packs that weren't picked up by new packs are removed
+ * when their packs are deleted.
+ *
+ * Generate an additional cruft pack, with one twist:
+ * `names` now includes the name of the cruft pack
+ * written in the previous step. So the contents of
+ * _this_ cruft pack exclude everything contained in the
+ * existing cruft pack (that is, all of the unreachable
+ * objects which are no older than
+ * `--cruft-expiration`).
+ *
+ * To make this work, cruft_expiration must become NULL
+ * so that this cruft pack doesn't actually prune any
+ * objects. If it were non-NULL, this call would always
+ * generate an empty pack (since every object not in the
+ * cruft pack generated above will have an mtime older
+ * than the expiration).
+ */
+ ret = write_cruft_pack(&cruft_po_args, expire_to,
+ pack_prefix,
+ NULL,
+ &names,
+ &existing_nonkept_packs,
+ &existing_kept_packs);
+ if (ret)
+ goto cleanup;
+ }
}
+ string_list_sort(&names);
+
close_object_store(the_repository->objects);
/*
* Ok we have prepared all new packfiles.
*/
for_each_string_list_item(item, &names) {
+ struct generated_pack_data *data = item->util;
+
for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
- char *fname, *fname_old;
+ char *fname;
fname = mkpathdup("%s/pack-%s%s",
packdir, item->string, exts[ext].name);
- fname_old = mkpathdup("%s-%s%s",
- packtmp, item->string, exts[ext].name);
- if (((uintptr_t)item->util) & ((uintptr_t)1 << ext)) {
+ if (data->tempfiles[ext]) {
+ const char *fname_old = get_tempfile_path(data->tempfiles[ext]);
struct stat statbuffer;
+
if (!stat(fname_old, &statbuffer)) {
statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
chmod(fname_old, statbuffer.st_mode);
}
- if (rename(fname_old, fname))
- die_errno(_("renaming '%s' failed"), fname_old);
+ if (rename_tempfile(&data->tempfiles[ext], fname))
+ die_errno(_("renaming pack to '%s' failed"), fname);
} else if (!exts[ext].optional)
- die(_("missing required file: %s"), fname_old);
+ die(_("pack-objects did not write a '%s' file for pack %s-%s"),
+ exts[ext].name, packtmp, item->string);
else if (unlink(fname) < 0 && errno != ENOENT)
die_errno(_("could not unlink: %s"), fname);
free(fname);
- free(fname_old);
}
}
/* End of pack replacement. */
if (delete_redundant && pack_everything & ALL_INTO_ONE) {
const int hexsz = the_hash_algo->hexsz;
- string_list_sort(&names);
for_each_string_list_item(item, &existing_nonkept_packs) {
char *sha1;
size_t len = strlen(item->string);
@@ -885,7 +1147,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
* was given) and that we will actually delete this pack
* (if `-d` was given).
*/
- item->util = (void*)(intptr_t)!string_list_has_string(&names, sha1);
+ if (!string_list_has_string(&names, sha1))
+ item->util = (void*)(uintptr_t)((size_t)item->util | DELETE_PACK);
}
}
@@ -898,10 +1161,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL,
show_progress, write_bitmaps > 0);
+ if (!ret && write_bitmaps)
+ remove_redundant_bitmaps(&include, packdir);
+
string_list_clear(&include, 0);
if (ret)
- return ret;
+ goto cleanup;
}
reprepare_packed_git(the_repository);
@@ -909,7 +1175,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (delete_redundant) {
int opts = 0;
for_each_string_list_item(item, &existing_nonkept_packs) {
- if (!item->util)
+ if (!((uintptr_t)item->util & DELETE_PACK))
continue;
remove_redundant_pack(packdir, item->string);
}
@@ -928,6 +1194,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
strbuf_addstr(&buf, pack_basename(p));
strbuf_strip_suffix(&buf, ".pack");
+ if ((p->pack_keep) ||
+ (string_list_has_string(&existing_kept_packs,
+ buf.buf)))
+ continue;
+
remove_redundant_pack(packdir, buf.buf);
}
strbuf_release(&buf);
@@ -945,7 +1216,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (run_update_server_info)
update_server_info(0);
- remove_temporary_files();
if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0)) {
unsigned flags = 0;
@@ -954,12 +1224,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
write_midx_file(get_object_directory(), NULL, NULL, flags);
}
- string_list_clear(&names, 0);
- string_list_clear(&rollback, 0);
+cleanup:
+ string_list_clear(&names, 1);
string_list_clear(&existing_nonkept_packs, 0);
string_list_clear(&existing_kept_packs, 0);
clear_pack_geometry(geometry);
- strbuf_release(&line);
- return 0;
+ return ret;
}
diff --git a/builtin/replace.c b/builtin/replace.c
index 5068f4f0b2..9ceaa25233 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -8,15 +8,23 @@
* git-tag.sh and mktag.c by Linus Torvalds.
*/
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
+#include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "refs.h"
#include "parse-options.h"
+#include "path.h"
#include "run-command.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "replace-object.h"
#include "repository.h"
#include "tag.h"
+#include "wildmatch.h"
static const char * const git_replace_usage[] = {
N_("git replace [-f] <object> <replacement>"),
@@ -54,7 +62,7 @@ static int show_reference(struct repository *r, const char *refname,
struct object_id object;
enum object_type obj_type, repl_type;
- if (get_oid(refname, &object))
+ if (repo_get_oid(r, refname, &object))
return error(_("failed to resolve '%s' as a valid ref"), refname);
obj_type = oid_object_info(r, &object, NULL);
@@ -72,7 +80,7 @@ static int list_replace_refs(const char *pattern, const char *format)
{
struct show_data data;
- if (pattern == NULL)
+ if (!pattern)
pattern = "*";
data.pattern = pattern;
@@ -106,12 +114,13 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
size_t base_len;
int had_error = 0;
struct object_id oid;
+ const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
strbuf_addstr(&ref, git_replace_ref_base);
base_len = ref.len;
for (p = argv; *p; p++) {
- if (get_oid(*p, &oid)) {
+ if (repo_get_oid(the_repository, *p, &oid)) {
error("failed to resolve '%s' as a valid ref", *p);
had_error = 1;
continue;
@@ -147,6 +156,8 @@ static int check_ref_valid(struct object_id *object,
struct strbuf *ref,
int force)
{
+ const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
+
strbuf_reset(ref);
strbuf_addf(ref, "%s%s", git_replace_ref_base, oid_to_hex(object));
if (check_refname_format(ref->buf, 0))
@@ -203,10 +214,10 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f
{
struct object_id object, repl;
- if (get_oid(object_ref, &object))
+ if (repo_get_oid(the_repository, object_ref, &object))
return error(_("failed to resolve '%s' as a valid ref"),
object_ref);
- if (get_oid(replace_ref, &repl))
+ if (repo_get_oid(the_repository, replace_ref, &repl))
return error(_("failed to resolve '%s' as a valid ref"),
replace_ref);
@@ -317,7 +328,7 @@ static int edit_and_replace(const char *object_ref, int force, int raw)
struct object_id old_oid, new_oid, prev;
struct strbuf ref = STRBUF_INIT;
- if (get_oid(object_ref, &old_oid) < 0)
+ if (repo_get_oid(the_repository, object_ref, &old_oid) < 0)
return error(_("not a valid object name: '%s'"), object_ref);
type = oid_object_info(the_repository, &old_oid, NULL);
@@ -372,7 +383,7 @@ static int replace_parents(struct strbuf *buf, int argc, const char **argv)
struct object_id oid;
struct commit *commit;
- if (get_oid(argv[i], &oid) < 0) {
+ if (repo_get_oid(the_repository, argv[i], &oid) < 0) {
strbuf_release(&new_parents);
return error(_("not a valid object name: '%s'"),
argv[i]);
@@ -419,7 +430,7 @@ static int check_one_mergetag(struct commit *commit,
/* iterate over new parents */
for (i = 1; i < mergetag_data->argc; i++) {
struct object_id oid;
- if (get_oid(mergetag_data->argv[i], &oid) < 0)
+ if (repo_get_oid(the_repository, mergetag_data->argv[i], &oid) < 0)
return error(_("not a valid object name: '%s'"),
mergetag_data->argv[i]);
if (oideq(get_tagged_oid(tag), &oid))
@@ -449,15 +460,15 @@ static int create_graft(int argc, const char **argv, int force, int gentle)
const char *buffer;
unsigned long size;
- if (get_oid(old_ref, &old_oid) < 0)
+ if (repo_get_oid(the_repository, old_ref, &old_oid) < 0)
return error(_("not a valid object name: '%s'"), old_ref);
commit = lookup_commit_reference(the_repository, &old_oid);
if (!commit)
return error(_("could not parse %s"), old_ref);
- buffer = get_commit_buffer(commit, &size);
+ buffer = repo_get_commit_buffer(the_repository, commit, &size);
strbuf_add(&buf, buffer, size);
- unuse_commit_buffer(commit, buffer);
+ repo_unuse_commit_buffer(the_repository, commit, buffer);
if (replace_parents(&buf, argc - 1, &argv[1]) < 0) {
strbuf_release(&buf);
@@ -556,7 +567,7 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
OPT_END()
};
- read_replace_refs = 0;
+ disable_replace_refs();
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
diff --git a/builtin/rerere.c b/builtin/rerere.c
index 83d7a778e3..07a9d37275 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -1,8 +1,9 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "dir.h"
+#include "gettext.h"
#include "parse-options.h"
+#include "repository.h"
#include "string-list.h"
#include "rerere.h"
#include "xdiff/xdiff.h"
@@ -10,11 +11,11 @@
#include "pathspec.h"
static const char * const rerere_usage[] = {
- N_("git rerere [clear | forget <path>... | status | remaining | diff | gc]"),
+ N_("git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"),
NULL,
};
-static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
+static int outf(void *dummy UNUSED, mmbuffer_t *ptr, int nbuf)
{
int i;
for (i = 0; i < nbuf; i++)
diff --git a/builtin/reset.c b/builtin/reset.c
index 344fff8f3a..7f18dc03b8 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -7,9 +7,14 @@
*
* Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
+#include "advice.h"
#include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
#include "lockfile.h"
#include "tag.h"
#include "object.h"
@@ -20,12 +25,19 @@
#include "diffcore.h"
#include "tree.h"
#include "branch.h"
+#include "object-name.h"
#include "parse-options.h"
+#include "path.h"
#include "unpack-trees.h"
#include "cache-tree.h"
+#include "setup.h"
+#include "sparse-index.h"
#include "submodule.h"
#include "submodule-config.h"
+#include "trace.h"
+#include "trace2.h"
#include "dir.h"
+#include "add-interactive.h"
#define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
@@ -73,20 +85,22 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t
case HARD:
opts.update = 1;
opts.reset = UNPACK_RESET_OVERWRITE_UNTRACKED;
+ opts.skip_cache_tree_update = 1;
break;
case MIXED:
opts.reset = UNPACK_RESET_PROTECT_UNTRACKED;
+ opts.skip_cache_tree_update = 1;
/* but opts.update=0, so working tree not updated */
break;
default:
BUG("invalid reset_type passed to reset_index");
}
- read_cache_unmerged();
+ repo_read_index_unmerged(the_repository);
if (reset_type == KEEP) {
struct object_id head_oid;
- if (get_oid("HEAD", &head_oid))
+ if (repo_get_oid(the_repository, "HEAD", &head_oid))
return error(_("You do not have a valid HEAD."));
if (!fill_tree_descriptor(the_repository, desc + nr, &head_oid))
return error(_("Failed to find tree of HEAD."));
@@ -121,7 +135,7 @@ static void print_new_head_line(struct commit *commit)
struct strbuf buf = STRBUF_INIT;
printf(_("HEAD is now at %s"),
- find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+ repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
if (buf.len > 0)
@@ -131,7 +145,8 @@ static void print_new_head_line(struct commit *commit)
}
static void update_index_from_diff(struct diff_queue_struct *q,
- struct diff_options *opt, void *data)
+ struct diff_options *opt UNUSED,
+ void *data)
{
int i;
int intent_to_add = *(int *)data;
@@ -143,7 +158,7 @@ static void update_index_from_diff(struct diff_queue_struct *q,
struct cache_entry *ce;
if (!is_in_reset_tree && !intent_to_add) {
- remove_file_from_cache(one->path);
+ remove_file_from_index(&the_index, one->path);
continue;
}
@@ -158,8 +173,8 @@ static void update_index_from_diff(struct diff_queue_struct *q,
* if this entry is outside the sparse cone - this is necessary
* to properly construct the reset sparse directory.
*/
- pos = cache_name_pos(one->path, strlen(one->path));
- if ((pos >= 0 && ce_skip_worktree(active_cache[pos])) ||
+ pos = index_name_pos(&the_index, one->path, strlen(one->path));
+ if ((pos >= 0 && ce_skip_worktree(the_index.cache[pos])) ||
(pos < 0 && !path_in_sparse_checkout(one->path, &the_index)))
ce->ce_flags |= CE_SKIP_WORKTREE;
@@ -170,92 +185,11 @@ static void update_index_from_diff(struct diff_queue_struct *q,
ce->ce_flags |= CE_INTENT_TO_ADD;
set_object_name_for_intent_to_add_entry(ce);
}
- add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
+ add_index_entry(&the_index, ce,
+ ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
}
}
-static int pathspec_needs_expanded_index(const struct pathspec *pathspec)
-{
- unsigned int i, pos;
- int res = 0;
- char *skip_worktree_seen = NULL;
-
- /*
- * When using a magic pathspec, assume for the sake of simplicity that
- * the index needs to be expanded to match all matchable files.
- */
- if (pathspec->magic)
- return 1;
-
- for (i = 0; i < pathspec->nr; i++) {
- struct pathspec_item item = pathspec->items[i];
-
- /*
- * If the pathspec item has a wildcard, the index should be expanded
- * if the pathspec has the possibility of matching a subset of entries inside
- * of a sparse directory (but not the entire directory).
- *
- * If the pathspec item is a literal path, the index only needs to be expanded
- * if a) the pathspec isn't in the sparse checkout cone (to make sure we don't
- * expand for in-cone files) and b) it doesn't match any sparse directories
- * (since we can reset whole sparse directories without expanding them).
- */
- if (item.nowildcard_len < item.len) {
- /*
- * Special case: if the pattern is a path inside the cone
- * followed by only wildcards, the pattern cannot match
- * partial sparse directories, so we know we don't need to
- * expand the index.
- *
- * Examples:
- * - in-cone/foo***: doesn't need expanded index
- * - not-in-cone/bar*: may need expanded index
- * - **.c: may need expanded index
- */
- if (strspn(item.original + item.nowildcard_len, "*") == item.len - item.nowildcard_len &&
- path_in_cone_mode_sparse_checkout(item.original, &the_index))
- continue;
-
- for (pos = 0; pos < active_nr; pos++) {
- struct cache_entry *ce = active_cache[pos];
-
- if (!S_ISSPARSEDIR(ce->ce_mode))
- continue;
-
- /*
- * If the pre-wildcard length is longer than the sparse
- * directory name and the sparse directory is the first
- * component of the pathspec, need to expand the index.
- */
- if (item.nowildcard_len > ce_namelen(ce) &&
- !strncmp(item.original, ce->name, ce_namelen(ce))) {
- res = 1;
- break;
- }
-
- /*
- * If the pre-wildcard length is shorter than the sparse
- * directory and the pathspec does not match the whole
- * directory, need to expand the index.
- */
- if (!strncmp(item.original, ce->name, item.nowildcard_len) &&
- wildmatch(item.original, ce->name, 0)) {
- res = 1;
- break;
- }
- }
- } else if (!path_in_cone_mode_sparse_checkout(item.original, &the_index) &&
- !matches_skip_worktree(pathspec, i, &skip_worktree_seen))
- res = 1;
-
- if (res > 0)
- break;
- }
-
- free(skip_worktree_seen);
- return res;
-}
-
static int read_from_tree(const struct pathspec *pathspec,
struct object_id *tree_oid,
int intent_to_add)
@@ -273,7 +207,7 @@ static int read_from_tree(const struct pathspec *pathspec,
opt.change = diff_change;
opt.add_remove = diff_addremove;
- if (pathspec->nr && the_index.sparse_index && pathspec_needs_expanded_index(pathspec))
+ if (pathspec->nr && pathspec_needs_expanded_index(&the_index, pathspec))
ensure_full_index(&the_index);
if (do_diff_cache(tree_oid, &opt))
@@ -300,7 +234,7 @@ static void set_reflog_message(struct strbuf *sb, const char *action,
static void die_if_unmerged_cache(int reset_type)
{
- if (is_merge() || unmerged_cache())
+ if (is_merge() || unmerged_index(&the_index))
die(_("Cannot do a %s reset in the middle of a merge."),
_(reset_type_names[reset_type]));
@@ -337,8 +271,8 @@ static void parse_args(struct pathspec *pathspec,
* has to be unambiguous. If there is a single argument, it
* can not be a tree
*/
- else if ((!argv[1] && !get_oid_committish(argv[0], &unused)) ||
- (argv[1] && !get_oid_treeish(argv[0], &unused))) {
+ else if ((!argv[1] && !repo_get_oid_committish(the_repository, argv[0], &unused)) ||
+ (argv[1] && !repo_get_oid_treeish(the_repository, argv[0], &unused))) {
/*
* Ok, argv[0] looks like a commit/tree; it should not
* be a filename.
@@ -365,9 +299,9 @@ static int reset_refs(const char *rev, const struct object_id *oid)
struct object_id *orig = NULL, oid_orig,
*old_orig = NULL, oid_old_orig;
- if (!get_oid("ORIG_HEAD", &oid_old_orig))
+ if (!repo_get_oid(the_repository, "ORIG_HEAD", &oid_old_orig))
old_orig = &oid_old_orig;
- if (!get_oid("HEAD", &oid_orig)) {
+ if (!repo_get_oid(the_repository, "HEAD", &oid_orig)) {
orig = &oid_orig;
set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
@@ -381,12 +315,13 @@ static int reset_refs(const char *rev, const struct object_id *oid)
return update_ref_status;
}
-static int git_reset_config(const char *var, const char *value, void *cb)
+static int git_reset_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (!strcmp(var, "submodule.recurse"))
return git_default_submodule_config(var, value, cb);
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
}
int cmd_reset(int argc, const char **argv, const char *prefix)
@@ -394,7 +329,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
int reset_type = NONE, update_ref_status = 0, quiet = 0;
int no_refresh = 0;
int patch_mode = 0, pathspec_file_nul = 0, unborn;
- const char *rev, *pathspec_from_file = NULL;
+ const char *rev;
+ char *pathspec_from_file = NULL;
struct object_id oid;
struct pathspec pathspec;
int intent_to_add = 0;
@@ -442,13 +378,14 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
- unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
+ unborn = !strcmp(rev, "HEAD") && repo_get_oid(the_repository, "HEAD",
+ &oid);
if (unborn) {
/* reset on unborn branch: treat as reset to empty tree */
oidcpy(&oid, the_hash_algo->empty_tree);
} else if (!pathspec.nr && !patch_mode) {
struct commit *commit;
- if (get_oid_committish(rev, &oid))
+ if (repo_get_oid_committish(the_repository, rev, &oid))
die(_("Failed to resolve '%s' as a valid revision."), rev);
commit = lookup_commit_reference(the_repository, &oid);
if (!commit)
@@ -456,7 +393,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
oidcpy(&oid, &commit->object.oid);
} else {
struct tree *tree;
- if (get_oid_treeish(rev, &oid))
+ if (repo_get_oid_treeish(the_repository, rev, &oid))
die(_("Failed to resolve '%s' as a valid tree."), rev);
tree = parse_tree_indirect(&oid);
if (!tree)
@@ -468,7 +405,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
if (reset_type != NONE)
die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}");
trace2_cmd_mode("patch-interactive");
- return run_add_interactive(rev, "--patch=reset", &pathspec);
+ update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, rev,
+ &pathspec);
+ goto cleanup;
}
/* git reset tree [--] paths... can be used to
@@ -502,7 +441,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
die(_("index file corrupt"));
/* Soft reset does not touch the index file nor the working tree
@@ -513,11 +452,14 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
if (reset_type != SOFT) {
struct lock_file lock = LOCK_INIT;
- hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &lock,
+ LOCK_DIE_ON_ERROR);
if (reset_type == MIXED) {
int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
- if (read_from_tree(&pathspec, &oid, intent_to_add))
- return 1;
+ if (read_from_tree(&pathspec, &oid, intent_to_add)) {
+ update_ref_status = 1;
+ goto cleanup;
+ }
the_index.updated_skipworktree = 1;
if (!no_refresh && get_git_work_tree()) {
uint64_t t_begin, t_delta_in_ms;
@@ -536,7 +478,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
char *ref = NULL;
int err;
- dwim_ref(rev, strlen(rev), &dummy, &ref, 0);
+ repo_dwim_ref(the_repository, rev, strlen(rev),
+ &dummy, &ref, 0);
if (ref && !starts_with(ref, "refs/"))
FREE_AND_NULL(ref);
@@ -563,5 +506,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
if (!pathspec.nr)
remove_branch_state(the_repository, 0);
+ discard_index(&the_index);
+
+cleanup:
+ clear_pathspec(&pathspec);
+ free(pathspec_from_file);
return update_ref_status;
}
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 572da1472e..ff715d6918 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -1,16 +1,20 @@
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "commit.h"
#include "diff.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "revision.h"
#include "list-objects.h"
#include "list-objects-filter.h"
#include "list-objects-filter-options.h"
#include "object.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-file.h"
+#include "object-store-ll.h"
#include "pack.h"
#include "pack-bitmap.h"
-#include "builtin.h"
#include "log-tree.h"
#include "graph.h"
#include "bisect.h"
@@ -20,7 +24,8 @@
#include "packfile.h"
static const char rev_list_usage[] =
-"git rev-list [<options>] <commit-id>... [-- <path>...]\n"
+"git rev-list [<options>] <commit>... [--] [<path>...]\n"
+"\n"
" limiting output:\n"
" --max-count=<n>\n"
" --max-age=<epoch>\n"
@@ -37,6 +42,7 @@ static const char rev_list_usage[] =
" --tags\n"
" --remotes\n"
" --stdin\n"
+" --exclude-hidden=[fetch|receive|uploadpack]\n"
" --quiet\n"
" ordering output:\n"
" --topo-order\n"
@@ -46,6 +52,7 @@ static const char rev_list_usage[] =
" --parents\n"
" --children\n"
" --objects | --objects-edge\n"
+" --disk-usage[=human]\n"
" --unpacked\n"
" --header | --pretty\n"
" --[no-]object-names\n"
@@ -81,6 +88,7 @@ static int arg_show_object_names = 1;
static int show_disk_usage;
static off_t total_disk_usage;
+static int human_readable;
static off_t get_object_disk_usage(struct object *obj)
{
@@ -130,7 +138,7 @@ static void show_commit(struct commit *commit, void *data)
if (!revs->graph)
fputs(get_revision_mark(revs, commit), stdout);
if (revs->abbrev_commit && revs->abbrev)
- fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev),
+ fputs(repo_find_unique_abbrev(the_repository, &commit->object.oid, revs->abbrev),
stdout);
else
fputs(oid_to_hex(&commit->object.oid), stdout);
@@ -213,10 +221,8 @@ static void show_commit(struct commit *commit, void *data)
static void finish_commit(struct commit *commit)
{
- if (commit->parents) {
- free_commit_list(commit->parents);
- commit->parents = NULL;
- }
+ free_commit_list(commit->parents);
+ commit->parents = NULL;
free_commit_buffer(the_repository->parsed_objects,
commit);
}
@@ -255,7 +261,8 @@ static inline void finish_object__ma(struct object *obj)
}
}
-static int finish_object(struct object *obj, const char *name, void *cb_data)
+static int finish_object(struct object *obj, const char *name UNUSED,
+ void *cb_data)
{
struct rev_list_info *info = cb_data;
if (oid_object_info_extended(the_repository, &obj->oid, NULL, 0) < 0) {
@@ -360,16 +367,27 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
static int show_object_fast(
const struct object_id *oid,
- enum object_type type,
- int exclude,
- uint32_t name_hash,
- struct packed_git *found_pack,
- off_t found_offset)
+ enum object_type type UNUSED,
+ int exclude UNUSED,
+ uint32_t name_hash UNUSED,
+ struct packed_git *found_pack UNUSED,
+ off_t found_offset UNUSED)
{
fprintf(stdout, "%s\n", oid_to_hex(oid));
return 1;
}
+static void print_disk_usage(off_t size)
+{
+ struct strbuf sb = STRBUF_INIT;
+ if (human_readable)
+ strbuf_humanise_bytes(&sb, size);
+ else
+ strbuf_addf(&sb, "%"PRIuMAX, (uintmax_t)size);
+ puts(sb.buf);
+ strbuf_release(&sb);
+}
+
static inline int parse_missing_action_value(const char *value)
{
if (!strcmp(value, "error")) {
@@ -475,6 +493,7 @@ static int try_bitmap_disk_usage(struct rev_info *revs,
int filter_provided_objects)
{
struct bitmap_index *bitmap_git;
+ off_t size_from_bitmap;
if (!show_disk_usage)
return -1;
@@ -483,8 +502,8 @@ static int try_bitmap_disk_usage(struct rev_info *revs,
if (!bitmap_git)
return -1;
- printf("%"PRIuMAX"\n",
- (uintmax_t)get_disk_usage_from_bitmap(bitmap_git, revs));
+ size_from_bitmap = get_disk_usage_from_bitmap(bitmap_git, revs);
+ print_disk_usage(size_from_bitmap);
return 0;
}
@@ -502,6 +521,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
int use_bitmap_index = 0;
int filter_provided_objects = 0;
const char *show_progress = NULL;
+ int ret = 0;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(rev_list_usage);
@@ -585,7 +605,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
}
if (!strcmp(arg, "--test-bitmap")) {
test_bitmap_walk(&revs);
- return 0;
+ goto cleanup;
}
if (skip_prefix(arg, "--progress=", &arg)) {
show_progress = arg;
@@ -625,7 +645,21 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
continue;
}
- if (!strcmp(arg, "--disk-usage")) {
+ if (skip_prefix(arg, "--disk-usage", &arg)) {
+ if (*arg == '=') {
+ if (!strcmp(++arg, "human")) {
+ human_readable = 1;
+ } else
+ die(_("invalid value for '%s': '%s', the only allowed format is '%s'"),
+ "--disk-usage=<format>", arg, "human");
+ } else if (*arg) {
+ /*
+ * Arguably should goto a label to continue chain of ifs?
+ * Doesn't matter unless we try to add --disk-usage-foo
+ * afterwards.
+ */
+ usage(rev_list_usage);
+ }
show_disk_usage = 1;
info.flags |= REV_LIST_QUIET;
continue;
@@ -674,11 +708,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (use_bitmap_index) {
if (!try_bitmap_count(&revs, filter_provided_objects))
- return 0;
+ goto cleanup;
if (!try_bitmap_disk_usage(&revs, filter_provided_objects))
- return 0;
+ goto cleanup;
if (!try_bitmap_traversal(&revs, filter_provided_objects))
- return 0;
+ goto cleanup;
}
if (prepare_revision_walk(&revs))
@@ -698,8 +732,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
find_bisection(&revs.commits, &reaches, &all, bisect_flags);
- if (bisect_show_vars)
- return show_bisect_vars(&info, reaches, all);
+ if (bisect_show_vars) {
+ ret = show_bisect_vars(&info, reaches, all);
+ goto cleanup;
+ }
}
if (filter_provided_objects) {
@@ -752,7 +788,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
}
if (show_disk_usage)
- printf("%"PRIuMAX"\n", (uintmax_t)total_disk_usage);
+ print_disk_usage(total_disk_usage);
- return 0;
+cleanup:
+ release_revisions(&revs);
+ return ret;
}
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 8480a59f57..434646b074 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -3,16 +3,24 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
+#include "abspath.h"
#include "config.h"
#include "commit.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
#include "refs.h"
#include "quote.h"
-#include "builtin.h"
+#include "object-name.h"
#include "parse-options.h"
+#include "path.h"
#include "diff.h"
+#include "read-cache-ll.h"
#include "revision.h"
+#include "setup.h"
#include "split-index.h"
#include "submodule.h"
#include "commit-reach.h"
@@ -39,7 +47,7 @@ static int abbrev_ref_strict;
static int output_sq;
static int stuck_long;
-static struct string_list *ref_excludes;
+static struct ref_exclusions ref_excludes = REF_EXCLUSIONS_INIT;
/*
* Some arguments are relevant "revision" arguments,
@@ -136,7 +144,9 @@ static void show_rev(int type, const struct object_id *oid, const char *name)
struct object_id discard;
char *full;
- switch (dwim_ref(name, strlen(name), &discard, &full, 0)) {
+ switch (repo_dwim_ref(the_repository, name,
+ strlen(name), &discard, &full,
+ 0)) {
case 0:
/*
* Not found -- not a ref. We could
@@ -147,9 +157,12 @@ static void show_rev(int type, const struct object_id *oid, const char *name)
*/
break;
case 1: /* happy */
- if (abbrev_ref)
+ if (abbrev_ref) {
+ char *old = full;
full = shorten_unambiguous_ref(full,
abbrev_ref_strict);
+ free(old);
+ }
show_with_type(type, full);
break;
default: /* ambiguous */
@@ -162,7 +175,8 @@ static void show_rev(int type, const struct object_id *oid, const char *name)
}
}
else if (abbrev)
- show_with_type(type, find_unique_abbrev(oid, abbrev));
+ show_with_type(type,
+ repo_find_unique_abbrev(the_repository, oid, abbrev));
else
show_with_type(type, oid_to_hex(oid));
}
@@ -187,7 +201,7 @@ static int show_default(void)
struct object_id oid;
def = NULL;
- if (!get_oid(s, &oid)) {
+ if (!repo_get_oid(the_repository, s, &oid)) {
show_rev(NORMAL, &oid, s);
return 1;
}
@@ -195,15 +209,17 @@ static int show_default(void)
return 0;
}
-static int show_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data)
+static int show_reference(const char *refname, const struct object_id *oid,
+ int flag UNUSED, void *cb_data UNUSED)
{
- if (ref_excluded(ref_excludes, refname))
+ if (ref_excluded(&ref_excludes, refname))
return 0;
show_rev(NORMAL, oid, refname);
return 0;
}
-static int anti_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data)
+static int anti_reference(const char *refname, const struct object_id *oid,
+ int flag UNUSED, void *cb_data UNUSED)
{
show_rev(REVERSED, oid, refname);
return 0;
@@ -277,7 +293,7 @@ static int try_difference(const char *arg)
return 0;
}
- if (!get_oid_committish(start, &start_oid) && !get_oid_committish(end, &end_oid)) {
+ if (!repo_get_oid_committish(the_repository, start, &start_oid) && !repo_get_oid_committish(the_repository, end, &end_oid)) {
show_rev(NORMAL, &end_oid, end);
show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start);
if (symmetric) {
@@ -289,7 +305,7 @@ static int try_difference(const char *arg)
*dotdot = '.';
return 0;
}
- exclude = get_merge_bases(a, b);
+ exclude = repo_get_merge_bases(the_repository, a, b);
while (exclude) {
struct commit *commit = pop_commit(&exclude);
show_rev(REVERSED, &commit->object.oid, NULL);
@@ -335,7 +351,7 @@ static int try_parent_shorthands(const char *arg)
return 0;
*dotdot = 0;
- if (get_oid_committish(arg, &oid) ||
+ if (repo_get_oid_committish(the_repository, arg, &oid) ||
!(commit = lookup_commit_reference(the_repository, &oid))) {
*dotdot = '^';
return 0;
@@ -476,9 +492,12 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
/* name(s) */
s = strpbrk(sb.buf, flag_chars);
- if (s == NULL)
+ if (!s)
s = help;
+ if (s == sb.buf)
+ die(_("missing opt-spec before option flags"));
+
if (s - sb.buf == 1) /* short option only */
o->short_name = *sb.buf;
else if (sb.buf[1] != ',') /* long option only */
@@ -525,6 +544,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
strbuf_addstr(&parsed, " --");
sq_quote_argv(&parsed, argv);
puts(parsed.buf);
+ strbuf_release(&parsed);
return 0;
}
@@ -580,7 +600,7 @@ static void handle_ref_opt(const char *pattern, const char *prefix)
for_each_glob_ref_in(show_reference, pattern, prefix, NULL);
else
for_each_ref_in(prefix, show_reference, NULL);
- clear_ref_exclusion(&ref_excludes);
+ clear_ref_exclusions(&ref_excludes);
}
enum format_type {
@@ -723,6 +743,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
prefix = setup_git_directory();
git_config(git_default_config, NULL);
did_repo_setup = 1;
+
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
}
if (!strcmp(arg, "--")) {
@@ -855,11 +878,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (!strcmp(arg, "--all")) {
for_each_ref(show_reference, NULL);
- clear_ref_exclusion(&ref_excludes);
+ clear_ref_exclusions(&ref_excludes);
continue;
}
if (skip_prefix(arg, "--disambiguate=", &arg)) {
- for_each_abbrev(arg, show_abbrev, NULL);
+ repo_for_each_abbrev(the_repository, arg,
+ show_abbrev, NULL);
continue;
}
if (!strcmp(arg, "--bisect")) {
@@ -868,10 +892,14 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (opt_with_value(arg, "--branches", &arg)) {
+ if (ref_excludes.hidden_refs_configured)
+ return error(_("--exclude-hidden cannot be used together with --branches"));
handle_ref_opt(arg, "refs/heads/");
continue;
}
if (opt_with_value(arg, "--tags", &arg)) {
+ if (ref_excludes.hidden_refs_configured)
+ return error(_("--exclude-hidden cannot be used together with --tags"));
handle_ref_opt(arg, "refs/tags/");
continue;
}
@@ -880,6 +908,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (opt_with_value(arg, "--remotes", &arg)) {
+ if (ref_excludes.hidden_refs_configured)
+ return error(_("--exclude-hidden cannot be used together with --remotes"));
handle_ref_opt(arg, "refs/remotes/");
continue;
}
@@ -887,6 +917,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
add_ref_exclusion(&ref_excludes, arg);
continue;
}
+ if (skip_prefix(arg, "--exclude-hidden=", &arg)) {
+ exclude_hidden_refs(&ref_excludes, arg);
+ continue;
+ }
if (!strcmp(arg, "--show-toplevel")) {
const char *work_tree = get_git_work_tree();
if (work_tree)
@@ -989,7 +1023,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--shared-index-path")) {
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
die(_("Could not read the index"));
if (the_index.split_index) {
const struct object_id *oid = &the_index.split_index->base_oid;
diff --git a/builtin/revert.c b/builtin/revert.c
index 51776abea6..e6f9a1ad26 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -1,8 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "config.h"
#include "builtin.h"
#include "parse-options.h"
#include "diff.h"
+#include "gettext.h"
+#include "repository.h"
#include "revision.h"
#include "rerere.h"
#include "dir.h"
@@ -21,14 +23,15 @@
*/
static const char * const revert_usage[] = {
- N_("git revert [<options>] <commit-ish>..."),
- N_("git revert <subcommand>"),
+ N_("git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] <commit>..."),
+ N_("git revert (--continue | --skip | --abort | --quit)"),
NULL
};
static const char * const cherry_pick_usage[] = {
- N_("git cherry-pick [<options>] <commit-ish>..."),
- N_("git cherry-pick <subcommand>"),
+ N_("git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+ " [-S[<keyid>]] <commit>..."),
+ N_("git cherry-pick (--continue | --skip | --abort | --quit)"),
NULL
};
@@ -42,20 +45,6 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
}
-static int option_parse_x(const struct option *opt,
- const char *arg, int unset)
-{
- struct replay_opts **opts_ptr = opt->value;
- struct replay_opts *opts = *opts_ptr;
-
- if (unset)
- return 0;
-
- ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
- opts->xopts[opts->xopts_nr++] = xstrdup(arg);
- return 0;
-}
-
static int option_parse_m(const struct option *opt,
const char *arg, int unset)
{
@@ -92,7 +81,8 @@ static void verify_opt_compatible(const char *me, const char *base_opt, ...)
die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
}
-static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
+static int run_sequencer(int argc, const char **argv, const char *prefix,
+ struct replay_opts *opts)
{
const char * const * usage_str = revert_or_cherry_pick_usage(opts);
const char *me = action_name(opts);
@@ -112,8 +102,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
N_("select mainline parent"), option_parse_m),
OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")),
- OPT_CALLBACK('X', "strategy-option", &opts, N_("option"),
- N_("option for merge strategy"), option_parse_x),
+ OPT_STRVEC('X', "strategy-option", &opts->xopts, N_("option"),
+ N_("option for merge strategy")),
{ OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_END()
@@ -130,11 +120,18 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
OPT_END(),
};
options = parse_options_concat(options, cp_extra);
+ } else if (opts->action == REPLAY_REVERT) {
+ struct option cp_extra[] = {
+ OPT_BOOL(0, "reference", &opts->commit_use_reference,
+ N_("use the 'reference' format to refer to commits")),
+ OPT_END(),
+ };
+ options = parse_options_concat(options, cp_extra);
}
- argc = parse_options(argc, argv, NULL, options, usage_str,
+ argc = parse_options(argc, argv, prefix, options, usage_str,
PARSE_OPT_KEEP_ARGV0 |
- PARSE_OPT_KEEP_UNKNOWN);
+ PARSE_OPT_KEEP_UNKNOWN_OPT);
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
@@ -167,7 +164,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
"--signoff", opts->signoff,
"--mainline", opts->mainline,
"--strategy", opts->strategy ? 1 : 0,
- "--strategy-option", opts->xopts ? 1 : 0,
+ "--strategy-option", opts->xopts.nr ? 1 : 0,
"-x", opts->record_origin,
"--ff", opts->allow_ff,
"--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
@@ -213,6 +210,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
opts->strategy = xstrdup_or_null(opts->strategy);
if (!opts->strategy && getenv("GIT_TEST_MERGE_ALGORITHM"))
opts->strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM"));
+ free(options);
if (cmd == 'q') {
int ret = sequencer_remove_state(opts);
@@ -236,9 +234,10 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
opts.action = REPLAY_REVERT;
sequencer_init_config(&opts);
- res = run_sequencer(argc, argv, &opts);
+ res = run_sequencer(argc, argv, prefix, &opts);
if (res < 0)
die(_("revert failed"));
+ replay_opts_release(&opts);
return res;
}
@@ -249,8 +248,9 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
opts.action = REPLAY_PICK;
sequencer_init_config(&opts);
- res = run_sequencer(argc, argv, &opts);
+ res = run_sequencer(argc, argv, prefix, &opts);
if (res < 0)
die(_("cherry-pick failed"));
+ replay_opts_release(&opts);
return res;
}
diff --git a/builtin/rm.c b/builtin/rm.c
index 84a935a16e..dff819ae50 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -3,21 +3,30 @@
*
* Copyright (C) Linus Torvalds 2006
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "advice.h"
#include "config.h"
#include "lockfile.h"
#include "dir.h"
#include "cache-tree.h"
+#include "gettext.h"
+#include "hash.h"
#include "tree-walk.h"
+#include "object-name.h"
#include "parse-options.h"
+#include "read-cache.h"
+#include "repository.h"
#include "string-list.h"
+#include "setup.h"
+#include "sparse-index.h"
#include "submodule.h"
#include "pathspec.h"
static const char * const builtin_rm_usage[] = {
- N_("git rm [<options>] [--] <file>..."),
+ N_("git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+ " [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+ " [--] [<pathspec>...]"),
NULL
};
@@ -33,8 +42,8 @@ static int get_ours_cache_pos(const char *path, int pos)
{
int i = -pos - 1;
- while ((i < active_nr) && !strcmp(active_cache[i]->name, path)) {
- if (ce_stage(active_cache[i]) == 2)
+ while ((i < the_index.cache_nr) && !strcmp(the_index.cache[i]->name, path)) {
+ if (ce_stage(the_index.cache[i]) == 2)
return i;
i++;
}
@@ -70,13 +79,13 @@ static void submodules_absorb_gitdir_if_needed(void)
int pos;
const struct cache_entry *ce;
- pos = cache_name_pos(name, strlen(name));
+ pos = index_name_pos(&the_index, name, strlen(name));
if (pos < 0) {
pos = get_ours_cache_pos(name, pos);
if (pos < 0)
continue;
}
- ce = active_cache[pos];
+ ce = the_index.cache[pos];
if (!S_ISGITLINK(ce->ce_mode) ||
!file_exists(ce->name) ||
@@ -84,8 +93,7 @@ static void submodules_absorb_gitdir_if_needed(void)
continue;
if (!submodule_uses_gitfile(name))
- absorb_git_dir_into_superproject(name,
- ABSORB_GITDIR_RECURSE_SUBMODULES);
+ absorb_git_dir_into_superproject(name, NULL);
}
}
@@ -115,7 +123,7 @@ static int check_local_mod(struct object_id *head, int index_only)
int local_changes = 0;
int staged_changes = 0;
- pos = cache_name_pos(name, strlen(name));
+ pos = index_name_pos(&the_index, name, strlen(name));
if (pos < 0) {
/*
* Skip unmerged entries except for populated submodules
@@ -125,11 +133,11 @@ static int check_local_mod(struct object_id *head, int index_only)
if (pos < 0)
continue;
- if (!S_ISGITLINK(active_cache[pos]->ce_mode) ||
+ if (!S_ISGITLINK(the_index.cache[pos]->ce_mode) ||
is_empty_dir(name))
continue;
}
- ce = active_cache[pos];
+ ce = the_index.cache[pos];
if (lstat(ce->name, &st) < 0) {
if (!is_missing_file_error(errno))
@@ -166,7 +174,7 @@ static int check_local_mod(struct object_id *head, int index_only)
* Is the index different from the file in the work tree?
* If it's a submodule, is its work tree modified?
*/
- if (ce_match_stat(ce, &st, 0) ||
+ if (ie_match_stat(&the_index, ce, &st, 0) ||
(S_ISGITLINK(ce->ce_mode) &&
bad_to_remove_submodule(ce->name,
SUBMODULE_REMOVAL_DIE_ON_ERROR |
@@ -287,19 +295,22 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
if (!index_only)
setup_work_tree();
- hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+ repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
die(_("index file corrupt"));
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
seen = xcalloc(pathspec.nr, 1);
- /* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
- for (i = 0; i < active_nr; i++) {
- const struct cache_entry *ce = active_cache[i];
+ if (pathspec_needs_expanded_index(&the_index, &pathspec))
+ ensure_full_index(&the_index);
+
+ for (i = 0; i < the_index.cache_nr; i++) {
+ const struct cache_entry *ce = the_index.cache[i];
if (!include_sparse &&
(ce_skip_worktree(ce) ||
@@ -366,7 +377,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
*/
if (!force) {
struct object_id oid;
- if (get_oid("HEAD", &oid))
+ if (repo_get_oid(the_repository, "HEAD", &oid))
oidclr(&oid);
if (check_local_mod(&oid, index_only))
exit(1);
@@ -381,7 +392,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
if (!quiet)
printf("rm '%s'\n", path);
- if (remove_file_from_cache(path))
+ if (remove_file_from_index(&the_index, path))
die(_("git rm: unable to remove %s"), path);
}
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 64962be016..cd6d9e4112 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -1,6 +1,7 @@
#include "builtin.h"
#include "config.h"
#include "commit.h"
+#include "hex.h"
#include "refs.h"
#include "pkt-line.h"
#include "sideband.h"
@@ -15,11 +16,14 @@
#include "gpg-interface.h"
#include "gettext.h"
#include "protocol.h"
+#include "parse-options.h"
+#include "write-or-die.h"
static const char * const send_pack_usage[] = {
N_("git send-pack [--mirror] [--dry-run] [--force]\n"
" [--receive-pack=<git-receive-pack>]\n"
" [--verbose] [--thin] [--atomic]\n"
+ " [--[no-]signed | --signed=(true|false|if-asked)]\n"
" [<host>:]<directory> (--all | <ref>...)"),
NULL,
};
@@ -127,10 +131,9 @@ static void print_helper_status(struct ref *ref)
strbuf_release(&buf);
}
-static int send_pack_config(const char *k, const char *v, void *cb)
+static int send_pack_config(const char *k, const char *v,
+ const struct config_context *ctx, void *cb)
{
- git_gpg_config(k, v, NULL);
-
if (!strcmp(k, "push.gpgsign")) {
const char *value;
if (!git_config_get_value("push.gpgsign", &value)) {
@@ -149,7 +152,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)
}
}
}
- return git_default_config(k, v, cb);
+ return git_default_config(k, v, ctx, cb);
}
int cmd_send_pack(int argc, const char **argv, const char *prefix)
@@ -274,7 +277,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
fd[0] = 0;
fd[1] = 1;
} else {
- conn = git_connect(fd, dest, receivepack,
+ conn = git_connect(fd, dest, "git-receive-pack", receivepack,
args.verbose ? CONNECT_VERBOSE : 0);
}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 26c5c0cf93..1307ed2b88 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -1,12 +1,15 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "commit.h"
#include "diff.h"
+#include "environment.h"
+#include "gettext.h"
#include "string-list.h"
+#include "repository.h"
#include "revision.h"
#include "utf8.h"
#include "mailmap.h"
+#include "setup.h"
#include "shortlog.h"
#include "parse-options.h"
#include "trailer.h"
@@ -81,8 +84,10 @@ static void insert_one_record(struct shortlog *log,
format_subject(&subject, oneline, " ");
buffer = strbuf_detach(&subject, NULL);
- if (item->util == NULL)
- item->util = xcalloc(1, sizeof(struct string_list));
+ if (!item->util) {
+ item->util = xmalloc(sizeof(struct string_list));
+ string_list_init_nodup(item->util);
+ }
string_list_append(item->util, buffer);
}
}
@@ -130,7 +135,9 @@ static void read_from_stdin(struct shortlog *log)
match = committer_match;
break;
case SHORTLOG_GROUP_TRAILER:
- die(_("using --group=trailer with stdin is not supported"));
+ die(_("using %s with stdin is not supported"), "--group=trailer");
+ case SHORTLOG_GROUP_FORMAT:
+ die(_("using %s with stdin is not supported"), "--group=format");
default:
BUG("unhandled shortlog group");
}
@@ -168,11 +175,15 @@ static void insert_records_from_trailers(struct shortlog *log,
const char *commit_buffer, *body;
struct strbuf ident = STRBUF_INIT;
+ if (!log->trailers.nr)
+ return;
+
/*
- * Using format_commit_message("%B") would be simpler here, but
+ * Using repo_format_commit_message("%B") would be simpler here, but
* this saves us copying the message.
*/
- commit_buffer = logmsg_reencode(commit, NULL, ctx->output_encoding);
+ commit_buffer = repo_logmsg_reencode(the_repository, commit, NULL,
+ ctx->output_encoding);
body = strstr(commit_buffer, "\n\n");
if (!body)
return;
@@ -195,12 +206,38 @@ static void insert_records_from_trailers(struct shortlog *log,
trailer_iterator_release(&iter);
strbuf_release(&ident);
- unuse_commit_buffer(commit, commit_buffer);
+ repo_unuse_commit_buffer(the_repository, commit, commit_buffer);
+}
+
+static int shortlog_needs_dedup(const struct shortlog *log)
+{
+ return HAS_MULTI_BITS(log->groups) || log->format.nr > 1 || log->trailers.nr;
+}
+
+static void insert_records_from_format(struct shortlog *log,
+ struct strset *dups,
+ struct commit *commit,
+ struct pretty_print_context *ctx,
+ const char *oneline)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct string_list_item *item;
+
+ for_each_string_list_item(item, &log->format) {
+ strbuf_reset(&buf);
+
+ repo_format_commit_message(the_repository, commit,
+ item->string, &buf, ctx);
+
+ if (!shortlog_needs_dedup(log) || strset_add(dups, buf.buf))
+ insert_one_record(log, buf.buf, oneline);
+ }
+
+ strbuf_release(&buf);
}
void shortlog_add_commit(struct shortlog *log, struct commit *commit)
{
- struct strbuf ident = STRBUF_INIT;
struct strbuf oneline = STRBUF_INIT;
struct strset dups = STRSET_INIT;
struct pretty_print_context ctx = {0};
@@ -209,41 +246,22 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
ctx.fmt = CMIT_FMT_USERFORMAT;
ctx.abbrev = log->abbrev;
ctx.print_email_subject = 1;
- ctx.date_mode.type = DATE_NORMAL;
+ ctx.date_mode = log->date_mode;
ctx.output_encoding = get_log_output_encoding();
if (!log->summary) {
if (log->user_format)
pretty_print_commit(&ctx, commit, &oneline);
else
- format_commit_message(commit, "%s", &oneline, &ctx);
+ repo_format_commit_message(the_repository, commit,
+ "%s", &oneline, &ctx);
}
oneline_str = oneline.len ? oneline.buf : "<none>";
- if (log->groups & SHORTLOG_GROUP_AUTHOR) {
- strbuf_reset(&ident);
- format_commit_message(commit,
- log->email ? "%aN <%aE>" : "%aN",
- &ident, &ctx);
- if (!HAS_MULTI_BITS(log->groups) ||
- strset_add(&dups, ident.buf))
- insert_one_record(log, ident.buf, oneline_str);
- }
- if (log->groups & SHORTLOG_GROUP_COMMITTER) {
- strbuf_reset(&ident);
- format_commit_message(commit,
- log->email ? "%cN <%cE>" : "%cN",
- &ident, &ctx);
- if (!HAS_MULTI_BITS(log->groups) ||
- strset_add(&dups, ident.buf))
- insert_one_record(log, ident.buf, oneline_str);
- }
- if (log->groups & SHORTLOG_GROUP_TRAILER) {
- insert_records_from_trailers(log, &dups, commit, &ctx, oneline_str);
- }
+ insert_records_from_trailers(log, &dups, commit, &ctx, oneline_str);
+ insert_records_from_format(log, &dups, commit, &ctx, oneline_str);
strset_clear(&dups);
- strbuf_release(&ident);
strbuf_release(&oneline);
}
@@ -312,6 +330,7 @@ static int parse_group_option(const struct option *opt, const char *arg, int uns
if (unset) {
log->groups = 0;
string_list_clear(&log->trailers, 0);
+ string_list_clear(&log->format, 0);
} else if (!strcasecmp(arg, "author"))
log->groups |= SHORTLOG_GROUP_AUTHOR;
else if (!strcasecmp(arg, "committer"))
@@ -319,8 +338,15 @@ static int parse_group_option(const struct option *opt, const char *arg, int uns
else if (skip_prefix(arg, "trailer:", &field)) {
log->groups |= SHORTLOG_GROUP_TRAILER;
string_list_append(&log->trailers, field);
- } else
+ } else if (skip_prefix(arg, "format:", &field)) {
+ log->groups |= SHORTLOG_GROUP_FORMAT;
+ string_list_append(&log->format, field);
+ } else if (strchr(arg, '%')) {
+ log->groups |= SHORTLOG_GROUP_FORMAT;
+ string_list_append(&log->format, arg);
+ } else {
return error(_("unknown group type: %s"), arg);
+ }
return 0;
}
@@ -338,6 +364,19 @@ void shortlog_init(struct shortlog *log)
log->in2 = DEFAULT_INDENT2;
log->trailers.strdup_strings = 1;
log->trailers.cmp = strcasecmp;
+ log->format.strdup_strings = 1;
+}
+
+void shortlog_finish_setup(struct shortlog *log)
+{
+ if (log->groups & SHORTLOG_GROUP_AUTHOR)
+ string_list_append(&log->format,
+ log->email ? "%aN <%aE>" : "%aN");
+ if (log->groups & SHORTLOG_GROUP_COMMITTER)
+ string_list_append(&log->format,
+ log->email ? "%cN <%cE>" : "%cN");
+
+ string_list_sort(&log->trailers);
}
int cmd_shortlog(int argc, const char **argv, const char *prefix)
@@ -379,6 +418,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
break;
case PARSE_OPT_HELP:
case PARSE_OPT_ERROR:
+ case PARSE_OPT_SUBCOMMAND:
exit(129);
case PARSE_OPT_COMPLETE:
exit(0);
@@ -404,10 +444,11 @@ parse_done:
log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;
log.abbrev = rev.abbrev;
log.file = rev.diffopt.file;
+ log.date_mode = rev.date_mode;
if (!log.groups)
log.groups = SHORTLOG_GROUP_AUTHOR;
- string_list_sort(&log.trailers);
+ shortlog_finish_setup(&log);
/* assume HEAD if from a tty */
if (!nongit && !rev.pending.nr && isatty(0))
@@ -420,6 +461,8 @@ parse_done:
else
get_from_rev(&rev, &log);
+ release_revisions(&rev);
+
shortlog_output(&log);
if (log.file != stdout)
fclose(log.file);
@@ -439,7 +482,7 @@ void shortlog_output(struct shortlog *log)
struct strbuf sb = STRBUF_INIT;
if (log->sort_by_number)
- QSORT(log->list.items, log->list.nr,
+ STABLE_QSORT(log->list.items, log->list.nr,
log->summary ? compare_by_counter : compare_by_list);
for (i = 0; i < log->list.nr; i++) {
const struct string_list_item *item = &log->list.items[i];
@@ -474,4 +517,5 @@ void shortlog_output(struct shortlog *log)
log->list.strdup_strings = 1;
string_list_clear(&log->list, 1);
clear_mailmap(&log->mailmap);
+ string_list_clear(&log->format, 0);
}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 64c649c6a2..a86b3c7677 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -1,20 +1,27 @@
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
#include "pretty.h"
#include "refs.h"
-#include "builtin.h"
#include "color.h"
#include "strvec.h"
+#include "object-name.h"
#include "parse-options.h"
+#include "repository.h"
#include "dir.h"
#include "commit-slab.h"
#include "date.h"
+#include "wildmatch.h"
static const char* show_branch_usage[] = {
N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
" [--current] [--color[=<when>] | --no-color] [--sparse]\n"
" [--more=<n> | --list | --independent | --merge-base]\n"
- " [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"),
+ " [--no-name | --sha1-name] [--topics]\n"
+ " [(<rev> | <glob>)...]"),
N_("git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"),
NULL
};
@@ -239,7 +246,7 @@ static void join_revs(struct commit_list **list_p,
parents = parents->next;
if ((this_flag & flags) == flags)
continue;
- parse_commit(p);
+ repo_parse_commit(the_repository, p);
if (mark_seen(p, seen_p) && !still_interesting)
extra--;
p->object.flags |= flags;
@@ -311,8 +318,8 @@ static void show_one_commit(struct commit *commit, int no_name)
}
else
printf("[%s] ",
- find_unique_abbrev(&commit->object.oid,
- DEFAULT_ABBREV));
+ repo_find_unique_abbrev(the_repository, &commit->object.oid,
+ DEFAULT_ABBREV));
}
puts(pretty_str);
strbuf_release(&pretty);
@@ -404,7 +411,7 @@ static int append_ref(const char *refname, const struct object_id *oid,
}
static int append_head_ref(const char *refname, const struct object_id *oid,
- int flag, void *cb_data)
+ int flag UNUSED, void *cb_data UNUSED)
{
struct object_id tmp;
int ofs = 11;
@@ -413,13 +420,13 @@ static int append_head_ref(const char *refname, const struct object_id *oid,
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (get_oid(refname + ofs, &tmp) || !oideq(&tmp, oid))
+ if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
ofs = 5;
return append_ref(refname + ofs, oid, 0);
}
static int append_remote_ref(const char *refname, const struct object_id *oid,
- int flag, void *cb_data)
+ int flag UNUSED, void *cb_data UNUSED)
{
struct object_id tmp;
int ofs = 13;
@@ -428,13 +435,13 @@ static int append_remote_ref(const char *refname, const struct object_id *oid,
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (get_oid(refname + ofs, &tmp) || !oideq(&tmp, oid))
+ if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
ofs = 5;
return append_ref(refname + ofs, oid, 0);
}
static int append_tag_ref(const char *refname, const struct object_id *oid,
- int flag, void *cb_data)
+ int flag UNUSED, void *cb_data UNUSED)
{
if (!starts_with(refname, "refs/tags/"))
return 0;
@@ -532,7 +539,7 @@ static int show_independent(struct commit **rev,
static void append_one_rev(const char *av)
{
struct object_id revkey;
- if (!get_oid(av, &revkey)) {
+ if (!repo_get_oid(the_repository, av, &revkey)) {
append_ref(av, &revkey, 0);
return;
}
@@ -552,7 +559,8 @@ static void append_one_rev(const char *av)
die("bad sha1 reference %s", av);
}
-static int git_show_branch_config(const char *var, const char *value, void *cb)
+static int git_show_branch_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (!strcmp(var, "showbranch.default")) {
if (!value)
@@ -572,7 +580,10 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
return 0;
}
- return git_color_default_config(var, value, cb);
+ if (git_color_config(var, value, cb) < 0)
+ return -1;
+
+ return git_default_config(var, value, ctx, cb);
}
static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
@@ -745,7 +756,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
die(Q_("only %d entry can be shown at one time.",
"only %d entries can be shown at one time.",
MAX_REVS), MAX_REVS);
- if (!dwim_ref(*av, strlen(*av), &oid, &ref, 0))
+ if (!repo_dwim_ref(the_repository, *av, strlen(*av), &oid,
+ &ref, 0))
die(_("no such ref %s"), *av);
/* Has the base been specified? */
@@ -835,13 +847,13 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
die(Q_("cannot handle more than %d rev.",
"cannot handle more than %d revs.",
MAX_REVS), MAX_REVS);
- if (get_oid(ref_name[num_rev], &revkey))
+ if (repo_get_oid(the_repository, ref_name[num_rev], &revkey))
die(_("'%s' is not a valid ref."), ref_name[num_rev]);
commit = lookup_commit_reference(the_repository, &revkey);
if (!commit)
die(_("cannot find commit %s (%s)"),
ref_name[num_rev], oid_to_hex(&revkey));
- parse_commit(commit);
+ repo_parse_commit(the_repository, commit);
mark_seen(commit, &seen);
/* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1,
@@ -955,5 +967,6 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (shown_merge_point && --extra < 0)
break;
}
+ free(head);
return 0;
}
diff --git a/builtin/show-index.c b/builtin/show-index.c
index 0e0b9fb95b..540dc3dad1 100644
--- a/builtin/show-index.c
+++ b/builtin/show-index.c
@@ -1,7 +1,10 @@
#include "builtin.h"
-#include "cache.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
#include "pack.h"
#include "parse-options.h"
+#include "repository.h"
static const char *const show_index_usage[] = {
"git show-index [--object-format=<hash-algorithm>]",
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 7f8a5332f8..5110814f79 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -1,15 +1,19 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
+#include "gettext.h"
+#include "hex.h"
#include "refs.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
#include "object.h"
#include "tag.h"
#include "string-list.h"
#include "parse-options.h"
static const char * const show_ref_usage[] = {
- N_("git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"),
+ N_("git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+ " [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+ " [--heads] [--] [<pattern>...]"),
N_("git show-ref --exclude-existing[=<pattern>]"),
NULL
};
@@ -24,14 +28,14 @@ static void show_one(const char *refname, const struct object_id *oid)
const char *hex;
struct object_id peeled;
- if (!has_object_file(oid))
+ if (!repo_has_object_file(the_repository, oid))
die("git show-ref: bad ref %s (%s)", refname,
oid_to_hex(oid));
if (quiet)
return;
- hex = find_unique_abbrev(oid, abbrev);
+ hex = repo_find_unique_abbrev(the_repository, oid, abbrev);
if (hash_only)
printf("%s\n", hex);
else
@@ -41,25 +45,17 @@ static void show_one(const char *refname, const struct object_id *oid)
return;
if (!peel_iterated_oid(oid, &peeled)) {
- hex = find_unique_abbrev(&peeled, abbrev);
+ hex = repo_find_unique_abbrev(the_repository, &peeled, abbrev);
printf("%s %s^{}\n", hex, refname);
}
}
static int show_ref(const char *refname, const struct object_id *oid,
- int flag, void *cbdata)
+ int flag UNUSED, void *cbdata UNUSED)
{
if (show_head && !strcmp(refname, "HEAD"))
goto match;
- if (tags_only || heads_only) {
- int match;
-
- match = heads_only && starts_with(refname, "refs/heads/");
- match |= tags_only && starts_with(refname, "refs/tags/");
- if (!match)
- return 0;
- }
if (pattern) {
int reflen = strlen(refname);
const char **p = pattern, *m;
@@ -85,8 +81,9 @@ match:
return 0;
}
-static int add_existing(const char *refname, const struct object_id *oid,
- int flag, void *cbdata)
+static int add_existing(const char *refname,
+ const struct object_id *oid UNUSED,
+ int flag UNUSED, void *cbdata)
{
struct string_list *list = (struct string_list *)cbdata;
string_list_insert(list, refname);
@@ -216,7 +213,14 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
if (show_head)
head_ref(show_ref, NULL);
- for_each_ref(show_ref, NULL);
+ if (heads_only || tags_only) {
+ if (heads_only)
+ for_each_fullref_in("refs/heads/", show_ref, NULL);
+ if (tags_only)
+ for_each_fullref_in("refs/tags/", show_ref, NULL);
+ } else {
+ for_each_ref(show_ref, NULL);
+ }
if (!found_match) {
if (verify && !quiet)
die("No match");
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 0217d44c5b..5c8ffb1f75 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -1,7 +1,10 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-file.h"
+#include "object-name.h"
#include "parse-options.h"
#include "pathspec.h"
#include "repository.h"
@@ -14,13 +17,14 @@
#include "unpack-trees.h"
#include "wt-status.h"
#include "quote.h"
+#include "setup.h"
#include "sparse-index.h"
#include "worktree.h"
static const char *empty_base = "";
static char const * const builtin_sparse_checkout_usage[] = {
- N_("git sparse-checkout (init|list|set|add|reapply|disable) <options>"),
+ N_("git sparse-checkout (init | list | set | add | reapply | disable | check-rules) [<options>]"),
NULL
};
@@ -48,7 +52,7 @@ static char const * const builtin_sparse_checkout_list_usage[] = {
NULL
};
-static int sparse_checkout_list(int argc, const char **argv)
+static int sparse_checkout_list(int argc, const char **argv, const char *prefix)
{
static struct option builtin_sparse_checkout_list_options[] = {
OPT_END(),
@@ -57,10 +61,11 @@ static int sparse_checkout_list(int argc, const char **argv)
char *sparse_filename;
int res;
+ setup_work_tree();
if (!core_apply_sparse_checkout)
die(_("this worktree is not sparse"));
- argc = parse_options(argc, argv, NULL,
+ argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_list_options,
builtin_sparse_checkout_list_usage, 0);
@@ -128,7 +133,7 @@ static void clean_tracked_sparse_directories(struct repository *r)
* sparse index will not delete directories that contain
* conflicted entries or submodules.
*/
- if (!r->index->sparse_index) {
+ if (r->index->sparse_index == INDEX_EXPANDED) {
/*
* If something, such as a merge conflict or other concern,
* prevents us from converting to a sparse index, then do
@@ -218,14 +223,13 @@ static int update_working_directory(struct pattern_list *pl)
o.src_index = r->index;
o.dst_index = r->index;
o.skip_sparse_checkout = 0;
- o.pl = pl;
setup_work_tree();
repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR);
setup_unpack_trees_porcelain(&o, "sparse-checkout");
- result = update_sparsity(&o);
+ result = update_sparsity(&o, pl);
clear_unpack_trees_porcelain(&o);
if (result == UPDATE_SPARSITY_WARNINGS)
@@ -382,26 +386,29 @@ static int set_config(enum sparse_checkout_mode mode)
return 0;
}
-static int update_modes(int *cone_mode, int *sparse_index)
-{
- int mode, record_mode;
-
- /* Determine if we need to record the mode; ensure sparse checkout on */
- record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
-
+static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
/* If not specified, use previous definition of cone mode */
if (*cone_mode == -1 && core_apply_sparse_checkout)
*cone_mode = core_sparse_checkout_cone;
/* Set cone/non-cone mode appropriately */
core_apply_sparse_checkout = 1;
- if (*cone_mode == 1) {
- mode = MODE_CONE_PATTERNS;
+ if (*cone_mode == 1 || *cone_mode == -1) {
core_sparse_checkout_cone = 1;
- } else {
- mode = MODE_ALL_PATTERNS;
- core_sparse_checkout_cone = 0;
+ return MODE_CONE_PATTERNS;
}
+ core_sparse_checkout_cone = 0;
+ return MODE_ALL_PATTERNS;
+}
+
+static int update_modes(int *cone_mode, int *sparse_index)
+{
+ int mode, record_mode;
+
+ /* Determine if we need to record the mode; ensure sparse checkout on */
+ record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+
+ mode = update_cone_mode(cone_mode);
if (record_mode && set_config(mode))
return 1;
@@ -413,6 +420,9 @@ static int update_modes(int *cone_mode, int *sparse_index)
/* force an index rewrite */
repo_read_index(the_repository);
the_repository->index->updated_workdir = 1;
+
+ if (!*sparse_index)
+ ensure_full_index(the_repository->index);
}
return 0;
@@ -428,7 +438,7 @@ static struct sparse_checkout_init_opts {
int sparse_index;
} init_opts;
-static int sparse_checkout_init(int argc, const char **argv)
+static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
{
struct pattern_list pl;
char *sparse_filename;
@@ -444,12 +454,13 @@ static int sparse_checkout_init(int argc, const char **argv)
OPT_END(),
};
+ setup_work_tree();
repo_read_index(the_repository);
init_opts.cone_mode = -1;
init_opts.sparse_index = -1;
- argc = parse_options(argc, argv, NULL,
+ argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_init_options,
builtin_sparse_checkout_init_usage, 0);
@@ -467,7 +478,7 @@ static int sparse_checkout_init(int argc, const char **argv)
return update_working_directory(NULL);
}
- if (get_oid("HEAD", &oid)) {
+ if (repo_get_oid(the_repository, "HEAD", &oid)) {
FILE *fp;
/* assume we are in a fresh repo, but update the sparse-checkout file */
@@ -541,7 +552,7 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
static void add_patterns_from_input(struct pattern_list *pl,
int argc, const char **argv,
- int use_stdin)
+ FILE *file)
{
int i;
if (core_sparse_checkout_cone) {
@@ -551,9 +562,9 @@ static void add_patterns_from_input(struct pattern_list *pl,
hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
pl->use_cone_patterns = 1;
- if (use_stdin) {
+ if (file) {
struct strbuf unquoted = STRBUF_INIT;
- while (!strbuf_getline(&line, stdin)) {
+ while (!strbuf_getline(&line, file)) {
if (line.buf[0] == '"') {
strbuf_reset(&unquoted);
if (unquote_c_style(&unquoted, line.buf, NULL))
@@ -575,10 +586,10 @@ static void add_patterns_from_input(struct pattern_list *pl,
}
}
} else {
- if (use_stdin) {
+ if (file) {
struct strbuf line = STRBUF_INIT;
- while (!strbuf_getline(&line, stdin)) {
+ while (!strbuf_getline(&line, file)) {
size_t len;
char *buf = strbuf_detach(&line, &len);
add_pattern(buf, empty_base, 0, pl, 0);
@@ -605,7 +616,8 @@ static void add_patterns_cone_mode(int argc, const char **argv,
struct pattern_list existing;
char *sparse_filename = get_sparse_checkout_filename();
- add_patterns_from_input(pl, argc, argv, use_stdin);
+ add_patterns_from_input(pl, argc, argv,
+ use_stdin ? stdin : NULL);
memset(&existing, 0, sizeof(existing));
existing.use_cone_patterns = core_sparse_checkout_cone;
@@ -642,7 +654,7 @@ static void add_patterns_literal(int argc, const char **argv,
pl, NULL, 0))
die(_("unable to load existing sparse-checkout patterns"));
free(sparse_filename);
- add_patterns_from_input(pl, argc, argv, use_stdin);
+ add_patterns_from_input(pl, argc, argv, use_stdin ? stdin : NULL);
}
static int modify_pattern_list(int argc, const char **argv, int use_stdin,
@@ -661,7 +673,8 @@ static int modify_pattern_list(int argc, const char **argv, int use_stdin,
break;
case REPLACE:
- add_patterns_from_input(pl, argc, argv, use_stdin);
+ add_patterns_from_input(pl, argc, argv,
+ use_stdin ? stdin : NULL);
break;
}
@@ -756,6 +769,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix)
OPT_END(),
};
+ setup_work_tree();
if (!core_apply_sparse_checkout)
die(_("no sparse-checkout to add to"));
@@ -764,7 +778,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_add_options,
builtin_sparse_checkout_add_usage,
- PARSE_OPT_KEEP_UNKNOWN);
+ PARSE_OPT_KEEP_UNKNOWN_OPT);
sanitize_paths(argc, argv, prefix, add_opts.skip_checks);
@@ -802,6 +816,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
OPT_END(),
};
+ setup_work_tree();
repo_read_index(the_repository);
set_opts.cone_mode = -1;
@@ -810,7 +825,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_set_options,
builtin_sparse_checkout_set_usage,
- PARSE_OPT_KEEP_UNKNOWN);
+ PARSE_OPT_KEEP_UNKNOWN_OPT);
if (update_modes(&set_opts.cone_mode, &set_opts.sparse_index))
return 1;
@@ -840,7 +855,8 @@ static struct sparse_checkout_reapply_opts {
int sparse_index;
} reapply_opts;
-static int sparse_checkout_reapply(int argc, const char **argv)
+static int sparse_checkout_reapply(int argc, const char **argv,
+ const char *prefix)
{
static struct option builtin_sparse_checkout_reapply_options[] = {
OPT_BOOL(0, "cone", &reapply_opts.cone_mode,
@@ -850,13 +866,14 @@ static int sparse_checkout_reapply(int argc, const char **argv)
OPT_END(),
};
+ setup_work_tree();
if (!core_apply_sparse_checkout)
die(_("must be in a sparse-checkout to reapply sparsity patterns"));
reapply_opts.cone_mode = -1;
reapply_opts.sparse_index = -1;
- argc = parse_options(argc, argv, NULL,
+ argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_reapply_options,
builtin_sparse_checkout_reapply_usage, 0);
@@ -873,7 +890,8 @@ static char const * const builtin_sparse_checkout_disable_usage[] = {
NULL
};
-static int sparse_checkout_disable(int argc, const char **argv)
+static int sparse_checkout_disable(int argc, const char **argv,
+ const char *prefix)
{
static struct option builtin_sparse_checkout_disable_options[] = {
OPT_END(),
@@ -892,7 +910,8 @@ static int sparse_checkout_disable(int argc, const char **argv)
* forcibly return to a dense checkout regardless of initial state.
*/
- argc = parse_options(argc, argv, NULL,
+ setup_work_tree();
+ argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_disable_options,
builtin_sparse_checkout_disable_usage, 0);
@@ -917,38 +936,113 @@ static int sparse_checkout_disable(int argc, const char **argv)
return set_config(MODE_NO_PATTERNS);
}
-int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
+static char const * const builtin_sparse_checkout_check_rules_usage[] = {
+ N_("git sparse-checkout check-rules [-z] [--skip-checks]"
+ "[--[no-]cone] [--rules-file <file>]"),
+ NULL
+};
+
+static struct sparse_checkout_check_rules_opts {
+ int cone_mode;
+ int null_termination;
+ char *rules_file;
+} check_rules_opts;
+
+static int check_rules(struct pattern_list *pl, int null_terminated) {
+ struct strbuf line = STRBUF_INIT;
+ struct strbuf unquoted = STRBUF_INIT;
+ char *path;
+ int line_terminator = null_terminated ? 0 : '\n';
+ strbuf_getline_fn getline_fn = null_terminated ? strbuf_getline_nul
+ : strbuf_getline;
+ the_repository->index->sparse_checkout_patterns = pl;
+ while (!getline_fn(&line, stdin)) {
+ path = line.buf;
+ if (!null_terminated && line.buf[0] == '"') {
+ strbuf_reset(&unquoted);
+ if (unquote_c_style(&unquoted, line.buf, NULL))
+ die(_("unable to unquote C-style string '%s'"),
+ line.buf);
+
+ path = unquoted.buf;
+ }
+
+ if (path_in_sparse_checkout(path, the_repository->index))
+ write_name_quoted(path, stdout, line_terminator);
+ }
+ strbuf_release(&line);
+ strbuf_release(&unquoted);
+
+ return 0;
+}
+
+static int sparse_checkout_check_rules(int argc, const char **argv, const char *prefix)
{
- static struct option builtin_sparse_checkout_options[] = {
+ static struct option builtin_sparse_checkout_check_rules_options[] = {
+ OPT_BOOL('z', NULL, &check_rules_opts.null_termination,
+ N_("terminate input and output files by a NUL character")),
+ OPT_BOOL(0, "cone", &check_rules_opts.cone_mode,
+ N_("when used with --rules-file interpret patterns as cone mode patterns")),
+ OPT_FILENAME(0, "rules-file", &check_rules_opts.rules_file,
+ N_("use patterns in <file> instead of the current ones.")),
OPT_END(),
};
- if (argc == 2 && !strcmp(argv[1], "-h"))
- usage_with_options(builtin_sparse_checkout_usage,
- builtin_sparse_checkout_options);
+ FILE *fp;
+ int ret;
+ struct pattern_list pl = {0};
+ char *sparse_filename;
+ check_rules_opts.cone_mode = -1;
+
+ argc = parse_options(argc, argv, prefix,
+ builtin_sparse_checkout_check_rules_options,
+ builtin_sparse_checkout_check_rules_usage,
+ PARSE_OPT_KEEP_UNKNOWN_OPT);
+
+ if (check_rules_opts.rules_file && check_rules_opts.cone_mode < 0)
+ check_rules_opts.cone_mode = 1;
+
+ update_cone_mode(&check_rules_opts.cone_mode);
+ pl.use_cone_patterns = core_sparse_checkout_cone;
+ if (check_rules_opts.rules_file) {
+ fp = xfopen(check_rules_opts.rules_file, "r");
+ add_patterns_from_input(&pl, argc, argv, fp);
+ fclose(fp);
+ } else {
+ sparse_filename = get_sparse_checkout_filename();
+ if (add_patterns_from_file_to_list(sparse_filename, "", 0, &pl,
+ NULL, 0))
+ die(_("unable to load existing sparse-checkout patterns"));
+ free(sparse_filename);
+ }
+
+ ret = check_rules(&pl, check_rules_opts.null_termination);
+ clear_pattern_list(&pl);
+ return ret;
+}
+
+int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
+{
+ parse_opt_subcommand_fn *fn = NULL;
+ struct option builtin_sparse_checkout_options[] = {
+ OPT_SUBCOMMAND("list", &fn, sparse_checkout_list),
+ OPT_SUBCOMMAND("init", &fn, sparse_checkout_init),
+ OPT_SUBCOMMAND("set", &fn, sparse_checkout_set),
+ OPT_SUBCOMMAND("add", &fn, sparse_checkout_add),
+ OPT_SUBCOMMAND("reapply", &fn, sparse_checkout_reapply),
+ OPT_SUBCOMMAND("disable", &fn, sparse_checkout_disable),
+ OPT_SUBCOMMAND("check-rules", &fn, sparse_checkout_check_rules),
+ OPT_END(),
+ };
argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_options,
- builtin_sparse_checkout_usage,
- PARSE_OPT_STOP_AT_NON_OPTION);
+ builtin_sparse_checkout_usage, 0);
git_config(git_default_config, NULL);
- if (argc > 0) {
- if (!strcmp(argv[0], "list"))
- return sparse_checkout_list(argc, argv);
- if (!strcmp(argv[0], "init"))
- return sparse_checkout_init(argc, argv);
- if (!strcmp(argv[0], "set"))
- return sparse_checkout_set(argc, argv, prefix);
- if (!strcmp(argv[0], "add"))
- return sparse_checkout_add(argc, argv, prefix);
- if (!strcmp(argv[0], "reapply"))
- return sparse_checkout_reapply(argc, argv);
- if (!strcmp(argv[0], "disable"))
- return sparse_checkout_disable(argc, argv);
- }
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
- usage_with_options(builtin_sparse_checkout_usage,
- builtin_sparse_checkout_options);
+ return fn(argc, argv, prefix);
}
diff --git a/builtin/stash.c b/builtin/stash.c
index 0c7b6a9588..fe64cde9ce 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1,91 +1,126 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
+#include "abspath.h"
#include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
+#include "object-name.h"
#include "parse-options.h"
#include "refs.h"
#include "lockfile.h"
#include "cache-tree.h"
#include "unpack-trees.h"
#include "merge-recursive.h"
+#include "merge-ort-wrappers.h"
#include "strvec.h"
#include "run-command.h"
#include "dir.h"
#include "entry.h"
+#include "preload-index.h"
+#include "read-cache.h"
#include "rerere.h"
#include "revision.h"
+#include "setup.h"
+#include "sparse-index.h"
#include "log-tree.h"
#include "diffcore.h"
#include "exec-cmd.h"
#include "reflog.h"
+#include "add-interactive.h"
#define INCLUDE_ALL_FILES 2
+#define BUILTIN_STASH_LIST_USAGE \
+ N_("git stash list [<log-options>]")
+#define BUILTIN_STASH_SHOW_USAGE \
+ N_("git stash show [-u | --include-untracked | --only-untracked] [<diff-options>] [<stash>]")
+#define BUILTIN_STASH_DROP_USAGE \
+ N_("git stash drop [-q | --quiet] [<stash>]")
+#define BUILTIN_STASH_POP_USAGE \
+ N_("git stash pop [--index] [-q | --quiet] [<stash>]")
+#define BUILTIN_STASH_APPLY_USAGE \
+ N_("git stash apply [--index] [-q | --quiet] [<stash>]")
+#define BUILTIN_STASH_BRANCH_USAGE \
+ N_("git stash branch <branchname> [<stash>]")
+#define BUILTIN_STASH_STORE_USAGE \
+ N_("git stash store [(-m | --message) <message>] [-q | --quiet] <commit>")
+#define BUILTIN_STASH_PUSH_USAGE \
+ N_("git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
+ " [-u | --include-untracked] [-a | --all] [(-m | --message) <message>]\n" \
+ " [--pathspec-from-file=<file> [--pathspec-file-nul]]\n" \
+ " [--] [<pathspec>...]]")
+#define BUILTIN_STASH_SAVE_USAGE \
+ N_("git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
+ " [-u | --include-untracked] [-a | --all] [<message>]")
+#define BUILTIN_STASH_CREATE_USAGE \
+ N_("git stash create [<message>]")
+#define BUILTIN_STASH_CLEAR_USAGE \
+ "git stash clear"
+
static const char * const git_stash_usage[] = {
- N_("git stash list [<options>]"),
- N_("git stash show [<options>] [<stash>]"),
- N_("git stash drop [-q|--quiet] [<stash>]"),
- N_("git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"),
- N_("git stash branch <branchname> [<stash>]"),
- "git stash clear",
- N_("git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n"
- " [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
- " [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
- " [--] [<pathspec>...]]"),
- N_("git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n"
- " [-u|--include-untracked] [-a|--all] [<message>]"),
+ BUILTIN_STASH_LIST_USAGE,
+ BUILTIN_STASH_SHOW_USAGE,
+ BUILTIN_STASH_DROP_USAGE,
+ BUILTIN_STASH_POP_USAGE,
+ BUILTIN_STASH_APPLY_USAGE,
+ BUILTIN_STASH_BRANCH_USAGE,
+ BUILTIN_STASH_PUSH_USAGE,
+ BUILTIN_STASH_SAVE_USAGE,
+ BUILTIN_STASH_CLEAR_USAGE,
+ BUILTIN_STASH_CREATE_USAGE,
+ BUILTIN_STASH_STORE_USAGE,
NULL
};
static const char * const git_stash_list_usage[] = {
- N_("git stash list [<options>]"),
+ BUILTIN_STASH_LIST_USAGE,
NULL
};
static const char * const git_stash_show_usage[] = {
- N_("git stash show [<options>] [<stash>]"),
+ BUILTIN_STASH_SHOW_USAGE,
NULL
};
static const char * const git_stash_drop_usage[] = {
- N_("git stash drop [-q|--quiet] [<stash>]"),
+ BUILTIN_STASH_DROP_USAGE,
NULL
};
static const char * const git_stash_pop_usage[] = {
- N_("git stash pop [--index] [-q|--quiet] [<stash>]"),
+ BUILTIN_STASH_POP_USAGE,
NULL
};
static const char * const git_stash_apply_usage[] = {
- N_("git stash apply [--index] [-q|--quiet] [<stash>]"),
+ BUILTIN_STASH_APPLY_USAGE,
NULL
};
static const char * const git_stash_branch_usage[] = {
- N_("git stash branch <branchname> [<stash>]"),
+ BUILTIN_STASH_BRANCH_USAGE,
NULL
};
static const char * const git_stash_clear_usage[] = {
- "git stash clear",
+ BUILTIN_STASH_CLEAR_USAGE,
NULL
};
static const char * const git_stash_store_usage[] = {
- N_("git stash store [-m|--message <message>] [-q|--quiet] <commit>"),
+ BUILTIN_STASH_STORE_USAGE,
NULL
};
static const char * const git_stash_push_usage[] = {
- N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
- " [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
- " [--] [<pathspec>...]]"),
+ BUILTIN_STASH_PUSH_USAGE,
NULL
};
static const char * const git_stash_save_usage[] = {
- N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
- " [-u|--include-untracked] [-a|--all] [<message>]"),
+ BUILTIN_STASH_SAVE_USAGE,
NULL
};
@@ -116,6 +151,10 @@ struct stash_info {
int has_u;
};
+#define STASH_INFO_INIT { \
+ .revision = STRBUF_INIT, \
+}
+
static void free_stash_info(struct stash_info *info)
{
strbuf_release(&info->revision);
@@ -157,10 +196,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
if (argc == 1)
commit = argv[0];
- strbuf_init(&info->revision, 0);
if (!commit) {
if (!ref_exists(ref_stash)) {
- free_stash_info(info);
fprintf_ln(stderr, _("No stash entries found."));
return -1;
}
@@ -174,11 +211,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
revision = info->revision.buf;
- if (get_oid(revision, &info->w_commit)) {
- error(_("%s is not a valid reference"), revision);
- free_stash_info(info);
- return -1;
- }
+ if (repo_get_oid(the_repository, revision, &info->w_commit))
+ return error(_("%s is not a valid reference"), revision);
assert_stash_like(info, revision);
@@ -187,7 +221,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
end_of_rev = strchrnul(revision, '@');
strbuf_add(&symbolic, revision, end_of_rev - revision);
- ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref, 0);
+ ret = repo_dwim_ref(the_repository, symbolic.buf, symbolic.len,
+ &dummy, &expanded_ref, 0);
strbuf_release(&symbolic);
switch (ret) {
case 0: /* Not found, but valid ref */
@@ -197,7 +232,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
break;
default: /* Invalid or ambiguous */
- free_stash_info(info);
+ break;
}
free(expanded_ref);
@@ -207,7 +242,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
static int do_clear_stash(void)
{
struct object_id obj;
- if (get_oid(ref_stash, &obj))
+ if (repo_get_oid(the_repository, ref_stash, &obj))
return 0;
return delete_ref(NULL, ref_stash, &obj, 0);
@@ -238,11 +273,11 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
struct tree *tree;
struct lock_file lock_file = LOCK_INIT;
- read_cache_preload(NULL);
- if (refresh_cache(REFRESH_QUIET))
+ repo_read_index_preload(the_repository, NULL, 0);
+ if (refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL))
return -1;
- hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
memset(&opts, 0, sizeof(opts));
@@ -356,7 +391,7 @@ static int restore_untracked(struct object_id *u_tree)
cp.git_cmd = 1;
strvec_push(&cp.args, "read-tree");
strvec_push(&cp.args, oid_to_hex(u_tree));
- strvec_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
+ strvec_pushf(&cp.env, "GIT_INDEX_FILE=%s",
stash_index_path.buf);
if (run_command(&cp)) {
remove_path(stash_index_path.buf);
@@ -366,7 +401,7 @@ static int restore_untracked(struct object_id *u_tree)
child_process_init(&cp);
cp.git_cmd = 1;
strvec_pushl(&cp.args, "checkout-index", "--all", NULL);
- strvec_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
+ strvec_pushf(&cp.env, "GIT_INDEX_FILE=%s",
stash_index_path.buf);
res = run_command(&cp);
@@ -403,7 +438,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
* to the index before a merge was run) and the current index
* (reflecting the changes brought in by the merge).
*/
- diff_setup(&diff_opts);
+ repo_diff_setup(the_repository, &diff_opts);
diff_opts.flags.recursive = 1;
diff_opts.detect_rename = 0;
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -431,10 +466,10 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
* path, but left it out of the working tree, then clear the
* SKIP_WORKTREE bit and write it to the working tree.
*/
- if (pos >= 0 && ce_skip_worktree(active_cache[pos])) {
+ if (pos >= 0 && ce_skip_worktree(the_index.cache[pos])) {
struct stat st;
- ce = active_cache[pos];
+ ce = the_index.cache[pos];
if (!lstat(ce->name, &st)) {
/* Conflicting path present; relocate it */
struct strbuf new_path = STRBUF_INIT;
@@ -492,19 +527,21 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
static int do_apply_stash(const char *prefix, struct stash_info *info,
int index, int quiet)
{
- int ret;
+ int clean, ret;
int has_index = index;
struct merge_options o;
struct object_id c_tree;
struct object_id index_tree;
- struct commit *result;
- const struct object_id *bases[1];
+ struct tree *head, *merge, *merge_base;
+ struct lock_file lock = LOCK_INIT;
- read_cache_preload(NULL);
- if (refresh_and_write_cache(REFRESH_QUIET, 0, 0))
+ repo_read_index_preload(the_repository, NULL, 0);
+ if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0,
+ NULL, NULL, NULL))
return -1;
- if (write_cache_as_tree(&c_tree, 0, NULL))
+ if (write_index_as_tree(&c_tree, &the_index, get_index_file(), 0,
+ NULL))
return error(_("cannot apply a stash in the middle of a merge"));
if (index) {
@@ -526,14 +563,15 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
return error(_("conflicts in index. "
"Try without --index."));
- discard_cache();
- read_cache();
- if (write_cache_as_tree(&index_tree, 0, NULL))
+ discard_index(&the_index);
+ repo_read_index(the_repository);
+ if (write_index_as_tree(&index_tree, &the_index,
+ get_index_file(), 0, NULL))
return error(_("could not save index tree"));
reset_head();
- discard_cache();
- read_cache();
+ discard_index(&the_index);
+ repo_read_index(the_repository);
}
}
@@ -541,6 +579,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
o.branch1 = "Updated upstream";
o.branch2 = "Stashed changes";
+ o.ancestor = "Stash base";
if (oideq(&info->b_tree, &c_tree))
o.branch1 = "Version stash was based on";
@@ -551,12 +590,28 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
if (o.verbosity >= 3)
printf_ln(_("Merging %s with %s"), o.branch1, o.branch2);
- bases[0] = &info->b_tree;
+ head = lookup_tree(o.repo, &c_tree);
+ merge = lookup_tree(o.repo, &info->w_tree);
+ merge_base = lookup_tree(o.repo, &info->b_tree);
+
+ repo_hold_locked_index(o.repo, &lock, LOCK_DIE_ON_ERROR);
+ clean = merge_ort_nonrecursive(&o, head, merge, merge_base);
+
+ /*
+ * If 'clean' >= 0, reverse the value for 'ret' so 'ret' is 0 when the
+ * merge was clean, and nonzero if the merge was unclean or encountered
+ * an error.
+ */
+ ret = clean >= 0 ? !clean : clean;
+
+ if (ret < 0)
+ rollback_lock_file(&lock);
+ else if (write_locked_index(o.repo->index, &lock,
+ COMMIT_LOCK | SKIP_IF_UNCHANGED))
+ ret = error(_("could not write index"));
- ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,
- &result);
if (ret) {
- rerere(0);
+ repo_rerere(the_repository, 0);
if (index)
fprintf_ln(stderr, _("Index was not unstashed."));
@@ -585,9 +640,9 @@ restore_untracked:
*/
cp.git_cmd = 1;
cp.dir = prefix;
- strvec_pushf(&cp.env_array, GIT_WORK_TREE_ENVIRONMENT"=%s",
+ strvec_pushf(&cp.env, GIT_WORK_TREE_ENVIRONMENT"=%s",
absolute_path(get_git_work_tree()));
- strvec_pushf(&cp.env_array, GIT_DIR_ENVIRONMENT"=%s",
+ strvec_pushf(&cp.env, GIT_DIR_ENVIRONMENT"=%s",
absolute_path(get_git_dir()));
strvec_push(&cp.args, "status");
run_command(&cp);
@@ -598,10 +653,10 @@ restore_untracked:
static int apply_stash(int argc, const char **argv, const char *prefix)
{
- int ret;
+ int ret = -1;
int quiet = 0;
int index = 0;
- struct stash_info info;
+ struct stash_info info = STASH_INFO_INIT;
struct option options[] = {
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
OPT_BOOL(0, "index", &index,
@@ -613,16 +668,20 @@ static int apply_stash(int argc, const char **argv, const char *prefix)
git_stash_apply_usage, 0);
if (get_stash_info(&info, argc, argv))
- return -1;
+ goto cleanup;
ret = do_apply_stash(prefix, &info, index, quiet);
+cleanup:
free_stash_info(&info);
return ret;
}
-static int reject_reflog_ent(struct object_id *ooid, struct object_id *noid,
- const char *email, timestamp_t timestamp, int tz,
- const char *message, void *cb_data)
+static int reject_reflog_ent(struct object_id *ooid UNUSED,
+ struct object_id *noid UNUSED,
+ const char *email UNUSED,
+ timestamp_t timestamp UNUSED,
+ int tz UNUSED, const char *message UNUSED,
+ void *cb_data UNUSED)
{
return 1;
}
@@ -651,20 +710,25 @@ static int do_drop_stash(struct stash_info *info, int quiet)
return 0;
}
-static void assert_stash_ref(struct stash_info *info)
+static int get_stash_info_assert(struct stash_info *info, int argc,
+ const char **argv)
{
- if (!info->is_stash_ref) {
- error(_("'%s' is not a stash reference"), info->revision.buf);
- free_stash_info(info);
- exit(1);
- }
+ int ret = get_stash_info(info, argc, argv);
+
+ if (ret < 0)
+ return ret;
+
+ if (!info->is_stash_ref)
+ return error(_("'%s' is not a stash reference"), info->revision.buf);
+
+ return 0;
}
static int drop_stash(int argc, const char **argv, const char *prefix)
{
- int ret;
+ int ret = -1;
int quiet = 0;
- struct stash_info info;
+ struct stash_info info = STASH_INFO_INIT;
struct option options[] = {
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
OPT_END()
@@ -673,22 +737,21 @@ static int drop_stash(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options,
git_stash_drop_usage, 0);
- if (get_stash_info(&info, argc, argv))
- return -1;
-
- assert_stash_ref(&info);
+ if (get_stash_info_assert(&info, argc, argv))
+ goto cleanup;
ret = do_drop_stash(&info, quiet);
+cleanup:
free_stash_info(&info);
return ret;
}
static int pop_stash(int argc, const char **argv, const char *prefix)
{
- int ret;
+ int ret = -1;
int index = 0;
int quiet = 0;
- struct stash_info info;
+ struct stash_info info = STASH_INFO_INIT;
struct option options[] = {
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
OPT_BOOL(0, "index", &index,
@@ -699,25 +762,25 @@ static int pop_stash(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options,
git_stash_pop_usage, 0);
- if (get_stash_info(&info, argc, argv))
- return -1;
+ if (get_stash_info_assert(&info, argc, argv))
+ goto cleanup;
- assert_stash_ref(&info);
if ((ret = do_apply_stash(prefix, &info, index, quiet)))
printf_ln(_("The stash entry is kept in case "
"you need it again."));
else
ret = do_drop_stash(&info, quiet);
+cleanup:
free_stash_info(&info);
return ret;
}
static int branch_stash(int argc, const char **argv, const char *prefix)
{
- int ret;
+ int ret = -1;
const char *branch = NULL;
- struct stash_info info;
+ struct stash_info info = STASH_INFO_INIT;
struct child_process cp = CHILD_PROCESS_INIT;
struct option options[] = {
OPT_END()
@@ -734,7 +797,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
branch = argv[0];
if (get_stash_info(&info, argc - 1, argv + 1))
- return -1;
+ goto cleanup;
cp.git_cmd = 1;
strvec_pushl(&cp.args, "checkout", "-b", NULL);
@@ -746,8 +809,8 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
if (!ret && info.is_stash_ref)
ret = do_drop_stash(&info, 0);
+cleanup:
free_stash_info(&info);
-
return ret;
}
@@ -760,7 +823,7 @@ static int list_stash(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options,
git_stash_list_usage,
- PARSE_OPT_KEEP_UNKNOWN);
+ PARSE_OPT_KEEP_UNKNOWN_OPT);
if (!ref_exists(ref_stash))
return 0;
@@ -778,7 +841,8 @@ static int show_stat = 1;
static int show_patch;
static int show_include_untracked;
-static int git_stash_config(const char *var, const char *value, void *cb)
+static int git_stash_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (!strcmp(var, "stash.showstat")) {
show_stat = git_config_bool(var, value);
@@ -792,7 +856,7 @@ static int git_stash_config(const char *var, const char *value, void *cb)
show_include_untracked = git_config_bool(var, value);
return 0;
}
- return git_diff_basic_config(var, value, cb);
+ return git_diff_basic_config(var, value, ctx, cb);
}
static void diff_include_untracked(const struct stash_info *info, struct diff_options *diff_opt)
@@ -825,8 +889,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
static int show_stash(int argc, const char **argv, const char *prefix)
{
int i;
- int ret = 0;
- struct stash_info info;
+ int ret = -1;
+ struct stash_info info = STASH_INFO_INIT;
struct rev_info rev;
struct strvec stash_args = STRVEC_INIT;
struct strvec revision_args = STRVEC_INIT;
@@ -844,13 +908,14 @@ static int show_stash(int argc, const char **argv, const char *prefix)
UNTRACKED_ONLY, PARSE_OPT_NONEG),
OPT_END()
};
+ int do_usage = 0;
init_diff_ui_defaults();
git_config(git_diff_ui_config, NULL);
- init_revisions(&rev, prefix);
+ repo_init_revisions(the_repository, &rev, prefix);
argc = parse_options(argc, argv, prefix, options, git_stash_show_usage,
- PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
+ PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT |
PARSE_OPT_KEEP_DASHDASH);
strvec_push(&revision_args, argv[0]);
@@ -861,10 +926,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
strvec_push(&revision_args, argv[i]);
}
- ret = get_stash_info(&info, stash_args.nr, stash_args.v);
- strvec_clear(&stash_args);
- if (ret)
- return -1;
+ if (get_stash_info(&info, stash_args.nr, stash_args.v))
+ goto cleanup;
/*
* The config settings are applied only if there are not passed
@@ -878,16 +941,14 @@ static int show_stash(int argc, const char **argv, const char *prefix)
rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
if (!show_stat && !show_patch) {
- free_stash_info(&info);
- return 0;
+ ret = 0;
+ goto cleanup;
}
}
argc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL);
- if (argc > 1) {
- free_stash_info(&info);
- usage_with_options(git_stash_show_usage, options);
- }
+ if (argc > 1)
+ goto usage;
if (!rev.diffopt.output_format) {
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
diff_setup_done(&rev.diffopt);
@@ -912,8 +973,17 @@ static int show_stash(int argc, const char **argv, const char *prefix)
}
log_tree_diff_flush(&rev);
+ ret = diff_result_code(&rev.diffopt, 0);
+cleanup:
+ strvec_clear(&stash_args);
free_stash_info(&info);
- return diff_result_code(&rev.diffopt, 0);
+ release_revisions(&rev);
+ if (do_usage)
+ usage_with_options(git_stash_show_usage, options);
+ return ret;
+usage:
+ do_usage = 1;
+ goto cleanup;
}
static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
@@ -951,7 +1021,7 @@ static int store_stash(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options,
git_stash_store_usage,
- PARSE_OPT_KEEP_UNKNOWN);
+ PARSE_OPT_KEEP_UNKNOWN_OPT);
if (argc != 1) {
if (!quiet)
@@ -1025,13 +1095,13 @@ static int check_changes_tracked_files(const struct pathspec *ps)
int ret = 0;
/* No initial commit. */
- if (get_oid("HEAD", &dummy))
+ if (repo_get_oid(the_repository, "HEAD", &dummy))
return -1;
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
return -1;
- init_revisions(&rev, NULL);
+ repo_init_revisions(the_repository, &rev, NULL);
copy_pathspec(&rev.prune_data, ps);
rev.diffopt.flags.quick = 1;
@@ -1047,7 +1117,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)
goto done;
}
- object_array_clear(&rev.pending);
result = run_diff_files(&rev, 0);
if (diff_result_code(&rev.diffopt, result)) {
ret = 1;
@@ -1055,7 +1124,7 @@ static int check_changes_tracked_files(const struct pathspec *ps)
}
done:
- clear_pathspec(&rev.prune_data);
+ release_revisions(&rev);
return ret;
}
@@ -1083,12 +1152,12 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,
int ret = 0;
struct strbuf untracked_msg = STRBUF_INIT;
struct child_process cp_upd_index = CHILD_PROCESS_INIT;
- struct index_state istate = { NULL };
+ struct index_state istate = INDEX_STATE_INIT(the_repository);
cp_upd_index.git_cmd = 1;
strvec_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
"--remove", "--stdin", NULL);
- strvec_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
+ strvec_pushf(&cp_upd_index.env, "GIT_INDEX_FILE=%s",
stash_index_path.buf);
strbuf_addf(&untracked_msg, "untracked files on %s\n", msg->buf);
@@ -1111,7 +1180,7 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,
}
done:
- discard_index(&istate);
+ release_index(&istate);
strbuf_release(&untracked_msg);
remove_path(stash_index_path.buf);
return ret;
@@ -1122,7 +1191,7 @@ static int stash_staged(struct stash_info *info, struct strbuf *out_patch,
{
int ret = 0;
struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
- struct index_state istate = { NULL };
+ struct index_state istate = INDEX_STATE_INIT(the_repository);
if (write_index_as_tree(&info->w_tree, &istate, the_repository->index_file,
0, NULL)) {
@@ -1145,7 +1214,7 @@ static int stash_staged(struct stash_info *info, struct strbuf *out_patch,
}
done:
- discard_index(&istate);
+ release_index(&istate);
return ret;
}
@@ -1155,14 +1224,14 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
int ret = 0;
struct child_process cp_read_tree = CHILD_PROCESS_INIT;
struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
- struct index_state istate = { NULL };
+ struct index_state istate = INDEX_STATE_INIT(the_repository);
char *old_index_env = NULL, *old_repo_index_file;
remove_path(stash_index_path.buf);
cp_read_tree.git_cmd = 1;
strvec_pushl(&cp_read_tree.args, "read-tree", "HEAD", NULL);
- strvec_pushf(&cp_read_tree.env_array, "GIT_INDEX_FILE=%s",
+ strvec_pushf(&cp_read_tree.env, "GIT_INDEX_FILE=%s",
stash_index_path.buf);
if (run_command(&cp_read_tree)) {
ret = -1;
@@ -1175,7 +1244,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
- ret = run_add_interactive(NULL, "--patch=stash", ps);
+ ret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps);
the_repository->index_file = old_repo_index_file;
if (old_index_env && *old_index_env)
@@ -1206,7 +1275,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
}
done:
- discard_index(&istate);
+ release_index(&istate);
remove_path(stash_index_path.buf);
return ret;
}
@@ -1217,9 +1286,9 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
struct rev_info rev;
struct child_process cp_upd_index = CHILD_PROCESS_INIT;
struct strbuf diff_output = STRBUF_INIT;
- struct index_state istate = { NULL };
+ struct index_state istate = INDEX_STATE_INIT(the_repository);
- init_revisions(&rev, NULL);
+ repo_init_revisions(the_repository, &rev, NULL);
copy_pathspec(&rev.prune_data, ps);
set_alternate_index_output(stash_index_path.buf);
@@ -1233,7 +1302,7 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
rev.diffopt.format_callback = add_diff_to_buf;
rev.diffopt.format_callback_data = &diff_output;
- if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
+ if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) {
ret = -1;
goto done;
}
@@ -1249,7 +1318,7 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
strvec_pushl(&cp_upd_index.args, "update-index",
"--ignore-skip-worktree-entries",
"-z", "--add", "--remove", "--stdin", NULL);
- strvec_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
+ strvec_pushf(&cp_upd_index.env, "GIT_INDEX_FILE=%s",
stash_index_path.buf);
if (pipe_command(&cp_upd_index, diff_output.buf, diff_output.len,
@@ -1265,10 +1334,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
}
done:
- discard_index(&istate);
- UNLEAK(rev);
- object_array_clear(&rev.pending);
- clear_pathspec(&rev.prune_data);
+ release_index(&istate);
+ release_revisions(&rev);
strbuf_release(&diff_output);
remove_path(stash_index_path.buf);
return ret;
@@ -1293,13 +1360,14 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
prepare_fallback_ident("git stash", "git@stash");
- read_cache_preload(NULL);
- if (refresh_and_write_cache(REFRESH_QUIET, 0, 0) < 0) {
+ repo_read_index_preload(the_repository, NULL, 0);
+ if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0,
+ NULL, NULL, NULL) < 0) {
ret = -1;
goto done;
}
- if (get_oid("HEAD", &info->b_commit)) {
+ if (repo_get_oid(the_repository, "HEAD", &info->b_commit)) {
if (!quiet)
fprintf_ln(stderr, _("You do not have "
"the initial commit yet"));
@@ -1317,14 +1385,16 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
if (flags & REF_ISSYMREF)
skip_prefix(branch_ref, "refs/heads/", &branch_name);
- head_short_sha1 = find_unique_abbrev(&head_commit->object.oid,
- DEFAULT_ABBREV);
+ head_short_sha1 = repo_find_unique_abbrev(the_repository,
+ &head_commit->object.oid,
+ DEFAULT_ABBREV);
strbuf_addf(&msg, "%s: %s ", branch_name, head_short_sha1);
pp_commit_easy(CMIT_FMT_ONELINE, head_commit, &msg);
strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf);
commit_list_insert(head_commit, &parents);
- if (write_cache_as_tree(&info->i_tree, 0, NULL) ||
+ if (write_index_as_tree(&info->i_tree, &the_index, get_index_file(), 0,
+ NULL) ||
commit_tree(commit_tree_label.buf, commit_tree_label.len,
&info->i_tree, parents, &info->i_commit, NULL, NULL)) {
if (!quiet)
@@ -1408,11 +1478,11 @@ done:
return ret;
}
-static int create_stash(int argc, const char **argv, const char *prefix)
+static int create_stash(int argc, const char **argv, const char *prefix UNUSED)
{
- int ret = 0;
+ int ret;
struct strbuf stash_msg_buf = STRBUF_INIT;
- struct stash_info info;
+ struct stash_info info = STASH_INFO_INIT;
struct pathspec ps;
/* Starting with argv[1], since argv[0] is "create" */
@@ -1427,6 +1497,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)
if (!ret)
printf_ln("%s", oid_to_hex(&info.w_commit));
+ free_stash_info(&info);
strbuf_release(&stash_msg_buf);
return ret;
}
@@ -1435,7 +1506,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
int keep_index, int patch_mode, int include_untracked, int only_staged)
{
int ret = 0;
- struct stash_info info;
+ struct stash_info info = STASH_INFO_INIT;
struct strbuf patch = STRBUF_INIT;
struct strbuf stash_msg_buf = STRBUF_INIT;
struct strbuf untracked_files = STRBUF_INIT;
@@ -1461,15 +1532,15 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
goto done;
}
- read_cache_preload(NULL);
+ repo_read_index_preload(the_repository, NULL, 0);
if (!include_untracked && ps->nr) {
int i;
char *ps_matched = xcalloc(ps->nr, 1);
/* TODO: audit for interaction with sparse-index. */
ensure_full_index(&the_index);
- for (i = 0; i < active_nr; i++)
- ce_path_match(&the_index, active_cache[i], ps,
+ for (i = 0; i < the_index.cache_nr; i++)
+ ce_path_match(&the_index, the_index.cache[i], ps,
ps_matched);
if (report_path_error(ps_matched, ps)) {
@@ -1481,7 +1552,8 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
free(ps_matched);
}
- if (refresh_and_write_cache(REFRESH_QUIET, 0, 0)) {
+ if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0,
+ NULL, NULL, NULL)) {
ret = -1;
goto done;
}
@@ -1525,7 +1597,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
cp.git_cmd = 1;
if (startup_info->original_cwd) {
cp.dir = startup_info->original_cwd;
- strvec_pushf(&cp.env_array, "%s=%s",
+ strvec_pushf(&cp.env, "%s=%s",
GIT_WORK_TREE_ENVIRONMENT,
the_repository->worktree);
}
@@ -1538,7 +1610,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
goto done;
}
}
- discard_cache();
+ discard_index(&the_index);
if (ps->nr) {
struct child_process cp_add = CHILD_PROCESS_INIT;
struct child_process cp_diff = CHILD_PROCESS_INIT;
@@ -1634,7 +1706,10 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
}
done:
+ strbuf_release(&patch);
+ free_stash_info(&info);
strbuf_release(&stash_msg_buf);
+ strbuf_release(&untracked_files);
return ret;
}
@@ -1669,6 +1744,7 @@ static int push_stash(int argc, const char **argv, const char *prefix,
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
OPT_END()
};
+ int ret;
if (argc) {
force_assume = !strcmp(argv[0], "-p");
@@ -1708,8 +1784,15 @@ static int push_stash(int argc, const char **argv, const char *prefix,
die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
- return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
- include_untracked, only_staged);
+ ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
+ include_untracked, only_staged);
+ clear_pathspec(&ps);
+ return ret;
+}
+
+static int push_stash_unassumed(int argc, const char **argv, const char *prefix)
+{
+ return push_stash(argc, argv, prefix, 0);
}
static int save_stash(int argc, const char **argv, const char *prefix)
@@ -1760,47 +1843,40 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
pid_t pid = getpid();
const char *index_file;
struct strvec args = STRVEC_INIT;
-
+ parse_opt_subcommand_fn *fn = NULL;
struct option options[] = {
+ OPT_SUBCOMMAND("apply", &fn, apply_stash),
+ OPT_SUBCOMMAND("clear", &fn, clear_stash),
+ OPT_SUBCOMMAND("drop", &fn, drop_stash),
+ OPT_SUBCOMMAND("pop", &fn, pop_stash),
+ OPT_SUBCOMMAND("branch", &fn, branch_stash),
+ OPT_SUBCOMMAND("list", &fn, list_stash),
+ OPT_SUBCOMMAND("show", &fn, show_stash),
+ OPT_SUBCOMMAND("store", &fn, store_stash),
+ OPT_SUBCOMMAND("create", &fn, create_stash),
+ OPT_SUBCOMMAND("push", &fn, push_stash_unassumed),
+ OPT_SUBCOMMAND_F("save", &fn, save_stash, PARSE_OPT_NOCOMPLETE),
OPT_END()
};
git_config(git_stash_config, NULL);
argc = parse_options(argc, argv, prefix, options, git_stash_usage,
- PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
+ PARSE_OPT_SUBCOMMAND_OPTIONAL |
+ PARSE_OPT_KEEP_UNKNOWN_OPT |
+ PARSE_OPT_KEEP_DASHDASH);
+
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
index_file = get_index_file();
strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file,
(uintmax_t)pid);
- if (!argc)
- return !!push_stash(0, NULL, prefix, 0);
- else if (!strcmp(argv[0], "apply"))
- return !!apply_stash(argc, argv, prefix);
- else if (!strcmp(argv[0], "clear"))
- return !!clear_stash(argc, argv, prefix);
- else if (!strcmp(argv[0], "drop"))
- return !!drop_stash(argc, argv, prefix);
- else if (!strcmp(argv[0], "pop"))
- return !!pop_stash(argc, argv, prefix);
- else if (!strcmp(argv[0], "branch"))
- return !!branch_stash(argc, argv, prefix);
- else if (!strcmp(argv[0], "list"))
- return !!list_stash(argc, argv, prefix);
- else if (!strcmp(argv[0], "show"))
- return !!show_stash(argc, argv, prefix);
- else if (!strcmp(argv[0], "store"))
- return !!store_stash(argc, argv, prefix);
- else if (!strcmp(argv[0], "create"))
- return !!create_stash(argc, argv, prefix);
- else if (!strcmp(argv[0], "push"))
- return !!push_stash(argc, argv, prefix, 0);
- else if (!strcmp(argv[0], "save"))
- return !!save_stash(argc, argv, prefix);
- else if (*argv[0] != '-')
- usage_msg_optf(_("unknown subcommand: %s"),
- git_stash_usage, options, argv[0]);
+ if (fn)
+ return !!fn(argc, argv, prefix);
+ else if (!argc)
+ return !!push_stash_unassumed(0, NULL, prefix);
/* Assume 'stash push' */
strvec_push(&args, "push");
diff --git a/builtin/stripspace.c b/builtin/stripspace.c
index 1e34cf2beb..7b700a9fb1 100644
--- a/builtin/stripspace.c
+++ b/builtin/stripspace.c
@@ -1,8 +1,11 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
+#include "environment.h"
+#include "gettext.h"
#include "parse-options.h"
+#include "setup.h"
#include "strbuf.h"
+#include "write-or-die.h"
static void comment_lines(struct strbuf *buf)
{
@@ -10,7 +13,7 @@ static void comment_lines(struct strbuf *buf)
size_t len;
msg = strbuf_detach(buf, &len);
- strbuf_add_commented_lines(buf, msg, len);
+ strbuf_add_commented_lines(buf, msg, len, comment_line_char);
free(msg);
}
@@ -55,7 +58,8 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
die_errno("could not read the input");
if (mode == STRIP_DEFAULT || mode == STRIP_COMMENTS)
- strbuf_stripspace(&buf, mode == STRIP_COMMENTS);
+ strbuf_stripspace(&buf,
+ mode == STRIP_COMMENTS ? comment_line_char : '\0');
else
comment_lines(&buf);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 1a8e5d0621..f6871efd95 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1,12 +1,20 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "repository.h"
-#include "cache.h"
#include "config.h"
#include "parse-options.h"
#include "quote.h"
+#include "path.h"
#include "pathspec.h"
+#include "preload-index.h"
#include "dir.h"
+#include "read-cache.h"
+#include "setup.h"
+#include "sparse-index.h"
#include "submodule.h"
#include "submodule-config.h"
#include "string-list.h"
@@ -18,7 +26,9 @@
#include "revision.h"
#include "diffcore.h"
#include "diff.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
#include "advice.h"
#include "branch.h"
#include "list-objects-filter-options.h"
@@ -31,174 +41,61 @@
typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
void *cb_data);
-static char *repo_get_default_remote(struct repository *repo)
+static int repo_get_default_remote(struct repository *repo, char **default_remote)
{
- char *dest = NULL, *ret;
+ char *dest = NULL;
struct strbuf sb = STRBUF_INIT;
struct ref_store *store = get_main_ref_store(repo);
const char *refname = refs_resolve_ref_unsafe(store, "HEAD", 0, NULL,
NULL);
if (!refname)
- die(_("No such ref: %s"), "HEAD");
+ return die_message(_("No such ref: %s"), "HEAD");
/* detached HEAD */
- if (!strcmp(refname, "HEAD"))
- return xstrdup("origin");
+ if (!strcmp(refname, "HEAD")) {
+ *default_remote = xstrdup("origin");
+ return 0;
+ }
if (!skip_prefix(refname, "refs/heads/", &refname))
- die(_("Expecting a full ref name, got %s"), refname);
+ return die_message(_("Expecting a full ref name, got %s"),
+ refname);
strbuf_addf(&sb, "branch.%s.remote", refname);
if (repo_config_get_string(repo, sb.buf, &dest))
- ret = xstrdup("origin");
+ *default_remote = xstrdup("origin");
else
- ret = dest;
+ *default_remote = dest;
strbuf_release(&sb);
- return ret;
+ return 0;
}
-static char *get_default_remote_submodule(const char *module_path)
+static int get_default_remote_submodule(const char *module_path, char **default_remote)
{
struct repository subrepo;
+ int ret;
- repo_submodule_init(&subrepo, the_repository, module_path, null_oid());
- return repo_get_default_remote(&subrepo);
-}
-
-static char *get_default_remote(void)
-{
- return repo_get_default_remote(the_repository);
-}
-
-static int starts_with_dot_slash(const char *str)
-{
- return str[0] == '.' && is_dir_sep(str[1]);
-}
+ if (repo_submodule_init(&subrepo, the_repository, module_path,
+ null_oid()) < 0)
+ return die_message(_("could not get a repository handle for submodule '%s'"),
+ module_path);
+ ret = repo_get_default_remote(&subrepo, default_remote);
+ repo_clear(&subrepo);
-static int starts_with_dot_dot_slash(const char *str)
-{
- return str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]);
+ return ret;
}
-/*
- * Returns 1 if it was the last chop before ':'.
- */
-static int chop_last_dir(char **remoteurl, int is_relative)
+static char *get_default_remote(void)
{
- char *rfind = find_last_dir_sep(*remoteurl);
- if (rfind) {
- *rfind = '\0';
- return 0;
- }
+ char *default_remote;
+ int code = repo_get_default_remote(the_repository, &default_remote);
- rfind = strrchr(*remoteurl, ':');
- if (rfind) {
- *rfind = '\0';
- return 1;
- }
-
- if (is_relative || !strcmp(".", *remoteurl))
- die(_("cannot strip one component off url '%s'"),
- *remoteurl);
+ if (code)
+ exit(code);
- free(*remoteurl);
- *remoteurl = xstrdup(".");
- return 0;
-}
-
-/*
- * The `url` argument is the URL that navigates to the submodule origin
- * repo. When relative, this URL is relative to the superproject origin
- * URL repo. The `up_path` argument, if specified, is the relative
- * path that navigates from the submodule working tree to the superproject
- * working tree. Returns the origin URL of the submodule.
- *
- * Return either an absolute URL or filesystem path (if the superproject
- * origin URL is an absolute URL or filesystem path, respectively) or a
- * relative file system path (if the superproject origin URL is a relative
- * file system path).
- *
- * When the output is a relative file system path, the path is either
- * relative to the submodule working tree, if up_path is specified, or to
- * the superproject working tree otherwise.
- *
- * NEEDSWORK: This works incorrectly on the domain and protocol part.
- * remote_url url outcome expectation
- * http://a.com/b ../c http://a.com/c as is
- * http://a.com/b/ ../c http://a.com/c same as previous line, but
- * ignore trailing slash in url
- * http://a.com/b ../../c http://c error out
- * http://a.com/b ../../../c http:/c error out
- * http://a.com/b ../../../../c http:c error out
- * http://a.com/b ../../../../../c .:c error out
- * NEEDSWORK: Given how chop_last_dir() works, this function is broken
- * when a local part has a colon in its path component, too.
- */
-static char *relative_url(const char *remote_url,
- const char *url,
- const char *up_path)
-{
- int is_relative = 0;
- int colonsep = 0;
- char *out;
- char *remoteurl = xstrdup(remote_url);
- struct strbuf sb = STRBUF_INIT;
- size_t len = strlen(remoteurl);
-
- if (is_dir_sep(remoteurl[len-1]))
- remoteurl[len-1] = '\0';
-
- if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
- is_relative = 0;
- else {
- is_relative = 1;
- /*
- * Prepend a './' to ensure all relative
- * remoteurls start with './' or '../'
- */
- if (!starts_with_dot_slash(remoteurl) &&
- !starts_with_dot_dot_slash(remoteurl)) {
- strbuf_reset(&sb);
- strbuf_addf(&sb, "./%s", remoteurl);
- free(remoteurl);
- remoteurl = strbuf_detach(&sb, NULL);
- }
- }
- /*
- * When the url starts with '../', remove that and the
- * last directory in remoteurl.
- */
- while (url) {
- if (starts_with_dot_dot_slash(url)) {
- url += 3;
- colonsep |= chop_last_dir(&remoteurl, is_relative);
- } else if (starts_with_dot_slash(url))
- url += 2;
- else
- break;
- }
- strbuf_reset(&sb);
- strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
- if (ends_with(url, "/"))
- strbuf_setlen(&sb, sb.len - 1);
- free(remoteurl);
-
- if (starts_with_dot_slash(sb.buf))
- out = xstrdup(sb.buf + 2);
- else
- out = xstrdup(sb.buf);
-
- if (!up_path || !is_relative) {
- strbuf_release(&sb);
- return out;
- }
-
- strbuf_reset(&sb);
- strbuf_addf(&sb, "%s%s", up_path, out);
- free(out);
- return strbuf_detach(&sb, NULL);
+ return default_remote;
}
static char *resolve_relative_url(const char *rel_url, const char *up_path, int quiet)
@@ -225,31 +122,9 @@ static char *resolve_relative_url(const char *rel_url, const char *up_path, int
return resolved_url;
}
-static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
-{
- char *remoteurl, *res;
- const char *up_path, *url;
-
- if (argc != 4)
- die("resolve-relative-url-test only accepts three arguments: <up_path> <remoteurl> <url>");
-
- up_path = argv[1];
- remoteurl = xstrdup(argv[2]);
- url = argv[3];
-
- if (!strcmp(up_path, "(null)"))
- up_path = NULL;
-
- res = relative_url(remoteurl, url, up_path);
- puts(res);
- free(res);
- free(remoteurl);
- return 0;
-}
-
-static char *do_get_submodule_displaypath(const char *path,
- const char *prefix,
- const char *super_prefix)
+/* the result should be freed by the caller. */
+static char *get_submodule_displaypath(const char *path, const char *prefix,
+ const char *super_prefix)
{
if (prefix && super_prefix) {
BUG("cannot have prefix '%s' and superprefix '%s'",
@@ -266,13 +141,6 @@ static char *do_get_submodule_displaypath(const char *path,
}
}
-/* the result should be freed by the caller. */
-static char *get_submodule_displaypath(const char *path, const char *prefix)
-{
- const char *super_prefix = get_super_prefix();
- return do_get_submodule_displaypath(path, prefix, super_prefix);
-}
-
static char *compute_rev_name(const char *sub_path, const char* object_id)
{
struct strbuf sb = STRBUF_INIT;
@@ -292,7 +160,7 @@ static char *compute_rev_name(const char *sub_path, const char* object_id)
for (d = describe_argv; *d; d++) {
struct child_process cp = CHILD_PROCESS_INIT;
- prepare_submodule_repo_env(&cp.env_array);
+ prepare_submodule_repo_env(&cp.env);
cp.dir = sub_path;
cp.git_cmd = 1;
cp.no_stderr = 1;
@@ -317,13 +185,19 @@ struct module_list {
};
#define MODULE_LIST_INIT { 0 }
-static int module_list_compute(int argc, const char **argv,
+static void module_list_release(struct module_list *ml)
+{
+ free(ml->entries);
+}
+
+static int module_list_compute(const char **argv,
const char *prefix,
struct pathspec *pathspec,
struct module_list *list)
{
int i, result = 0;
char *ps_matched = NULL;
+
parse_pathspec(pathspec, 0,
PATHSPEC_PREFER_FULL,
prefix, argv);
@@ -331,11 +205,11 @@ static int module_list_compute(int argc, const char **argv,
if (pathspec->nr)
ps_matched = xcalloc(pathspec->nr, 1);
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
die(_("index file corrupt"));
- for (i = 0; i < active_nr; i++) {
- const struct cache_entry *ce = active_cache[i];
+ for (i = 0; i < the_index.cache_nr; i++) {
+ const struct cache_entry *ce = the_index.cache[i];
if (!match_pathspec(&the_index, pathspec, ce->name, ce_namelen(ce),
0, ps_matched, 1) ||
@@ -344,8 +218,8 @@ static int module_list_compute(int argc, const char **argv,
ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
list->entries[list->nr++] = ce;
- while (i + 1 < active_nr &&
- !strcmp(ce->name, active_cache[i + 1]->name))
+ while (i + 1 < the_index.cache_nr &&
+ !strcmp(ce->name, the_index.cache[i + 1]->name))
/*
* Skip entries with the same name in different stages
* to make sure an entry is returned only once.
@@ -378,7 +252,7 @@ static void module_list_active(struct module_list *list)
active_modules.entries[active_modules.nr++] = ce;
}
- free(list->entries);
+ module_list_release(list);
*list = active_modules;
}
@@ -401,49 +275,11 @@ static char *get_up_path(const char *path)
return strbuf_detach(&sb, NULL);
}
-static int module_list(int argc, const char **argv, const char *prefix)
-{
- int i;
- struct pathspec pathspec;
- struct module_list list = MODULE_LIST_INIT;
-
- struct option module_list_options[] = {
- OPT_STRING(0, "prefix", &prefix,
- N_("path"),
- N_("alternative anchor for relative paths")),
- OPT_END()
- };
-
- const char *const git_submodule_helper_usage[] = {
- N_("git submodule--helper list [--prefix=<path>] [<path>...]"),
- NULL
- };
-
- argc = parse_options(argc, argv, prefix, module_list_options,
- git_submodule_helper_usage, 0);
-
- if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
- return 1;
-
- for (i = 0; i < list.nr; i++) {
- const struct cache_entry *ce = list.entries[i];
-
- if (ce_stage(ce))
- printf("%06o %s U\t", ce->ce_mode,
- oid_to_hex(null_oid()));
- else
- printf("%06o %s %d\t", ce->ce_mode,
- oid_to_hex(&ce->oid), ce_stage(ce));
-
- fprintf(stdout, "%s\n", ce->name);
- }
- return 0;
-}
-
static void for_each_listed_submodule(const struct module_list *list,
each_submodule_fn fn, void *cb_data)
{
int i;
+
for (i = 0; i < list->nr; i++)
fn(list->entries[i], cb_data);
}
@@ -452,6 +288,7 @@ struct foreach_cb {
int argc;
const char **argv;
const char *prefix;
+ const char *super_prefix;
int quiet;
int recursive;
};
@@ -463,12 +300,12 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
struct foreach_cb *info = cb_data;
const char *path = list_item->name;
const struct object_id *ce_oid = &list_item->oid;
-
const struct submodule *sub;
struct child_process cp = CHILD_PROCESS_INIT;
char *displaypath;
- displaypath = get_submodule_displaypath(path, info->prefix);
+ displaypath = get_submodule_displaypath(path, info->prefix,
+ info->super_prefix);
sub = submodule_from_path(the_repository, null_oid(), path);
@@ -479,7 +316,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
if (!is_submodule_populated_gently(path, NULL))
goto cleanup;
- prepare_submodule_repo_env(&cp.env_array);
+ prepare_submodule_repo_env(&cp.env);
/*
* For the purpose of executing <command> in the submodule,
@@ -499,12 +336,12 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
char *toplevel = xgetcwd();
struct strbuf sb = STRBUF_INIT;
- strvec_pushf(&cp.env_array, "name=%s", sub->name);
- strvec_pushf(&cp.env_array, "sm_path=%s", path);
- strvec_pushf(&cp.env_array, "displaypath=%s", displaypath);
- strvec_pushf(&cp.env_array, "sha1=%s",
+ strvec_pushf(&cp.env, "name=%s", sub->name);
+ strvec_pushf(&cp.env, "sm_path=%s", path);
+ strvec_pushf(&cp.env, "displaypath=%s", displaypath);
+ strvec_pushf(&cp.env, "sha1=%s",
oid_to_hex(ce_oid));
- strvec_pushf(&cp.env_array, "toplevel=%s", toplevel);
+ strvec_pushf(&cp.env, "toplevel=%s", toplevel);
/*
* Since the path variable was accessible from the script
@@ -513,7 +350,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
* on windows. And since environment variables are
* case-insensitive in windows, it interferes with the
* existing PATH variable. Hence, to avoid that, we expose
- * path via the args strvec and not via env_array.
+ * path via the args strvec and not via env.
*/
sq_quote_buf(&sb, path);
strvec_pushf(&cp.args, "path=%s; %s",
@@ -536,12 +373,12 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
cpr.git_cmd = 1;
cpr.dir = path;
- prepare_submodule_repo_env(&cpr.env_array);
+ prepare_submodule_repo_env(&cpr.env);
- strvec_pushl(&cpr.args, "--super-prefix", NULL);
- strvec_pushf(&cpr.args, "%s/", displaypath);
strvec_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
NULL);
+ strvec_pushl(&cpr.args, "--super-prefix", NULL);
+ strvec_pushf(&cpr.args, "%s/", displaypath);
if (info->quiet)
strvec_push(&cpr.args, "--quiet");
@@ -562,26 +399,26 @@ cleanup:
static int module_foreach(int argc, const char **argv, const char *prefix)
{
struct foreach_cb info = FOREACH_CB_INIT;
- struct pathspec pathspec;
+ struct pathspec pathspec = { 0 };
struct module_list list = MODULE_LIST_INIT;
-
struct option module_foreach_options[] = {
+ OPT__SUPER_PREFIX(&info.super_prefix),
OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")),
OPT_BOOL(0, "recursive", &info.recursive,
N_("recurse into nested submodules")),
OPT_END()
};
-
const char *const git_submodule_helper_usage[] = {
- N_("git submodule--helper foreach [--quiet] [--recursive] [--] <command>"),
+ N_("git submodule foreach [--quiet] [--recursive] [--] <command>"),
NULL
};
+ int ret = 1;
argc = parse_options(argc, argv, prefix, module_foreach_options,
git_submodule_helper_usage, 0);
- if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
- return 1;
+ if (module_list_compute(NULL, prefix, &pathspec, &list) < 0)
+ goto cleanup;
info.argc = argc;
info.argv = argv;
@@ -589,27 +426,42 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
- return 0;
+ ret = 0;
+cleanup:
+ module_list_release(&list);
+ clear_pathspec(&pathspec);
+ return ret;
+}
+
+static int starts_with_dot_slash(const char *const path)
+{
+ return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH |
+ PATH_MATCH_XPLATFORM);
+}
+
+static int starts_with_dot_dot_slash(const char *const path)
+{
+ return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH |
+ PATH_MATCH_XPLATFORM);
}
struct init_cb {
const char *prefix;
- const char *superprefix;
+ const char *super_prefix;
unsigned int flags;
};
#define INIT_CB_INIT { 0 }
static void init_submodule(const char *path, const char *prefix,
- const char *superprefix, unsigned int flags)
+ const char *super_prefix,
+ unsigned int flags)
{
const struct submodule *sub;
struct strbuf sb = STRBUF_INIT;
- char *upd = NULL, *url = NULL, *displaypath;
+ const char *upd;
+ char *url = NULL, *displaypath;
- /* try superprefix from the environment, if it is not passed explicitly */
- if (!superprefix)
- superprefix = get_super_prefix();
- displaypath = do_get_submodule_displaypath(path, prefix, superprefix);
+ displaypath = get_submodule_displaypath(path, prefix, super_prefix);
sub = submodule_from_path(the_repository, null_oid(), path);
@@ -646,6 +498,7 @@ static void init_submodule(const char *path, const char *prefix,
if (starts_with_dot_dot_slash(url) ||
starts_with_dot_slash(url)) {
char *oldurl = url;
+
url = resolve_relative_url(oldurl, NULL, 0);
free(oldurl);
}
@@ -662,14 +515,15 @@ static void init_submodule(const char *path, const char *prefix,
/* Copy "update" setting when it is not set yet */
strbuf_addf(&sb, "submodule.%s.update", sub->name);
- if (git_config_get_string(sb.buf, &upd) &&
+ if (git_config_get_string_tmp(sb.buf, &upd) &&
sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
if (sub->update_strategy.type == SM_UPDATE_COMMAND) {
fprintf(stderr, _("warning: command update mode suggested for submodule '%s'\n"),
sub->name);
- upd = xstrdup("none");
- } else
- upd = xstrdup(submodule_strategy_to_string(&sub->update_strategy));
+ upd = "none";
+ } else {
+ upd = submodule_update_type_to_string(sub->update_strategy.type);
+ }
if (git_config_set_gently(sb.buf, upd))
die(_("Failed to register update mode for submodule path '%s'"), displaypath);
@@ -677,43 +531,43 @@ static void init_submodule(const char *path, const char *prefix,
strbuf_release(&sb);
free(displaypath);
free(url);
- free(upd);
}
static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data)
{
struct init_cb *info = cb_data;
- init_submodule(list_item->name, info->prefix, info->superprefix, info->flags);
+
+ init_submodule(list_item->name, info->prefix, info->super_prefix,
+ info->flags);
}
static int module_init(int argc, const char **argv, const char *prefix)
{
struct init_cb info = INIT_CB_INIT;
- struct pathspec pathspec;
+ struct pathspec pathspec = { 0 };
struct module_list list = MODULE_LIST_INIT;
int quiet = 0;
-
struct option module_init_options[] = {
OPT__QUIET(&quiet, N_("suppress output for initializing a submodule")),
OPT_END()
};
-
const char *const git_submodule_helper_usage[] = {
- N_("git submodule--helper init [<options>] [<path>]"),
+ N_("git submodule init [<options>] [<path>]"),
NULL
};
+ int ret = 1;
argc = parse_options(argc, argv, prefix, module_init_options,
git_submodule_helper_usage, 0);
- if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
- return 1;
+ if (module_list_compute(argv, prefix, &pathspec, &list) < 0)
+ goto cleanup;
/*
* If there are no path args and submodule.active is set then,
* by default, only initialize 'active' modules.
*/
- if (!argc && git_config_get_value_multi("submodule.active"))
+ if (!argc && !git_config_get("submodule.active"))
module_list_active(&list);
info.prefix = prefix;
@@ -722,11 +576,16 @@ static int module_init(int argc, const char **argv, const char *prefix)
for_each_listed_submodule(&list, init_submodule_cb, &info);
- return 0;
+ ret = 0;
+cleanup:
+ module_list_release(&list);
+ clear_pathspec(&pathspec);
+ return ret;
}
struct status_cb {
const char *prefix;
+ const char *super_prefix;
unsigned int flags;
};
#define STATUS_CB_INIT { 0 }
@@ -740,20 +599,23 @@ static void print_status(unsigned int flags, char state, const char *path,
printf("%c%s %s", state, oid_to_hex(oid), displaypath);
if (state == ' ' || state == '+') {
- const char *name = compute_rev_name(path, oid_to_hex(oid));
+ char *name = compute_rev_name(path, oid_to_hex(oid));
if (name)
printf(" (%s)", name);
+ free(name);
}
printf("\n");
}
-static int handle_submodule_head_ref(const char *refname,
- const struct object_id *oid, int flags,
+static int handle_submodule_head_ref(const char *refname UNUSED,
+ const struct object_id *oid,
+ int flags UNUSED,
void *cb_data)
{
struct object_id *output = cb_data;
+
if (oid)
oidcpy(output, oid);
@@ -762,20 +624,23 @@ static int handle_submodule_head_ref(const char *refname,
static void status_submodule(const char *path, const struct object_id *ce_oid,
unsigned int ce_flags, const char *prefix,
- unsigned int flags)
+ const char *super_prefix, unsigned int flags)
{
char *displaypath;
struct strvec diff_files_args = STRVEC_INIT;
- struct rev_info rev;
+ struct rev_info rev = REV_INFO_INIT;
int diff_files_result;
struct strbuf buf = STRBUF_INIT;
const char *git_dir;
+ struct setup_revision_opt opt = {
+ .free_removed_argv_elements = 1,
+ };
if (!submodule_from_path(the_repository, null_oid(), path))
die(_("no submodule mapping found in .gitmodules for path '%s'"),
path);
- displaypath = get_submodule_displaypath(path, prefix);
+ displaypath = get_submodule_displaypath(path, prefix, super_prefix);
if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
print_status(flags, 'U', path, null_oid(), displaypath);
@@ -803,9 +668,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
repo_init_revisions(the_repository, &rev, NULL);
rev.abbrev = 0;
- diff_files_args.nr = setup_revisions(diff_files_args.nr,
- diff_files_args.v,
- &rev, NULL);
+ setup_revisions(diff_files_args.nr, diff_files_args.v, &rev, &opt);
diff_files_result = run_diff_files(&rev, 0);
if (!diff_result_code(&rev.diffopt, diff_files_result)) {
@@ -833,12 +696,12 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
cpr.git_cmd = 1;
cpr.dir = path;
- prepare_submodule_repo_env(&cpr.env_array);
+ prepare_submodule_repo_env(&cpr.env);
- strvec_push(&cpr.args, "--super-prefix");
- strvec_pushf(&cpr.args, "%s/", displaypath);
strvec_pushl(&cpr.args, "submodule--helper", "status",
"--recursive", NULL);
+ strvec_push(&cpr.args, "--super-prefix");
+ strvec_pushf(&cpr.args, "%s/", displaypath);
if (flags & OPT_CACHED)
strvec_push(&cpr.args, "--cached");
@@ -853,40 +716,42 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
cleanup:
strvec_clear(&diff_files_args);
free(displaypath);
+ release_revisions(&rev);
}
static void status_submodule_cb(const struct cache_entry *list_item,
void *cb_data)
{
struct status_cb *info = cb_data;
+
status_submodule(list_item->name, &list_item->oid, list_item->ce_flags,
- info->prefix, info->flags);
+ info->prefix, info->super_prefix, info->flags);
}
static int module_status(int argc, const char **argv, const char *prefix)
{
struct status_cb info = STATUS_CB_INIT;
- struct pathspec pathspec;
+ struct pathspec pathspec = { 0 };
struct module_list list = MODULE_LIST_INIT;
int quiet = 0;
-
struct option module_status_options[] = {
+ OPT__SUPER_PREFIX(&info.super_prefix),
OPT__QUIET(&quiet, N_("suppress submodule status output")),
OPT_BIT(0, "cached", &info.flags, N_("use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED),
OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE),
OPT_END()
};
-
const char *const git_submodule_helper_usage[] = {
N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
NULL
};
+ int ret = 1;
argc = parse_options(argc, argv, prefix, module_status_options,
git_submodule_helper_usage, 0);
- if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
- return 1;
+ if (module_list_compute(argv, prefix, &pathspec, &list) < 0)
+ goto cleanup;
info.prefix = prefix;
if (quiet)
@@ -894,25 +759,11 @@ static int module_status(int argc, const char **argv, const char *prefix)
for_each_listed_submodule(&list, status_submodule_cb, &info);
- return 0;
-}
-
-static int module_name(int argc, const char **argv, const char *prefix)
-{
- const struct submodule *sub;
-
- if (argc != 2)
- usage(_("git submodule--helper name <path>"));
-
- sub = submodule_from_path(the_repository, null_oid(), argv[1]);
-
- if (!sub)
- die(_("no submodule mapping found in .gitmodules for path '%s'"),
- argv[1]);
-
- printf("%s\n", sub->name);
-
- return 0;
+ ret = 0;
+cleanup:
+ module_list_release(&list);
+ clear_pathspec(&pathspec);
+ return ret;
}
struct module_cb {
@@ -921,20 +772,39 @@ struct module_cb {
struct object_id oid_src;
struct object_id oid_dst;
char status;
- const char *sm_path;
+ char *sm_path;
};
#define MODULE_CB_INIT { 0 }
+static void module_cb_release(struct module_cb *mcb)
+{
+ free(mcb->sm_path);
+}
+
struct module_cb_list {
struct module_cb **entries;
int alloc, nr;
};
#define MODULE_CB_LIST_INIT { 0 }
+static void module_cb_list_release(struct module_cb_list *mcbl)
+{
+ int i;
+
+ for (i = 0; i < mcbl->nr; i++) {
+ struct module_cb *mcb = mcbl->entries[i];
+
+ module_cb_release(mcb);
+ free(mcb);
+ }
+ free(mcbl->entries);
+}
+
struct summary_cb {
int argc;
const char **argv;
const char *prefix;
+ const char *super_prefix;
unsigned int cached: 1;
unsigned int for_status: 1;
unsigned int files: 1;
@@ -955,7 +825,7 @@ static char *verify_submodule_committish(const char *sm_path,
cp_rev_parse.git_cmd = 1;
cp_rev_parse.dir = sm_path;
- prepare_submodule_repo_env(&cp_rev_parse.env_array);
+ prepare_submodule_repo_env(&cp_rev_parse.env);
strvec_pushl(&cp_rev_parse.args, "rev-parse", "-q", "--short", NULL);
strvec_pushf(&cp_rev_parse.args, "%s^0", committish);
strvec_push(&cp_rev_parse.args, "--");
@@ -967,7 +837,7 @@ static char *verify_submodule_committish(const char *sm_path,
return strbuf_detach(&result, NULL);
}
-static void print_submodule_summary(struct summary_cb *info, char *errmsg,
+static void print_submodule_summary(struct summary_cb *info, const char *errmsg,
int total_commits, const char *displaypath,
const char *src_abbrev, const char *dst_abbrev,
struct module_cb *p)
@@ -996,7 +866,7 @@ static void print_submodule_summary(struct summary_cb *info, char *errmsg,
cp_log.git_cmd = 1;
cp_log.dir = p->sm_path;
- prepare_submodule_repo_env(&cp_log.env_array);
+ prepare_submodule_repo_env(&cp_log.env);
strvec_pushl(&cp_log.args, "log", NULL);
if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst)) {
@@ -1025,12 +895,13 @@ static void generate_submodule_summary(struct summary_cb *info,
{
char *displaypath, *src_abbrev = NULL, *dst_abbrev;
int missing_src = 0, missing_dst = 0;
- char *errmsg = NULL;
+ struct strbuf errmsg = STRBUF_INIT;
int total_commits = -1;
if (!info->cached && oideq(&p->oid_dst, null_oid())) {
if (S_ISGITLINK(p->mod_dst)) {
struct ref_store *refs = get_submodule_ref_store(p->sm_path);
+
if (refs)
refs_head_ref(refs, handle_submodule_head_ref, &p->oid_dst);
} else if (S_ISLNK(p->mod_dst) || S_ISREG(p->mod_dst)) {
@@ -1095,7 +966,8 @@ static void generate_submodule_summary(struct summary_cb *info,
dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
}
- displaypath = get_submodule_displaypath(p->sm_path, info->prefix);
+ displaypath = get_submodule_displaypath(p->sm_path, info->prefix,
+ info->super_prefix);
if (!missing_src && !missing_dst) {
struct child_process cp_rev_list = CHILD_PROCESS_INIT;
@@ -1113,7 +985,7 @@ static void generate_submodule_summary(struct summary_cb *info,
cp_rev_list.git_cmd = 1;
cp_rev_list.dir = p->sm_path;
- prepare_submodule_repo_env(&cp_rev_list.env_array);
+ prepare_submodule_repo_env(&cp_rev_list.env);
if (!capture_command(&cp_rev_list, &sb_rev_list, 0))
total_commits = atoi(sb_rev_list.buf);
@@ -1125,28 +997,27 @@ static void generate_submodule_summary(struct summary_cb *info,
* submodule, i.e., deleted or changed to blob
*/
if (S_ISGITLINK(p->mod_dst)) {
- struct strbuf errmsg_str = STRBUF_INIT;
if (missing_src && missing_dst) {
- strbuf_addf(&errmsg_str, " Warn: %s doesn't contain commits %s and %s\n",
+ strbuf_addf(&errmsg, " Warn: %s doesn't contain commits %s and %s\n",
displaypath, oid_to_hex(&p->oid_src),
oid_to_hex(&p->oid_dst));
} else {
- strbuf_addf(&errmsg_str, " Warn: %s doesn't contain commit %s\n",
+ strbuf_addf(&errmsg, " Warn: %s doesn't contain commit %s\n",
displaypath, missing_src ?
oid_to_hex(&p->oid_src) :
oid_to_hex(&p->oid_dst));
}
- errmsg = strbuf_detach(&errmsg_str, NULL);
}
}
- print_submodule_summary(info, errmsg, total_commits,
- displaypath, src_abbrev,
+ print_submodule_summary(info, errmsg.len ? errmsg.buf : NULL,
+ total_commits, displaypath, src_abbrev,
dst_abbrev, p);
free(displaypath);
free(src_abbrev);
free(dst_abbrev);
+ strbuf_release(&errmsg);
}
static void prepare_submodule_summary(struct summary_cb *info,
@@ -1191,7 +1062,7 @@ static void prepare_submodule_summary(struct summary_cb *info,
}
static void submodule_summary_callback(struct diff_queue_struct *q,
- struct diff_options *options,
+ struct diff_options *options UNUSED,
void *data)
{
int i;
@@ -1230,7 +1101,11 @@ static int compute_summary_module_list(struct object_id *head_oid,
{
struct strvec diff_args = STRVEC_INIT;
struct rev_info rev;
+ struct setup_revision_opt opt = {
+ .free_removed_argv_elements = 1,
+ };
struct module_cb_list list = MODULE_CB_LIST_INIT;
+ int ret = 0;
strvec_push(&diff_args, get_diff_cmd(diff_cmd));
if (info->cached)
@@ -1243,10 +1118,10 @@ static int compute_summary_module_list(struct object_id *head_oid,
strvec_pushv(&diff_args, info->argv);
git_config(git_diff_basic_config, NULL);
- init_revisions(&rev, info->prefix);
+ repo_init_revisions(the_repository, &rev, info->prefix);
rev.abbrev = 0;
precompose_argv_prefix(diff_args.nr, diff_args.v, NULL);
- setup_revisions(diff_args.nr, diff_args.v, &rev, NULL);
+ setup_revisions(diff_args.nr, diff_args.v, &rev, &opt);
rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = submodule_summary_callback;
rev.diffopt.format_callback_data = &list;
@@ -1254,13 +1129,15 @@ static int compute_summary_module_list(struct object_id *head_oid,
if (!info->cached) {
if (diff_cmd == DIFF_INDEX)
setup_work_tree();
- if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
- perror("read_cache_preload");
- return -1;
+ if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) {
+ perror("repo_read_index_preload");
+ ret = -1;
+ goto cleanup;
}
- } else if (read_cache() < 0) {
- perror("read_cache");
- return -1;
+ } else if (repo_read_index(the_repository) < 0) {
+ perror("repo_read_cache");
+ ret = -1;
+ goto cleanup;
}
if (diff_cmd == DIFF_INDEX)
@@ -1268,8 +1145,11 @@ static int compute_summary_module_list(struct object_id *head_oid,
else
run_diff_files(&rev, 0);
prepare_submodule_summary(info, &list);
+cleanup:
strvec_clear(&diff_args);
- return 0;
+ release_revisions(&rev);
+ module_cb_list_release(&list);
+ return ret;
}
static int module_summary(int argc, const char **argv, const char *prefix)
@@ -1282,7 +1162,6 @@ static int module_summary(int argc, const char **argv, const char *prefix)
enum diff_cmd diff_cmd = DIFF_INDEX;
struct object_id head_oid;
int ret;
-
struct option module_summary_options[] = {
OPT_BOOL(0, "cached", &cached,
N_("use the commit stored in the index instead of the submodule HEAD")),
@@ -1294,9 +1173,8 @@ static int module_summary(int argc, const char **argv, const char *prefix)
N_("limit the summary size")),
OPT_END()
};
-
const char *const git_submodule_helper_usage[] = {
- N_("git submodule--helper summary [<options>] [<commit>] [--] [<path>]"),
+ N_("git submodule summary [<options>] [<commit>] [--] [<path>]"),
NULL
};
@@ -1306,7 +1184,7 @@ static int module_summary(int argc, const char **argv, const char *prefix)
if (!summary_limit)
return 0;
- if (!get_oid(argc ? argv[0] : "HEAD", &head_oid)) {
+ if (!repo_get_oid(the_repository, argc ? argv[0] : "HEAD", &head_oid)) {
if (argc) {
argv++;
argc--;
@@ -1319,7 +1197,7 @@ static int module_summary(int argc, const char **argv, const char *prefix)
argc--;
}
} else {
- if (get_oid("HEAD", &head_oid))
+ if (repo_get_oid(the_repository, "HEAD", &head_oid))
die(_("could not fetch a revision for HEAD"));
}
@@ -1344,18 +1222,20 @@ static int module_summary(int argc, const char **argv, const char *prefix)
struct sync_cb {
const char *prefix;
+ const char *super_prefix;
unsigned int flags;
};
#define SYNC_CB_INIT { 0 }
static void sync_submodule(const char *path, const char *prefix,
- unsigned int flags)
+ const char *super_prefix, unsigned int flags)
{
const struct submodule *sub;
char *remote_key = NULL;
char *sub_origin_url, *super_config_url, *displaypath, *default_remote;
struct strbuf sb = STRBUF_INIT;
char *sub_config_path = NULL;
+ int code;
if (!is_submodule_active(the_repository, path))
return;
@@ -1366,6 +1246,7 @@ static void sync_submodule(const char *path, const char *prefix,
if (starts_with_dot_dot_slash(sub->url) ||
starts_with_dot_slash(sub->url)) {
char *up_path = get_up_path(path);
+
sub_origin_url = resolve_relative_url(sub->url, up_path, 1);
super_config_url = resolve_relative_url(sub->url, NULL, 1);
free(up_path);
@@ -1378,7 +1259,7 @@ static void sync_submodule(const char *path, const char *prefix,
super_config_url = xstrdup("");
}
- displaypath = get_submodule_displaypath(path, prefix);
+ displaypath = get_submodule_displaypath(path, prefix, super_prefix);
if (!(flags & OPT_QUIET))
printf(_("Synchronizing submodule url for '%s'\n"),
@@ -1394,10 +1275,9 @@ static void sync_submodule(const char *path, const char *prefix,
goto cleanup;
strbuf_reset(&sb);
- default_remote = get_default_remote_submodule(path);
- if (!default_remote)
- die(_("failed to get the default remote for submodule '%s'"),
- path);
+ code = get_default_remote_submodule(path, &default_remote);
+ if (code)
+ exit(code);
remote_key = xstrfmt("remote.%s.url", default_remote);
free(default_remote);
@@ -1414,12 +1294,13 @@ static void sync_submodule(const char *path, const char *prefix,
cpr.git_cmd = 1;
cpr.dir = path;
- prepare_submodule_repo_env(&cpr.env_array);
+ prepare_submodule_repo_env(&cpr.env);
- strvec_push(&cpr.args, "--super-prefix");
- strvec_pushf(&cpr.args, "%s/", displaypath);
strvec_pushl(&cpr.args, "submodule--helper", "sync",
"--recursive", NULL);
+ strvec_push(&cpr.args, "--super-prefix");
+ strvec_pushf(&cpr.args, "%s/", displaypath);
+
if (flags & OPT_QUIET)
strvec_push(&cpr.args, "--quiet");
@@ -1441,34 +1322,36 @@ cleanup:
static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data)
{
struct sync_cb *info = cb_data;
- sync_submodule(list_item->name, info->prefix, info->flags);
+
+ sync_submodule(list_item->name, info->prefix, info->super_prefix,
+ info->flags);
}
static int module_sync(int argc, const char **argv, const char *prefix)
{
struct sync_cb info = SYNC_CB_INIT;
- struct pathspec pathspec;
+ struct pathspec pathspec = { 0 };
struct module_list list = MODULE_LIST_INIT;
int quiet = 0;
int recursive = 0;
-
struct option module_sync_options[] = {
+ OPT__SUPER_PREFIX(&info.super_prefix),
OPT__QUIET(&quiet, N_("suppress output of synchronizing submodule url")),
OPT_BOOL(0, "recursive", &recursive,
N_("recurse into nested submodules")),
OPT_END()
};
-
const char *const git_submodule_helper_usage[] = {
- N_("git submodule--helper sync [--quiet] [--recursive] [<path>]"),
+ N_("git submodule sync [--quiet] [--recursive] [<path>]"),
NULL
};
+ int ret = 1;
argc = parse_options(argc, argv, prefix, module_sync_options,
git_submodule_helper_usage, 0);
- if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
- return 1;
+ if (module_list_compute(argv, prefix, &pathspec, &list) < 0)
+ goto cleanup;
info.prefix = prefix;
if (quiet)
@@ -1478,7 +1361,11 @@ static int module_sync(int argc, const char **argv, const char *prefix)
for_each_listed_submodule(&list, sync_submodule_cb, &info);
- return 0;
+ ret = 0;
+cleanup:
+ module_list_release(&list);
+ clear_pathspec(&pathspec);
+ return ret;
}
struct deinit_cb {
@@ -1501,7 +1388,7 @@ static void deinit_submodule(const char *path, const char *prefix,
if (!sub || !sub->name)
goto cleanup;
- displaypath = get_submodule_displaypath(path, prefix);
+ displaypath = get_submodule_displaypath(path, prefix, NULL);
/* remove the submodule work tree (unless the user already did it) */
if (is_directory(path)) {
@@ -1515,13 +1402,13 @@ static void deinit_submodule(const char *path, const char *prefix,
".git file by using absorbgitdirs."),
displaypath);
- absorb_git_dir_into_superproject(path,
- ABSORB_GITDIR_RECURSE_SUBMODULES);
+ absorb_git_dir_into_superproject(path, NULL);
}
if (!(flags & OPT_FORCE)) {
struct child_process cp_rm = CHILD_PROCESS_INIT;
+
cp_rm.git_cmd = 1;
strvec_pushl(&cp_rm.args, "rm", "-qn",
path, NULL);
@@ -1558,6 +1445,7 @@ static void deinit_submodule(const char *path, const char *prefix,
/* remove the .git/config entries (unless the user already did it) */
if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) {
char *sub_key = xstrfmt("submodule.%s", sub->name);
+
/*
* remove the whole section so we have a clean state when
* the user later decides to init this submodule again
@@ -1585,23 +1473,22 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,
static int module_deinit(int argc, const char **argv, const char *prefix)
{
struct deinit_cb info = DEINIT_CB_INIT;
- struct pathspec pathspec;
+ struct pathspec pathspec = { 0 };
struct module_list list = MODULE_LIST_INIT;
int quiet = 0;
int force = 0;
int all = 0;
-
struct option module_deinit_options[] = {
OPT__QUIET(&quiet, N_("suppress submodule status output")),
OPT__FORCE(&force, N_("remove submodule working trees even if they contain local changes"), 0),
OPT_BOOL(0, "all", &all, N_("unregister all submodules")),
OPT_END()
};
-
const char *const git_submodule_helper_usage[] = {
N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
NULL
};
+ int ret = 1;
argc = parse_options(argc, argv, prefix, module_deinit_options,
git_submodule_helper_usage, 0);
@@ -1615,8 +1502,8 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
if (!argc && !all)
die(_("Use '--all' if you really want to deinitialize all submodules"));
- if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
- return 1;
+ if (module_list_compute(argv, prefix, &pathspec, &list) < 0)
+ goto cleanup;
info.prefix = prefix;
if (quiet)
@@ -1626,7 +1513,11 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
for_each_listed_submodule(&list, deinit_submodule_cb, &info);
- return 0;
+ ret = 0;
+cleanup:
+ module_list_release(&list);
+ clear_pathspec(&pathspec);
+ return ret;
}
struct module_clone_data {
@@ -1636,7 +1527,6 @@ struct module_clone_data {
const char *url;
const char *depth;
struct list_objects_filter_options *filter_options;
- struct string_list reference;
unsigned int quiet: 1;
unsigned int progress: 1;
unsigned int dissociate: 1;
@@ -1644,7 +1534,6 @@ struct module_clone_data {
int single_branch;
};
#define MODULE_CLONE_DATA_INIT { \
- .reference = STRING_LIST_INIT_NODUP, \
.single_branch = -1, \
}
@@ -1685,7 +1574,9 @@ static int add_possible_reference_from_superproject(
struct strbuf err = STRBUF_INIT;
strbuf_add(&sb, odb->path, len);
- repo_init(&alternate, sb.buf, NULL);
+ if (repo_init(&alternate, sb.buf, NULL) < 0)
+ die(_("could not get a repository handle for gitdir '%s'"),
+ sb.buf);
/*
* We need to end the new path with '/' to mark it as a dir,
@@ -1700,7 +1591,9 @@ static int add_possible_reference_from_superproject(
sm_alternate = compute_alternate_path(sb.buf, &err);
if (sm_alternate) {
- string_list_append(sas->reference, xstrdup(sb.buf));
+ char *p = strbuf_detach(&sb, NULL);
+
+ string_list_append(sas->reference, p)->util = p;
free(sm_alternate);
} else {
switch (sas->error_mode) {
@@ -1759,23 +1652,31 @@ static void prepare_possible_alternates(const char *sm_name,
free(error_strategy);
}
-static int clone_submodule(struct module_clone_data *clone_data)
+static char *clone_submodule_sm_gitdir(const char *name)
{
- char *p, *sm_gitdir;
- char *sm_alternate = NULL, *error_strategy = NULL;
struct strbuf sb = STRBUF_INIT;
- struct child_process cp = CHILD_PROCESS_INIT;
+ char *sm_gitdir;
- submodule_name_to_gitdir(&sb, the_repository, clone_data->name);
+ submodule_name_to_gitdir(&sb, the_repository, name);
sm_gitdir = absolute_pathdup(sb.buf);
- strbuf_reset(&sb);
+ strbuf_release(&sb);
- if (!is_absolute_path(clone_data->path)) {
- strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
- clone_data->path = strbuf_detach(&sb, NULL);
- } else {
- clone_data->path = xstrdup(clone_data->path);
- }
+ return sm_gitdir;
+}
+
+static int clone_submodule(const struct module_clone_data *clone_data,
+ struct string_list *reference)
+{
+ char *p;
+ char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
+ char *sm_alternate = NULL, *error_strategy = NULL;
+ struct child_process cp = CHILD_PROCESS_INIT;
+ const char *clone_data_path = clone_data->path;
+ char *to_free = NULL;
+
+ if (!is_absolute_path(clone_data->path))
+ clone_data_path = to_free = xstrfmt("%s/%s", get_git_work_tree(),
+ clone_data->path);
if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
die(_("refusing to create/use '%s' in another submodule's "
@@ -1785,7 +1686,7 @@ static int clone_submodule(struct module_clone_data *clone_data)
if (safe_create_leading_directories_const(sm_gitdir) < 0)
die(_("could not create directory '%s'"), sm_gitdir);
- prepare_possible_alternates(clone_data->name, &clone_data->reference);
+ prepare_possible_alternates(clone_data->name, reference);
strvec_push(&cp.args, "clone");
strvec_push(&cp.args, "--no-checkout");
@@ -1795,9 +1696,10 @@ static int clone_submodule(struct module_clone_data *clone_data)
strvec_push(&cp.args, "--progress");
if (clone_data->depth && *(clone_data->depth))
strvec_pushl(&cp.args, "--depth", clone_data->depth, NULL);
- if (clone_data->reference.nr) {
+ if (reference->nr) {
struct string_list_item *item;
- for_each_string_list_item(item, &clone_data->reference)
+
+ for_each_string_list_item(item, reference)
strvec_pushl(&cp.args, "--reference",
item->string, NULL);
}
@@ -1816,31 +1718,33 @@ static int clone_submodule(struct module_clone_data *clone_data)
strvec_push(&cp.args, "--");
strvec_push(&cp.args, clone_data->url);
- strvec_push(&cp.args, clone_data->path);
+ strvec_push(&cp.args, clone_data_path);
cp.git_cmd = 1;
- prepare_submodule_repo_env(&cp.env_array);
+ prepare_submodule_repo_env(&cp.env);
cp.no_stdin = 1;
if(run_command(&cp))
die(_("clone of '%s' into submodule path '%s' failed"),
- clone_data->url, clone_data->path);
+ clone_data->url, clone_data_path);
} else {
- if (clone_data->require_init && !access(clone_data->path, X_OK) &&
- !is_empty_dir(clone_data->path))
- die(_("directory not empty: '%s'"), clone_data->path);
- if (safe_create_leading_directories_const(clone_data->path) < 0)
- die(_("could not create directory '%s'"), clone_data->path);
- strbuf_addf(&sb, "%s/index", sm_gitdir);
- unlink_or_warn(sb.buf);
- strbuf_reset(&sb);
+ char *path;
+
+ if (clone_data->require_init && !access(clone_data_path, X_OK) &&
+ !is_empty_dir(clone_data_path))
+ die(_("directory not empty: '%s'"), clone_data_path);
+ if (safe_create_leading_directories_const(clone_data_path) < 0)
+ die(_("could not create directory '%s'"), clone_data_path);
+ path = xstrfmt("%s/index", sm_gitdir);
+ unlink_or_warn(path);
+ free(path);
}
- connect_work_tree_and_git_dir(clone_data->path, sm_gitdir, 0);
+ connect_work_tree_and_git_dir(clone_data_path, sm_gitdir, 0);
- p = git_pathdup_submodule(clone_data->path, "config");
+ p = git_pathdup_submodule(clone_data_path, "config");
if (!p)
- die(_("could not get submodule directory for '%s'"), clone_data->path);
+ die(_("could not get submodule directory for '%s'"), clone_data_path);
/* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */
git_config_get_string("submodule.alternateLocation", &sm_alternate);
@@ -1855,9 +1759,9 @@ static int clone_submodule(struct module_clone_data *clone_data)
free(sm_alternate);
free(error_strategy);
- strbuf_release(&sb);
free(sm_gitdir);
free(p);
+ free(to_free);
return 0;
}
@@ -1865,7 +1769,9 @@ static int module_clone(int argc, const char **argv, const char *prefix)
{
int dissociate = 0, quiet = 0, progress = 0, require_init = 0;
struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
- struct list_objects_filter_options filter_options;
+ struct string_list reference = STRING_LIST_INIT_NODUP;
+ struct list_objects_filter_options filter_options =
+ LIST_OBJECTS_FILTER_INIT;
struct option module_clone_options[] = {
OPT_STRING(0, "prefix", &clone_data.prefix,
@@ -1880,7 +1786,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
OPT_STRING(0, "url", &clone_data.url,
N_("string"),
N_("url where to clone the submodule from")),
- OPT_STRING_LIST(0, "reference", &clone_data.reference,
+ OPT_STRING_LIST(0, "reference", &reference,
N_("repo"),
N_("reference repository")),
OPT_BOOL(0, "dissociate", &dissociate,
@@ -1898,7 +1804,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
OPT_END()
};
-
const char *const git_submodule_helper_usage[] = {
N_("git submodule--helper clone [--prefix=<path>] [--quiet] "
"[--reference <repository>] [--name <name>] [--depth <depth>] "
@@ -1907,7 +1812,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)
NULL
};
- memset(&filter_options, 0, sizeof(filter_options));
argc = parse_options(argc, argv, prefix, module_clone_options,
git_submodule_helper_usage, 0);
@@ -1921,31 +1825,33 @@ static int module_clone(int argc, const char **argv, const char *prefix)
usage_with_options(git_submodule_helper_usage,
module_clone_options);
- clone_submodule(&clone_data);
+ clone_submodule(&clone_data, &reference);
list_objects_filter_release(&filter_options);
+ string_list_clear(&reference, 1);
return 0;
}
-static void determine_submodule_update_strategy(struct repository *r,
- int just_cloned,
- const char *path,
- const char *update,
- struct submodule_update_strategy *out)
+static int determine_submodule_update_strategy(struct repository *r,
+ int just_cloned,
+ const char *path,
+ enum submodule_update_type update,
+ struct submodule_update_strategy *out)
{
const struct submodule *sub = submodule_from_path(r, null_oid(), path);
char *key;
const char *val;
+ int ret;
key = xstrfmt("submodule.%s.update", sub->name);
if (update) {
- if (parse_submodule_update_strategy(update, out) < 0)
- die(_("Invalid update mode '%s' for submodule path '%s'"),
- update, path);
+ out->type = update;
} else if (!repo_config_get_string_tmp(r, key, &val)) {
- if (parse_submodule_update_strategy(val, out) < 0)
- die(_("Invalid update mode '%s' configured for submodule path '%s'"),
- val, path);
+ if (parse_submodule_update_strategy(val, out) < 0) {
+ ret = die_message(_("Invalid update mode '%s' configured for submodule path '%s'"),
+ val, path);
+ goto cleanup;
+ }
} else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
if (sub->update_strategy.type == SM_UPDATE_COMMAND)
BUG("how did we read update = !command from .gitmodules?");
@@ -1960,7 +1866,10 @@ static void determine_submodule_update_strategy(struct repository *r,
out->type == SM_UPDATE_NONE))
out->type = SM_UPDATE_CHECKOUT;
+ ret = 0;
+cleanup:
free(key);
+ return ret;
}
struct update_clone_data {
@@ -1974,7 +1883,7 @@ struct submodule_update_clone {
int current;
/* configuration parameters which are passed on to the children */
- struct update_data *update_data;
+ const struct update_data *update_data;
/* to be consumed by update_submodule() */
struct update_clone_data *update_clone;
@@ -1989,11 +1898,17 @@ struct submodule_update_clone {
};
#define SUBMODULE_UPDATE_CLONE_INIT { 0 }
+static void submodule_update_clone_release(struct submodule_update_clone *suc)
+{
+ free(suc->update_clone);
+ free(suc->failed_clones);
+}
+
struct update_data {
const char *prefix;
- const char *recursive_prefix;
- const char *displaypath;
- const char *update_default;
+ const char *super_prefix;
+ char *displaypath;
+ enum submodule_update_type update_default;
struct object_id suboid;
struct string_list references;
struct submodule_update_strategy update_strategy;
@@ -2028,6 +1943,12 @@ struct update_data {
.max_jobs = 1, \
}
+static void update_data_release(struct update_data *ud)
+{
+ free(ud->displaypath);
+ module_list_release(&ud->list);
+}
+
static void next_submodule_warn_missing(struct submodule_update_clone *suc,
struct strbuf *out, const char *displaypath)
{
@@ -2060,30 +1981,21 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
const char *update_string;
enum submodule_update_type update_type;
char *key;
- struct strbuf displaypath_sb = STRBUF_INIT;
+ const struct update_data *ud = suc->update_data;
+ char *displaypath = get_submodule_displaypath(ce->name, ud->prefix,
+ ud->super_prefix);
struct strbuf sb = STRBUF_INIT;
- const char *displaypath = NULL;
int needs_cloning = 0;
int need_free_url = 0;
if (ce_stage(ce)) {
- if (suc->update_data->recursive_prefix)
- strbuf_addf(&sb, "%s/%s", suc->update_data->recursive_prefix, ce->name);
- else
- strbuf_addstr(&sb, ce->name);
- strbuf_addf(out, _("Skipping unmerged submodule %s"), sb.buf);
+ strbuf_addf(out, _("Skipping unmerged submodule %s"), displaypath);
strbuf_addch(out, '\n');
goto cleanup;
}
sub = submodule_from_path(the_repository, null_oid(), ce->name);
- if (suc->update_data->recursive_prefix)
- displaypath = relative_path(suc->update_data->recursive_prefix,
- ce->name, &displaypath_sb);
- else
- displaypath = ce->name;
-
if (!sub) {
next_submodule_warn_missing(suc, out, displaypath);
goto cleanup;
@@ -2114,14 +2026,17 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
strbuf_reset(&sb);
strbuf_addf(&sb, "submodule.%s.url", sub->name);
if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) {
- if (starts_with_dot_slash(sub->url) ||
- starts_with_dot_dot_slash(sub->url)) {
+ if (sub->url && (starts_with_dot_slash(sub->url) ||
+ starts_with_dot_dot_slash(sub->url))) {
url = resolve_relative_url(sub->url, NULL, 0);
need_free_url = 1;
} else
url = sub->url;
}
+ if (!url)
+ die(_("cannot clone submodule '%s' without a URL"), sub->name);
+
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/.git", ce->name);
needs_cloning = !file_exists(sb.buf);
@@ -2162,6 +2077,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
strvec_pushl(&child->args, "--url", url, NULL);
if (suc->update_data->references.nr) {
struct string_list_item *item;
+
for_each_string_list_item(item, &suc->update_data->references)
strvec_pushl(&child->args, "--reference", item->string, NULL);
}
@@ -2173,7 +2089,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
"--no-single-branch");
cleanup:
- strbuf_release(&displaypath_sb);
+ free(displaypath);
strbuf_release(&sb);
if (need_free_url)
free((void*)url);
@@ -2194,6 +2110,7 @@ static int update_clone_get_next_task(struct child_process *child,
ce = suc->update_data->list.entries[suc->current];
if (prepare_to_clone_next_submodule(ce, child, suc, err)) {
int *p = xmalloc(sizeof(*p));
+
*p = suc->current;
*idx_task_cb = p;
suc->current++;
@@ -2209,6 +2126,7 @@ static int update_clone_get_next_task(struct child_process *child,
index = suc->current - suc->update_data->list.nr;
if (index < suc->failed_clones_nr) {
int *p;
+
ce = suc->failed_clones[index];
if (!prepare_to_clone_next_submodule(ce, child, suc, err)) {
suc->current ++;
@@ -2227,11 +2145,12 @@ static int update_clone_get_next_task(struct child_process *child,
return 0;
}
-static int update_clone_start_failure(struct strbuf *err,
+static int update_clone_start_failure(struct strbuf *err UNUSED,
void *suc_cb,
- void *idx_task_cb)
+ void *idx_task_cb UNUSED)
{
struct submodule_update_clone *suc = suc_cb;
+
suc->quickstop = 1;
return 1;
}
@@ -2243,9 +2162,9 @@ static int update_clone_task_finished(int result,
{
const struct cache_entry *ce;
struct submodule_update_clone *suc = suc_cb;
-
int *idxP = idx_task_cb;
int idx = *idxP;
+
free(idxP);
if (!result)
@@ -2275,26 +2194,28 @@ static int update_clone_task_finished(int result,
}
static int git_update_clone_config(const char *var, const char *value,
+ const struct config_context *ctx,
void *cb)
{
int *max_jobs = cb;
+
if (!strcmp(var, "submodule.fetchjobs"))
- *max_jobs = parse_submodule_fetchjobs(var, value);
+ *max_jobs = parse_submodule_fetchjobs(var, value, ctx->kvi);
return 0;
}
-static int is_tip_reachable(const char *path, struct object_id *oid)
+static int is_tip_reachable(const char *path, const struct object_id *oid)
{
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf rev = STRBUF_INIT;
char *hex = oid_to_hex(oid);
cp.git_cmd = 1;
- cp.dir = xstrdup(path);
+ cp.dir = path;
cp.no_stderr = 1;
strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
- prepare_submodule_repo_env(&cp.env_array);
+ prepare_submodule_repo_env(&cp.env);
if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len)
return 0;
@@ -2302,13 +2223,14 @@ static int is_tip_reachable(const char *path, struct object_id *oid)
return 1;
}
-static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid)
+static int fetch_in_submodule(const char *module_path, int depth, int quiet,
+ const struct object_id *oid)
{
struct child_process cp = CHILD_PROCESS_INIT;
- prepare_submodule_repo_env(&cp.env_array);
+ prepare_submodule_repo_env(&cp.env);
cp.git_cmd = 1;
- cp.dir = xstrdup(module_path);
+ cp.dir = module_path;
strvec_push(&cp.args, "fetch");
if (quiet)
@@ -2318,17 +2240,19 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
if (oid) {
char *hex = oid_to_hex(oid);
char *remote = get_default_remote();
+
strvec_pushl(&cp.args, remote, hex, NULL);
+ free(remote);
}
return run_command(&cp);
}
-static int run_update_command(struct update_data *ud, int subforce)
+static int run_update_command(const struct update_data *ud, int subforce)
{
struct child_process cp = CHILD_PROCESS_INIT;
char *oid = oid_to_hex(&ud->oid);
- int must_die_on_failure = 0;
+ int ret;
switch (ud->update_strategy.type) {
case SM_UPDATE_CHECKOUT:
@@ -2342,55 +2266,50 @@ static int run_update_command(struct update_data *ud, int subforce)
strvec_push(&cp.args, "rebase");
if (ud->quiet)
strvec_push(&cp.args, "--quiet");
- must_die_on_failure = 1;
break;
case SM_UPDATE_MERGE:
cp.git_cmd = 1;
strvec_push(&cp.args, "merge");
if (ud->quiet)
strvec_push(&cp.args, "--quiet");
- must_die_on_failure = 1;
break;
case SM_UPDATE_COMMAND:
cp.use_shell = 1;
strvec_push(&cp.args, ud->update_strategy.command);
- must_die_on_failure = 1;
break;
default:
- BUG("unexpected update strategy type: %s",
- submodule_strategy_to_string(&ud->update_strategy));
+ BUG("unexpected update strategy type: %d",
+ ud->update_strategy.type);
}
strvec_push(&cp.args, oid);
- cp.dir = xstrdup(ud->sm_path);
- prepare_submodule_repo_env(&cp.env_array);
- if (run_command(&cp)) {
+ cp.dir = ud->sm_path;
+ prepare_submodule_repo_env(&cp.env);
+ if ((ret = run_command(&cp))) {
switch (ud->update_strategy.type) {
case SM_UPDATE_CHECKOUT:
die_message(_("Unable to checkout '%s' in submodule path '%s'"),
oid, ud->displaypath);
+ /* No "ret" assignment, use "git checkout"'s */
break;
case SM_UPDATE_REBASE:
- die_message(_("Unable to rebase '%s' in submodule path '%s'"),
- oid, ud->displaypath);
+ ret = die_message(_("Unable to rebase '%s' in submodule path '%s'"),
+ oid, ud->displaypath);
break;
case SM_UPDATE_MERGE:
- die_message(_("Unable to merge '%s' in submodule path '%s'"),
- oid, ud->displaypath);
+ ret = die_message(_("Unable to merge '%s' in submodule path '%s'"),
+ oid, ud->displaypath);
break;
case SM_UPDATE_COMMAND:
- die_message(_("Execution of '%s %s' failed in submodule path '%s'"),
- ud->update_strategy.command, oid, ud->displaypath);
+ ret = die_message(_("Execution of '%s %s' failed in submodule path '%s'"),
+ ud->update_strategy.command, oid, ud->displaypath);
break;
default:
- BUG("unexpected update strategy type: %s",
- submodule_strategy_to_string(&ud->update_strategy));
+ BUG("unexpected update strategy type: %d",
+ ud->update_strategy.type);
}
- if (must_die_on_failure)
- exit(128);
- /* the command failed, but update must continue */
- return 1;
+ return ret;
}
if (ud->quiet)
@@ -2414,14 +2333,14 @@ static int run_update_command(struct update_data *ud, int subforce)
ud->displaypath, ud->update_strategy.command, oid);
break;
default:
- BUG("unexpected update strategy type: %s",
- submodule_strategy_to_string(&ud->update_strategy));
+ BUG("unexpected update strategy type: %d",
+ ud->update_strategy.type);
}
return 0;
}
-static int run_update_procedure(struct update_data *ud)
+static int run_update_procedure(const struct update_data *ud)
{
int subforce = is_null_oid(&ud->suboid) || ud->force;
@@ -2443,59 +2362,67 @@ static int run_update_procedure(struct update_data *ud)
*/
if (!is_tip_reachable(ud->sm_path, &ud->oid) &&
fetch_in_submodule(ud->sm_path, ud->depth, ud->quiet, &ud->oid))
- die(_("Fetched in submodule path '%s', but it did not "
- "contain %s. Direct fetching of that commit failed."),
- ud->displaypath, oid_to_hex(&ud->oid));
+ return die_message(_("Fetched in submodule path '%s', but it did not "
+ "contain %s. Direct fetching of that commit failed."),
+ ud->displaypath, oid_to_hex(&ud->oid));
}
return run_update_command(ud, subforce);
}
-static const char *remote_submodule_branch(const char *path)
+static int remote_submodule_branch(const char *path, const char **branch)
{
const struct submodule *sub;
- const char *branch = NULL;
char *key;
+ *branch = NULL;
sub = submodule_from_path(the_repository, null_oid(), path);
if (!sub)
- return NULL;
+ return die_message(_("could not initialize submodule at path '%s'"),
+ path);
key = xstrfmt("submodule.%s.branch", sub->name);
- if (repo_config_get_string_tmp(the_repository, key, &branch))
- branch = sub->branch;
+ if (repo_config_get_string_tmp(the_repository, key, branch))
+ *branch = sub->branch;
free(key);
- if (!branch)
- return "HEAD";
+ if (!*branch) {
+ *branch = "HEAD";
+ return 0;
+ }
- if (!strcmp(branch, ".")) {
+ if (!strcmp(*branch, ".")) {
const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
if (!refname)
- die(_("No such ref: %s"), "HEAD");
+ return die_message(_("No such ref: %s"), "HEAD");
/* detached HEAD */
if (!strcmp(refname, "HEAD"))
- die(_("Submodule (%s) branch configured to inherit "
- "branch from superproject, but the superproject "
- "is not on any branch"), sub->name);
+ return die_message(_("Submodule (%s) branch configured to inherit "
+ "branch from superproject, but the superproject "
+ "is not on any branch"), sub->name);
if (!skip_prefix(refname, "refs/heads/", &refname))
- die(_("Expecting a full ref name, got %s"), refname);
- return refname;
+ return die_message(_("Expecting a full ref name, got %s"),
+ refname);
+
+ *branch = refname;
+ return 0;
}
- return branch;
+ /* Our "branch" is coming from repo_config_get_string_tmp() */
+ return 0;
}
-static void ensure_core_worktree(const char *path)
+static int ensure_core_worktree(const char *path)
{
const char *cw;
struct repository subrepo;
if (repo_submodule_init(&subrepo, the_repository, path, null_oid()))
- die(_("could not get a repository handle for submodule '%s'"), path);
+ return die_message(_("could not get a repository handle for submodule '%s'"),
+ path);
if (!repo_config_get_string_tmp(&subrepo, "core.worktree", &cw)) {
char *cfg_file, *abs_path;
@@ -2513,15 +2440,39 @@ static void ensure_core_worktree(const char *path)
free(abs_path);
strbuf_release(&sb);
}
+
+ repo_clear(&subrepo);
+ return 0;
+}
+
+static const char *submodule_update_type_to_label(enum submodule_update_type type)
+{
+ switch (type) {
+ case SM_UPDATE_CHECKOUT:
+ return "checkout";
+ case SM_UPDATE_MERGE:
+ return "merge";
+ case SM_UPDATE_REBASE:
+ return "rebase";
+ case SM_UPDATE_UNSPECIFIED:
+ case SM_UPDATE_NONE:
+ case SM_UPDATE_COMMAND:
+ break;
+ }
+ BUG("unreachable with type %d", type);
}
-static void update_data_to_args(struct update_data *update_data, struct strvec *args)
+static void update_data_to_args(const struct update_data *update_data,
+ struct strvec *args)
{
+ enum submodule_update_type update_type = update_data->update_default;
+
strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL);
+ if (update_data->displaypath) {
+ strvec_push(args, "--super-prefix");
+ strvec_pushf(args, "%s/", update_data->displaypath);
+ }
strvec_pushf(args, "--jobs=%d", update_data->max_jobs);
- if (update_data->recursive_prefix)
- strvec_pushl(args, "--recursive-prefix",
- update_data->recursive_prefix, NULL);
if (update_data->quiet)
strvec_push(args, "--quiet");
if (update_data->force)
@@ -2540,10 +2491,13 @@ static void update_data_to_args(struct update_data *update_data, struct strvec *
strvec_push(args, "--require-init");
if (update_data->depth)
strvec_pushf(args, "--depth=%d", update_data->depth);
- if (update_data->update_default)
- strvec_pushl(args, "--update", update_data->update_default, NULL);
+ if (update_type != SM_UPDATE_UNSPECIFIED)
+ strvec_pushf(args, "--%s",
+ submodule_update_type_to_label(update_type));
+
if (update_data->references.nr) {
struct string_list_item *item;
+
for_each_string_list_item(item, &update_data->references)
strvec_pushl(args, "--reference", item->string, NULL);
}
@@ -2563,85 +2517,76 @@ static void update_data_to_args(struct update_data *update_data, struct strvec *
static int update_submodule(struct update_data *update_data)
{
- char *prefixed_path;
-
- ensure_core_worktree(update_data->sm_path);
-
- if (update_data->recursive_prefix)
- prefixed_path = xstrfmt("%s%s", update_data->recursive_prefix,
- update_data->sm_path);
- else
- prefixed_path = xstrdup(update_data->sm_path);
-
- update_data->displaypath = get_submodule_displaypath(prefixed_path,
- update_data->prefix);
- free(prefixed_path);
+ int ret;
- determine_submodule_update_strategy(the_repository, update_data->just_cloned,
- update_data->sm_path, update_data->update_default,
- &update_data->update_strategy);
+ ret = determine_submodule_update_strategy(the_repository,
+ update_data->just_cloned,
+ update_data->sm_path,
+ update_data->update_default,
+ &update_data->update_strategy);
+ if (ret)
+ return ret;
if (update_data->just_cloned)
oidcpy(&update_data->suboid, null_oid());
else if (resolve_gitlink_ref(update_data->sm_path, "HEAD", &update_data->suboid))
- die(_("Unable to find current revision in submodule path '%s'"),
- update_data->displaypath);
+ return die_message(_("Unable to find current revision in submodule path '%s'"),
+ update_data->displaypath);
if (update_data->remote) {
- char *remote_name = get_default_remote_submodule(update_data->sm_path);
- const char *branch = remote_submodule_branch(update_data->sm_path);
- char *remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
+ char *remote_name;
+ const char *branch;
+ char *remote_ref;
+ int code;
+
+ code = get_default_remote_submodule(update_data->sm_path, &remote_name);
+ if (code)
+ return code;
+ code = remote_submodule_branch(update_data->sm_path, &branch);
+ if (code)
+ return code;
+ remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
+
+ free(remote_name);
if (!update_data->nofetch) {
if (fetch_in_submodule(update_data->sm_path, update_data->depth,
0, NULL))
- die(_("Unable to fetch in submodule path '%s'"),
- update_data->sm_path);
+ return die_message(_("Unable to fetch in submodule path '%s'"),
+ update_data->sm_path);
}
if (resolve_gitlink_ref(update_data->sm_path, remote_ref, &update_data->oid))
- die(_("Unable to find %s revision in submodule path '%s'"),
- remote_ref, update_data->sm_path);
+ return die_message(_("Unable to find %s revision in submodule path '%s'"),
+ remote_ref, update_data->sm_path);
free(remote_ref);
}
- if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force)
- if (run_update_procedure(update_data))
- return 1;
+ if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {
+ ret = run_update_procedure(update_data);
+ if (ret)
+ return ret;
+ }
if (update_data->recursive) {
struct child_process cp = CHILD_PROCESS_INIT;
struct update_data next = *update_data;
- int res;
- if (update_data->recursive_prefix)
- prefixed_path = xstrfmt("%s%s/", update_data->recursive_prefix,
- update_data->sm_path);
- else
- prefixed_path = xstrfmt("%s/", update_data->sm_path);
-
- next.recursive_prefix = get_submodule_displaypath(prefixed_path,
- update_data->prefix);
next.prefix = NULL;
oidcpy(&next.oid, null_oid());
oidcpy(&next.suboid, null_oid());
cp.dir = update_data->sm_path;
cp.git_cmd = 1;
- prepare_submodule_repo_env(&cp.env_array);
+ prepare_submodule_repo_env(&cp.env);
update_data_to_args(&next, &cp.args);
- /* die() if child process die()'d */
- res = run_command(&cp);
- if (!res)
- return 0;
- die_message(_("Failed to recurse into submodule path '%s'"),
- update_data->displaypath);
- if (res == 128)
- exit(res);
- else if (res)
- return 1;
+ ret = run_command(&cp);
+ if (ret)
+ die_message(_("Failed to recurse into submodule path '%s'"),
+ update_data->displaypath);
+ return ret;
}
return 0;
@@ -2649,14 +2594,22 @@ static int update_submodule(struct update_data *update_data)
static int update_submodules(struct update_data *update_data)
{
- int i, res = 0;
+ int i, ret = 0;
struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
+ const struct run_process_parallel_opts opts = {
+ .tr2_category = "submodule",
+ .tr2_label = "parallel/update",
+
+ .processes = update_data->max_jobs,
+
+ .get_next_task = update_clone_get_next_task,
+ .start_failure = update_clone_start_failure,
+ .task_finished = update_clone_task_finished,
+ .data = &suc,
+ };
suc.update_data = update_data;
- run_processes_parallel_tr2(suc.update_data->max_jobs, update_clone_get_next_task,
- update_clone_start_failure,
- update_clone_task_finished, &suc, "submodule",
- "parallel/update");
+ run_processes_parallel(&opts);
/*
* We saved the output and put it out all at once now.
@@ -2667,34 +2620,51 @@ static int update_submodules(struct update_data *update_data)
* - the listener can avoid doing any work if fetching failed.
*/
if (suc.quickstop) {
- res = 1;
+ ret = 1;
goto cleanup;
}
for (i = 0; i < suc.update_clone_nr; i++) {
struct update_clone_data ucd = suc.update_clone[i];
+ int code;
oidcpy(&update_data->oid, &ucd.oid);
update_data->just_cloned = ucd.just_cloned;
update_data->sm_path = ucd.sub->path;
- if (update_submodule(update_data))
- res = 1;
+ code = ensure_core_worktree(update_data->sm_path);
+ if (code)
+ goto fail;
+
+ update_data->displaypath = get_submodule_displaypath(
+ update_data->sm_path, update_data->prefix,
+ update_data->super_prefix);
+ code = update_submodule(update_data);
+ FREE_AND_NULL(update_data->displaypath);
+fail:
+ if (!code)
+ continue;
+ ret = code;
+ if (ret == 128)
+ goto cleanup;
}
cleanup:
+ submodule_update_clone_release(&suc);
string_list_clear(&update_data->references, 0);
- return res;
+ return ret;
}
static int module_update(int argc, const char **argv, const char *prefix)
{
- struct pathspec pathspec;
+ struct pathspec pathspec = { 0 };
+ struct pathspec pathspec2 = { 0 };
struct update_data opt = UPDATE_DATA_INIT;
- struct list_objects_filter_options filter_options;
+ struct list_objects_filter_options filter_options =
+ LIST_OBJECTS_FILTER_INIT;
int ret;
-
struct option module_update_options[] = {
+ OPT__SUPER_PREFIX(&opt.super_prefix),
OPT__FORCE(&opt.force, N_("force checkout updates"), 0),
OPT_BOOL(0, "init", &opt.init,
N_("initialize uninitialized submodules before update")),
@@ -2704,16 +2674,15 @@ static int module_update(int argc, const char **argv, const char *prefix)
N_("traverse submodules recursively")),
OPT_BOOL('N', "no-fetch", &opt.nofetch,
N_("don't fetch new objects from the remote site")),
- OPT_STRING(0, "prefix", &opt.prefix,
- N_("path"),
- N_("path into the working tree")),
- OPT_STRING(0, "recursive-prefix", &opt.recursive_prefix,
- N_("path"),
- N_("path into the working tree, across nested "
- "submodule boundaries")),
- OPT_STRING(0, "update", &opt.update_default,
- N_("string"),
- N_("rebase, merge, checkout or none")),
+ OPT_SET_INT(0, "checkout", &opt.update_default,
+ N_("use the 'checkout' update strategy (default)"),
+ SM_UPDATE_CHECKOUT),
+ OPT_SET_INT('m', "merge", &opt.update_default,
+ N_("use the 'merge' update strategy"),
+ SM_UPDATE_MERGE),
+ OPT_SET_INT('r', "rebase", &opt.update_default,
+ N_("use the 'rebase' update strategy"),
+ SM_UPDATE_REBASE),
OPT_STRING_LIST(0, "reference", &opt.references, N_("repo"),
N_("reference repository")),
OPT_BOOL(0, "dissociate", &opt.dissociate,
@@ -2729,13 +2698,12 @@ static int module_update(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "progress", &opt.progress,
N_("force cloning progress")),
OPT_BOOL(0, "require-init", &opt.require_init,
- N_("disallow cloning into non-empty directory")),
+ N_("disallow cloning into non-empty directory, implies --init")),
OPT_BOOL(0, "single-branch", &opt.single_branch,
N_("clone only one branch, HEAD or --branch")),
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
OPT_END()
};
-
const char *const git_submodule_helper_usage[] = {
N_("git submodule [--quiet] update"
" [--init [--filter=<filter-spec>]] [--remote]"
@@ -2749,25 +2717,26 @@ static int module_update(int argc, const char **argv, const char *prefix)
update_clone_config_from_gitmodules(&opt.max_jobs);
git_config(git_update_clone_config, &opt.max_jobs);
- memset(&filter_options, 0, sizeof(filter_options));
argc = parse_options(argc, argv, prefix, module_update_options,
git_submodule_helper_usage, 0);
+ if (opt.require_init)
+ opt.init = 1;
+
if (filter_options.choice && !opt.init) {
usage_with_options(git_submodule_helper_usage,
module_update_options);
}
opt.filter_options = &filter_options;
+ opt.prefix = prefix;
if (opt.update_default)
- if (parse_submodule_update_strategy(opt.update_default,
- &opt.update_strategy) < 0)
- die(_("bad value for update parameter"));
+ opt.update_strategy.type = opt.update_default;
- if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {
- list_objects_filter_release(&filter_options);
- return 1;
+ if (module_list_compute(argv, prefix, &pathspec, &opt.list) < 0) {
+ ret = 1;
+ goto cleanup;
}
if (pathspec.nr)
@@ -2777,31 +2746,39 @@ static int module_update(int argc, const char **argv, const char *prefix)
struct module_list list = MODULE_LIST_INIT;
struct init_cb info = INIT_CB_INIT;
- if (module_list_compute(argc, argv, opt.prefix,
- &pathspec, &list) < 0)
- return 1;
+ if (module_list_compute(argv, opt.prefix,
+ &pathspec2, &list) < 0) {
+ module_list_release(&list);
+ ret = 1;
+ goto cleanup;
+ }
/*
* If there are no path args and submodule.active is set then,
* by default, only initialize 'active' modules.
*/
- if (!argc && git_config_get_value_multi("submodule.active"))
+ if (!argc && !git_config_get("submodule.active"))
module_list_active(&list);
info.prefix = opt.prefix;
- info.superprefix = opt.recursive_prefix;
+ info.super_prefix = opt.super_prefix;
if (opt.quiet)
info.flags |= OPT_QUIET;
for_each_listed_submodule(&list, init_submodule_cb, &info);
+ module_list_release(&list);
}
ret = update_submodules(&opt);
+cleanup:
+ update_data_release(&opt);
list_objects_filter_release(&filter_options);
+ clear_pathspec(&pathspec);
+ clear_pathspec(&pathspec2);
return ret;
}
-static int push_check(int argc, const char **argv, const char *prefix)
+static int push_check(int argc, const char **argv, const char *prefix UNUSED)
{
struct remote *remote;
const char *superproject_head;
@@ -2881,111 +2858,34 @@ static int push_check(int argc, const char **argv, const char *prefix)
static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
{
int i;
- struct pathspec pathspec;
+ struct pathspec pathspec = { 0 };
struct module_list list = MODULE_LIST_INIT;
- unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
-
+ const char *super_prefix = NULL;
struct option embed_gitdir_options[] = {
- OPT_STRING(0, "prefix", &prefix,
- N_("path"),
- N_("path into the working tree")),
- OPT_BIT(0, "--recursive", &flags, N_("recurse into submodules"),
- ABSORB_GITDIR_RECURSE_SUBMODULES),
+ OPT__SUPER_PREFIX(&super_prefix),
OPT_END()
};
-
const char *const git_submodule_helper_usage[] = {
- N_("git submodule--helper absorb-git-dirs [<options>] [<path>...]"),
+ N_("git submodule absorbgitdirs [<options>] [<path>...]"),
NULL
};
+ int ret = 1;
argc = parse_options(argc, argv, prefix, embed_gitdir_options,
git_submodule_helper_usage, 0);
- if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
- return 1;
+ if (module_list_compute(argv, prefix, &pathspec, &list) < 0)
+ goto cleanup;
for (i = 0; i < list.nr; i++)
- absorb_git_dir_into_superproject(list.entries[i]->name, flags);
-
- return 0;
-}
-
-static int is_active(int argc, const char **argv, const char *prefix)
-{
- if (argc != 2)
- die("submodule--helper is-active takes exactly 1 argument");
-
- return !is_submodule_active(the_repository, argv[1]);
-}
-
-/*
- * Exit non-zero if any of the submodule names given on the command line is
- * invalid. If no names are given, filter stdin to print only valid names
- * (which is primarily intended for testing).
- */
-static int check_name(int argc, const char **argv, const char *prefix)
-{
- if (argc > 1) {
- while (*++argv) {
- if (check_submodule_name(*argv) < 0)
- return 1;
- }
- } else {
- struct strbuf buf = STRBUF_INIT;
- while (strbuf_getline(&buf, stdin) != EOF) {
- if (!check_submodule_name(buf.buf))
- printf("%s\n", buf.buf);
- }
- strbuf_release(&buf);
- }
- return 0;
-}
-
-static int module_config(int argc, const char **argv, const char *prefix)
-{
- enum {
- CHECK_WRITEABLE = 1,
- DO_UNSET = 2
- } command = 0;
-
- struct option module_config_options[] = {
- OPT_CMDMODE(0, "check-writeable", &command,
- N_("check if it is safe to write to the .gitmodules file"),
- CHECK_WRITEABLE),
- OPT_CMDMODE(0, "unset", &command,
- N_("unset the config in the .gitmodules file"),
- DO_UNSET),
- OPT_END()
- };
- const char *const git_submodule_helper_usage[] = {
- N_("git submodule--helper config <name> [<value>]"),
- N_("git submodule--helper config --unset <name>"),
- "git submodule--helper config --check-writeable",
- NULL
- };
-
- argc = parse_options(argc, argv, prefix, module_config_options,
- git_submodule_helper_usage, PARSE_OPT_KEEP_ARGV0);
-
- if (argc == 1 && command == CHECK_WRITEABLE)
- return is_writing_gitmodules_ok() ? 0 : -1;
-
- /* Equivalent to ACTION_GET in builtin/config.c */
- if (argc == 2 && command != DO_UNSET)
- return print_config_from_gitmodules(the_repository, argv[1]);
-
- /* Equivalent to ACTION_SET in builtin/config.c */
- if (argc == 3 || (argc == 2 && command == DO_UNSET)) {
- const char *value = (argc == 3) ? argv[2] : NULL;
+ absorb_git_dir_into_superproject(list.entries[i]->name,
+ super_prefix);
- if (!is_writing_gitmodules_ok())
- die(_("please make sure that the .gitmodules file is in the working tree"));
-
- return config_set_in_gitmodules_file_gently(argv[1], value);
- }
-
- usage_with_options(git_submodule_helper_usage, module_config_options);
+ ret = 0;
+cleanup:
+ clear_pathspec(&pathspec);
+ module_list_release(&list);
+ return ret;
}
static int module_set_url(int argc, const char **argv, const char *prefix)
@@ -2994,13 +2894,12 @@ static int module_set_url(int argc, const char **argv, const char *prefix)
const char *newurl;
const char *path;
char *config_name;
-
struct option options[] = {
OPT__QUIET(&quiet, N_("suppress output for setting url of a submodule")),
OPT_END()
};
const char *const usage[] = {
- N_("git submodule--helper set-url [--quiet] <path> <newurl>"),
+ N_("git submodule set-url [--quiet] <path> <newurl>"),
NULL
};
@@ -3012,7 +2911,7 @@ static int module_set_url(int argc, const char **argv, const char *prefix)
config_name = xstrfmt("submodule.%s.url", path);
config_set_in_gitmodules_file_gently(config_name, newurl);
- sync_submodule(path, prefix, quiet ? OPT_QUIET : 0);
+ sync_submodule(path, prefix, NULL, quiet ? OPT_QUIET : 0);
free(config_name);
@@ -3025,13 +2924,13 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)
const char *opt_branch = NULL;
const char *path;
char *config_name;
-
- /*
- * We accept the `quiet` option for uniformity across subcommands,
- * though there is nothing to make less verbose in this subcommand.
- */
struct option options[] = {
+ /*
+ * We accept the `quiet` option for uniformity across subcommands,
+ * though there is nothing to make less verbose in this subcommand.
+ */
OPT_NOOP_NOARG('q', "quiet"),
+
OPT_BOOL('d', "default", &opt_default,
N_("set the default tracking branch to master")),
OPT_STRING('b', "branch", &opt_branch, N_("branch"),
@@ -3039,8 +2938,8 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)
OPT_END()
};
const char *const usage[] = {
- N_("git submodule--helper set-branch [-q|--quiet] (-d|--default) <path>"),
- N_("git submodule--helper set-branch [-q|--quiet] (-b|--branch) <branch> <path>"),
+ N_("git submodule set-branch [-q|--quiet] (-d|--default) <path>"),
+ N_("git submodule set-branch [-q|--quiet] (-b|--branch) <branch> <path>"),
NULL
};
@@ -3066,7 +2965,6 @@ static int module_create_branch(int argc, const char **argv, const char *prefix)
{
enum branch_track track;
int quiet = 0, force = 0, reflog = 0, dry_run = 0;
-
struct option options[] = {
OPT__QUIET(&quiet, N_("print only error messages")),
OPT__FORCE(&force, N_("force creation"), 0),
@@ -3122,15 +3020,17 @@ static void append_fetch_remotes(struct strbuf *msg, const char *git_dir_path)
struct strbuf sb_remote_out = STRBUF_INIT;
cp_remote.git_cmd = 1;
- strvec_pushf(&cp_remote.env_array,
+ strvec_pushf(&cp_remote.env,
"GIT_DIR=%s", git_dir_path);
- strvec_push(&cp_remote.env_array, "GIT_WORK_TREE=.");
+ strvec_push(&cp_remote.env, "GIT_WORK_TREE=.");
strvec_pushl(&cp_remote.args, "remote", "-v", NULL);
if (!capture_command(&cp_remote, &sb_remote_out, 0)) {
char *next_line;
char *line = sb_remote_out.buf;
+
while ((next_line = strchr(line, '\n')) != NULL) {
size_t len = next_line - line;
+
if (strip_suffix_mem(line, &len, " (fetch)"))
strbuf_addf(msg, " %.*s\n", (int)len, line);
line = next_line + 1;
@@ -3144,6 +3044,8 @@ static int add_submodule(const struct add_data *add_data)
{
char *submod_gitdir_path;
struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
+ struct string_list reference = STRING_LIST_INIT_NODUP;
+ int ret = -1;
/* perhaps the path already exists and is already a git repo, else clone it */
if (is_directory(add_data->sm_path)) {
@@ -3160,6 +3062,7 @@ static int add_submodule(const struct add_data *add_data)
free(submod_gitdir_path);
} else {
struct child_process cp = CHILD_PROCESS_INIT;
+
submod_gitdir_path = xstrfmt(".git/modules/%s", add_data->sm_name);
if (is_directory(submod_gitdir_path)) {
@@ -3198,17 +3101,19 @@ static int add_submodule(const struct add_data *add_data)
clone_data.url = add_data->realrepo;
clone_data.quiet = add_data->quiet;
clone_data.progress = add_data->progress;
- if (add_data->reference_path)
- string_list_append(&clone_data.reference,
- xstrdup(add_data->reference_path));
+ if (add_data->reference_path) {
+ char *p = xstrdup(add_data->reference_path);
+
+ string_list_append(&reference, p)->util = p;
+ }
clone_data.dissociate = add_data->dissociate;
if (add_data->depth >= 0)
clone_data.depth = xstrfmt("%d", add_data->depth);
- if (clone_submodule(&clone_data))
- return -1;
+ if (clone_submodule(&clone_data, &reference))
+ goto cleanup;
- prepare_submodule_repo_env(&cp.env_array);
+ prepare_submodule_repo_env(&cp.env);
cp.git_cmd = 1;
cp.dir = add_data->sm_path;
/*
@@ -3225,7 +3130,10 @@ static int add_submodule(const struct add_data *add_data)
if (run_command(&cp))
die(_("unable to checkout submodule '%s'"), add_data->sm_path);
}
- return 0;
+ ret = 0;
+cleanup:
+ string_list_clear(&reference, 1);
+ return ret;
}
static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
@@ -3246,7 +3154,6 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con
static void configure_added_submodule(struct add_data *add_data)
{
char *key;
- char *val = NULL;
struct child_process add_submod = CHILD_PROCESS_INIT;
struct child_process add_gitmodules = CHILD_PROCESS_INIT;
@@ -3291,7 +3198,7 @@ static void configure_added_submodule(struct add_data *add_data)
* is_submodule_active(), since that function needs to find
* out the value of "submodule.active" again anyway.
*/
- if (!git_config_get_string("submodule.active", &val) && val) {
+ if (!git_config_get("submodule.active")) {
/*
* If the submodule being added isn't already covered by the
* current configured pathspec, set the submodule's active flag
@@ -3314,7 +3221,7 @@ static void die_on_index_match(const char *path, int force)
const char *args[] = { path, NULL };
parse_pathspec(&ps, 0, PATHSPEC_PREFER_CWD, NULL, args);
- if (read_cache_preload(NULL) < 0)
+ if (repo_read_index_preload(the_repository, NULL, 0) < 0)
die(_("index file corrupt"));
if (ps.nr) {
@@ -3325,19 +3232,18 @@ static void die_on_index_match(const char *path, int force)
ensure_full_index(&the_index);
/*
- * Since there is only one pathspec, we just need
- * need to check ps_matched[0] to know if a cache
- * entry matched.
+ * Since there is only one pathspec, we just need to
+ * check ps_matched[0] to know if a cache entry matched.
*/
- for (i = 0; i < active_nr; i++) {
- ce_path_match(&the_index, active_cache[i], &ps,
+ for (i = 0; i < the_index.cache_nr; i++) {
+ ce_path_match(&the_index, the_index.cache[i], &ps,
ps_matched);
if (ps_matched[0]) {
if (!force)
die(_("'%s' already exists in the index"),
path);
- if (!S_ISGITLINK(active_cache[i]->ce_mode))
+ if (!S_ISGITLINK(the_index.cache[i]->ce_mode))
die(_("'%s' already exists in the index "
"and is not a submodule"), path);
break;
@@ -3365,7 +3271,6 @@ static int module_add(int argc, const char **argv, const char *prefix)
int force = 0, quiet = 0, progress = 0, dissociate = 0;
struct add_data add_data = ADD_DATA_INIT;
char *to_free = NULL;
-
struct option options[] = {
OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
N_("branch of repository to add as submodule")),
@@ -3377,16 +3282,17 @@ static int module_add(int argc, const char **argv, const char *prefix)
N_("reference repository")),
OPT_BOOL(0, "dissociate", &dissociate, N_("borrow the objects from reference repositories")),
OPT_STRING(0, "name", &add_data.sm_name, N_("name"),
- N_("sets the submodule’s name to the given string "
+ N_("sets the submodule's name to the given string "
"instead of defaulting to its path")),
OPT_INTEGER(0, "depth", &add_data.depth, N_("depth for shallow clones")),
OPT_END()
};
-
const char *const usage[] = {
- N_("git submodule--helper add [<options>] [--] <repository> [<path>]"),
+ N_("git submodule add [<options>] [--] <repository> [<path>]"),
NULL
};
+ struct strbuf sb = STRBUF_INIT;
+ int ret = 1;
argc = parse_options(argc, argv, prefix, options, usage, 0);
@@ -3406,8 +3312,12 @@ static int module_add(int argc, const char **argv, const char *prefix)
else
add_data.sm_path = xstrdup(argv[1]);
- if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
- add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
+ if (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {
+ char *sm_path = add_data.sm_path;
+
+ add_data.sm_path = xstrfmt("%s%s", prefix, sm_path);
+ free(sm_path);
+ }
if (starts_with_dot_dot_slash(add_data.repo) ||
starts_with_dot_slash(add_data.repo)) {
@@ -3436,20 +3346,17 @@ static int module_add(int argc, const char **argv, const char *prefix)
die_on_repo_without_commits(add_data.sm_path);
if (!force) {
- int exit_code = -1;
- struct strbuf sb = STRBUF_INIT;
struct child_process cp = CHILD_PROCESS_INIT;
+
cp.git_cmd = 1;
cp.no_stdout = 1;
strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
"--no-warn-embedded-repo", add_data.sm_path, NULL);
- if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
+ if ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
strbuf_complete_line(&sb);
fputs(sb.buf, stderr);
- free(add_data.sm_path);
- return exit_code;
+ goto cleanup;
}
- strbuf_release(&sb);
}
if(!add_data.sm_name)
@@ -3464,64 +3371,44 @@ static int module_add(int argc, const char **argv, const char *prefix)
add_data.progress = !!progress;
add_data.dissociate = !!dissociate;
- if (add_submodule(&add_data)) {
- free(add_data.sm_path);
- return 1;
- }
+ if (add_submodule(&add_data))
+ goto cleanup;
configure_added_submodule(&add_data);
+
+ ret = 0;
+cleanup:
free(add_data.sm_path);
free(to_free);
+ strbuf_release(&sb);
- return 0;
+ return ret;
}
-#define SUPPORT_SUPER_PREFIX (1<<0)
-
-struct cmd_struct {
- const char *cmd;
- int (*fn)(int, const char **, const char *);
- unsigned option;
-};
-
-static struct cmd_struct commands[] = {
- {"list", module_list, 0},
- {"name", module_name, 0},
- {"clone", module_clone, 0},
- {"add", module_add, SUPPORT_SUPER_PREFIX},
- {"update", module_update, 0},
- {"resolve-relative-url-test", resolve_relative_url_test, 0},
- {"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
- {"init", module_init, SUPPORT_SUPER_PREFIX},
- {"status", module_status, SUPPORT_SUPER_PREFIX},
- {"sync", module_sync, SUPPORT_SUPER_PREFIX},
- {"deinit", module_deinit, 0},
- {"summary", module_summary, SUPPORT_SUPER_PREFIX},
- {"push-check", push_check, 0},
- {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
- {"is-active", is_active, 0},
- {"check-name", check_name, 0},
- {"config", module_config, 0},
- {"set-url", module_set_url, 0},
- {"set-branch", module_set_branch, 0},
- {"create-branch", module_create_branch, 0},
-};
-
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
{
- int i;
- if (argc < 2 || !strcmp(argv[1], "-h"))
- usage("git submodule--helper <command>");
-
- for (i = 0; i < ARRAY_SIZE(commands); i++) {
- if (!strcmp(argv[1], commands[i].cmd)) {
- if (get_super_prefix() &&
- !(commands[i].option & SUPPORT_SUPER_PREFIX))
- die(_("%s doesn't support --super-prefix"),
- commands[i].cmd);
- return commands[i].fn(argc - 1, argv + 1, prefix);
- }
- }
+ parse_opt_subcommand_fn *fn = NULL;
+ const char *const usage[] = {
+ N_("git submodule--helper <command>"),
+ NULL
+ };
+ struct option options[] = {
+ OPT_SUBCOMMAND("clone", &fn, module_clone),
+ OPT_SUBCOMMAND("add", &fn, module_add),
+ OPT_SUBCOMMAND("update", &fn, module_update),
+ OPT_SUBCOMMAND("foreach", &fn, module_foreach),
+ OPT_SUBCOMMAND("init", &fn, module_init),
+ OPT_SUBCOMMAND("status", &fn, module_status),
+ OPT_SUBCOMMAND("sync", &fn, module_sync),
+ OPT_SUBCOMMAND("deinit", &fn, module_deinit),
+ OPT_SUBCOMMAND("summary", &fn, module_summary),
+ OPT_SUBCOMMAND("push-check", &fn, push_check),
+ OPT_SUBCOMMAND("absorbgitdirs", &fn, absorb_git_dirs),
+ OPT_SUBCOMMAND("set-url", &fn, module_set_url),
+ OPT_SUBCOMMAND("set-branch", &fn, module_set_branch),
+ OPT_SUBCOMMAND("create-branch", &fn, module_create_branch),
+ OPT_END()
+ };
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
- die(_("'%s' is not a valid submodule--helper "
- "subcommand"), argv[1]);
+ return fn(argc, argv, prefix);
}
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index e547a08d6c..c9defe4d2e 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -1,19 +1,24 @@
#include "builtin.h"
#include "config.h"
-#include "cache.h"
+#include "gettext.h"
#include "refs.h"
#include "parse-options.h"
+#include "strbuf.h"
static const char * const git_symbolic_ref_usage[] = {
- N_("git symbolic-ref [<options>] <name> [<ref>]"),
- N_("git symbolic-ref -d [-q] <name>"),
+ N_("git symbolic-ref [-m <reason>] <name> <ref>"),
+ N_("git symbolic-ref [-q] [--short] [--no-recurse] <name>"),
+ N_("git symbolic-ref --delete [-q] <name>"),
NULL
};
-static int check_symref(const char *HEAD, int quiet, int shorten, int print)
+static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, int print)
{
- int flag;
- const char *refname = resolve_ref_unsafe(HEAD, 0, NULL, &flag);
+ int resolve_flags, flag;
+ const char *refname;
+
+ resolve_flags = (recurse ? 0 : RESOLVE_REF_NO_RECURSE);
+ refname = resolve_ref_unsafe(HEAD, resolve_flags, NULL, &flag);
if (!refname)
die("No such ref: %s", HEAD);
@@ -35,13 +40,14 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int print)
int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
{
- int quiet = 0, delete = 0, shorten = 0, ret = 0;
+ int quiet = 0, delete = 0, shorten = 0, recurse = 1, ret = 0;
const char *msg = NULL;
struct option options[] = {
OPT__QUIET(&quiet,
N_("suppress error message for non-symbolic (detached) refs")),
OPT_BOOL('d', "delete", &delete, N_("delete symbolic ref")),
OPT_BOOL(0, "short", &shorten, N_("shorten ref output")),
+ OPT_BOOL(0, "recurse", &recurse, N_("recursively dereference (default)")),
OPT_STRING('m', NULL, &msg, N_("reason"), N_("reason of the update")),
OPT_END(),
};
@@ -55,7 +61,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
if (delete) {
if (argc != 1)
usage_with_options(git_symbolic_ref_usage, options);
- ret = check_symref(argv[0], 1, 0, 0);
+ ret = check_symref(argv[0], 1, 0, 0, 0);
if (ret)
die("Cannot delete %s, not a symbolic ref", argv[0]);
if (!strcmp(argv[0], "HEAD"))
@@ -65,12 +71,14 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
switch (argc) {
case 1:
- ret = check_symref(argv[0], quiet, shorten, 1);
+ ret = check_symref(argv[0], quiet, shorten, recurse, 1);
break;
case 2:
if (!strcmp(argv[0], "HEAD") &&
!starts_with(argv[1], "refs/"))
die("Refusing to point HEAD outside of refs/");
+ if (check_refname_format(argv[1], REFNAME_ALLOW_ONELEVEL) < 0)
+ die("Refusing to set '%s' to invalid ref '%s'", argv[0], argv[1]);
ret = !!create_symref(argv[0], argv[1], msg);
break;
default:
diff --git a/builtin/tag.c b/builtin/tag.c
index e5a8f85693..f33600c050 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -6,11 +6,17 @@
* Based on git-tag.sh and mktag.c by Linus Torvalds.
*/
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
+#include "advice.h"
+#include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "refs.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "tag.h"
#include "run-command.h"
#include "parse-options.h"
@@ -21,13 +27,16 @@
#include "column.h"
#include "ref-filter.h"
#include "date.h"
+#include "write-or-die.h"
static const char * const git_tag_usage[] = {
- N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
- " <tagname> [<head>]"),
+ N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+ " <tagname> [<commit> | <object>]"),
N_("git tag -d <tagname>..."),
- N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]\n"
- " [--format=<format>] [--merged <commit>] [--no-merged <commit>] [<pattern>...]"),
+ N_("git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+ " [--points-at <object>] [--column[=<options>] | --no-column]\n"
+ " [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+ " [--merged <commit>] [--no-merged <commit>] [<pattern>...]"),
N_("git tag -v [--format=<format>] <tagname>..."),
NULL
};
@@ -35,6 +44,7 @@ static const char * const git_tag_usage[] = {
static unsigned int colopts;
static int force_sign_annotate;
static int config_sign_tag = -1; /* unspecified */
+static int omit_empty = 0;
static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
struct ref_format *format)
@@ -64,6 +74,7 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
die(_("unable to parse format string"));
filter->with_commit_tag_algo = 1;
filter_refs(&array, filter, FILTER_REFS_TAGS);
+ filter_ahead_behind(the_repository, format, &array);
ref_array_sort(sorting, &array);
for (i = 0; i < array.nr; i++) {
@@ -72,7 +83,8 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
if (format_ref_array_item(array.items[i], format, &output, &err))
die("%s", err.buf);
fwrite(output.buf, 1, output.len, stdout);
- putchar('\n');
+ if (output.len || !omit_empty)
+ putchar('\n');
}
strbuf_release(&err);
@@ -135,7 +147,7 @@ static int delete_tags(const char **argv)
if (!ref_exists(name))
printf(_("Deleted tag '%s' (was %s)\n"),
item->string + 10,
- find_unique_abbrev(oid, DEFAULT_ABBREV));
+ repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV));
free(oid);
}
@@ -176,10 +188,9 @@ static const char tag_template_nocleanup[] =
"Lines starting with '%c' will be kept; you may remove them"
" yourself if you want to.\n");
-static int git_tag_config(const char *var, const char *value, void *cb)
+static int git_tag_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
- int status;
-
if (!strcmp(var, "tag.gpgsign")) {
config_sign_tag = git_config_bool(var, value);
return 0;
@@ -192,9 +203,6 @@ static int git_tag_config(const char *var, const char *value, void *cb)
return 0;
}
- status = git_gpg_config(var, value, cb);
- if (status)
- return status;
if (!strcmp(var, "tag.forcesignannotated")) {
force_sign_annotate = git_config_bool(var, value);
return 0;
@@ -202,7 +210,11 @@ static int git_tag_config(const char *var, const char *value, void *cb)
if (starts_with(var, "column."))
return git_column_config(var, value, "tag", &colopts);
- return git_color_default_config(var, value, cb);
+
+ if (git_color_config(var, value, cb) < 0)
+ return -1;
+
+ return git_default_config(var, value, ctx, cb);
}
static void write_tag_body(int fd, const struct object_id *oid)
@@ -213,7 +225,7 @@ static void write_tag_body(int fd, const struct object_id *oid)
struct strbuf payload = STRBUF_INIT;
struct strbuf signature = STRBUF_INIT;
- orig = buf = read_object_file(oid, &type, &size);
+ orig = buf = repo_read_object_file(the_repository, oid, &type, &size);
if (!buf)
return;
if (parse_signature(buf, size, &payload, &signature)) {
@@ -264,11 +276,10 @@ static const char message_advice_nested_tag[] =
static void create_tag(const struct object_id *object, const char *object_ref,
const char *tag,
struct strbuf *buf, struct create_tag_options *opt,
- struct object_id *prev, struct object_id *result)
+ struct object_id *prev, struct object_id *result, char *path)
{
enum object_type type;
struct strbuf header = STRBUF_INIT;
- char *path = NULL;
type = oid_object_info(the_repository, object, NULL);
if (type <= OBJ_NONE)
@@ -292,7 +303,6 @@ static void create_tag(const struct object_id *object, const char *object_ref,
int fd;
/* write the template message before editing: */
- path = git_pathdup("TAG_EDITMSG");
fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (opt->message_given) {
@@ -304,9 +314,11 @@ static void create_tag(const struct object_id *object, const char *object_ref,
struct strbuf buf = STRBUF_INIT;
strbuf_addch(&buf, '\n');
if (opt->cleanup_mode == CLEANUP_ALL)
- strbuf_commented_addf(&buf, _(tag_template), tag, comment_line_char);
+ strbuf_commented_addf(&buf, comment_line_char,
+ _(tag_template), tag, comment_line_char);
else
- strbuf_commented_addf(&buf, _(tag_template_nocleanup), tag, comment_line_char);
+ strbuf_commented_addf(&buf, comment_line_char,
+ _(tag_template_nocleanup), tag, comment_line_char);
write_or_die(fd, buf.buf, buf.len);
strbuf_release(&buf);
}
@@ -320,7 +332,8 @@ static void create_tag(const struct object_id *object, const char *object_ref,
}
if (opt->cleanup_mode != CLEANUP_NONE)
- strbuf_stripspace(buf, opt->cleanup_mode == CLEANUP_ALL);
+ strbuf_stripspace(buf,
+ opt->cleanup_mode == CLEANUP_ALL ? comment_line_char : '\0');
if (!opt->message_given && !buf->len)
die(_("no tag message?"));
@@ -334,10 +347,6 @@ static void create_tag(const struct object_id *object, const char *object_ref,
path);
exit(128);
}
- if (path) {
- unlink_or_warn(path);
- free(path);
- }
}
static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb)
@@ -364,7 +373,7 @@ static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb)
strbuf_addstr(sb, "object of unknown type");
break;
case OBJ_COMMIT:
- if ((buf = read_object_file(oid, &type, &size)) != NULL) {
+ if ((buf = repo_read_object_file(the_repository, oid, &type, &size))) {
subject_len = find_commit_subject(buf, &subject_start);
strbuf_insert(sb, sb->len, subject_start, subject_len);
} else {
@@ -372,7 +381,7 @@ static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb)
}
free(buf);
- if ((c = lookup_commit_reference(the_repository, oid)) != NULL)
+ if ((c = lookup_commit_reference(the_repository, oid)))
strbuf_addf(sb, ", %s", show_date(c->date, 0, DATE_MODE(SHORT)));
break;
case OBJ_TREE:
@@ -431,11 +440,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
int create_reflog = 0;
int annotate = 0, force = 0;
int cmdmode = 0, create_tag_object = 0;
- const char *msgfile = NULL, *keyid = NULL;
+ char *msgfile = NULL;
+ const char *keyid = NULL;
struct msg_arg msg = { .buf = STRBUF_INIT };
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
- struct ref_filter filter;
+ struct ref_filter filter = REF_FILTER_INIT;
struct ref_sorting *sorting;
struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
@@ -471,6 +481,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
OPT_MERGED(&filter, N_("print only tags that are merged")),
OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
+ OPT_BOOL(0, "omit-empty", &omit_empty,
+ N_("do not output a newline after empty formatted refs")),
OPT_REF_SORT(&sorting_options),
{
OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
@@ -485,13 +497,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
};
int ret = 0;
const char *only_in_list = NULL;
+ char *path = NULL;
setup_ref_filter_porcelain_msg();
git_config(git_tag_config, &sorting_options);
memset(&opt, 0, sizeof(opt));
- memset(&filter, 0, sizeof(filter));
filter.lines = -1;
opt.sign = -1;
@@ -591,7 +603,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (argc > 2)
die(_("too many arguments"));
- if (get_oid(object_ref, &object))
+ if (repo_get_oid(the_repository, object_ref, &object))
die(_("Failed to resolve '%s' as a valid ref."), object_ref);
if (strbuf_check_tag_ref(&ref, tag))
@@ -619,7 +631,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (create_tag_object) {
if (force_sign_annotate && !annotate)
opt.sign = 1;
- create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object);
+ path = git_pathdup("TAG_EDITMSG");
+ create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
+ path);
}
transaction = ref_transaction_begin(&err);
@@ -627,19 +641,30 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
ref_transaction_update(transaction, ref.buf, &object, &prev,
create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
reflog_msg.buf, &err) ||
- ref_transaction_commit(transaction, &err))
+ ref_transaction_commit(transaction, &err)) {
+ if (path)
+ fprintf(stderr,
+ _("The tag message has been left in %s\n"),
+ path);
die("%s", err.buf);
+ }
+ if (path) {
+ unlink_or_warn(path);
+ free(path);
+ }
ref_transaction_free(transaction);
if (force && !is_null_oid(&prev) && !oideq(&prev, &object))
printf(_("Updated tag '%s' (was %s)\n"), tag,
- find_unique_abbrev(&prev, DEFAULT_ABBREV));
+ repo_find_unique_abbrev(the_repository, &prev, DEFAULT_ABBREV));
cleanup:
ref_sorting_release(sorting);
+ ref_filter_clear(&filter);
strbuf_release(&buf);
strbuf_release(&ref);
strbuf_release(&reflog_msg);
strbuf_release(&msg.buf);
strbuf_release(&err);
+ free(msgfile);
return ret;
}
diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c
index 58652229f2..c129e2bb6c 100644
--- a/builtin/unpack-file.c
+++ b/builtin/unpack-file.c
@@ -1,6 +1,8 @@
#include "builtin.h"
#include "config.h"
-#include "object-store.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-store-ll.h"
static char *create_temp_file(struct object_id *oid)
{
@@ -10,7 +12,7 @@ static char *create_temp_file(struct object_id *oid)
unsigned long size;
int fd;
- buf = read_object_file(oid, &type, &size);
+ buf = repo_read_object_file(the_repository, oid, &type, &size);
if (!buf || type != OBJ_BLOB)
die("unable to read blob object %s", oid_to_hex(oid));
@@ -19,16 +21,17 @@ static char *create_temp_file(struct object_id *oid)
if (write_in_full(fd, buf, size) < 0)
die_errno("unable to write temp-file");
close(fd);
+ free(buf);
return path;
}
-int cmd_unpack_file(int argc, const char **argv, const char *prefix)
+int cmd_unpack_file(int argc, const char **argv, const char *prefix UNUSED)
{
struct object_id oid;
if (argc != 2 || !strcmp(argv[1], "-h"))
- usage("git unpack-file <sha1>");
- if (get_oid(argv[1], &oid))
+ usage("git unpack-file <blob>");
+ if (repo_get_oid(the_repository, argv[1], &oid))
die("Not a valid object name %s", argv[1]);
git_config(git_default_config, NULL);
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index dbeb0680a5..84b68304ed 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -1,12 +1,18 @@
#include "builtin.h"
-#include "cache.h"
+#include "bulk-checkin.h"
#include "config.h"
-#include "object-store.h"
+#include "environment.h"
+#include "gettext.h"
+#include "git-zlib.h"
+#include "hex.h"
+#include "object-store-ll.h"
#include "object.h"
#include "delta.h"
#include "pack.h"
#include "blob.h"
#include "commit.h"
+#include "replace-object.h"
+#include "strbuf.h"
#include "tag.h"
#include "tree.h"
#include "tree-walk.h"
@@ -96,15 +102,27 @@ static void use(int bytes)
display_throughput(progress, consumed_bytes);
}
+/*
+ * Decompress zstream from the standard input into a newly
+ * allocated buffer of specified size and return the buffer.
+ * The caller is responsible to free the returned buffer.
+ *
+ * But for dry_run mode, "get_data()" is only used to check the
+ * integrity of data, and the returned buffer is not used at all.
+ * Therefore, in dry_run mode, "get_data()" will release the small
+ * allocated buffer which is reused to hold temporary zstream output
+ * and return NULL instead of returning garbage data.
+ */
static void *get_data(unsigned long size)
{
git_zstream stream;
- void *buf = xmallocz(size);
+ unsigned long bufsize = dry_run && size > 8192 ? 8192 : size;
+ void *buf = xmallocz(bufsize);
memset(&stream, 0, sizeof(stream));
stream.next_out = buf;
- stream.avail_out = size;
+ stream.avail_out = bufsize;
stream.next_in = fill(1);
stream.avail_in = len;
git_inflate_init(&stream);
@@ -124,8 +142,17 @@ static void *get_data(unsigned long size)
}
stream.next_in = fill(1);
stream.avail_in = len;
+ if (dry_run) {
+ /* reuse the buffer in dry_run mode */
+ stream.next_out = buf;
+ stream.avail_out = bufsize > size - stream.total_out ?
+ size - stream.total_out :
+ bufsize;
+ }
}
git_inflate_end(&stream);
+ if (dry_run)
+ FREE_AND_NULL(buf);
return buf;
}
@@ -325,10 +352,70 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size,
{
void *buf = get_data(size);
- if (!dry_run && buf)
+ if (buf)
write_object(nr, type, buf, size);
- else
- free(buf);
+}
+
+struct input_zstream_data {
+ git_zstream *zstream;
+ unsigned char buf[8192];
+ int status;
+};
+
+static const void *feed_input_zstream(struct input_stream *in_stream,
+ unsigned long *readlen)
+{
+ struct input_zstream_data *data = in_stream->data;
+ git_zstream *zstream = data->zstream;
+ void *in = fill(1);
+
+ if (in_stream->is_finished) {
+ *readlen = 0;
+ return NULL;
+ }
+
+ zstream->next_out = data->buf;
+ zstream->avail_out = sizeof(data->buf);
+ zstream->next_in = in;
+ zstream->avail_in = len;
+
+ data->status = git_inflate(zstream, 0);
+
+ in_stream->is_finished = data->status != Z_OK;
+ use(len - zstream->avail_in);
+ *readlen = sizeof(data->buf) - zstream->avail_out;
+
+ return data->buf;
+}
+
+static void stream_blob(unsigned long size, unsigned nr)
+{
+ git_zstream zstream = { 0 };
+ struct input_zstream_data data = { 0 };
+ struct input_stream in_stream = {
+ .read = feed_input_zstream,
+ .data = &data,
+ };
+ struct obj_info *info = &obj_list[nr];
+
+ data.zstream = &zstream;
+ git_inflate_init(&zstream);
+
+ if (stream_loose_object(&in_stream, size, &info->oid))
+ die(_("failed to write object in stream"));
+
+ if (data.status != Z_STREAM_END)
+ die(_("inflate returned (%d)"), data.status);
+ git_inflate_end(&zstream);
+
+ if (strict) {
+ struct blob *blob = lookup_blob(the_repository, &info->oid);
+
+ if (!blob)
+ die(_("invalid blob object from stream"));
+ blob->object.flags |= FLAG_WRITTEN;
+ }
+ info->obj = NULL;
}
static int resolve_against_held(unsigned nr, const struct object_id *base,
@@ -358,11 +445,9 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
oidread(&base_oid, fill(the_hash_algo->rawsz));
use(the_hash_algo->rawsz);
delta_data = get_data(delta_size);
- if (dry_run || !delta_data) {
- free(delta_data);
+ if (!delta_data)
return;
- }
- if (has_object_file(&base_oid))
+ if (repo_has_object_file(the_repository, &base_oid))
; /* Ok we have this one */
else if (resolve_against_held(nr, &base_oid,
delta_data, delta_size))
@@ -397,10 +482,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
die("offset value out of bound for delta base object");
delta_data = get_data(delta_size);
- if (dry_run || !delta_data) {
- free(delta_data);
+ if (!delta_data)
return;
- }
lo = 0;
hi = nr;
while (lo < hi) {
@@ -430,7 +513,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
if (resolve_against_held(nr, &base_oid, delta_data, delta_size))
return;
- base = read_object_file(&base_oid, &type, &base_size);
+ base = repo_read_object_file(the_repository, &base_oid, &type,
+ &base_size);
if (!base) {
error("failed to read delta-pack base object %s",
oid_to_hex(&base_oid));
@@ -467,9 +551,14 @@ static void unpack_one(unsigned nr)
}
switch (type) {
+ case OBJ_BLOB:
+ if (!dry_run && size > big_file_threshold) {
+ stream_blob(size, nr);
+ return;
+ }
+ /* fallthrough */
case OBJ_COMMIT:
case OBJ_TREE:
- case OBJ_BLOB:
case OBJ_TAG:
unpack_non_delta_entry(type, size, nr);
return;
@@ -503,22 +592,24 @@ static void unpack_all(void)
if (!quiet)
progress = start_progress(_("Unpacking objects"), nr_objects);
CALLOC_ARRAY(obj_list, nr_objects);
+ begin_odb_transaction();
for (i = 0; i < nr_objects; i++) {
unpack_one(i);
display_progress(progress, i + 1);
}
+ end_odb_transaction();
stop_progress(&progress);
if (delta_list)
die("unresolved deltas left after unpacking");
}
-int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
+int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED)
{
int i;
struct object_id oid;
- read_replace_refs = 0;
+ disable_replace_refs();
git_config(git_default_config, NULL);
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 876112abb2..aee3cb8cbd 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -3,21 +3,32 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
+#include "bulk-checkin.h"
#include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
#include "lockfile.h"
#include "quote.h"
#include "cache-tree.h"
#include "tree-walk.h"
-#include "builtin.h"
+#include "object-file.h"
#include "refs.h"
#include "resolve-undo.h"
#include "parse-options.h"
#include "pathspec.h"
#include "dir.h"
+#include "read-cache.h"
+#include "repository.h"
+#include "setup.h"
+#include "sparse-index.h"
#include "split-index.h"
+#include "symlinks.h"
#include "fsmonitor.h"
+#include "write-or-die.h"
/*
* Default to not allowing changes to the list of files. The
@@ -57,6 +68,14 @@ static void report(const char *fmt, ...)
if (!verbose)
return;
+ /*
+ * It is possible, though unlikely, that a caller could use the verbose
+ * output to synchronize with addition of objects to the object
+ * database. The current implementation of ODB transactions leaves
+ * objects invisible while a transaction is active, so flush the
+ * transaction here before reporting a change made by update-index.
+ */
+ flush_odb_transaction();
va_start(vp, fmt);
vprintf(fmt, vp);
putchar('\n');
@@ -228,16 +247,16 @@ done:
static int mark_ce_flags(const char *path, int flag, int mark)
{
int namelen = strlen(path);
- int pos = cache_name_pos(path, namelen);
+ int pos = index_name_pos(&the_index, path, namelen);
if (0 <= pos) {
- mark_fsmonitor_invalid(&the_index, active_cache[pos]);
+ mark_fsmonitor_invalid(&the_index, the_index.cache[pos]);
if (mark)
- active_cache[pos]->ce_flags |= flag;
+ the_index.cache[pos]->ce_flags |= flag;
else
- active_cache[pos]->ce_flags &= ~flag;
- active_cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
+ the_index.cache[pos]->ce_flags &= ~flag;
+ the_index.cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
cache_tree_invalidate_path(&the_index, path);
- active_cache_changed |= CE_ENTRY_CHANGED;
+ the_index.cache_changed |= CE_ENTRY_CHANGED;
return 0;
}
return -1;
@@ -247,7 +266,7 @@ static int remove_one_path(const char *path)
{
if (!allow_remove)
return error("%s: does not exist and --remove not passed", path);
- if (remove_file_from_cache(path))
+ if (remove_file_from_index(&the_index, path))
return error("%s: cannot remove from the index", path);
return 0;
}
@@ -272,7 +291,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
struct cache_entry *ce;
/* Was the old index entry already up-to-date? */
- if (old && !ce_stage(old) && !ce_match_stat(old, st, 0))
+ if (old && !ce_stage(old) && !ie_match_stat(&the_index, old, st, 0))
return 0;
ce = make_empty_cache_entry(&the_index, len);
@@ -289,7 +308,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
}
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
- if (add_cache_entry(ce, option)) {
+ if (add_index_entry(&the_index, ce, option)) {
discard_cache_entry(ce);
return error("%s: cannot add to the index - missing --add option?", path);
}
@@ -322,11 +341,11 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
static int process_directory(const char *path, int len, struct stat *st)
{
struct object_id oid;
- int pos = cache_name_pos(path, len);
+ int pos = index_name_pos(&the_index, path, len);
/* Exact match: file or existing gitlink */
if (pos >= 0) {
- const struct cache_entry *ce = active_cache[pos];
+ const struct cache_entry *ce = the_index.cache[pos];
if (S_ISGITLINK(ce->ce_mode)) {
/* Do nothing to the index if there is no HEAD! */
@@ -341,8 +360,8 @@ static int process_directory(const char *path, int len, struct stat *st)
/* Inexact match: is there perhaps a subdirectory match? */
pos = -pos-1;
- while (pos < active_nr) {
- const struct cache_entry *ce = active_cache[pos++];
+ while (pos < the_index.cache_nr) {
+ const struct cache_entry *ce = the_index.cache[pos++];
if (strncmp(ce->name, path, len))
break;
@@ -372,8 +391,8 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
if (has_symlink_leading_path(path, len))
return error("'%s' is beyond a symbolic link", path);
- pos = cache_name_pos(path, len);
- ce = pos < 0 ? NULL : active_cache[pos];
+ pos = index_name_pos(&the_index, path, len);
+ ce = pos < 0 ? NULL : the_index.cache[pos];
if (ce && ce_skip_worktree(ce)) {
/*
* working directory version is assumed "good"
@@ -381,7 +400,7 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
* On the other hand, removing it from index should work
*/
if (!ignore_skip_worktree_entries && allow_remove &&
- remove_file_from_cache(path))
+ remove_file_from_index(&the_index, path))
return error("%s: cannot remove from the index", path);
return 0;
}
@@ -420,7 +439,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
ce->ce_flags |= CE_VALID;
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
- if (add_cache_entry(ce, option))
+ if (add_index_entry(&the_index, ce, option))
return error("%s: cannot add to the index - missing --add option?",
path);
report("add '%s'", path);
@@ -432,11 +451,11 @@ static void chmod_path(char flip, const char *path)
int pos;
struct cache_entry *ce;
- pos = cache_name_pos(path, strlen(path));
+ pos = index_name_pos(&the_index, path, strlen(path));
if (pos < 0)
goto fail;
- ce = active_cache[pos];
- if (chmod_cache_entry(ce, flip) < 0)
+ ce = the_index.cache[pos];
+ if (chmod_index_entry(&the_index, ce, flip) < 0)
goto fail;
report("chmod %cx '%s'", flip, path);
@@ -479,7 +498,7 @@ static void update_one(const char *path)
}
if (force_remove) {
- if (remove_file_from_cache(path))
+ if (remove_file_from_index(&the_index, path))
die("git update-index: unable to remove %s", path);
report("remove '%s'", path);
return;
@@ -562,7 +581,7 @@ static void read_index_info(int nul_term_line)
if (!mode) {
/* mode == 0 means there is no such path -- remove */
- if (remove_file_from_cache(path_name))
+ if (remove_file_from_index(&the_index, path_name))
die("git update-index: unable to remove %s",
ptr);
}
@@ -629,12 +648,12 @@ static int unresolve_one(const char *path)
struct cache_entry *ce_2 = NULL, *ce_3 = NULL;
/* See if there is such entry in the index. */
- pos = cache_name_pos(path, namelen);
+ pos = index_name_pos(&the_index, path, namelen);
if (0 <= pos) {
/* already merged */
- pos = unmerge_cache_entry_at(pos);
- if (pos < active_nr) {
- const struct cache_entry *ce = active_cache[pos];
+ pos = unmerge_index_entry_at(&the_index, pos);
+ if (pos < the_index.cache_nr) {
+ const struct cache_entry *ce = the_index.cache[pos];
if (ce_stage(ce) &&
ce_namelen(ce) == namelen &&
!memcmp(ce->name, path, namelen))
@@ -647,8 +666,8 @@ static int unresolve_one(const char *path)
* want to do anything in the former case.
*/
pos = -pos-1;
- if (pos < active_nr) {
- const struct cache_entry *ce = active_cache[pos];
+ if (pos < the_index.cache_nr) {
+ const struct cache_entry *ce = the_index.cache[pos];
if (ce_namelen(ce) == namelen &&
!memcmp(ce->name, path, namelen)) {
fprintf(stderr,
@@ -677,13 +696,13 @@ static int unresolve_one(const char *path)
goto free_return;
}
- remove_file_from_cache(path);
- if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
+ remove_file_from_index(&the_index, path);
+ if (add_index_entry(&the_index, ce_2, ADD_CACHE_OK_TO_ADD)) {
error("%s: cannot add our version to the index.", path);
ret = -1;
goto free_return;
}
- if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD))
+ if (!add_index_entry(&the_index, ce_3, ADD_CACHE_OK_TO_ADD))
return 0;
error("%s: cannot add their version to the index.", path);
ret = -1;
@@ -723,7 +742,7 @@ static int do_unresolve(int ac, const char **av,
return err;
}
-static int do_reupdate(int ac, const char **av,
+static int do_reupdate(const char **paths,
const char *prefix)
{
/* Read HEAD and run update-index on paths that are
@@ -735,7 +754,7 @@ static int do_reupdate(int ac, const char **av,
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_CWD,
- prefix, av + 1);
+ prefix, paths);
if (read_ref("HEAD", &head_oid))
/* If there is no HEAD, that means it is an initial
@@ -743,8 +762,8 @@ static int do_reupdate(int ac, const char **av,
*/
has_head = 0;
redo:
- for (pos = 0; pos < active_nr; pos++) {
- const struct cache_entry *ce = active_cache[pos];
+ for (pos = 0; pos < the_index.cache_nr; pos++) {
+ const struct cache_entry *ce = the_index.cache[pos];
struct cache_entry *old = NULL;
int save_nr;
char *path;
@@ -773,12 +792,12 @@ static int do_reupdate(int ac, const char **av,
* path anymore, in which case, under 'allow_remove',
* or worse yet 'allow_replace', active_nr may decrease.
*/
- save_nr = active_nr;
+ save_nr = the_index.cache_nr;
path = xstrdup(ce->name);
update_one(path);
free(path);
discard_cache_entry(old);
- if (save_nr != active_nr)
+ if (save_nr != the_index.cache_nr)
goto redo;
}
clear_pathspec(&pathspec);
@@ -793,18 +812,19 @@ struct refresh_params {
static int refresh(struct refresh_params *o, unsigned int flag)
{
setup_work_tree();
- read_cache();
- *o->has_errors |= refresh_cache(o->flags | flag);
+ repo_read_index(the_repository);
+ *o->has_errors |= refresh_index(&the_index, o->flags | flag, NULL,
+ NULL, NULL);
if (has_racy_timestamp(&the_index)) {
/*
* Even if nothing else has changed, updating the file
* increases the chance that racy timestamps become
* non-racy, helping future run-time performance.
* We do that even in case of "errors" returned by
- * refresh_cache() as these are no actual errors.
+ * refresh_index() as these are no actual errors.
* cmd_status() does the same.
*/
- active_cache_changed |= SOMETHING_CHANGED;
+ the_index.cache_changed |= SOMETHING_CHANGED;
}
return 0;
}
@@ -841,7 +861,7 @@ static int resolve_undo_clear_callback(const struct option *opt,
{
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
- resolve_undo_clear();
+ resolve_undo_clear_index(&the_index);
return 0;
}
@@ -942,7 +962,7 @@ static enum parse_opt_result unresolve_callback(
*has_errors = do_unresolve(ctx->argc, ctx->argv,
prefix, prefix ? strlen(prefix) : 0);
if (*has_errors)
- active_cache_changed = 0;
+ the_index.cache_changed = 0;
ctx->argv += ctx->argc - 1;
ctx->argc = 1;
@@ -961,9 +981,9 @@ static enum parse_opt_result reupdate_callback(
/* consume remaining arguments. */
setup_work_tree();
- *has_errors = do_reupdate(ctx->argc, ctx->argv, prefix);
+ *has_errors = do_reupdate(ctx->argv + 1, prefix);
if (*has_errors)
- active_cache_changed = 0;
+ the_index.cache_changed = 0;
ctx->argv += ctx->argc - 1;
ctx->argc = 1;
@@ -1100,11 +1120,11 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
the_repository->settings.command_requires_full_index = 0;
/* we will diagnose later if it turns out that we need to update it */
- newfd = hold_locked_index(&lock_file, 0);
+ newfd = repo_hold_locked_index(the_repository, &lock_file, 0);
if (newfd < 0)
lock_error = errno;
- entries = read_cache();
+ entries = repo_read_index(the_repository);
if (entries < 0)
die("cache corrupted");
@@ -1116,6 +1136,12 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
*/
parse_options_start(&ctx, argc, argv, prefix,
options, PARSE_OPT_STOP_AT_NON_OPTION);
+
+ /*
+ * Allow the object layer to optimize adding multiple objects in
+ * a batch.
+ */
+ begin_odb_transaction();
while (ctx.argc) {
if (parseopt_state != PARSE_OPT_DONE)
parseopt_state = parse_options_step(&ctx, options,
@@ -1163,7 +1189,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
INDEX_FORMAT_LB, INDEX_FORMAT_UB);
if (the_index.version != preferred_index_format)
- active_cache_changed |= SOMETHING_CHANGED;
+ the_index.cache_changed |= SOMETHING_CHANGED;
the_index.version = preferred_index_format;
}
@@ -1190,6 +1216,11 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
strbuf_release(&buf);
}
+ /*
+ * By now we have added all of the new objects
+ */
+ end_odb_transaction();
+
if (split_index > 0) {
if (git_config_get_split_index() == 0)
warning(_("core.splitIndex is set to false; "
@@ -1237,6 +1268,22 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
if (fsmonitor > 0) {
enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
+ enum fsmonitor_reason reason = fsm_settings__get_reason(r);
+
+ /*
+ * The user wants to turn on FSMonitor using the command
+ * line argument. (We don't know (or care) whether that
+ * is the IPC or HOOK version.)
+ *
+ * Use one of the __get routines to force load the FSMonitor
+ * config settings into the repo-settings. That will detect
+ * whether the file system is compatible so that we can stop
+ * here with a nice error message.
+ */
+ if (reason > FSMONITOR_REASON_OK)
+ die("%s",
+ fsm_settings__get_incompatible_msg(r, reason));
+
if (fsm_mode == FSMONITOR_MODE_DISABLED) {
warning(_("core.fsmonitor is unset; "
"set it if you really want to "
@@ -1254,7 +1301,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
report(_("fsmonitor disabled"));
}
- if (active_cache_changed || force_write) {
+ if (the_index.cache_changed || force_write) {
if (newfd < 0) {
if (refresh_args.flags & REFRESH_QUIET)
exit(128);
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index a84e7b47a2..242102273e 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -1,9 +1,12 @@
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
+#include "gettext.h"
+#include "hash.h"
#include "refs.h"
-#include "builtin.h"
+#include "object-name.h"
#include "parse-options.h"
#include "quote.h"
+#include "repository.h"
#include "strvec.h"
static const char * const git_update_ref_usage[] = {
@@ -116,7 +119,7 @@ static int parse_next_oid(const char **next, const char *end,
(*next)++;
*next = parse_arg(*next, &arg);
if (arg.len) {
- if (get_oid(arg.buf, oid))
+ if (repo_get_oid(the_repository, arg.buf, oid))
goto invalid;
} else {
/* Without -z, an empty value means all zeros: */
@@ -134,7 +137,7 @@ static int parse_next_oid(const char **next, const char *end,
*next += arg.len;
if (arg.len) {
- if (get_oid(arg.buf, oid))
+ if (repo_get_oid(the_repository, arg.buf, oid))
goto invalid;
} else if (flags & PARSE_SHA1_ALLOW_EMPTY) {
/* With -z, treat an empty value as all zeros: */
@@ -549,7 +552,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
refname = argv[0];
value = argv[1];
oldval = argv[2];
- if (get_oid(value, &oid))
+ if (repo_get_oid(the_repository, value, &oid))
die("%s: not a valid SHA1", value);
}
@@ -560,7 +563,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
* must not already exist:
*/
oidclr(&oldoid);
- else if (get_oid(oldval, &oldoid))
+ else if (repo_get_oid(the_repository, oldval, &oldoid))
die("%s: not a valid old SHA1", oldval);
}
diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c
index 880fffec58..1dc3971ede 100644
--- a/builtin/update-server-info.c
+++ b/builtin/update-server-info.c
@@ -1,10 +1,11 @@
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
+#include "config.h"
+#include "gettext.h"
#include "parse-options.h"
+#include "server-info.h"
static const char * const update_server_info_usage[] = {
- "git update-server-info [--force]",
+ "git update-server-info [-f | --force]",
NULL
};
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index 98d028dae6..1b09e5e1aa 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -1,16 +1,17 @@
/*
* Copyright (c) 2006 Franck Bui-Huu
*/
-#include "cache.h"
#include "builtin.h"
#include "archive.h"
+#include "path.h"
#include "pkt-line.h"
#include "sideband.h"
+#include "repository.h"
#include "run-command.h"
#include "strvec.h"
static const char upload_archive_usage[] =
- "git upload-archive <repo>";
+ "git upload-archive <repository>";
static const char deadchild[] =
"git upload-archive: archiver died with error";
@@ -79,6 +80,8 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
{
struct child_process writer = CHILD_PROCESS_INIT;
+ BUG_ON_NON_EMPTY_PREFIX(prefix);
+
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(upload_archive_usage);
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
index 125af53885..9b021ef026 100644
--- a/builtin/upload-pack.c
+++ b/builtin/upload-pack.c
@@ -1,14 +1,17 @@
-#include "cache.h"
#include "builtin.h"
#include "exec-cmd.h"
+#include "gettext.h"
#include "pkt-line.h"
#include "parse-options.h"
+#include "path.h"
#include "protocol.h"
+#include "replace-object.h"
#include "upload-pack.h"
#include "serve.h"
static const char * const upload_pack_usage[] = {
- N_("git upload-pack [<options>] <dir>"),
+ N_("git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+ " [--advertise-refs] <directory>"),
NULL
};
@@ -33,7 +36,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
};
packet_trace_identity("upload-pack");
- read_replace_refs = 0;
+ disable_replace_refs();
argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
diff --git a/builtin/var.c b/builtin/var.c
index 491db27429..74161bdf1c 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -4,84 +4,216 @@
* Copyright (C) Eric Biederman, 2005
*/
#include "builtin.h"
+#include "attr.h"
#include "config.h"
+#include "editor.h"
+#include "ident.h"
+#include "pager.h"
#include "refs.h"
+#include "path.h"
+#include "strbuf.h"
static const char var_usage[] = "git var (-l | <variable>)";
-static const char *editor(int flag)
+static char *committer(int ident_flag)
{
- const char *pgm = git_editor();
+ return xstrdup_or_null(git_committer_info(ident_flag));
+}
- if (!pgm && flag & IDENT_STRICT)
- die("Terminal is dumb, but EDITOR unset");
+static char *author(int ident_flag)
+{
+ return xstrdup_or_null(git_author_info(ident_flag));
+}
- return pgm;
+static char *editor(int ident_flag UNUSED)
+{
+ return xstrdup_or_null(git_editor());
}
-static const char *pager(int flag)
+static char *sequence_editor(int ident_flag UNUSED)
+{
+ return xstrdup_or_null(git_sequence_editor());
+}
+
+static char *pager(int ident_flag UNUSED)
{
const char *pgm = git_pager(1);
if (!pgm)
pgm = "cat";
- return pgm;
+ return xstrdup(pgm);
+}
+
+static char *default_branch(int ident_flag UNUSED)
+{
+ return xstrdup_or_null(git_default_branch_name(1));
+}
+
+static char *shell_path(int ident_flag UNUSED)
+{
+ return xstrdup(SHELL_PATH);
+}
+
+static char *git_attr_val_system(int ident_flag UNUSED)
+{
+ if (git_attr_system_is_enabled()) {
+ char *file = xstrdup(git_attr_system_file());
+ normalize_path_copy(file, file);
+ return file;
+ }
+ return NULL;
}
-static const char *default_branch(int flag)
+static char *git_attr_val_global(int ident_flag UNUSED)
{
- return git_default_branch_name(1);
+ char *file = xstrdup(git_attr_global_file());
+ if (file) {
+ normalize_path_copy(file, file);
+ return file;
+ }
+ return NULL;
+}
+
+static char *git_config_val_system(int ident_flag UNUSED)
+{
+ if (git_config_system()) {
+ char *file = git_system_config();
+ normalize_path_copy(file, file);
+ return file;
+ }
+ return NULL;
+}
+
+static char *git_config_val_global(int ident_flag UNUSED)
+{
+ struct strbuf buf = STRBUF_INIT;
+ char *user, *xdg;
+ size_t unused;
+
+ git_global_config(&user, &xdg);
+ if (xdg && *xdg) {
+ normalize_path_copy(xdg, xdg);
+ strbuf_addf(&buf, "%s\n", xdg);
+ }
+ if (user && *user) {
+ normalize_path_copy(user, user);
+ strbuf_addf(&buf, "%s\n", user);
+ }
+ free(xdg);
+ free(user);
+ strbuf_trim_trailing_newline(&buf);
+ if (buf.len == 0) {
+ strbuf_release(&buf);
+ return NULL;
+ }
+ return strbuf_detach(&buf, &unused);
}
struct git_var {
const char *name;
- const char *(*read)(int);
+ char *(*read)(int);
+ int multivalued;
};
static struct git_var git_vars[] = {
- { "GIT_COMMITTER_IDENT", git_committer_info },
- { "GIT_AUTHOR_IDENT", git_author_info },
- { "GIT_EDITOR", editor },
- { "GIT_PAGER", pager },
- { "GIT_DEFAULT_BRANCH", default_branch },
- { "", NULL },
+ {
+ .name = "GIT_COMMITTER_IDENT",
+ .read = committer,
+ },
+ {
+ .name = "GIT_AUTHOR_IDENT",
+ .read = author,
+ },
+ {
+ .name = "GIT_EDITOR",
+ .read = editor,
+ },
+ {
+ .name = "GIT_SEQUENCE_EDITOR",
+ .read = sequence_editor,
+ },
+ {
+ .name = "GIT_PAGER",
+ .read = pager,
+ },
+ {
+ .name = "GIT_DEFAULT_BRANCH",
+ .read = default_branch,
+ },
+ {
+ .name = "GIT_SHELL_PATH",
+ .read = shell_path,
+ },
+ {
+ .name = "GIT_ATTR_SYSTEM",
+ .read = git_attr_val_system,
+ },
+ {
+ .name = "GIT_ATTR_GLOBAL",
+ .read = git_attr_val_global,
+ },
+ {
+ .name = "GIT_CONFIG_SYSTEM",
+ .read = git_config_val_system,
+ },
+ {
+ .name = "GIT_CONFIG_GLOBAL",
+ .read = git_config_val_global,
+ .multivalued = 1,
+ },
+ {
+ .name = "",
+ .read = NULL,
+ },
};
static void list_vars(void)
{
struct git_var *ptr;
- const char *val;
+ char *val;
for (ptr = git_vars; ptr->read; ptr++)
- if ((val = ptr->read(0)))
- printf("%s=%s\n", ptr->name, val);
+ if ((val = ptr->read(0))) {
+ if (ptr->multivalued && *val) {
+ struct string_list list = STRING_LIST_INIT_DUP;
+ int i;
+
+ string_list_split(&list, val, '\n', -1);
+ for (i = 0; i < list.nr; i++)
+ printf("%s=%s\n", ptr->name, list.items[i].string);
+ string_list_clear(&list, 0);
+ } else {
+ printf("%s=%s\n", ptr->name, val);
+ }
+ free(val);
+ }
}
-static const char *read_var(const char *var)
+static const struct git_var *get_git_var(const char *var)
{
struct git_var *ptr;
- const char *val;
- val = NULL;
for (ptr = git_vars; ptr->read; ptr++) {
if (strcmp(var, ptr->name) == 0) {
- val = ptr->read(IDENT_STRICT);
- break;
+ return ptr;
}
}
- return val;
+ return NULL;
}
-static int show_config(const char *var, const char *value, void *cb)
+static int show_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (value)
printf("%s=%s\n", var, value);
else
printf("%s\n", var);
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
}
-int cmd_var(int argc, const char **argv, const char *prefix)
+int cmd_var(int argc, const char **argv, const char *prefix UNUSED)
{
- const char *val = NULL;
+ const struct git_var *git_var;
+ char *val;
+
if (argc != 2)
usage(var_usage);
@@ -91,11 +223,17 @@ int cmd_var(int argc, const char **argv, const char *prefix)
return 0;
}
git_config(git_default_config, NULL);
- val = read_var(argv[1]);
- if (!val)
+
+ git_var = get_git_var(argv[1]);
+ if (!git_var)
usage(var_usage);
+ val = git_var->read(IDENT_STRICT);
+ if (!val)
+ return 1;
+
printf("%s\n", val);
+ free(val);
return 0;
}
diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c
index 40c69a0bed..9680b58701 100644
--- a/builtin/verify-commit.c
+++ b/builtin/verify-commit.c
@@ -5,10 +5,11 @@
*
* Based on git-verify-tag
*/
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
-#include "object-store.h"
+#include "config.h"
+#include "gettext.h"
+#include "object-name.h"
+#include "object-store-ll.h"
#include "repository.h"
#include "commit.h"
#include "run-command.h"
@@ -16,7 +17,7 @@
#include "gpg-interface.h"
static const char * const verify_commit_usage[] = {
- N_("git verify-commit [-v | --verbose] <commit>..."),
+ N_("git verify-commit [-v | --verbose] [--raw] <commit>..."),
NULL
};
@@ -39,7 +40,7 @@ static int verify_commit(const char *name, unsigned flags)
struct object_id oid;
struct object *obj;
- if (get_oid(name, &oid))
+ if (repo_get_oid(the_repository, name, &oid))
return error("commit '%s' not found.", name);
obj = parse_object(the_repository, &oid);
@@ -52,14 +53,6 @@ static int verify_commit(const char *name, unsigned flags)
return run_gpg_verify((struct commit *)obj, flags);
}
-static int git_verify_commit_config(const char *var, const char *value, void *cb)
-{
- int status = git_gpg_config(var, value, cb);
- if (status)
- return status;
- return git_default_config(var, value, cb);
-}
-
int cmd_verify_commit(int argc, const char **argv, const char *prefix)
{
int i = 1, verbose = 0, had_error = 0;
@@ -70,7 +63,7 @@ int cmd_verify_commit(int argc, const char **argv, const char *prefix)
OPT_END()
};
- git_config(git_verify_commit_config, NULL);
+ git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, verify_commit_options,
verify_commit_usage, PARSE_OPT_KEEP_ARGV0);
diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c
index 05c5213594..011dddd2dc 100644
--- a/builtin/verify-pack.c
+++ b/builtin/verify-pack.c
@@ -1,8 +1,9 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
+#include "gettext.h"
#include "run-command.h"
#include "parse-options.h"
+#include "strbuf.h"
#define VERIFY_PACK_VERBOSE 01
#define VERIFY_PACK_STAT_ONLY 02
@@ -56,7 +57,7 @@ static int verify_one_pack(const char *path, unsigned int flags, const char *has
}
static const char * const verify_pack_usage[] = {
- N_("git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."),
+ N_("git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."),
NULL
};
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index f45136a06b..d8753270eb 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -5,28 +5,21 @@
*
* Based on git-verify-tag.sh
*/
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
+#include "config.h"
+#include "gettext.h"
#include "tag.h"
#include "run-command.h"
+#include "object-name.h"
#include "parse-options.h"
#include "gpg-interface.h"
#include "ref-filter.h"
static const char * const verify_tag_usage[] = {
- N_("git verify-tag [-v | --verbose] [--format=<format>] <tag>..."),
+ N_("git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."),
NULL
};
-static int git_verify_tag_config(const char *var, const char *value, void *cb)
-{
- int status = git_gpg_config(var, value, cb);
- if (status)
- return status;
- return git_default_config(var, value, cb);
-}
-
int cmd_verify_tag(int argc, const char **argv, const char *prefix)
{
int i = 1, verbose = 0, had_error = 0;
@@ -39,7 +32,7 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
OPT_END()
};
- git_config(git_verify_tag_config, NULL);
+ git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, verify_tag_options,
verify_tag_usage, PARSE_OPT_KEEP_ARGV0);
@@ -60,7 +53,7 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
struct object_id oid;
const char *name = argv[i++];
- if (get_oid(name, &oid)) {
+ if (repo_get_oid(the_repository, name, &oid)) {
had_error = !!error("tag '%s' not found.", name);
continue;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 8b32cd1651..2ce39b593c 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -1,12 +1,23 @@
-#include "cache.h"
+#include "builtin.h"
+#include "abspath.h"
+#include "advice.h"
#include "checkout.h"
#include "config.h"
-#include "builtin.h"
+#include "copy.h"
#include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-file.h"
+#include "object-name.h"
#include "parse-options.h"
+#include "path.h"
#include "strvec.h"
#include "branch.h"
+#include "read-cache-ll.h"
#include "refs.h"
+#include "remote.h"
+#include "repository.h"
#include "run-command.h"
#include "hook.h"
#include "sigchain.h"
@@ -15,15 +26,91 @@
#include "worktree.h"
#include "quote.h"
-static const char * const worktree_usage[] = {
- N_("git worktree add [<options>] <path> [<commit-ish>]"),
- N_("git worktree list [<options>]"),
- N_("git worktree lock [<options>] <path>"),
- N_("git worktree move <worktree> <new-path>"),
- N_("git worktree prune [<options>]"),
- N_("git worktree remove [<options>] <worktree>"),
- N_("git worktree repair [<path>...]"),
- N_("git worktree unlock <path>"),
+#define BUILTIN_WORKTREE_ADD_USAGE \
+ N_("git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n" \
+ " [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]")
+
+#define BUILTIN_WORKTREE_LIST_USAGE \
+ N_("git worktree list [-v | --porcelain [-z]]")
+#define BUILTIN_WORKTREE_LOCK_USAGE \
+ N_("git worktree lock [--reason <string>] <worktree>")
+#define BUILTIN_WORKTREE_MOVE_USAGE \
+ N_("git worktree move <worktree> <new-path>")
+#define BUILTIN_WORKTREE_PRUNE_USAGE \
+ N_("git worktree prune [-n] [-v] [--expire <expire>]")
+#define BUILTIN_WORKTREE_REMOVE_USAGE \
+ N_("git worktree remove [-f] <worktree>")
+#define BUILTIN_WORKTREE_REPAIR_USAGE \
+ N_("git worktree repair [<path>...]")
+#define BUILTIN_WORKTREE_UNLOCK_USAGE \
+ N_("git worktree unlock <worktree>")
+
+#define WORKTREE_ADD_DWIM_ORPHAN_INFER_TEXT \
+ _("No possible source branch, inferring '--orphan'")
+
+#define WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT \
+ _("If you meant to create a worktree containing a new orphan branch\n" \
+ "(branch with no commits) for this repository, you can do so\n" \
+ "using the --orphan flag:\n" \
+ "\n" \
+ " git worktree add --orphan -b %s %s\n")
+
+#define WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT \
+ _("If you meant to create a worktree containing a new orphan branch\n" \
+ "(branch with no commits) for this repository, you can do so\n" \
+ "using the --orphan flag:\n" \
+ "\n" \
+ " git worktree add --orphan %s\n")
+
+static const char * const git_worktree_usage[] = {
+ BUILTIN_WORKTREE_ADD_USAGE,
+ BUILTIN_WORKTREE_LIST_USAGE,
+ BUILTIN_WORKTREE_LOCK_USAGE,
+ BUILTIN_WORKTREE_MOVE_USAGE,
+ BUILTIN_WORKTREE_PRUNE_USAGE,
+ BUILTIN_WORKTREE_REMOVE_USAGE,
+ BUILTIN_WORKTREE_REPAIR_USAGE,
+ BUILTIN_WORKTREE_UNLOCK_USAGE,
+ NULL
+};
+
+static const char * const git_worktree_add_usage[] = {
+ BUILTIN_WORKTREE_ADD_USAGE,
+ NULL,
+};
+
+static const char * const git_worktree_list_usage[] = {
+ BUILTIN_WORKTREE_LIST_USAGE,
+ NULL
+};
+
+static const char * const git_worktree_lock_usage[] = {
+ BUILTIN_WORKTREE_LOCK_USAGE,
+ NULL
+};
+
+static const char * const git_worktree_move_usage[] = {
+ BUILTIN_WORKTREE_MOVE_USAGE,
+ NULL
+};
+
+static const char * const git_worktree_prune_usage[] = {
+ BUILTIN_WORKTREE_PRUNE_USAGE,
+ NULL
+};
+
+static const char * const git_worktree_remove_usage[] = {
+ BUILTIN_WORKTREE_REMOVE_USAGE,
+ NULL
+};
+
+static const char * const git_worktree_repair_usage[] = {
+ BUILTIN_WORKTREE_REPAIR_USAGE,
+ NULL
+};
+
+static const char * const git_worktree_unlock_usage[] = {
+ BUILTIN_WORKTREE_UNLOCK_USAGE,
NULL
};
@@ -32,6 +119,7 @@ struct add_opts {
int detach;
int quiet;
int checkout;
+ int orphan;
const char *keep_locked;
};
@@ -40,14 +128,15 @@ static int verbose;
static int guess_remote;
static timestamp_t expire;
-static int git_worktree_config(const char *var, const char *value, void *cb)
+static int git_worktree_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (!strcmp(var, "worktree.guessremote")) {
guess_remote = git_config_bool(var, value);
return 0;
}
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
}
static int delete_git_dir(const char *id)
@@ -115,7 +204,7 @@ static void prune_worktrees(void)
{
struct strbuf reason = STRBUF_INIT;
struct strbuf main_path = STRBUF_INIT;
- struct string_list kept = STRING_LIST_INIT_NODUP;
+ struct string_list kept = STRING_LIST_INIT_DUP;
DIR *dir = opendir(git_path("worktrees"));
struct dirent *d;
if (!dir)
@@ -126,14 +215,14 @@ static void prune_worktrees(void)
if (should_prune_worktree(d->d_name, &reason, &path, expire))
prune_worktree(d->d_name, reason.buf);
else if (path)
- string_list_append(&kept, path)->util = xstrdup(d->d_name);
+ string_list_append_nodup(&kept, path)->util = xstrdup(d->d_name);
}
closedir(dir);
strbuf_add_absolute_path(&main_path, get_git_common_dir());
/* massage main worktree absolute path to match 'gitdir' content */
strbuf_strip_suffix(&main_path, "/.");
- string_list_append(&kept, strbuf_detach(&main_path, NULL));
+ string_list_append_nodup(&kept, strbuf_detach(&main_path, NULL));
prune_dups(&kept);
string_list_clear(&kept, 1);
@@ -153,9 +242,10 @@ static int prune(int ac, const char **av, const char *prefix)
};
expire = TIME_MAX;
- ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ ac = parse_options(ac, av, prefix, options, git_worktree_prune_usage,
+ 0);
if (ac)
- usage_with_options(worktree_usage, options);
+ usage_with_options(git_worktree_prune_usage, options);
prune_worktrees();
return 0;
}
@@ -260,7 +350,6 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)
if (file_exists(from_file)) {
struct config_set cs = { { 0 } };
- const char *core_worktree;
int bare;
if (safe_create_leading_directories(to_file) ||
@@ -279,7 +368,7 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)
to_file, "core.bare", NULL, "true", 0))
error(_("failed to unset '%s' in '%s'"),
"core.bare", to_file);
- if (!git_configset_get_value(&cs, "core.worktree", &core_worktree) &&
+ if (!git_configset_get(&cs, "core.worktree") &&
git_config_set_in_file_gently(to_file,
"core.worktree", NULL))
error(_("failed to unset '%s' in '%s'"),
@@ -301,7 +390,23 @@ static int checkout_worktree(const struct add_opts *opts,
strvec_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL);
if (opts->quiet)
strvec_push(&cp.args, "--quiet");
- strvec_pushv(&cp.env_array, child_env->v);
+ strvec_pushv(&cp.env, child_env->v);
+ return run_command(&cp);
+}
+
+static int make_worktree_orphan(const char * ref, const struct add_opts *opts,
+ struct strvec *child_env)
+{
+ struct strbuf symref = STRBUF_INIT;
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ validate_new_branchname(ref, &symref, 0);
+ strvec_pushl(&cp.args, "symbolic-ref", "HEAD", symref.buf, NULL);
+ if (opts->quiet)
+ strvec_push(&cp.args, "--quiet");
+ strvec_pushv(&cp.env, child_env->v);
+ strbuf_release(&symref);
+ cp.git_cmd = 1;
return run_command(&cp);
}
@@ -334,7 +439,7 @@ static int add_worktree(const char *path, const char *refname,
die_if_checked_out(symref.buf, 0);
}
commit = lookup_commit_reference_by_name(refname);
- if (!commit)
+ if (!commit && !opts->orphan)
die(_("invalid reference: %s"), refname);
name = worktree_basename(path, &len);
@@ -416,28 +521,32 @@ static int add_worktree(const char *path, const char *refname,
* values from the current worktree into the new one, that way the
* new worktree behaves the same as this one.
*/
- if (repository_format_worktree_config)
+ if (the_repository->repository_format_worktree_config)
copy_filtered_worktree_config(sb_repo.buf);
strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
cp.git_cmd = 1;
- if (!is_branch)
+ if (!is_branch && commit) {
strvec_pushl(&cp.args, "update-ref", "HEAD",
oid_to_hex(&commit->object.oid), NULL);
- else {
+ } else {
strvec_pushl(&cp.args, "symbolic-ref", "HEAD",
symref.buf, NULL);
if (opts->quiet)
strvec_push(&cp.args, "--quiet");
}
- strvec_pushv(&cp.env_array, child_env.v);
+ strvec_pushv(&cp.env, child_env.v);
ret = run_command(&cp);
if (ret)
goto done;
+ if (opts->orphan &&
+ (ret = make_worktree_orphan(refname, opts, &child_env)))
+ goto done;
+
if (opts->checkout &&
(ret = checkout_worktree(opts, &child_env)))
goto done;
@@ -457,7 +566,7 @@ done:
* Hook failure does not warrant worktree deletion, so run hook after
* is_junk is cleared, but do return appropriate code when hook fails.
*/
- if (!ret && opts->checkout) {
+ if (!ret && opts->checkout && !opts->orphan) {
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
strvec_pushl(&opt.env, "GIT_DIR", "GIT_WORK_TREE", NULL);
@@ -493,7 +602,7 @@ static void print_preparing_worktree_line(int detach,
else
fprintf_ln(stderr, _("Preparing worktree (resetting branch '%s'; was at %s)"),
new_branch,
- find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+ repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
} else if (new_branch) {
fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
} else {
@@ -505,14 +614,131 @@ static void print_preparing_worktree_line(int detach,
else {
struct commit *commit = lookup_commit_reference_by_name(branch);
if (!commit)
- die(_("invalid reference: %s"), branch);
+ BUG(_("unreachable: invalid reference: %s"), branch);
fprintf_ln(stderr, _("Preparing worktree (detached HEAD %s)"),
- find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+ repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
}
strbuf_release(&s);
}
}
+/**
+ * Callback to short circuit iteration over refs on the first reference
+ * corresponding to a valid oid.
+ *
+ * Returns 0 on failure and non-zero on success.
+ */
+static int first_valid_ref(const char *refname,
+ const struct object_id *oid,
+ int flags,
+ void *cb_data)
+{
+ return 1;
+}
+
+/**
+ * Verifies HEAD and determines whether there exist any valid local references.
+ *
+ * - Checks whether HEAD points to a valid reference.
+ *
+ * - Checks whether any valid local branches exist.
+ *
+ * - Emits a warning if there exist any valid branches but HEAD does not point
+ * to a valid reference.
+ *
+ * Returns 1 if any of the previous checks are true, otherwise returns 0.
+ */
+static int can_use_local_refs(const struct add_opts *opts)
+{
+ if (head_ref(first_valid_ref, NULL)) {
+ return 1;
+ } else if (for_each_branch_ref(first_valid_ref, NULL)) {
+ if (!opts->quiet) {
+ struct strbuf path = STRBUF_INIT;
+ struct strbuf contents = STRBUF_INIT;
+
+ strbuf_add_real_path(&path, get_worktree_git_dir(NULL));
+ strbuf_addstr(&path, "/HEAD");
+ strbuf_read_file(&contents, path.buf, 64);
+ strbuf_stripspace(&contents, 0);
+ strbuf_strip_suffix(&contents, "\n");
+
+ warning(_("HEAD points to an invalid (or orphaned) reference.\n"
+ "HEAD path: '%s'\n"
+ "HEAD contents: '%s'"),
+ path.buf, contents.buf);
+ strbuf_release(&path);
+ strbuf_release(&contents);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Reports whether the necessary flags were set and whether the repository has
+ * remote references to attempt DWIM tracking of upstream branches.
+ *
+ * 1. Checks that `--guess-remote` was used or `worktree.guessRemote = true`.
+ *
+ * 2. Checks whether any valid remote branches exist.
+ *
+ * 3. Checks that there exists at least one remote and emits a warning/error
+ * if both checks 1. and 2. are false (can be bypassed with `--force`).
+ *
+ * Returns 1 if checks 1. and 2. are true, otherwise 0.
+ */
+static int can_use_remote_refs(const struct add_opts *opts)
+{
+ if (!guess_remote) {
+ return 0;
+ } else if (for_each_remote_ref(first_valid_ref, NULL)) {
+ return 1;
+ } else if (!opts->force && remote_get(NULL)) {
+ die(_("No local or remote refs exist despite at least one remote\n"
+ "present, stopping; use 'add -f' to overide or fetch a remote first"));
+ }
+ return 0;
+}
+
+/**
+ * Determines whether `--orphan` should be inferred in the evaluation of
+ * `worktree add path/` or `worktree add -b branch path/` and emits an error
+ * if the supplied arguments would produce an illegal combination when the
+ * `--orphan` flag is included.
+ *
+ * `opts` and `opt_track` contain the other options & flags supplied to the
+ * command.
+ *
+ * remote determines whether to check `can_use_remote_refs()` or not. This
+ * is primarily to differentiate between the basic `add` DWIM and `add -b`.
+ *
+ * Returns 1 when inferring `--orphan`, 0 otherwise, and emits an error when
+ * `--orphan` is inferred but doing so produces an illegal combination of
+ * options and flags. Additionally produces an error when remote refs are
+ * checked and the repo is in a state that looks like the user added a remote
+ * but forgot to fetch (and did not override the warning with -f).
+ */
+static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)
+{
+ if (can_use_local_refs(opts)) {
+ return 0;
+ } else if (remote && can_use_remote_refs(opts)) {
+ return 0;
+ } else if (!opts->quiet) {
+ fprintf_ln(stderr, WORKTREE_ADD_DWIM_ORPHAN_INFER_TEXT);
+ }
+
+ if (opt_track) {
+ die(_("'%s' and '%s' cannot be used together"), "--orphan",
+ "--track");
+ } else if (!opts->checkout) {
+ die(_("'%s' and '%s' cannot be used together"), "--orphan",
+ "--no-checkout");
+ }
+ return 1;
+}
+
static const char *dwim_branch(const char *path, const char **new_branch)
{
int n;
@@ -549,6 +775,7 @@ static int add(int ac, const char **av, const char *prefix)
const char *opt_track = NULL;
const char *lock_reason = NULL;
int keep_locked = 0;
+ int used_new_branch_options;
struct option options[] = {
OPT__FORCE(&opts.force,
N_("checkout <branch> even if already checked out in other worktree"),
@@ -557,6 +784,7 @@ static int add(int ac, const char **av, const char *prefix)
N_("create a new branch")),
OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
N_("create or reset a branch")),
+ OPT_BOOL(0, "orphan", &opts.orphan, N_("create unborn/orphaned branch")),
OPT_BOOL('d', "detach", &opts.detach, N_("detach HEAD at named commit")),
OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
OPT_BOOL(0, "lock", &keep_locked, N_("keep the new working tree locked")),
@@ -570,12 +798,24 @@ static int add(int ac, const char **av, const char *prefix)
N_("try to match the new branch name with a remote-tracking branch")),
OPT_END()
};
+ int ret;
memset(&opts, 0, sizeof(opts));
opts.checkout = 1;
- ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ ac = parse_options(ac, av, prefix, options, git_worktree_add_usage, 0);
if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach");
+ if (opts.detach && opts.orphan)
+ die(_("options '%s', and '%s' cannot be used together"),
+ "--orphan", "--detach");
+ if (opts.orphan && opt_track)
+ die(_("'%s' and '%s' cannot be used together"), "--orphan", "--track");
+ if (opts.orphan && !opts.checkout)
+ die(_("'%s' and '%s' cannot be used together"), "--orphan",
+ "--no-checkout");
+ if (opts.orphan && ac == 2)
+ die(_("'%s' and '%s' cannot be used together"), "--orphan",
+ _("<commit-ish>"));
if (lock_reason && !keep_locked)
die(_("the option '%s' requires '%s'"), "--reason", "--lock");
if (lock_reason)
@@ -584,10 +824,11 @@ static int add(int ac, const char **av, const char *prefix)
opts.keep_locked = _("added with --lock");
if (ac < 1 || ac > 2)
- usage_with_options(worktree_usage, options);
+ usage_with_options(git_worktree_add_usage, options);
path = prefix_filename(prefix, av[0]);
branch = ac < 2 ? "HEAD" : av[1];
+ used_new_branch_options = new_branch || new_branch_force;
if (!strcmp(branch, "-"))
branch = "@{-1}";
@@ -604,13 +845,28 @@ static int add(int ac, const char **av, const char *prefix)
strbuf_release(&symref);
}
- if (ac < 2 && !new_branch && !opts.detach) {
+ if (opts.orphan && !new_branch) {
+ int n;
+ const char *s = worktree_basename(path, &n);
+ new_branch = xstrndup(s, n);
+ } else if (opts.orphan) {
+ // No-op
+ } else if (opts.detach) {
+ // Check HEAD
+ if (!strcmp(branch, "HEAD"))
+ can_use_local_refs(&opts);
+ } else if (ac < 2 && new_branch) {
+ // DWIM: Infer --orphan when repo has no refs.
+ opts.orphan = dwim_orphan(&opts, !!opt_track, 0);
+ } else if (ac < 2) {
+ // DWIM: Guess branch name from path.
const char *s = dwim_branch(path, &new_branch);
if (s)
branch = s;
- }
- if (ac == 2 && !new_branch && !opts.detach) {
+ // DWIM: Infer --orphan when repo has no refs.
+ opts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);
+ } else if (ac == 2) {
struct object_id oid;
struct commit *commit;
const char *remote;
@@ -623,11 +879,31 @@ static int add(int ac, const char **av, const char *prefix)
branch = remote;
}
}
+
+ if (!strcmp(branch, "HEAD"))
+ can_use_local_refs(&opts);
+
+ }
+
+ if (!opts.orphan && !lookup_commit_reference_by_name(branch)) {
+ int attempt_hint = !opts.quiet && (ac < 2);
+ if (attempt_hint && used_new_branch_options) {
+ advise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN,
+ WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT,
+ new_branch, path);
+ } else if (attempt_hint) {
+ advise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN,
+ WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT, path);
+ }
+ die(_("invalid reference: %s"), branch);
}
+
if (!opts.quiet)
print_preparing_worktree_line(opts.detach, branch, new_branch, !!new_branch_force);
- if (new_branch) {
+ if (opts.orphan) {
+ branch = new_branch;
+ } else if (new_branch) {
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
strvec_push(&cp.args, "branch");
@@ -646,9 +922,9 @@ static int add(int ac, const char **av, const char *prefix)
die(_("--[no-]track can only be used if a new branch is created"));
}
- UNLEAK(path);
- UNLEAK(opts);
- return add_worktree(path, branch, &opts);
+ ret = add_worktree(path, branch, &opts);
+ free(path);
+ return ret;
}
static void show_worktree_porcelain(struct worktree *wt, int line_terminator)
@@ -696,7 +972,7 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
strbuf_addstr(&sb, "(bare)");
else {
strbuf_addf(&sb, "%-*s ", abbrev_len,
- find_unique_abbrev(&wt->head_oid, DEFAULT_ABBREV));
+ repo_find_unique_abbrev(the_repository, &wt->head_oid, DEFAULT_ABBREV));
if (wt->is_detached)
strbuf_addstr(&sb, "(detached HEAD)");
else if (wt->head_ref) {
@@ -733,7 +1009,7 @@ static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen)
if (path_len > *maxlen)
*maxlen = path_len;
- sha1_len = strlen(find_unique_abbrev(&wt[i]->head_oid, *abbrev));
+ sha1_len = strlen(repo_find_unique_abbrev(the_repository, &wt[i]->head_oid, *abbrev));
if (sha1_len > *abbrev)
*abbrev = sha1_len;
}
@@ -772,9 +1048,9 @@ static int list(int ac, const char **av, const char *prefix)
};
expire = TIME_MAX;
- ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ ac = parse_options(ac, av, prefix, options, git_worktree_list_usage, 0);
if (ac)
- usage_with_options(worktree_usage, options);
+ usage_with_options(git_worktree_list_usage, options);
else if (verbose && porcelain)
die(_("options '%s' and '%s' cannot be used together"), "--verbose", "--porcelain");
else if (!line_terminator && !porcelain)
@@ -811,9 +1087,9 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
};
struct worktree **worktrees, *wt;
- ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ ac = parse_options(ac, av, prefix, options, git_worktree_lock_usage, 0);
if (ac != 1)
- usage_with_options(worktree_usage, options);
+ usage_with_options(git_worktree_lock_usage, options);
worktrees = get_worktrees();
wt = find_worktree(worktrees, prefix, av[0]);
@@ -844,9 +1120,9 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
struct worktree **worktrees, *wt;
int ret;
- ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ ac = parse_options(ac, av, prefix, options, git_worktree_unlock_usage, 0);
if (ac != 1)
- usage_with_options(worktree_usage, options);
+ usage_with_options(git_worktree_unlock_usage, options);
worktrees = get_worktrees();
wt = find_worktree(worktrees, prefix, av[0]);
@@ -863,7 +1139,7 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
static void validate_no_submodules(const struct worktree *wt)
{
- struct index_state istate = { NULL };
+ struct index_state istate = INDEX_STATE_INIT(the_repository);
struct strbuf path = STRBUF_INIT;
int i, found_submodules = 0;
@@ -914,9 +1190,10 @@ static int move_worktree(int ac, const char **av, const char *prefix)
const char *reason = NULL;
char *path;
- ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ ac = parse_options(ac, av, prefix, options, git_worktree_move_usage,
+ 0);
if (ac != 2)
- usage_with_options(worktree_usage, options);
+ usage_with_options(git_worktree_move_usage, options);
path = prefix_filename(prefix, av[1]);
strbuf_addstr(&dst, path);
@@ -989,9 +1266,9 @@ static void check_clean_worktree(struct worktree *wt,
validate_no_submodules(wt);
child_process_init(&cp);
- strvec_pushf(&cp.env_array, "%s=%s/.git",
+ strvec_pushf(&cp.env, "%s=%s/.git",
GIT_DIR_ENVIRONMENT, wt->path);
- strvec_pushf(&cp.env_array, "%s=%s",
+ strvec_pushf(&cp.env, "%s=%s",
GIT_WORK_TREE_ENVIRONMENT, wt->path);
strvec_pushl(&cp.args, "status",
"--porcelain", "--ignore-submodules=none",
@@ -1042,9 +1319,9 @@ static int remove_worktree(int ac, const char **av, const char *prefix)
const char *reason = NULL;
int ret = 0;
- ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ ac = parse_options(ac, av, prefix, options, git_worktree_remove_usage, 0);
if (ac != 1)
- usage_with_options(worktree_usage, options);
+ usage_with_options(git_worktree_remove_usage, options);
worktrees = get_worktrees();
wt = find_worktree(worktrees, prefix, av[0]);
@@ -1102,7 +1379,7 @@ static int repair(int ac, const char **av, const char *prefix)
};
int rc = 0;
- ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ ac = parse_options(ac, av, prefix, options, git_worktree_repair_usage, 0);
p = ac > 0 ? av : self;
for (; *p; p++)
repair_worktree_at_path(*p, report_repair, &rc);
@@ -1112,31 +1389,28 @@ static int repair(int ac, const char **av, const char *prefix)
int cmd_worktree(int ac, const char **av, const char *prefix)
{
+ parse_opt_subcommand_fn *fn = NULL;
struct option options[] = {
+ OPT_SUBCOMMAND("add", &fn, add),
+ OPT_SUBCOMMAND("prune", &fn, prune),
+ OPT_SUBCOMMAND("list", &fn, list),
+ OPT_SUBCOMMAND("lock", &fn, lock_worktree),
+ OPT_SUBCOMMAND("unlock", &fn, unlock_worktree),
+ OPT_SUBCOMMAND("move", &fn, move_worktree),
+ OPT_SUBCOMMAND("remove", &fn, remove_worktree),
+ OPT_SUBCOMMAND("repair", &fn, repair),
OPT_END()
};
git_config(git_worktree_config, NULL);
- if (ac < 2)
- usage_with_options(worktree_usage, options);
if (!prefix)
prefix = "";
- if (!strcmp(av[1], "add"))
- return add(ac - 1, av + 1, prefix);
- if (!strcmp(av[1], "prune"))
- return prune(ac - 1, av + 1, prefix);
- if (!strcmp(av[1], "list"))
- return list(ac - 1, av + 1, prefix);
- if (!strcmp(av[1], "lock"))
- return lock_worktree(ac - 1, av + 1, prefix);
- if (!strcmp(av[1], "unlock"))
- return unlock_worktree(ac - 1, av + 1, prefix);
- if (!strcmp(av[1], "move"))
- return move_worktree(ac - 1, av + 1, prefix);
- if (!strcmp(av[1], "remove"))
- return remove_worktree(ac - 1, av + 1, prefix);
- if (!strcmp(av[1], "repair"))
- return repair(ac - 1, av + 1, prefix);
- usage_with_options(worktree_usage, options);
+
+ ac = parse_options(ac, av, prefix, options, git_worktree_usage, 0);
+
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
+ return fn(ac, av, prefix);
}
diff --git a/builtin/write-tree.c b/builtin/write-tree.c
index 45d61707e7..66e83d0ecb 100644
--- a/builtin/write-tree.c
+++ b/builtin/write-tree.c
@@ -3,13 +3,16 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
-#include "cache.h"
#include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
#include "tree.h"
#include "cache-tree.h"
#include "parse-options.h"
+#include "repository.h"
static const char * const write_tree_usage[] = {
N_("git write-tree [--missing-ok] [--prefix=<prefix>/]"),
@@ -38,7 +41,11 @@ int cmd_write_tree(int argc, const char **argv, const char *cmd_prefix)
argc = parse_options(argc, argv, cmd_prefix, write_tree_options,
write_tree_usage, 0);
- ret = write_cache_as_tree(&oid, flags, tree_prefix);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
+ ret = write_index_as_tree(&oid, &the_index, get_index_file(), flags,
+ tree_prefix);
switch (ret) {
case 0:
printf("%s\n", oid_to_hex(&oid));