summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/am.c4
-rw-r--r--builtin/blame.c62
-rw-r--r--builtin/branch.c19
-rw-r--r--builtin/cat-file.c3
-rw-r--r--builtin/checkout.c904
-rw-r--r--builtin/clean.c3
-rw-r--r--builtin/clone.c79
-rw-r--r--builtin/commit-graph.c68
-rw-r--r--builtin/commit.c41
-rw-r--r--builtin/describe.c4
-rw-r--r--builtin/env--helper.c95
-rw-r--r--builtin/fast-export.c4
-rw-r--r--builtin/fetch.c81
-rw-r--r--builtin/fsck.c8
-rw-r--r--builtin/gc.c12
-rw-r--r--builtin/grep.c6
-rw-r--r--builtin/interpret-trailers.c3
-rw-r--r--builtin/log.c26
-rw-r--r--builtin/ls-files.c2
-rw-r--r--builtin/merge-tree.c22
-rw-r--r--builtin/merge.c55
-rw-r--r--builtin/multi-pack-index.c14
-rw-r--r--builtin/name-rev.c3
-rw-r--r--builtin/pack-objects.c26
-rw-r--r--builtin/prune.c2
-rw-r--r--builtin/pull.c7
-rw-r--r--builtin/rebase.c65
-rw-r--r--builtin/receive-pack.c3
-rw-r--r--builtin/repack.c34
-rw-r--r--builtin/reset.c6
-rw-r--r--builtin/rev-list.c19
-rw-r--r--builtin/revert.c7
-rw-r--r--builtin/rm.c4
-rw-r--r--builtin/stash.c41
-rw-r--r--builtin/submodule--helper.c1
-rw-r--r--builtin/tag.c22
-rw-r--r--builtin/unpack-objects.c2
-rw-r--r--builtin/update-index.c2
-rw-r--r--builtin/verify-commit.c1
-rw-r--r--builtin/verify-tag.c1
40 files changed, 1187 insertions, 574 deletions
diff --git a/builtin/am.c b/builtin/am.c
index 78389d08b6..1aea657a7f 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1801,7 +1801,7 @@ next:
*/
if (!state->rebasing) {
am_destroy(state);
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
}
}
@@ -1956,7 +1956,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
if (merge_tree(remote_tree))
return -1;
- remove_branch_state(the_repository);
+ remove_branch_state(the_repository, 0);
return 0;
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 21cde57e71..b6534d4dea 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -53,14 +53,17 @@ static int no_whole_file_rename;
static int show_progress;
static char repeated_meta_color[COLOR_MAXLEN];
static int coloring_mode;
+static struct string_list ignore_revs_file_list = STRING_LIST_INIT_NODUP;
+static int mark_unblamable_lines;
+static int mark_ignored_lines;
static struct date_mode blame_date_mode = { DATE_ISO8601 };
static size_t blame_date_width;
static struct string_list mailmap = STRING_LIST_INIT_NODUP;
-#ifndef DEBUG
-#define DEBUG 0
+#ifndef DEBUG_BLAME
+#define DEBUG_BLAME 0
#endif
static unsigned blame_move_score;
@@ -480,6 +483,14 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
}
}
+ if (mark_unblamable_lines && ent->unblamable) {
+ length--;
+ putchar('*');
+ }
+ if (mark_ignored_lines && ent->ignored) {
+ length--;
+ putchar('?');
+ }
printf("%.*s", length, hex);
if (opt & OUTPUT_ANNOTATE_COMPAT) {
const char *name;
@@ -696,6 +707,24 @@ static int git_blame_config(const char *var, const char *value, void *cb)
parse_date_format(value, &blame_date_mode);
return 0;
}
+ if (!strcmp(var, "blame.ignorerevsfile")) {
+ const char *str;
+ int ret;
+
+ ret = git_config_pathname(&str, var, value);
+ if (ret)
+ return ret;
+ string_list_insert(&ignore_revs_file_list, str);
+ return 0;
+ }
+ if (!strcmp(var, "blame.markunblamablelines")) {
+ mark_unblamable_lines = git_config_bool(var, value);
+ return 0;
+ }
+ if (!strcmp(var, "blame.markignoredlines")) {
+ mark_ignored_lines = git_config_bool(var, value);
+ return 0;
+ }
if (!strcmp(var, "color.blame.repeatedlines")) {
if (color_parse_mem(value, strlen(value), repeated_meta_color))
warning(_("invalid color '%s' in color.blame.repeatedLines"),
@@ -775,6 +804,27 @@ static int is_a_rev(const char *name)
return OBJ_NONE < oid_object_info(the_repository, &oid, NULL);
}
+static void build_ignorelist(struct blame_scoreboard *sb,
+ struct string_list *ignore_revs_file_list,
+ struct string_list *ignore_rev_list)
+{
+ struct string_list_item *i;
+ struct object_id oid;
+
+ oidset_init(&sb->ignore_list, 0);
+ for_each_string_list_item(i, ignore_revs_file_list) {
+ if (!strcmp(i->string, ""))
+ oidset_clear(&sb->ignore_list);
+ else
+ oidset_parse_file(&sb->ignore_list, i->string);
+ }
+ for_each_string_list_item(i, ignore_rev_list) {
+ if (get_oid_committish(i->string, &oid))
+ die(_("cannot find revision %s to ignore"), i->string);
+ oidset_insert(&sb->ignore_list, &oid);
+ }
+}
+
int cmd_blame(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
@@ -786,6 +836,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
struct progress_info pi = { NULL, 0 };
struct string_list range_list = STRING_LIST_INIT_NODUP;
+ struct string_list ignore_rev_list = STRING_LIST_INIT_NODUP;
int output_option = 0, opt = 0;
int show_stats = 0;
const char *revs_file = NULL;
@@ -807,6 +858,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR),
OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL),
OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE),
+ OPT_STRING_LIST(0, "ignore-rev", &ignore_rev_list, N_("rev"), N_("Ignore <rev> when blaming")),
+ OPT_STRING_LIST(0, "ignore-revs-file", &ignore_revs_file_list, N_("file"), N_("Ignore revisions from <file>")),
OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE),
OPT_BIT(0, "color-by-age", &output_option, N_("color lines by age"), OUTPUT_SHOW_AGE_WITH_COLOR),
@@ -1012,6 +1065,9 @@ parse_done:
sb.contents_from = contents_from;
sb.reverse = reverse;
sb.repo = the_repository;
+ build_ignorelist(&sb, &ignore_revs_file_list, &ignore_rev_list);
+ string_list_clear(&ignore_revs_file_list, 0);
+ string_list_clear(&ignore_rev_list, 0);
setup_scoreboard(&sb, path, &o);
lno = sb.num_lines;
@@ -1062,7 +1118,7 @@ parse_done:
if (blame_copy_score)
sb.copy_score = blame_copy_score;
- sb.debug = DEBUG;
+ sb.debug = DEBUG_BLAME;
sb.on_sanity_fail = &sanity_check_on_fail;
sb.show_root = show_root;
diff --git a/builtin/branch.c b/builtin/branch.c
index d4359b33ac..2ef214632f 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -47,6 +47,7 @@ static char branch_colors[][COLOR_MAXLEN] = {
GIT_COLOR_NORMAL, /* LOCAL */
GIT_COLOR_GREEN, /* CURRENT */
GIT_COLOR_BLUE, /* UPSTREAM */
+ GIT_COLOR_CYAN, /* WORKTREE */
};
enum color_branch {
BRANCH_COLOR_RESET = 0,
@@ -54,7 +55,8 @@ enum color_branch {
BRANCH_COLOR_REMOTE = 2,
BRANCH_COLOR_LOCAL = 3,
BRANCH_COLOR_CURRENT = 4,
- BRANCH_COLOR_UPSTREAM = 5
+ BRANCH_COLOR_UPSTREAM = 5,
+ BRANCH_COLOR_WORKTREE = 6
};
static const char *color_branch_slots[] = {
@@ -64,6 +66,7 @@ static const char *color_branch_slots[] = {
[BRANCH_COLOR_LOCAL] = "local",
[BRANCH_COLOR_CURRENT] = "current",
[BRANCH_COLOR_UPSTREAM] = "upstream",
+ [BRANCH_COLOR_WORKTREE] = "worktree",
};
static struct string_list output = STRING_LIST_INIT_DUP;
@@ -342,9 +345,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
struct strbuf local = STRBUF_INIT;
struct strbuf remote = STRBUF_INIT;
- strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)",
- branch_get_color(BRANCH_COLOR_CURRENT),
- branch_get_color(BRANCH_COLOR_LOCAL));
+ strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)",
+ branch_get_color(BRANCH_COLOR_CURRENT),
+ branch_get_color(BRANCH_COLOR_WORKTREE),
+ branch_get_color(BRANCH_COLOR_LOCAL));
strbuf_addf(&remote, " %s",
branch_get_color(BRANCH_COLOR_REMOTE));
@@ -363,9 +367,13 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
strbuf_addf(&local, " %s ", obname.buf);
if (filter->verbose > 1)
+ {
+ strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)",
+ branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET));
strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
"%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)",
branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));
+ }
else
strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
@@ -830,7 +838,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
strbuf_release(&buf);
} else if (argc > 0 && argc <= 2) {
if (filter.kind != FILTER_REFS_BRANCHES)
- die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
+ die(_("The -a, and -r, options to 'git branch' do not take a branch name.\n"
+ "Did you mean to use: -a|-r --list <pattern>?"));
if (track == BRANCH_TRACK_OVERRIDE)
die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 0f092382e1..995d47c85a 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -172,7 +172,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
* fall-back to the usual case.
*/
}
- buf = read_object_with_reference(&oid, exp_type, &size, NULL);
+ buf = read_object_with_reference(the_repository,
+ &oid, exp_type, &size, NULL);
break;
default:
diff --git a/builtin/checkout.c b/builtin/checkout.c
index ffa776c6e1..91f8509f85 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,32 +1,31 @@
#define USE_THE_INDEX_COMPATIBILITY_MACROS
#include "builtin.h"
-#include "config.h"
+#include "advice.h"
+#include "blob.h"
+#include "branch.h"
+#include "cache-tree.h"
#include "checkout.h"
+#include "commit.h"
+#include "config.h"
+#include "diff.h"
+#include "dir.h"
+#include "ll-merge.h"
#include "lockfile.h"
+#include "merge-recursive.h"
+#include "object-store.h"
#include "parse-options.h"
#include "refs.h"
-#include "object-store.h"
-#include "commit.h"
+#include "remote.h"
+#include "resolve-undo.h"
+#include "revision.h"
+#include "run-command.h"
+#include "submodule.h"
+#include "submodule-config.h"
#include "tree.h"
#include "tree-walk.h"
-#include "cache-tree.h"
#include "unpack-trees.h"
-#include "dir.h"
-#include "run-command.h"
-#include "merge-recursive.h"
-#include "branch.h"
-#include "diff.h"
-#include "revision.h"
-#include "remote.h"
-#include "blob.h"
+#include "wt-status.h"
#include "xdiff-interface.h"
-#include "ll-merge.h"
-#include "resolve-undo.h"
-#include "submodule-config.h"
-#include "submodule.h"
-#include "advice.h"
-
-static int checkout_optimize_new_branch;
static const char * const checkout_usage[] = {
N_("git checkout [<options>] <branch>"),
@@ -34,12 +33,23 @@ static const char * const checkout_usage[] = {
NULL,
};
+static const char * const switch_branch_usage[] = {
+ N_("git switch [<options>] [<branch>]"),
+ NULL,
+};
+
+static const char * const restore_usage[] = {
+ N_("git restore [<options>] [--source=<branch>] <file>..."),
+ NULL,
+};
+
struct checkout_opts {
int patch_mode;
int quiet;
int merge;
int force;
int force_detach;
+ int implicit_detach;
int writeout_stage;
int overwrite_ignore;
int ignore_skipworktree;
@@ -47,10 +57,19 @@ struct checkout_opts {
int show_progress;
int count_checkout_paths;
int overlay_mode;
- /*
- * If new checkout options are added, skip_merge_working_tree
- * should be updated accordingly.
- */
+ int dwim_new_local_branch;
+ int discard_changes;
+ int accept_ref;
+ int accept_pathspec;
+ int switch_branch_doing_nothing_is_ok;
+ int only_merge_on_switching_branches;
+ int can_switch_when_in_progress;
+ int orphan_from_empty_tree;
+ int empty_pathspec_ok;
+ int checkout_index;
+ int checkout_worktree;
+ const char *ignore_unmerged_opt;
+ int ignore_unmerged;
const char *new_branch;
const char *new_branch_force;
@@ -58,10 +77,12 @@ struct checkout_opts {
int new_branch_log;
enum branch_track track;
struct diff_options diff_options;
+ char *conflict_style;
int branch_exists;
const char *prefix;
struct pathspec pathspec;
+ const char *from_treeish;
struct tree *source_tree;
};
@@ -313,17 +334,74 @@ static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,
}
}
+static int checkout_worktree(const struct checkout_opts *opts)
+{
+ struct checkout state = CHECKOUT_INIT;
+ int nr_checkouts = 0, nr_unmerged = 0;
+ int errs = 0;
+ int pos;
+
+ state.force = 1;
+ state.refresh_cache = 1;
+ state.istate = &the_index;
+
+ enable_delayed_checkout(&state);
+ for (pos = 0; pos < active_nr; pos++) {
+ struct cache_entry *ce = active_cache[pos];
+ if (ce->ce_flags & CE_MATCHED) {
+ if (!ce_stage(ce)) {
+ errs |= checkout_entry(ce, &state,
+ NULL, &nr_checkouts);
+ continue;
+ }
+ if (opts->writeout_stage)
+ errs |= checkout_stage(opts->writeout_stage,
+ ce, pos,
+ &state,
+ &nr_checkouts, opts->overlay_mode);
+ else if (opts->merge)
+ errs |= checkout_merged(pos, &state,
+ &nr_unmerged);
+ pos = skip_same_name(ce, pos) - 1;
+ }
+ }
+ remove_marked_cache_entries(&the_index, 1);
+ remove_scheduled_dirs();
+ errs |= finish_delayed_checkout(&state, &nr_checkouts);
+
+ if (opts->count_checkout_paths) {
+ if (nr_unmerged)
+ fprintf_ln(stderr, Q_("Recreated %d merge conflict",
+ "Recreated %d merge conflicts",
+ nr_unmerged),
+ nr_unmerged);
+ if (opts->source_tree)
+ fprintf_ln(stderr, Q_("Updated %d path from %s",
+ "Updated %d paths from %s",
+ nr_checkouts),
+ nr_checkouts,
+ find_unique_abbrev(&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",
+ nr_checkouts),
+ nr_checkouts);
+ }
+
+ return errs;
+}
+
static int checkout_paths(const struct checkout_opts *opts,
const char *revision)
{
int pos;
- struct checkout state = CHECKOUT_INIT;
static char *ps_matched;
struct object_id rev;
struct commit *head;
int errs = 0;
struct lock_file lock_file = LOCK_INIT;
- int nr_checkouts = 0, nr_unmerged = 0;
+ int checkout_index;
trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
@@ -333,8 +411,9 @@ static int checkout_paths(const struct checkout_opts *opts,
if (opts->new_branch_log)
die(_("'%s' cannot be used with updating paths"), "-l");
- if (opts->force && opts->patch_mode)
- die(_("'%s' cannot be used with updating paths"), "-f");
+ if (opts->ignore_unmerged && opts->patch_mode)
+ die(_("'%s' cannot be used with updating paths"),
+ opts->ignore_unmerged_opt);
if (opts->force_detach)
die(_("'%s' cannot be used with updating paths"), "--detach");
@@ -342,16 +421,46 @@ static int checkout_paths(const struct checkout_opts *opts,
if (opts->merge && opts->patch_mode)
die(_("'%s' cannot be used with %s"), "--merge", "--patch");
- if (opts->force && opts->merge)
- die(_("'%s' cannot be used with %s"), "-f", "-m");
+ if (opts->ignore_unmerged && opts->merge)
+ die(_("'%s' cannot be used with %s"),
+ opts->ignore_unmerged_opt, "-m");
if (opts->new_branch)
die(_("Cannot update paths and switch to branch '%s' at the same time."),
opts->new_branch);
- if (opts->patch_mode)
- return run_add_interactive(revision, "--patch=checkout",
- &opts->pathspec);
+ if (!opts->checkout_worktree && !opts->checkout_index)
+ die(_("neither '%s' or '%s' is specified"),
+ "--staged", "--worktree");
+
+ if (!opts->checkout_worktree && !opts->from_treeish)
+ 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");
+
+ if (opts->patch_mode) {
+ const char *patch_mode;
+
+ if (opts->checkout_index && opts->checkout_worktree)
+ patch_mode = "--patch=checkout";
+ else if (opts->checkout_index && !opts->checkout_worktree)
+ patch_mode = "--patch=reset";
+ else if (!opts->checkout_index && opts->checkout_worktree)
+ patch_mode = "--patch=worktree";
+ else
+ BUG("either flag must have been set, worktree=%d, index=%d",
+ opts->checkout_worktree, opts->checkout_index);
+ return run_add_interactive(revision, patch_mode, &opts->pathspec);
+ }
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
if (read_cache_preload(&opts->pathspec) < 0)
@@ -392,8 +501,9 @@ static int checkout_paths(const struct checkout_opts *opts,
if (ce->ce_flags & CE_MATCHED) {
if (!ce_stage(ce))
continue;
- if (opts->force) {
- warning(_("path '%s' is unmerged"), ce->name);
+ if (opts->ignore_unmerged) {
+ if (!opts->quiet)
+ warning(_("path '%s' is unmerged"), ce->name);
} else if (opts->writeout_stage) {
errs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode);
} else if (opts->merge) {
@@ -409,57 +519,31 @@ static int checkout_paths(const struct checkout_opts *opts,
return 1;
/* Now we are committed to check them out */
- state.force = 1;
- state.refresh_cache = 1;
- state.istate = &the_index;
+ if (opts->checkout_worktree)
+ errs |= checkout_worktree(opts);
- enable_delayed_checkout(&state);
- for (pos = 0; pos < active_nr; pos++) {
- struct cache_entry *ce = active_cache[pos];
- if (ce->ce_flags & CE_MATCHED) {
- if (!ce_stage(ce)) {
- errs |= checkout_entry(ce, &state,
- NULL, &nr_checkouts);
- continue;
- }
- if (opts->writeout_stage)
- errs |= checkout_stage(opts->writeout_stage,
- ce, pos,
- &state,
- &nr_checkouts, opts->overlay_mode);
- else if (opts->merge)
- errs |= checkout_merged(pos, &state,
- &nr_unmerged);
- pos = skip_same_name(ce, pos) - 1;
- }
- }
- remove_marked_cache_entries(&the_index, 1);
- remove_scheduled_dirs();
- errs |= finish_delayed_checkout(&state, &nr_checkouts);
+ /*
+ * Allow updating the index when checking out from the index.
+ * This is to save new stat info.
+ */
+ if (opts->checkout_worktree && !opts->checkout_index && !opts->source_tree)
+ checkout_index = 1;
+ else
+ checkout_index = opts->checkout_index;
- if (opts->count_checkout_paths) {
- if (nr_unmerged)
- fprintf_ln(stderr, Q_("Recreated %d merge conflict",
- "Recreated %d merge conflicts",
- nr_unmerged),
- nr_unmerged);
- if (opts->source_tree)
- fprintf_ln(stderr, Q_("Updated %d path from %s",
- "Updated %d paths from %s",
- nr_checkouts),
- nr_checkouts,
- find_unique_abbrev(&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",
- nr_checkouts),
- nr_checkouts);
+ if (checkout_index) {
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ die(_("unable to write new index file"));
+ } else {
+ /*
+ * NEEDSWORK: if --worktree is not specified, we
+ * should save stat info of checked out files in the
+ * index to avoid the next (potentially costly)
+ * refresh. But it's a bit tricker to do...
+ */
+ rollback_lock_file(&lock_file);
}
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
- die(_("unable to write new index file"));
-
read_ref_full("HEAD", 0, &rev, NULL);
head = lookup_commit_reference_gently(the_repository, &rev, 1);
@@ -553,112 +637,6 @@ static void setup_branch_path(struct branch_info *branch)
branch->path = strbuf_detach(&buf, NULL);
}
-/*
- * Skip merging the trees, updating the index and working directory if and
- * only if we are creating a new branch via "git checkout -b <new_branch>."
- */
-static int skip_merge_working_tree(const struct checkout_opts *opts,
- const struct branch_info *old_branch_info,
- const struct branch_info *new_branch_info)
-{
- /*
- * Do the merge if sparse checkout is on and the user has not opted in
- * to the optimized behavior
- */
- if (core_apply_sparse_checkout && !checkout_optimize_new_branch)
- return 0;
-
- /*
- * We must do the merge if we are actually moving to a new commit.
- */
- if (!old_branch_info->commit || !new_branch_info->commit ||
- !oideq(&old_branch_info->commit->object.oid,
- &new_branch_info->commit->object.oid))
- return 0;
-
- /*
- * opts->patch_mode cannot be used with switching branches so is
- * not tested here
- */
-
- /*
- * opts->quiet only impacts output so doesn't require a merge
- */
-
- /*
- * Honor the explicit request for a three-way merge or to throw away
- * local changes
- */
- if (opts->merge || opts->force)
- return 0;
-
- /*
- * --detach is documented as "updating the index and the files in the
- * working tree" but this optimization skips those steps so fall through
- * to the regular code path.
- */
- if (opts->force_detach)
- return 0;
-
- /*
- * opts->writeout_stage cannot be used with switching branches so is
- * not tested here
- */
-
- /*
- * Honor the explicit ignore requests
- */
- if (!opts->overwrite_ignore || opts->ignore_skipworktree ||
- opts->ignore_other_worktrees)
- return 0;
-
- /*
- * opts->show_progress only impacts output so doesn't require a merge
- */
-
- /*
- * opts->overlay_mode cannot be used with switching branches so is
- * not tested here
- */
-
- /*
- * If we aren't creating a new branch any changes or updates will
- * happen in the existing branch. Since that could only be updating
- * the index and working directory, we don't want to skip those steps
- * or we've defeated any purpose in running the command.
- */
- if (!opts->new_branch)
- return 0;
-
- /*
- * new_branch_force is defined to "create/reset and checkout a branch"
- * so needs to go through the merge to do the reset
- */
- if (opts->new_branch_force)
- return 0;
-
- /*
- * A new orphaned branch requrires the index and the working tree to be
- * adjusted to <start_point>
- */
- if (opts->new_orphan_branch)
- return 0;
-
- /*
- * Remaining variables are not checkout options but used to track state
- */
-
- /*
- * Do the merge if this is the initial checkout. We cannot use
- * is_cache_unborn() here because the index hasn't been loaded yet
- * so cache_nr and timestamp.sec are always zero.
- */
- if (!file_exists(get_index_file()))
- return 0;
-
- return 1;
-}
-
static int merge_working_tree(const struct checkout_opts *opts,
struct branch_info *old_branch_info,
struct branch_info *new_branch_info,
@@ -666,15 +644,21 @@ static int merge_working_tree(const struct checkout_opts *opts,
{
int ret;
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)
return error(_("index file corrupt"));
resolve_undo_clear();
- if (opts->force) {
- ret = reset_tree(get_commit_tree(new_branch_info->commit),
- opts, 1, writeout_error);
+ 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);
+ if (opts->discard_changes) {
+ ret = reset_tree(new_tree, opts, 1, writeout_error);
if (ret)
return ret;
} else {
@@ -712,7 +696,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
&old_branch_info->commit->object.oid :
the_hash_algo->empty_tree);
init_tree_desc(&trees[0], tree->buffer, tree->size);
- tree = parse_tree_indirect(&new_branch_info->commit->object.oid);
+ parse_tree(new_tree);
+ tree = new_tree;
init_tree_desc(&trees[1], tree->buffer, tree->size);
ret = unpack_trees(2, trees, &topts);
@@ -777,7 +762,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
o.verbosity = 0;
work = write_tree_from_memory(&o);
- ret = reset_tree(get_commit_tree(new_branch_info->commit),
+ ret = reset_tree(new_tree,
opts, 1,
writeout_error);
if (ret)
@@ -786,13 +771,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
o.branch1 = new_branch_info->name;
o.branch2 = "local";
ret = merge_trees(&o,
- get_commit_tree(new_branch_info->commit),
+ new_tree,
work,
old_tree,
&result);
if (ret < 0)
exit(128);
- ret = reset_tree(get_commit_tree(new_branch_info->commit),
+ ret = reset_tree(new_tree,
opts, 0,
writeout_error);
strbuf_release(&o.obuf);
@@ -810,7 +795,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
- if (!opts->force && !opts->quiet)
+ if (!opts->discard_changes && !opts->quiet && new_branch_info->commit)
show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
return 0;
@@ -915,7 +900,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
delete_reflog(old_branch_info->path);
}
}
- remove_branch_state(the_repository);
+ remove_branch_state(the_repository, !opts->quiet);
strbuf_release(&msg);
if (!opts->quiet &&
(new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD"))))
@@ -1011,7 +996,10 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
add_pending_object(&revs, object, oid_to_hex(&object->oid));
for_each_ref(add_pending_uninteresting_ref, &revs);
- add_pending_oid(&revs, "HEAD", &new_commit->object.oid, UNINTERESTING);
+ if (new_commit)
+ add_pending_oid(&revs, "HEAD",
+ &new_commit->object.oid,
+ UNINTERESTING);
if (prepare_revision_walk(&revs))
die(_("internal error in revision walk"));
@@ -1032,6 +1020,7 @@ static int switch_branches(const struct checkout_opts *opts,
void *path_to_free;
struct object_id rev;
int flag, writeout_error = 0;
+ int do_merge = 1;
trace2_cmd_mode("branch");
@@ -1045,22 +1034,26 @@ static int switch_branches(const struct checkout_opts *opts,
if (old_branch_info.path)
skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name);
+ if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
+ if (new_branch_info->name)
+ BUG("'switch --orphan' should never accept a commit as starting point");
+ new_branch_info->commit = NULL;
+ new_branch_info->name = "(empty)";
+ do_merge = 1;
+ }
+
if (!new_branch_info->name) {
new_branch_info->name = "HEAD";
new_branch_info->commit = old_branch_info.commit;
if (!new_branch_info->commit)
die(_("You are on a branch yet to be born"));
parse_commit_or_die(new_branch_info->commit);
+
+ if (opts->only_merge_on_switching_branches)
+ do_merge = 0;
}
- /* optimize the "checkout -b <new_branch> path */
- if (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {
- if (!checkout_optimize_new_branch && !opts->quiet) {
- if (read_cache_preload(NULL) < 0)
- return error(_("index file corrupt"));
- show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
- }
- } else {
+ if (do_merge) {
ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
if (ret) {
free(path_to_free);
@@ -1080,11 +1073,6 @@ static int switch_branches(const struct checkout_opts *opts,
static int git_checkout_config(const char *var, const char *value, void *cb)
{
- if (!strcmp(var, "checkout.optimizenewbranch")) {
- checkout_optimize_new_branch = git_config_bool(var, value);
- return 0;
- }
-
if (!strcmp(var, "diff.ignoresubmodules")) {
struct checkout_opts *opts = cb;
handle_ignore_submodules_arg(&opts->diff_options, value);
@@ -1097,6 +1085,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
return git_xmerge_config(var, value, NULL);
}
+static void setup_new_branch_info_and_source_tree(
+ struct branch_info *new_branch_info,
+ struct checkout_opts *opts,
+ struct object_id *rev,
+ const char *arg)
+{
+ struct tree **source_tree = &opts->source_tree;
+ struct object_id branch_rev;
+
+ new_branch_info->name = arg;
+ setup_branch_path(new_branch_info);
+
+ if (!check_refname_format(new_branch_info->path, 0) &&
+ !read_ref(new_branch_info->path, &branch_rev))
+ oidcpy(rev, &branch_rev);
+ else
+ new_branch_info->path = NULL; /* not an existing branch */
+
+ new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
+ if (!new_branch_info->commit) {
+ /* not a commit */
+ *source_tree = parse_tree_indirect(rev);
+ } else {
+ parse_commit_or_die(new_branch_info->commit);
+ *source_tree = get_commit_tree(new_branch_info->commit);
+ }
+}
+
static int parse_branchname_arg(int argc, const char **argv,
int dwim_new_local_branch_ok,
struct branch_info *new_branch_info,
@@ -1104,10 +1120,8 @@ static int parse_branchname_arg(int argc, const char **argv,
struct object_id *rev,
int *dwim_remotes_matched)
{
- struct tree **source_tree = &opts->source_tree;
const char **new_branch = &opts->new_branch;
int argcount = 0;
- struct object_id branch_rev;
const char *arg;
int dash_dash_pos;
int has_dash_dash = 0;
@@ -1157,10 +1171,16 @@ static int parse_branchname_arg(int argc, const char **argv,
if (!argc)
return 0;
+ if (!opts->accept_pathspec) {
+ if (argc > 1)
+ die(_("only one reference expected"));
+ has_dash_dash = 1; /* helps disambiguate */
+ }
+
arg = argv[0];
dash_dash_pos = -1;
for (i = 0; i < argc; i++) {
- if (!strcmp(argv[i], "--")) {
+ if (opts->accept_pathspec && !strcmp(argv[i], "--")) {
dash_dash_pos = i;
break;
}
@@ -1194,11 +1214,12 @@ static int parse_branchname_arg(int argc, const char **argv,
recover_with_dwim = 0;
/*
- * Accept "git checkout foo" and "git checkout foo --"
- * as candidates for dwim.
+ * Accept "git checkout foo", "git checkout foo --"
+ * and "git switch foo" as candidates for dwim.
*/
if (!(argc == 1 && !has_dash_dash) &&
- !(argc == 2 && has_dash_dash))
+ !(argc == 2 && has_dash_dash) &&
+ opts->accept_pathspec)
recover_with_dwim = 0;
if (recover_with_dwim) {
@@ -1229,26 +1250,11 @@ static int parse_branchname_arg(int argc, const char **argv,
argv++;
argc--;
- new_branch_info->name = arg;
- setup_branch_path(new_branch_info);
-
- if (!check_refname_format(new_branch_info->path, 0) &&
- !read_ref(new_branch_info->path, &branch_rev))
- oidcpy(rev, &branch_rev);
- else
- new_branch_info->path = NULL; /* not an existing branch */
-
- new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
- if (!new_branch_info->commit) {
- /* not a commit */
- *source_tree = parse_tree_indirect(rev);
- } else {
- parse_commit_or_die(new_branch_info->commit);
- *source_tree = get_commit_tree(new_branch_info->commit);
- }
+ setup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);
- if (!*source_tree) /* case (1): want a tree */
+ if (!opts->source_tree) /* case (1): want a tree */
die(_("reference is not a tree: %s"), arg);
+
if (!has_dash_dash) { /* case (3).(d) -> (1) */
/*
* Do not complain the most common case
@@ -1258,7 +1264,7 @@ static int parse_branchname_arg(int argc, const char **argv,
*/
if (argc)
verify_non_filename(opts->prefix, arg);
- } else {
+ } else if (opts->accept_pathspec) {
argcount++;
argv++;
argc--;
@@ -1285,6 +1291,60 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
return status;
}
+static void die_expecting_a_branch(const struct branch_info *branch_info)
+{
+ struct object_id oid;
+ char *to_free;
+
+ if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free) == 1) {
+ const char *ref = to_free;
+
+ if (skip_prefix(ref, "refs/tags/", &ref))
+ die(_("a branch is expected, got tag '%s'"), ref);
+ if (skip_prefix(ref, "refs/remotes/", &ref))
+ die(_("a branch is expected, got remote branch '%s'"), ref);
+ die(_("a branch is expected, got '%s'"), ref);
+ }
+ if (branch_info->commit)
+ die(_("a branch is expected, got commit '%s'"), branch_info->name);
+ /*
+ * This case should never happen because we already die() on
+ * non-commit, but just in case.
+ */
+ die(_("a branch is expected, got '%s'"), branch_info->name);
+}
+
+static void die_if_some_operation_in_progress(void)
+{
+ struct wt_status_state state;
+
+ memset(&state, 0, sizeof(state));
+ wt_status_get_state(the_repository, &state, 0);
+
+ if (state.merge_in_progress)
+ die(_("cannot switch branch while merging\n"
+ "Consider \"git merge --quit\" "
+ "or \"git worktree add\"."));
+ if (state.am_in_progress)
+ die(_("cannot switch branch in the middle of an am session\n"
+ "Consider \"git am --quit\" "
+ "or \"git worktree add\"."));
+ if (state.rebase_interactive_in_progress || state.rebase_in_progress)
+ die(_("cannot switch branch while rebasing\n"
+ "Consider \"git rebase --quit\" "
+ "or \"git worktree add\"."));
+ if (state.cherry_pick_in_progress)
+ die(_("cannot switch branch while cherry-picking\n"
+ "Consider \"git cherry-pick --quit\" "
+ "or \"git worktree add\"."));
+ if (state.revert_in_progress)
+ die(_("cannot switch branch while reverting\n"
+ "Consider \"git revert --quit\" "
+ "or \"git worktree add\"."));
+ if (state.bisect_in_progress)
+ warning(_("you are switching branch while bisecting"));
+}
+
static int checkout_branch(struct checkout_opts *opts,
struct branch_info *new_branch_info)
{
@@ -1295,9 +1355,9 @@ static int checkout_branch(struct checkout_opts *opts,
die(_("'%s' cannot be used with switching branches"),
"--patch");
- if (!opts->overlay_mode)
+ if (opts->overlay_mode != -1)
die(_("'%s' cannot be used with switching branches"),
- "--no-overlay");
+ "--[no]-overlay");
if (opts->writeout_stage)
die(_("'%s' cannot be used with switching branches"),
@@ -1306,6 +1366,9 @@ static int checkout_branch(struct checkout_opts *opts,
if (opts->force && opts->merge)
die(_("'%s' cannot be used with '%s'"), "-f", "-m");
+ if (opts->discard_changes && opts->merge)
+ die(_("'%s' cannot be used with '%s'"), "--discard-changes", "--merge");
+
if (opts->force_detach && opts->new_branch)
die(_("'%s' cannot be used with '%s'"),
"--detach", "-b/-B/--orphan");
@@ -1313,6 +1376,8 @@ static int checkout_branch(struct checkout_opts *opts,
if (opts->new_orphan_branch) {
if (opts->track != BRANCH_TRACK_UNSPECIFIED)
die(_("'%s' cannot be used with '%s'"), "--orphan", "-t");
+ if (opts->orphan_from_empty_tree && new_branch_info->name)
+ die(_("'%s' cannot take <start-point>"), "--orphan");
} else if (opts->force_detach) {
if (opts->track != BRANCH_TRACK_UNSPECIFIED)
die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
@@ -1323,6 +1388,23 @@ static int checkout_branch(struct checkout_opts *opts,
die(_("Cannot switch branch to a non-commit '%s'"),
new_branch_info->name);
+ if (!opts->switch_branch_doing_nothing_is_ok &&
+ !new_branch_info->name &&
+ !opts->new_branch &&
+ !opts->force_detach)
+ die(_("missing branch or commit argument"));
+
+ if (!opts->implicit_detach &&
+ !opts->force_detach &&
+ !opts->new_branch &&
+ !opts->new_branch_force &&
+ new_branch_info->name &&
+ !new_branch_info->path)
+ die_expecting_a_branch(new_branch_info);
+
+ if (!opts->can_switch_when_in_progress)
+ die_if_some_operation_in_progress();
+
if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
!opts->ignore_other_worktrees) {
int flag;
@@ -1344,99 +1426,148 @@ static int checkout_branch(struct checkout_opts *opts,
return switch_branches(opts, new_branch_info);
}
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static struct option *add_common_options(struct checkout_opts *opts,
+ struct option *prevopts)
{
- struct checkout_opts opts;
- struct branch_info new_branch_info;
- char *conflict_style = NULL;
- int dwim_new_local_branch, no_dwim_new_local_branch = 0;
- int dwim_remotes_matched = 0;
struct option options[] = {
- OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
- OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
- N_("create and checkout a new branch")),
- OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
- N_("create/reset and checkout a branch")),
- OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
- OPT_BOOL(0, "detach", &opts.force_detach, N_("detach HEAD at named commit")),
- OPT_SET_INT('t', "track", &opts.track, N_("set upstream info for new branch"),
+ OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+ { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+ "checkout", "control recursive updating of submodules",
+ PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
+ OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
+ OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+ OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
+ N_("conflict style (merge or diff3)")),
+ OPT_END()
+ };
+ struct option *newopts = parse_options_concat(prevopts, options);
+ free(prevopts);
+ return newopts;
+}
+
+static struct option *add_common_switch_branch_options(
+ struct checkout_opts *opts, struct option *prevopts)
+{
+ struct option options[] = {
+ OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+ OPT_SET_INT('t', "track", &opts->track, N_("set upstream info for new branch"),
BRANCH_TRACK_EXPLICIT),
- OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
- OPT_SET_INT_F('2', "ours", &opts.writeout_stage,
+ OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
+ PARSE_OPT_NOCOMPLETE),
+ OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+ OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
+ N_("update ignored files (default)"),
+ PARSE_OPT_NOCOMPLETE),
+ OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
+ N_("do not check if another worktree is holding the given ref")),
+ OPT_END()
+ };
+ struct option *newopts = parse_options_concat(prevopts, options);
+ free(prevopts);
+ return newopts;
+}
+
+static struct option *add_checkout_path_options(struct checkout_opts *opts,
+ struct option *prevopts)
+{
+ struct option options[] = {
+ OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
N_("checkout our version for unmerged files"),
2, PARSE_OPT_NONEG),
- OPT_SET_INT_F('3', "theirs", &opts.writeout_stage,
+ OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
N_("checkout their version for unmerged files"),
3, PARSE_OPT_NONEG),
- OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
- PARSE_OPT_NOCOMPLETE),
- OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
- OPT_BOOL_F(0, "overwrite-ignore", &opts.overwrite_ignore,
- N_("update ignored files (default)"),
- PARSE_OPT_NOCOMPLETE),
- OPT_STRING(0, "conflict", &conflict_style, N_("style"),
- N_("conflict style (merge or diff3)")),
- OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
- OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
+ OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
+ OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
N_("do not limit pathspecs to sparse entries only")),
- OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
- N_("do not second guess 'git checkout <no-such-branch>'")),
- OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
- N_("do not check if another worktree is holding the given ref")),
- { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
- "checkout", "control recursive updating of submodules",
- PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
- OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
- OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
- OPT_END(),
+ OPT_END()
};
+ struct option *newopts = parse_options_concat(prevopts, options);
+ free(prevopts);
+ return newopts;
+}
+
+static int checkout_main(int argc, const char **argv, const char *prefix,
+ struct checkout_opts *opts, struct option *options,
+ const char * const usagestr[])
+{
+ struct branch_info new_branch_info;
+ int dwim_remotes_matched = 0;
+ int parseopt_flags = 0;
- memset(&opts, 0, sizeof(opts));
memset(&new_branch_info, 0, sizeof(new_branch_info));
- opts.overwrite_ignore = 1;
- opts.prefix = prefix;
- opts.show_progress = -1;
- opts.overlay_mode = -1;
+ opts->overwrite_ignore = 1;
+ opts->prefix = prefix;
+ opts->show_progress = -1;
+
+ git_config(git_checkout_config, opts);
- git_config(git_checkout_config, &opts);
+ opts->track = BRANCH_TRACK_UNSPECIFIED;
- opts.track = BRANCH_TRACK_UNSPECIFIED;
+ if (!opts->accept_pathspec && !opts->accept_ref)
+ BUG("make up your mind, you need to take _something_");
+ if (opts->accept_pathspec && opts->accept_ref)
+ parseopt_flags = PARSE_OPT_KEEP_DASHDASH;
- argc = parse_options(argc, argv, prefix, options, checkout_usage,
- PARSE_OPT_KEEP_DASHDASH);
+ argc = parse_options(argc, argv, prefix, options,
+ usagestr, parseopt_flags);
- dwim_new_local_branch = !no_dwim_new_local_branch;
- if (opts.show_progress < 0) {
- if (opts.quiet)
- opts.show_progress = 0;
+ if (opts->show_progress < 0) {
+ if (opts->quiet)
+ opts->show_progress = 0;
else
- opts.show_progress = isatty(2);
+ opts->show_progress = isatty(2);
}
- if (conflict_style) {
- opts.merge = 1; /* implied */
- git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
+ if (opts->conflict_style) {
+ opts->merge = 1; /* implied */
+ git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
+ }
+ if (opts->force) {
+ opts->discard_changes = 1;
+ opts->ignore_unmerged_opt = "--force";
+ opts->ignore_unmerged = 1;
}
- if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
+ if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
die(_("-b, -B and --orphan are mutually exclusive"));
- if (opts.overlay_mode == 1 && opts.patch_mode)
+ if (opts->overlay_mode == 1 && opts->patch_mode)
die(_("-p and --overlay are mutually exclusive"));
+ if (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {
+ if (opts->checkout_index < 0)
+ opts->checkout_index = 0;
+ if (opts->checkout_worktree < 0)
+ opts->checkout_worktree = 0;
+ } else {
+ if (opts->checkout_index < 0)
+ opts->checkout_index = -opts->checkout_index - 1;
+ if (opts->checkout_worktree < 0)
+ opts->checkout_worktree = -opts->checkout_worktree - 1;
+ }
+ if (opts->checkout_index < 0 || opts->checkout_worktree < 0)
+ BUG("these flags should be non-negative by now");
+ /*
+ * convenient shortcut: "git restore --staged" equals
+ * "git restore --staged --source HEAD"
+ */
+ if (!opts->from_treeish && opts->checkout_index && !opts->checkout_worktree)
+ opts->from_treeish = "HEAD";
+
/*
* From here on, new_branch will contain the branch to be checked out,
* and new_branch_force and new_orphan_branch will tell us which one of
* -b/-B/--orphan is being used.
*/
- if (opts.new_branch_force)
- opts.new_branch = opts.new_branch_force;
+ if (opts->new_branch_force)
+ opts->new_branch = opts->new_branch_force;
- if (opts.new_orphan_branch)
- opts.new_branch = opts.new_orphan_branch;
+ if (opts->new_orphan_branch)
+ opts->new_branch = opts->new_orphan_branch;
/* --track without -b/-B/--orphan should DWIM */
- if (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {
+ if (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {
const char *argv0 = argv[0];
if (!argc || !strcmp(argv0, "--"))
die(_("--track needs a branch name"));
@@ -1445,7 +1576,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
argv0 = strchr(argv0, '/');
if (!argv0 || !argv0[1])
die(_("missing branch name; try -b"));
- opts.new_branch = argv0 + 1;
+ opts->new_branch = argv0 + 1;
}
/*
@@ -1461,59 +1592,75 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
* including "last branch" syntax and DWIM-ery for names of
* remote branches, erroring out for invalid or ambiguous cases.
*/
- if (argc) {
+ if (argc && opts->accept_ref) {
struct object_id rev;
int dwim_ok =
- !opts.patch_mode &&
- dwim_new_local_branch &&
- opts.track == BRANCH_TRACK_UNSPECIFIED &&
- !opts.new_branch;
+ !opts->patch_mode &&
+ opts->dwim_new_local_branch &&
+ opts->track == BRANCH_TRACK_UNSPECIFIED &&
+ !opts->new_branch;
int n = parse_branchname_arg(argc, argv, dwim_ok,
- &new_branch_info, &opts, &rev,
+ &new_branch_info, opts, &rev,
&dwim_remotes_matched);
argv += n;
argc -= n;
+ } else if (!opts->accept_ref && opts->from_treeish) {
+ struct object_id rev;
+
+ if (get_oid_mb(opts->from_treeish, &rev))
+ die(_("could not resolve %s"), opts->from_treeish);
+
+ setup_new_branch_info_and_source_tree(&new_branch_info,
+ opts, &rev,
+ opts->from_treeish);
+
+ if (!opts->source_tree)
+ die(_("reference is not a tree: %s"), opts->from_treeish);
}
+ if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&
+ !opts->patch_mode) /* patch mode is special */
+ die(_("you must specify path(s) to restore"));
+
if (argc) {
- parse_pathspec(&opts.pathspec, 0,
- opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
+ parse_pathspec(&opts->pathspec, 0,
+ opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
prefix, argv);
- if (!opts.pathspec.nr)
+ if (!opts->pathspec.nr)
die(_("invalid path specification"));
/*
* Try to give more helpful suggestion.
* new_branch && argc > 1 will be caught later.
*/
- if (opts.new_branch && argc == 1)
+ if (opts->new_branch && argc == 1)
die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
- argv[0], opts.new_branch);
+ argv[0], opts->new_branch);
- if (opts.force_detach)
+ if (opts->force_detach)
die(_("git checkout: --detach does not take a path argument '%s'"),
argv[0]);
- if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
+ if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)
die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
"checking out of the index."));
}
- if (opts.new_branch) {
+ if (opts->new_branch) {
struct strbuf buf = STRBUF_INIT;
- if (opts.new_branch_force)
- opts.branch_exists = validate_branchname(opts.new_branch, &buf);
+ if (opts->new_branch_force)
+ opts->branch_exists = validate_branchname(opts->new_branch, &buf);
else
- opts.branch_exists =
- validate_new_branchname(opts.new_branch, &buf, 0);
+ opts->branch_exists =
+ validate_new_branchname(opts->new_branch, &buf, 0);
strbuf_release(&buf);
}
UNLEAK(opts);
- if (opts.patch_mode || opts.pathspec.nr) {
- int ret = checkout_paths(&opts, new_branch_info.name);
+ if (opts->patch_mode || opts->pathspec.nr) {
+ int ret = checkout_paths(opts, new_branch_info.name);
if (ret && dwim_remotes_matched > 1 &&
advice_checkout_ambiguous_remote_branch_name)
advise(_("'%s' matched more than one remote tracking branch.\n"
@@ -1532,6 +1679,123 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
dwim_remotes_matched);
return ret;
} else {
- return checkout_branch(&opts, &new_branch_info);
+ return checkout_branch(opts, &new_branch_info);
}
}
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+ struct checkout_opts opts;
+ struct option *options;
+ struct option checkout_options[] = {
+ OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+ N_("create and checkout a new branch")),
+ OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+ N_("create/reset and checkout a branch")),
+ OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
+ OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+ N_("second guess 'git checkout <no-such-branch>' (default)")),
+ OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
+ OPT_END()
+ };
+ int ret;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.dwim_new_local_branch = 1;
+ opts.switch_branch_doing_nothing_is_ok = 1;
+ opts.only_merge_on_switching_branches = 0;
+ opts.accept_ref = 1;
+ opts.accept_pathspec = 1;
+ opts.implicit_detach = 1;
+ opts.can_switch_when_in_progress = 1;
+ opts.orphan_from_empty_tree = 0;
+ opts.empty_pathspec_ok = 1;
+ opts.overlay_mode = -1;
+ opts.checkout_index = -2; /* default on */
+ opts.checkout_worktree = -2; /* default on */
+
+ options = parse_options_dup(checkout_options);
+ options = add_common_options(&opts, options);
+ options = add_common_switch_branch_options(&opts, options);
+ options = add_checkout_path_options(&opts, options);
+
+ ret = checkout_main(argc, argv, prefix, &opts,
+ options, checkout_usage);
+ FREE_AND_NULL(options);
+ return ret;
+}
+
+int cmd_switch(int argc, const char **argv, const char *prefix)
+{
+ struct checkout_opts opts;
+ struct option *options = NULL;
+ struct option switch_options[] = {
+ OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
+ N_("create and switch to a new branch")),
+ OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
+ N_("create/reset and switch to a branch")),
+ OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+ N_("second guess 'git switch <no-such-branch>'")),
+ OPT_BOOL(0, "discard-changes", &opts.discard_changes,
+ N_("throw away local modifications")),
+ OPT_END()
+ };
+ int ret;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.dwim_new_local_branch = 1;
+ opts.accept_ref = 1;
+ opts.accept_pathspec = 0;
+ opts.switch_branch_doing_nothing_is_ok = 0;
+ opts.only_merge_on_switching_branches = 1;
+ opts.implicit_detach = 0;
+ opts.can_switch_when_in_progress = 0;
+ opts.orphan_from_empty_tree = 1;
+ opts.overlay_mode = -1;
+
+ options = parse_options_dup(switch_options);
+ options = add_common_options(&opts, options);
+ options = add_common_switch_branch_options(&opts, options);
+
+ ret = checkout_main(argc, argv, prefix, &opts,
+ options, switch_branch_usage);
+ FREE_AND_NULL(options);
+ return ret;
+}
+
+int cmd_restore(int argc, const char **argv, const char *prefix)
+{
+ struct checkout_opts opts;
+ struct option *options;
+ struct option restore_options[] = {
+ OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
+ N_("where the checkout from")),
+ OPT_BOOL('S', "staged", &opts.checkout_index,
+ N_("restore the index")),
+ OPT_BOOL('W', "worktree", &opts.checkout_worktree,
+ N_("restore the working tree (default)")),
+ OPT_BOOL(0, "ignore-unmerged", &opts.ignore_unmerged,
+ N_("ignore unmerged entries")),
+ OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
+ OPT_END()
+ };
+ int ret;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.accept_ref = 0;
+ opts.accept_pathspec = 1;
+ opts.empty_pathspec_ok = 0;
+ opts.overlay_mode = 0;
+ opts.checkout_index = -1; /* default off */
+ opts.checkout_worktree = -2; /* default on */
+ opts.ignore_unmerged_opt = "--ignore-unmerged";
+
+ options = parse_options_dup(restore_options);
+ options = add_common_options(&opts, options);
+ options = add_checkout_path_options(&opts, options);
+
+ ret = checkout_main(argc, argv, prefix, &opts,
+ options, restore_usage);
+ FREE_AND_NULL(options);
+ return ret;
+}
diff --git a/builtin/clean.c b/builtin/clean.c
index aaba4af3c2..d5579da716 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -34,6 +34,7 @@ static const char *msg_would_remove = N_("Would remove %s\n");
static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
static const char *msg_warn_remove_failed = N_("failed to remove %s");
+static const char *msg_warn_lstat_failed = N_("could not lstat %s\n");
enum color_clean {
CLEAN_COLOR_RESET = 0,
@@ -194,7 +195,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
strbuf_setlen(path, len);
strbuf_addstr(path, e->d_name);
if (lstat(path->buf, &st))
- ; /* fall thru */
+ warning_errno(_(msg_warn_lstat_failed), path->buf);
else if (S_ISDIR(st.st_mode)) {
if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
ret = 1;
diff --git a/builtin/clone.c b/builtin/clone.c
index 5b9ebe9947..f665b28ccc 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -23,6 +23,8 @@
#include "transport.h"
#include "strbuf.h"
#include "dir.h"
+#include "dir-iterator.h"
+#include "iterator.h"
#include "sigchain.h"
#include "branch.h"
#include "remote.h"
@@ -394,50 +396,55 @@ static void copy_alternates(struct strbuf *src, const char *src_repo)
fclose(in);
}
+static void mkdir_if_missing(const char *pathname, mode_t mode)
+{
+ struct stat st;
+
+ if (!mkdir(pathname, mode))
+ return;
+
+ if (errno != EEXIST)
+ die_errno(_("failed to create directory '%s'"), pathname);
+ else if (stat(pathname, &st))
+ die_errno(_("failed to stat '%s'"), pathname);
+ else if (!S_ISDIR(st.st_mode))
+ die(_("%s exists and is not a directory"), pathname);
+}
+
static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
- const char *src_repo, int src_baselen)
+ const char *src_repo)
{
- struct dirent *de;
- struct stat buf;
int src_len, dest_len;
- DIR *dir;
-
- dir = opendir(src->buf);
- if (!dir)
- die_errno(_("failed to open '%s'"), src->buf);
-
- if (mkdir(dest->buf, 0777)) {
- if (errno != EEXIST)
- die_errno(_("failed to create directory '%s'"), dest->buf);
- else if (stat(dest->buf, &buf))
- die_errno(_("failed to stat '%s'"), dest->buf);
- else if (!S_ISDIR(buf.st_mode))
- die(_("%s exists and is not a directory"), dest->buf);
- }
+ struct dir_iterator *iter;
+ int iter_status;
+ unsigned int flags;
+
+ mkdir_if_missing(dest->buf, 0777);
+
+ flags = DIR_ITERATOR_PEDANTIC | DIR_ITERATOR_FOLLOW_SYMLINKS;
+ iter = dir_iterator_begin(src->buf, flags);
+
+ if (!iter)
+ die_errno(_("failed to start iterator over '%s'"), src->buf);
strbuf_addch(src, '/');
src_len = src->len;
strbuf_addch(dest, '/');
dest_len = dest->len;
- while ((de = readdir(dir)) != NULL) {
+ while ((iter_status = dir_iterator_advance(iter)) == ITER_OK) {
strbuf_setlen(src, src_len);
- strbuf_addstr(src, de->d_name);
+ strbuf_addstr(src, iter->relative_path);
strbuf_setlen(dest, dest_len);
- strbuf_addstr(dest, de->d_name);
- if (stat(src->buf, &buf)) {
- warning (_("failed to stat %s\n"), src->buf);
- continue;
- }
- if (S_ISDIR(buf.st_mode)) {
- if (de->d_name[0] != '.')
- copy_or_link_directory(src, dest,
- src_repo, src_baselen);
+ strbuf_addstr(dest, iter->relative_path);
+
+ if (S_ISDIR(iter->st.st_mode)) {
+ mkdir_if_missing(dest->buf, 0777);
continue;
}
/* Files that cannot be copied bit-for-bit... */
- if (!strcmp(src->buf + src_baselen, "/info/alternates")) {
+ if (!fspathcmp(iter->relative_path, "info/alternates")) {
copy_alternates(src, src_repo);
continue;
}
@@ -445,7 +452,7 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
if (unlink(dest->buf) && errno != ENOENT)
die_errno(_("failed to unlink '%s'"), dest->buf);
if (!option_no_hardlinks) {
- if (!link(src->buf, dest->buf))
+ if (!link(real_path(src->buf), dest->buf))
continue;
if (option_local > 0)
die_errno(_("failed to create link '%s'"), dest->buf);
@@ -454,7 +461,11 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
if (copy_file_with_time(dest->buf, src->buf, 0666))
die_errno(_("failed to copy file to '%s'"), dest->buf);
}
- closedir(dir);
+
+ if (iter_status != ITER_DONE) {
+ strbuf_setlen(src, src_len);
+ die(_("failed to iterate over '%s'"), src->buf);
+ }
}
static void clone_local(const char *src_repo, const char *dest_repo)
@@ -472,7 +483,7 @@ static void clone_local(const char *src_repo, const char *dest_repo)
get_common_dir(&dest, dest_repo);
strbuf_addstr(&src, "/objects");
strbuf_addstr(&dest, "/objects");
- copy_or_link_directory(&src, &dest, src_repo, src.len);
+ copy_or_link_directory(&src, &dest, src_repo);
strbuf_release(&src);
strbuf_release(&dest);
}
@@ -494,7 +505,7 @@ static enum {
static const char junk_leave_repo_msg[] =
N_("Clone succeeded, but checkout failed.\n"
"You can inspect what was checked out with 'git status'\n"
- "and retry the checkout with 'git checkout -f HEAD'\n");
+ "and retry with 'git restore --source=HEAD :/'\n");
static void remove_junk(void)
{
@@ -1252,7 +1263,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
transport_disconnect(transport);
if (option_dissociate) {
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
dissociate_from_references();
}
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 537fdfd0f0..38027b83d9 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -5,17 +5,18 @@
#include "parse-options.h"
#include "repository.h"
#include "commit-graph.h"
+#include "object-store.h"
static char const * const builtin_commit_graph_usage[] = {
N_("git commit-graph [--object-dir <objdir>]"),
N_("git commit-graph read [--object-dir <objdir>]"),
- N_("git commit-graph verify [--object-dir <objdir>]"),
- N_("git commit-graph write [--object-dir <objdir>] [--append] [--reachable|--stdin-packs|--stdin-commits]"),
+ N_("git commit-graph verify [--object-dir <objdir>] [--shallow]"),
+ N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] <split options>"),
NULL
};
static const char * const builtin_commit_graph_verify_usage[] = {
- N_("git commit-graph verify [--object-dir <objdir>]"),
+ N_("git commit-graph verify [--object-dir <objdir>] [--shallow]"),
NULL
};
@@ -25,7 +26,7 @@ static const char * const builtin_commit_graph_read_usage[] = {
};
static const char * const builtin_commit_graph_write_usage[] = {
- N_("git commit-graph write [--object-dir <objdir>] [--append] [--reachable|--stdin-packs|--stdin-commits]"),
+ N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] <split options>"),
NULL
};
@@ -35,9 +36,10 @@ static struct opts_commit_graph {
int stdin_packs;
int stdin_commits;
int append;
+ int split;
+ int shallow;
} opts;
-
static int graph_verify(int argc, const char **argv)
{
struct commit_graph *graph = NULL;
@@ -45,11 +47,14 @@ static int graph_verify(int argc, const char **argv)
int open_ok;
int fd;
struct stat st;
+ int flags = 0;
static struct option builtin_commit_graph_verify_options[] = {
OPT_STRING(0, "object-dir", &opts.obj_dir,
N_("dir"),
N_("The object directory to store the graph")),
+ OPT_BOOL(0, "shallow", &opts.shallow,
+ N_("if the commit-graph is split, only verify the tip file")),
OPT_END(),
};
@@ -59,21 +64,27 @@ static int graph_verify(int argc, const char **argv)
if (!opts.obj_dir)
opts.obj_dir = get_object_directory();
+ if (opts.shallow)
+ flags |= COMMIT_GRAPH_VERIFY_SHALLOW;
graph_name = get_commit_graph_filename(opts.obj_dir);
open_ok = open_commit_graph(graph_name, &fd, &st);
- if (!open_ok && errno == ENOENT)
- return 0;
- if (!open_ok)
+ if (!open_ok && errno != ENOENT)
die_errno(_("Could not open commit-graph '%s'"), graph_name);
- graph = load_commit_graph_one_fd_st(fd, &st);
+
FREE_AND_NULL(graph_name);
+ if (open_ok)
+ graph = load_commit_graph_one_fd_st(fd, &st);
+ else
+ graph = read_commit_graph_one(the_repository, opts.obj_dir);
+
+ /* Return failure if open_ok predicted success */
if (!graph)
- return 1;
+ return !!open_ok;
UNLEAK(graph);
- return verify_commit_graph(the_repository, graph);
+ return verify_commit_graph(the_repository, graph, flags);
}
static int graph_read(int argc, const char **argv)
@@ -135,12 +146,15 @@ static int graph_read(int argc, const char **argv)
}
extern int read_replace_refs;
+static struct split_commit_graph_opts split_opts;
static int graph_write(int argc, const char **argv)
{
struct string_list *pack_indexes = NULL;
struct string_list *commit_hex = NULL;
struct string_list lines;
+ int result = 0;
+ unsigned int flags = COMMIT_GRAPH_PROGRESS;
static struct option builtin_commit_graph_write_options[] = {
OPT_STRING(0, "object-dir", &opts.obj_dir,
@@ -154,9 +168,21 @@ static int graph_write(int argc, const char **argv)
N_("start walk at commits listed by stdin")),
OPT_BOOL(0, "append", &opts.append,
N_("include all commits already in the commit-graph file")),
+ OPT_BOOL(0, "split", &opts.split,
+ N_("allow writing an incremental commit-graph file")),
+ OPT_INTEGER(0, "max-commits", &split_opts.max_commits,
+ N_("maximum number of commits in a non-base split commit-graph")),
+ OPT_INTEGER(0, "size-multiple", &split_opts.size_multiple,
+ N_("maximum ratio between two levels of a split commit-graph")),
+ OPT_EXPIRY_DATE(0, "expire-time", &split_opts.expire_time,
+ N_("maximum number of commits in a non-base split commit-graph")),
OPT_END(),
};
+ split_opts.size_multiple = 2;
+ split_opts.max_commits = 0;
+ split_opts.expire_time = 0;
+
argc = parse_options(argc, argv, NULL,
builtin_commit_graph_write_options,
builtin_commit_graph_write_usage, 0);
@@ -165,11 +191,16 @@ static int graph_write(int argc, const char **argv)
die(_("use at most one of --reachable, --stdin-commits, or --stdin-packs"));
if (!opts.obj_dir)
opts.obj_dir = get_object_directory();
+ if (opts.append)
+ flags |= COMMIT_GRAPH_APPEND;
+ if (opts.split)
+ flags |= COMMIT_GRAPH_SPLIT;
read_replace_refs = 0;
if (opts.reachable) {
- write_commit_graph_reachable(opts.obj_dir, opts.append, 1);
+ if (write_commit_graph_reachable(opts.obj_dir, flags, &split_opts))
+ return 1;
return 0;
}
@@ -188,14 +219,15 @@ static int graph_write(int argc, const char **argv)
UNLEAK(buf);
}
- write_commit_graph(opts.obj_dir,
- pack_indexes,
- commit_hex,
- opts.append,
- 1);
+ if (write_commit_graph(opts.obj_dir,
+ pack_indexes,
+ commit_hex,
+ flags,
+ &split_opts))
+ result = 1;
UNLEAK(lines);
- return 0;
+ return result;
}
int cmd_commit_graph(int argc, const char **argv, const char *prefix)
diff --git a/builtin/commit.c b/builtin/commit.c
index 1c9e8e2228..ae7aaf6dc6 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -60,15 +60,18 @@ N_("The previous cherry-pick is now empty, possibly due to conflict resolution.\
"\n");
static const char empty_cherry_pick_advice_single[] =
-N_("Otherwise, please use 'git reset'\n");
+N_("Otherwise, please use 'git cherry-pick --skip'\n");
static const char empty_cherry_pick_advice_multi[] =
-N_("If you wish to skip this commit, use:\n"
+N_("and then use:\n"
"\n"
-" git reset\n"
+" git cherry-pick --continue\n"
"\n"
-"Then \"git cherry-pick --continue\" will resume cherry-picking\n"
-"the remaining commits.\n");
+"to resume cherry-picking the remaining commits.\n"
+"If you wish to skip this commit, use:\n"
+"\n"
+" git cherry-pick --skip\n"
+"\n");
static const char *color_status_slots[] = {
[WT_STATUS_HEADER] = "header",
@@ -1078,9 +1081,11 @@ static const char *read_commit_message(const char *name)
static struct status_deferred_config {
enum wt_status_format status_format;
int show_branch;
+ enum ahead_behind_flags ahead_behind;
} status_deferred_config = {
STATUS_FORMAT_UNSPECIFIED,
- -1 /* unspecified */
+ -1, /* unspecified */
+ AHEAD_BEHIND_UNSPECIFIED,
};
static void finalize_deferred_config(struct wt_status *s)
@@ -1107,6 +1112,17 @@ static void finalize_deferred_config(struct wt_status *s)
if (s->show_branch < 0)
s->show_branch = 0;
+ /*
+ * If the user did not give a "--[no]-ahead-behind" command
+ * line argument *AND* we will print in a human-readable format
+ * (short, long etc.) then we inherit from the status.aheadbehind
+ * config setting. In all other cases (and porcelain V[12] formats
+ * in particular), we inherit _FULL for backwards compatibility.
+ */
+ if (use_deferred_config &&
+ s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)
+ s->ahead_behind_flags = status_deferred_config.ahead_behind;
+
if (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)
s->ahead_behind_flags = AHEAD_BEHIND_FULL;
}
@@ -1246,6 +1262,10 @@ static int git_status_config(const char *k, const char *v, void *cb)
status_deferred_config.show_branch = git_config_bool(k, v);
return 0;
}
+ if (!strcmp(k, "status.aheadbehind")) {
+ status_deferred_config.ahead_behind = git_config_bool(k, v);
+ return 0;
+ }
if (!strcmp(k, "status.showstash")) {
s->show_stash = git_config_bool(k, v);
return 0;
@@ -1658,7 +1678,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
die("%s", err.buf);
}
- sequencer_post_commit_cleanup(the_repository);
+ sequencer_post_commit_cleanup(the_repository, 0);
unlink(git_path_merge_head(the_repository));
unlink(git_path_merge_msg(the_repository));
unlink(git_path_merge_mode(the_repository));
@@ -1667,10 +1687,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (commit_index_files())
die(_("repository has been updated, but unable to write\n"
"new_index file. Check that disk is not full and quota is\n"
- "not exceeded, and then \"git reset HEAD\" to recover."));
+ "not exceeded, and then \"git restore --staged :/\" to recover."));
- if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
- write_commit_graph_reachable(get_object_directory(), 0, 0);
+ if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
+ write_commit_graph_reachable(get_object_directory(), 0, NULL))
+ return 1;
repo_rerere(the_repository, 0);
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
diff --git a/builtin/describe.c b/builtin/describe.c
index 1409cedce2..200154297d 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -76,7 +76,7 @@ static int commit_name_neq(const void *unused_cmp_data,
static inline struct commit_name *find_commit_name(const struct object_id *peeled)
{
- return hashmap_get_from_hash(&names, sha1hash(peeled->hash), peeled->hash);
+ return hashmap_get_from_hash(&names, oidhash(peeled), peeled);
}
static int replace_name(struct commit_name *e,
@@ -123,7 +123,7 @@ static void add_to_known_names(const char *path,
if (!e) {
e = xmalloc(sizeof(struct commit_name));
oidcpy(&e->peeled, peeled);
- hashmap_entry_init(e, sha1hash(peeled->hash));
+ hashmap_entry_init(e, oidhash(peeled));
hashmap_add(&names, e);
e->path = NULL;
}
diff --git a/builtin/env--helper.c b/builtin/env--helper.c
new file mode 100644
index 0000000000..23c214fff6
--- /dev/null
+++ b/builtin/env--helper.c
@@ -0,0 +1,95 @@
+#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
+};
+
+static enum {
+ ENV_HELPER_TYPE_BOOL = 1,
+ ENV_HELPER_TYPE_ULONG
+} cmdmode = 0;
+
+static int option_parse_type(const struct option *opt, const char *arg,
+ int 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;
+ 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 c22cef3b2f..f541f55d33 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -275,7 +275,7 @@ static void export_blob(const struct object_id *oid)
if (is_null_oid(oid))
return;
- object = lookup_object(the_repository, oid->hash);
+ object = lookup_object(the_repository, oid);
if (object && object->flags & SHOWN)
return;
@@ -453,7 +453,7 @@ static void show_filemodify(struct diff_queue_struct *q,
&spec->oid));
else {
struct object *object = lookup_object(the_repository,
- spec->oid.hash);
+ &spec->oid);
printf("M %06o :%d ", spec->mode,
get_object_mark(object));
}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 4ba63d5ac6..53ce99d2bb 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -24,6 +24,8 @@
#include "list-objects-filter-options.h"
#include "commit-reach.h"
+#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
+
static const char * const builtin_fetch_usage[] = {
N_("git fetch [<options>] [<repository> [<refspec>...]]"),
N_("git fetch [<options>] <group>"),
@@ -39,6 +41,8 @@ enum {
};
static int fetch_prune_config = -1; /* unspecified */
+static int fetch_show_forced_updates = 1;
+static uint64_t forced_updates_ms = 0;
static int prune = -1; /* unspecified */
#define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
@@ -48,6 +52,7 @@ static int prune_tags = -1; /* unspecified */
static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
static int progress = -1;
+static int enable_auto_gc = 1;
static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
static int max_children = 1;
static enum transport_family family;
@@ -79,6 +84,11 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
return 0;
}
+ if (!strcmp(k, "fetch.showforcedupdates")) {
+ fetch_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;
@@ -169,6 +179,10 @@ static struct option builtin_fetch_options[] = {
OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
N_("report that we have only objects reachable from this object")),
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+ OPT_BOOL(0, "auto-gc", &enable_auto_gc,
+ N_("run 'gc --auto' after fetching")),
+ OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
+ N_("check for forced-updates on all updated branches")),
OPT_END()
};
@@ -239,6 +253,7 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
struct refname_hash_entry {
struct hashmap_entry ent; /* must be the first member */
struct object_id oid;
+ int ignore;
char refname[FLEX_ARRAY];
};
@@ -287,6 +302,11 @@ static int refname_hash_exists(struct hashmap *map, const char *refname)
return !!hashmap_get_from_hash(map, strhash(refname), refname);
}
+static void clear_item(struct refname_hash_entry *item)
+{
+ item->ignore = 1;
+}
+
static void find_non_local_tags(const struct ref *refs,
struct ref **head,
struct ref ***tail)
@@ -319,7 +339,7 @@ static void find_non_local_tags(const struct ref *refs,
!will_fetch(head, ref->old_oid.hash) &&
!has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
!will_fetch(head, item->oid.hash))
- oidclr(&item->oid);
+ clear_item(item);
item = NULL;
continue;
}
@@ -333,7 +353,7 @@ static void find_non_local_tags(const struct ref *refs,
if (item &&
!has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
!will_fetch(head, item->oid.hash))
- oidclr(&item->oid);
+ clear_item(item);
item = NULL;
@@ -354,7 +374,7 @@ static void find_non_local_tags(const struct ref *refs,
if (item &&
!has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
!will_fetch(head, item->oid.hash))
- oidclr(&item->oid);
+ clear_item(item);
/*
* For all the tags in the remote_refs_list,
@@ -362,19 +382,21 @@ static void find_non_local_tags(const struct ref *refs,
*/
for_each_string_list_item(remote_ref_item, &remote_refs_list) {
const char *refname = remote_ref_item->string;
+ struct ref *rm;
item = hashmap_get_from_hash(&remote_refs, strhash(refname), refname);
if (!item)
BUG("unseen remote ref?");
/* Unless we have already decided to ignore this item... */
- if (!is_null_oid(&item->oid)) {
- struct ref *rm = alloc_ref(item->refname);
- rm->peer_ref = alloc_ref(item->refname);
- oidcpy(&rm->old_oid, &item->oid);
- **tail = rm;
- *tail = &rm->next;
- }
+ if (item->ignore)
+ continue;
+
+ rm = alloc_ref(item->refname);
+ rm->peer_ref = alloc_ref(item->refname);
+ oidcpy(&rm->old_oid, &item->oid);
+ **tail = rm;
+ *tail = &rm->next;
}
hashmap_free(&remote_refs, 1);
string_list_clear(&remote_refs_list, 0);
@@ -699,6 +721,7 @@ static int update_local_ref(struct ref *ref,
enum object_type type;
struct branch *current_branch = branch_get(NULL);
const char *pretty_ref = prettify_refname(ref->name);
+ int fast_forward = 0;
type = oid_object_info(the_repository, &ref->new_oid, NULL);
if (type < 0)
@@ -773,9 +796,18 @@ static int update_local_ref(struct ref *ref,
return r;
}
- if (in_merge_bases(current, updated)) {
+ if (fetch_show_forced_updates) {
+ uint64_t t_before = getnanotime();
+ fast_forward = in_merge_bases(current, updated);
+ forced_updates_ms += (getnanotime() - t_before) / 1000000;
+ } else {
+ fast_forward = 1;
+ }
+
+ if (fast_forward) {
struct strbuf quickref = STRBUF_INIT;
int r;
+
strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
strbuf_addstr(&quickref, "..");
strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
@@ -971,6 +1003,17 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
" 'git remote prune %s' to remove any old, conflicting "
"branches"), remote_name);
+ if (advice_fetch_show_forced_updates) {
+ if (!fetch_show_forced_updates) {
+ warning(_("Fetch normally indicates which branches had a forced update, but that check has been disabled."));
+ warning(_("To re-enable, use '--show-forced-updates' flag or run 'git config fetch.showForcedUpdates true'."));
+ } else if (forced_updates_ms > FORCED_UPDATES_DELAY_WARNING_IN_MS) {
+ warning(_("It took %.2f seconds to check forced updates. You can use '--no-show-forced-updates'\n"),
+ forced_updates_ms / 1000.0);
+ warning(_("or run 'git config fetch.showForcedUpdates false' to avoid this check.\n"));
+ }
+ }
+
abort:
strbuf_release(&note);
free(url);
@@ -1424,7 +1467,7 @@ static int fetch_multiple(struct string_list *list)
return errcode;
}
- argv_array_pushl(&argv, "fetch", "--append", NULL);
+ argv_array_pushl(&argv, "fetch", "--append", "--no-auto-gc", NULL);
add_options_to_argv(&argv);
for (i = 0; i < list->nr; i++) {
@@ -1672,13 +1715,15 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
string_list_clear(&list, 0);
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
- argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
- if (verbosity < 0)
- argv_array_push(&argv_gc_auto, "--quiet");
- run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD);
- argv_array_clear(&argv_gc_auto);
+ if (enable_auto_gc) {
+ argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
+ if (verbosity < 0)
+ argv_array_push(&argv_gc_auto, "--quiet");
+ run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD);
+ argv_array_clear(&argv_gc_auto);
+ }
return result;
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index d26fb0a044..18403a94fa 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -238,7 +238,7 @@ static int mark_used(struct object *obj, int type, void *data, struct fsck_optio
static void mark_unreachable_referents(const struct object_id *oid)
{
struct fsck_options options = FSCK_OPTIONS_DEFAULT;
- struct object *obj = lookup_object(the_repository, oid->hash);
+ struct object *obj = lookup_object(the_repository, oid);
if (!obj || !(obj->flags & HAS_OBJ))
return; /* not part of our original set */
@@ -497,7 +497,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
struct object *obj;
if (!is_null_oid(oid)) {
- obj = lookup_object(the_repository, oid->hash);
+ obj = lookup_object(the_repository, oid);
if (obj && (obj->flags & HAS_OBJ)) {
if (timestamp && name_objects)
add_decoration(fsck_walk_options.object_names,
@@ -756,7 +756,7 @@ static int fsck_cache_tree(struct cache_tree *it)
static void mark_object_for_connectivity(const struct object_id *oid)
{
- struct object *obj = lookup_unknown_object(oid->hash);
+ struct object *obj = lookup_unknown_object(oid);
obj->flags |= HAS_OBJ;
}
@@ -879,7 +879,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
struct object_id oid;
if (!get_oid(arg, &oid)) {
struct object *obj = lookup_object(the_repository,
- oid.hash);
+ &oid);
if (!obj || !(obj->flags & HAS_OBJ)) {
if (is_promisor_object(&oid))
diff --git a/builtin/gc.c b/builtin/gc.c
index 8943bcc300..c18efadda5 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -653,7 +653,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
gc_before_repack();
if (!repository_format_precious_objects) {
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
die(FAILED_RUN, repack.argv[0]);
@@ -681,13 +681,15 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
report_garbage = report_pack_garbage;
reprepare_packed_git(the_repository);
if (pack_garbage.nr > 0) {
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
clean_pack_garbage();
}
- if (gc_write_commit_graph)
- write_commit_graph_reachable(get_object_directory(), 0,
- !quiet && !daemonized);
+ if (gc_write_commit_graph &&
+ write_commit_graph_reachable(get_object_directory(),
+ !quiet && !daemonized ? COMMIT_GRAPH_PROGRESS : 0,
+ NULL))
+ return 1;
if (auto_gc && too_many_loose_objects())
warning(_("There are too many unreachable loose objects; "
diff --git a/builtin/grep.c b/builtin/grep.c
index 580fd38f41..560051784e 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -458,7 +458,8 @@ static int grep_submodule(struct grep_opt *opt,
object = parse_object_or_die(oid, oid_to_hex(oid));
grep_read_lock();
- data = read_object_with_reference(&object->oid, tree_type,
+ data = read_object_with_reference(&subrepo,
+ &object->oid, tree_type,
&size, NULL);
grep_read_unlock();
@@ -623,7 +624,8 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
int hit, len;
grep_read_lock();
- data = read_object_with_reference(&obj->oid, tree_type,
+ data = read_object_with_reference(opt->repo,
+ &obj->oid, tree_type,
&size, NULL);
grep_read_unlock();
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 8ae40dec47..f101d092b8 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -10,6 +10,7 @@
#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>...]"),
@@ -112,6 +113,8 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
OPT_END()
};
+ git_config(git_default_config, NULL);
+
argc = parse_options(argc, argv, prefix, options,
git_interpret_trailers_usage, 0);
diff --git a/builtin/log.c b/builtin/log.c
index 7c8767d3bc..1cf9e37736 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -47,7 +47,7 @@ static int default_follow;
static int default_show_signature;
static int decoration_style;
static int decoration_given;
-static int use_mailmap_config;
+static int use_mailmap_config = -1;
static const char *fmt_patch_subject_prefix = "PATCH";
static const char *fmt_pretty;
@@ -63,9 +63,14 @@ struct line_opt_callback_data {
struct string_list args;
};
+static int session_is_interactive(void)
+{
+ return isatty(1) || pager_in_use();
+}
+
static int auto_decoration_style(void)
{
- return (isatty(1) || pager_in_use()) ? DECORATE_SHORT_REFS : 0;
+ return session_is_interactive() ? DECORATE_SHORT_REFS : 0;
}
static int parse_decoration_style(const char *value)
@@ -151,6 +156,16 @@ static void cmd_log_init_defaults(struct rev_info *rev)
parse_date_format(default_date_mode, &rev->date_mode);
}
+static char warn_unspecified_mailmap_msg[] =
+N_("log.mailmap is not set; its implicit value will change in an\n"
+ "upcoming release. To squelch this message and preserve current\n"
+ "behaviour, set the log.mailmap configuration value to false.\n"
+ "\n"
+ "To squelch this message and adopt the new behaviour now, set the\n"
+ "log.mailmap configuration value to true.\n"
+ "\n"
+ "See 'git help config' and search for 'log.mailmap' for further information.");
+
static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
struct rev_info *rev, struct setup_revision_opt *opt)
{
@@ -199,6 +214,13 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
memset(&w, 0, sizeof(w));
userformat_find_requirements(NULL, &w);
+ if (mailmap < 0) {
+ if (session_is_interactive() && !rev->pretty_given)
+ warning("%s\n", _(warn_unspecified_mailmap_msg));
+
+ mailmap = 0;
+ }
+
if (!rev->show_notes_given && (!rev->pretty_given || w.notes))
rev->show_notes = 1;
if (rev->show_notes)
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 7f83c9a6f2..670e8fb93c 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -373,7 +373,7 @@ static void prune_index(struct index_state *istate,
first = pos;
last = istate->cache_nr;
while (last > first) {
- int next = (last + first) >> 1;
+ int next = first + ((last - first) >> 1);
const struct cache_entry *ce = istate->cache[next];
if (!strncmp(ce->name, prefix, prefixlen)) {
first = next+1;
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 34ca0258b1..97b54caeb9 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -205,6 +205,7 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s
static void unresolved_directory(const struct traverse_info *info,
struct name_entry n[3])
{
+ struct repository *r = the_repository;
char *newbase;
struct name_entry *p;
struct tree_desc t[3];
@@ -220,9 +221,9 @@ static void unresolved_directory(const struct traverse_info *info,
newbase = traverse_path(info, p);
#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL)
- buf0 = fill_tree_descriptor(t + 0, ENTRY_OID(n + 0));
- buf1 = fill_tree_descriptor(t + 1, ENTRY_OID(n + 1));
- buf2 = fill_tree_descriptor(t + 2, ENTRY_OID(n + 2));
+ buf0 = fill_tree_descriptor(r, t + 0, ENTRY_OID(n + 0));
+ buf1 = fill_tree_descriptor(r, t + 1, ENTRY_OID(n + 1));
+ buf2 = fill_tree_descriptor(r, t + 2, ENTRY_OID(n + 2));
#undef ENTRY_OID
merge_trees(t, newbase);
@@ -351,14 +352,16 @@ static void merge_trees(struct tree_desc t[3], const char *base)
traverse_trees(&the_index, 3, t, &info);
}
-static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
+static void *get_tree_descriptor(struct repository *r,
+ struct tree_desc *desc,
+ const char *rev)
{
struct object_id oid;
void *buf;
- if (get_oid(rev, &oid))
+ if (repo_get_oid(r, rev, &oid))
die("unknown rev %s", rev);
- buf = fill_tree_descriptor(desc, &oid);
+ buf = fill_tree_descriptor(r, desc, &oid);
if (!buf)
die("%s is not a tree", rev);
return buf;
@@ -366,15 +369,16 @@ static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
int cmd_merge_tree(int argc, const char **argv, const char *prefix)
{
+ 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(t+0, argv[1]);
- buf2 = get_tree_descriptor(t+1, argv[2]);
- buf3 = get_tree_descriptor(t+2, argv[3]);
+ 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, "");
free(buf1);
free(buf2);
diff --git a/builtin/merge.c b/builtin/merge.c
index 6e99aead46..e2ccbc44e2 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -453,7 +453,7 @@ static void finish(struct commit *head_commit,
* We ignore errors in 'gc --auto', since the
* user should see them.
*/
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
}
}
@@ -892,6 +892,7 @@ static int finish_automerge(struct commit *head,
struct strbuf buf = STRBUF_INIT;
struct object_id result_commit;
+ write_tree_trivial(result_tree);
free_commit_list(common);
parents = remoteheads;
if (!head_subsumed || fast_forward == FF_NO)
@@ -1586,8 +1587,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
save_state(&stash))
oidclr(&stash);
- for (i = 0; i < use_strategies_nr; i++) {
- int ret;
+ for (i = 0; !merge_was_ok && i < use_strategies_nr; i++) {
+ int ret, cnt;
if (i) {
printf(_("Rewinding the tree to pristine...\n"));
restore_state(&head_commit->object.oid, &stash);
@@ -1604,40 +1605,26 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
ret = try_merge_strategy(use_strategies[i]->name,
common, remoteheads,
head_commit);
- if (!option_commit && !ret) {
- merge_was_ok = 1;
- /*
- * This is necessary here just to avoid writing
- * the tree, but later we will *not* exit with
- * status code 1 because merge_was_ok is set.
- */
- ret = 1;
- }
-
- if (ret) {
- /*
- * The backend exits with 1 when conflicts are
- * left to be resolved, with 2 when it does not
- * handle the given merge at all.
- */
- if (ret == 1) {
- int cnt = evaluate_result();
-
- if (best_cnt <= 0 || cnt <= best_cnt) {
- best_strategy = use_strategies[i]->name;
- best_cnt = cnt;
+ /*
+ * The backend exits with 1 when conflicts are
+ * left to be resolved, with 2 when it does not
+ * handle the given merge at all.
+ */
+ if (ret < 2) {
+ if (!ret) {
+ if (option_commit) {
+ /* Automerge succeeded. */
+ automerge_was_ok = 1;
+ break;
}
+ merge_was_ok = 1;
+ }
+ cnt = evaluate_result();
+ if (best_cnt <= 0 || cnt <= best_cnt) {
+ best_strategy = use_strategies[i]->name;
+ best_cnt = cnt;
}
- if (merge_was_ok)
- break;
- else
- continue;
}
-
- /* Automerge succeeded. */
- write_tree_trivial(&result_tree);
- automerge_was_ok = 1;
- break;
}
/*
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index 72dfd3dadc..b1ea1a6aa1 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -6,12 +6,13 @@
#include "trace2.h"
static char const * const builtin_multi_pack_index_usage[] = {
- N_("git multi-pack-index [--object-dir=<dir>] (write|verify)"),
+ N_("git multi-pack-index [--object-dir=<dir>] (write|verify|expire|repack --batch-size=<size>)"),
NULL
};
static struct opts_multi_pack_index {
const char *object_dir;
+ unsigned long batch_size;
} opts;
int cmd_multi_pack_index(int argc, const char **argv,
@@ -20,6 +21,8 @@ int cmd_multi_pack_index(int argc, const char **argv,
static struct option builtin_multi_pack_index_options[] = {
OPT_FILENAME(0, "object-dir", &opts.object_dir,
N_("object directory containing set of packfile and pack-index pairs")),
+ OPT_MAGNITUDE(0, "batch-size", &opts.batch_size,
+ N_("during repack, collect pack-files of smaller size into a batch that is larger than this size")),
OPT_END(),
};
@@ -43,10 +46,17 @@ int cmd_multi_pack_index(int argc, const char **argv,
trace2_cmd_mode(argv[0]);
+ if (!strcmp(argv[0], "repack"))
+ return midx_repack(the_repository, opts.object_dir, (size_t)opts.batch_size);
+ if (opts.batch_size)
+ die(_("--batch-size option is only for 'repack' subcommand"));
+
if (!strcmp(argv[0], "write"))
return write_midx_file(opts.object_dir);
if (!strcmp(argv[0], "verify"))
return verify_midx_file(the_repository, opts.object_dir);
+ if (!strcmp(argv[0], "expire"))
+ return expire_midx_packs(the_repository, opts.object_dir);
- die(_("unrecognized verb: %s"), argv[0]);
+ die(_("unrecognized subcommand: %s"), argv[0]);
}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 16df43473a..c785fe16ba 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -378,8 +378,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
*(p+1) = 0;
if (!get_oid(p - (hexsz - 1), &oid)) {
struct object *o =
- lookup_object(the_repository,
- oid.hash);
+ lookup_object(the_repository, &oid);
if (o)
name = get_rev_name(o, &buf);
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index b2be8869c2..267c562b1f 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -606,12 +606,12 @@ static int mark_tagged(const char *path, const struct object_id *oid, int flag,
void *cb_data)
{
struct object_id peeled;
- struct object_entry *entry = packlist_find(&to_pack, oid->hash, NULL);
+ struct object_entry *entry = packlist_find(&to_pack, oid, NULL);
if (entry)
entry->tagged = 1;
if (!peel_ref(path, &peeled)) {
- entry = packlist_find(&to_pack, peeled.hash, NULL);
+ entry = packlist_find(&to_pack, &peeled, NULL);
if (entry)
entry->tagged = 1;
}
@@ -996,7 +996,7 @@ static int have_duplicate_entry(const struct object_id *oid,
{
struct object_entry *entry;
- entry = packlist_find(&to_pack, oid->hash, index_pos);
+ entry = packlist_find(&to_pack, oid, index_pos);
if (!entry)
return 0;
@@ -1428,7 +1428,8 @@ static void add_preferred_base(struct object_id *oid)
if (window <= num_preferred_base++)
return;
- data = read_object_with_reference(oid, tree_type, &size, &tree_oid);
+ data = read_object_with_reference(the_repository, oid,
+ tree_type, &size, &tree_oid);
if (!data)
return;
@@ -1494,11 +1495,13 @@ static int can_reuse_delta(const unsigned char *base_sha1,
if (!base_sha1)
return 0;
+ oidread(&base_oid, base_sha1);
+
/*
* First see if we're already sending the base (or it's explicitly in
* our "excluded" list).
*/
- base = packlist_find(&to_pack, base_sha1, NULL);
+ base = packlist_find(&to_pack, &base_oid, NULL);
if (base) {
if (!in_same_island(&delta->idx.oid, &base->idx.oid))
return 0;
@@ -1511,7 +1514,6 @@ static int can_reuse_delta(const unsigned char *base_sha1,
* even if it was buried too deep in history to make it into the
* packing list.
*/
- oidread(&base_oid, base_sha1);
if (thin && bitmap_has_oid_in_uninteresting(bitmap_git, &base_oid)) {
if (use_delta_islands) {
if (!in_same_island(&delta->idx.oid, &base_oid))
@@ -2571,7 +2573,7 @@ static void add_tag_chain(const struct object_id *oid)
* it was included via bitmaps, we would not have parsed it
* previously).
*/
- if (packlist_find(&to_pack, oid->hash, NULL))
+ if (packlist_find(&to_pack, oid, NULL))
return;
tag = lookup_tag(the_repository, oid);
@@ -2595,7 +2597,7 @@ static int add_ref_tag(const char *path, const struct object_id *oid, int flag,
if (starts_with(path, "refs/tags/") && /* is a tag? */
!peel_ref(path, &peeled) && /* peelable? */
- packlist_find(&to_pack, peeled.hash, NULL)) /* object packed? */
+ packlist_find(&to_pack, &peeled, NULL)) /* object packed? */
add_tag_chain(oid);
return 0;
}
@@ -2795,7 +2797,7 @@ static void show_object(struct object *obj, const char *name, void *data)
for (p = strchr(name, '/'); p; p = strchr(p + 1, '/'))
depth++;
- ent = packlist_find(&to_pack, obj->oid.hash, NULL);
+ ent = packlist_find(&to_pack, &obj->oid, NULL);
if (ent && depth > oe_tree_depth(&to_pack, ent))
oe_set_tree_depth(&to_pack, ent, depth);
}
@@ -2922,7 +2924,7 @@ static void add_objects_in_unpacked_packs(void)
for (i = 0; i < p->num_objects; i++) {
nth_packed_object_oid(&oid, p, i);
- o = lookup_unknown_object(oid.hash);
+ o = lookup_unknown_object(&oid);
if (!(o->flags & OBJECT_ADDED))
mark_in_pack_object(o, p, &in_pack);
o->flags |= OBJECT_ADDED;
@@ -3026,7 +3028,7 @@ static void loosen_unused_packed_objects(void)
for (i = 0; i < p->num_objects; i++) {
nth_packed_object_oid(&oid, p, i);
- if (!packlist_find(&to_pack, oid.hash, NULL) &&
+ if (!packlist_find(&to_pack, &oid, NULL) &&
!has_sha1_pack_kept_or_nonlocal(&oid) &&
!loosened_object_can_be_discarded(&oid, p->mtime))
if (force_object_loose(&oid, p->mtime))
@@ -3134,7 +3136,7 @@ static void get_object_list(int ac, const char **av)
return;
if (use_delta_islands)
- load_delta_islands(the_repository);
+ load_delta_islands(the_repository, progress);
if (prepare_revision_walk(&revs))
die(_("revision walk setup failed"));
diff --git a/builtin/prune.c b/builtin/prune.c
index 97613eccb5..2b76872ad2 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -53,7 +53,7 @@ static int is_object_reachable(const struct object_id *oid,
perform_reachability_traversal(revs);
- obj = lookup_object(the_repository, oid->hash);
+ obj = lookup_object(the_repository, oid);
return obj && (obj->flags & SEEN);
}
diff --git a/builtin/pull.c b/builtin/pull.c
index 9dd32a115b..f1eaf6e6ed 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -128,6 +128,7 @@ static char *opt_update_shallow;
static char *opt_refmap;
static char *opt_ipv4;
static char *opt_ipv6;
+static int opt_show_forced_updates = -1;
static struct option pull_options[] = {
/* Shared options */
@@ -240,6 +241,8 @@ static struct option pull_options[] = {
OPT_PASSTHRU('6', "ipv6", &opt_ipv6, NULL,
N_("use IPv6 addresses only"),
PARSE_OPT_NOARG),
+ OPT_BOOL(0, "show-forced-updates", &opt_show_forced_updates,
+ N_("check for forced-updates on all updated branches")),
OPT_END()
};
@@ -549,6 +552,10 @@ static int run_fetch(const char *repo, const char **refspecs)
argv_array_push(&args, opt_ipv4);
if (opt_ipv6)
argv_array_push(&args, opt_ipv6);
+ if (opt_show_forced_updates > 0)
+ argv_array_push(&args, "--show-forced-updates");
+ else if (opt_show_forced_updates == 0)
+ argv_array_push(&args, "--no-show-forced-updates");
if (repo) {
argv_array_push(&args, repo);
diff --git a/builtin/rebase.c b/builtin/rebase.c
index b8116db487..670096c065 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -738,20 +738,30 @@ static int finish_rebase(struct rebase_options *opts)
{
struct strbuf dir = STRBUF_INIT;
const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+ int ret = 0;
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
apply_autostash(opts);
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
/*
* We ignore errors in 'gc --auto', since the
* user should see them.
*/
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
- strbuf_addstr(&dir, opts->state_dir);
- remove_dir_recursively(&dir, 0);
- strbuf_release(&dir);
+ if (opts->type == REBASE_INTERACTIVE) {
+ struct replay_opts replay = REPLAY_OPTS_INIT;
- return 0;
+ replay.action = REPLAY_INTERACTIVE_REBASE;
+ ret = sequencer_remove_state(&replay);
+ } else {
+ strbuf_addstr(&dir, opts->state_dir);
+ if (remove_dir_recursively(&dir, 0))
+ ret = error(_("could not remove '%s'"),
+ opts->state_dir);
+ strbuf_release(&dir);
+ }
+
+ return ret;
}
static struct commit *peel_committish(const char *name)
@@ -840,13 +850,13 @@ static int reset_head(struct object_id *oid, const char *action,
goto leave_reset_head;
}
- if (!reset_hard && !fill_tree_descriptor(&desc[nr++], &head_oid)) {
+ if (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {
ret = error(_("failed to find tree of %s"),
oid_to_hex(&head_oid));
goto leave_reset_head;
}
- if (!fill_tree_descriptor(&desc[nr++], oid)) {
+ if (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {
ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
goto leave_reset_head;
}
@@ -1379,6 +1389,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct string_list strategy_options = STRING_LIST_INIT_NODUP;
struct object_id squash_onto;
char *squash_onto_name = NULL;
+ int reschedule_failed_exec = -1;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", &options.onto_name,
N_("revision"),
@@ -1471,7 +1482,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "root", &options.root,
N_("rebase all reachable commits up to the root(s)")),
OPT_BOOL(0, "reschedule-failed-exec",
- &options.reschedule_failed_exec,
+ &reschedule_failed_exec,
N_("automatically re-schedule any `exec` that fails")),
OPT_END(),
};
@@ -1481,10 +1492,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
- prefix = setup_git_directory();
- trace_repo_setup(prefix);
- setup_work_tree();
-
options.allow_empty_message = 1;
git_config(rebase_config, &options);
@@ -1600,7 +1607,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
NULL, NULL) < 0)
die(_("could not discard worktree changes"));
- remove_branch_state(the_repository);
+ remove_branch_state(the_repository, 0);
if (read_basic_state(&options))
exit(1);
goto run_rebase;
@@ -1620,16 +1627,24 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
NULL, NULL) < 0)
die(_("could not move back to %s"),
oid_to_hex(&options.orig_head));
- remove_branch_state(the_repository);
- ret = finish_rebase(&options);
+ remove_branch_state(the_repository, 0);
+ ret = !!finish_rebase(&options);
goto cleanup;
}
case ACTION_QUIT: {
- strbuf_reset(&buf);
- strbuf_addstr(&buf, options.state_dir);
- ret = !!remove_dir_recursively(&buf, 0);
- if (ret)
- die(_("could not remove '%s'"), options.state_dir);
+ if (options.type == REBASE_INTERACTIVE) {
+ struct replay_opts replay = REPLAY_OPTS_INIT;
+
+ replay.action = REPLAY_INTERACTIVE_REBASE;
+ ret = !!sequencer_remove_state(&replay);
+ } else {
+ strbuf_reset(&buf);
+ strbuf_addstr(&buf, options.state_dir);
+ ret = !!remove_dir_recursively(&buf, 0);
+ if (ret)
+ error(_("could not remove '%s'"),
+ options.state_dir);
+ }
goto cleanup;
}
case ACTION_EDIT_TODO:
@@ -1778,8 +1793,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
break;
}
- if (options.reschedule_failed_exec && !is_interactive(&options))
- die(_("%s requires an interactive rebase"), "--reschedule-failed-exec");
+ if (reschedule_failed_exec > 0 && !is_interactive(&options))
+ die(_("--reschedule-failed-exec requires "
+ "--exec or --interactive"));
+ if (reschedule_failed_exec >= 0)
+ options.reschedule_failed_exec = reschedule_failed_exec;
if (options.git_am_opts.argc) {
/* all am options except -q are compatible only with --am */
@@ -2104,7 +2122,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
strbuf_addf(&msg, "%s: checkout %s",
getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
if (reset_head(&options.onto->object.oid, "checkout", NULL,
- RESET_HEAD_DETACH | RESET_ORIG_HEAD |
+ RESET_HEAD_DETACH | RESET_ORIG_HEAD |
RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
NULL, msg.buf))
die(_("Could not detach HEAD"));
@@ -2141,6 +2159,7 @@ run_rebase:
ret = !!run_specific_rebase(&options, action);
cleanup:
+ strbuf_release(&buf);
strbuf_release(&revisions);
free(options.head_name);
free(options.gpg_sign_opt);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 77b7122456..dcf385511f 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -12,7 +12,6 @@
#include "object.h"
#include "remote.h"
#include "connect.h"
-#include "transport.h"
#include "string-list.h"
#include "sha1-array.h"
#include "connected.h"
@@ -2042,7 +2041,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
proc.git_cmd = 1;
proc.argv = argv_gc_auto;
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
if (!start_command(&proc)) {
if (use_sideband)
copy_to_sideband(proc.err, -1, NULL);
diff --git a/builtin/repack.c b/builtin/repack.c
index caca113927..30982ed2a2 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -89,6 +89,17 @@ static void remove_pack_on_signal(int signo)
raise(signo);
}
+static int has_pack_keep_file(void)
+{
+ struct packed_git *p;
+
+ for (p = get_all_packs(the_repository); p; p = p->next) {
+ if (p->pack_keep)
+ return 1;
+ }
+ return 0;
+}
+
/*
* Adds all packs hex strings to the fname list, which do not
* have a corresponding .keep file. These packs are not to
@@ -129,19 +140,9 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list,
static void remove_redundant_pack(const char *dir_name, const char *base_name)
{
- const char *exts[] = {".pack", ".idx", ".keep", ".bitmap", ".promisor"};
- int i;
struct strbuf buf = STRBUF_INIT;
- size_t plen;
-
- strbuf_addf(&buf, "%s/%s", dir_name, base_name);
- plen = buf.len;
-
- for (i = 0; i < ARRAY_SIZE(exts); i++) {
- strbuf_setlen(&buf, plen);
- strbuf_addstr(&buf, exts[i]);
- unlink(buf.buf);
- }
+ strbuf_addf(&buf, "%s/%s.pack", dir_name, base_name);
+ unlink_pack_path(buf.buf, 1);
strbuf_release(&buf);
}
@@ -343,9 +344,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
die(_("--keep-unreachable and -A are incompatible"));
- if (write_bitmaps < 0)
+ if (write_bitmaps < 0) {
write_bitmaps = (pack_everything & ALL_INTO_ONE) &&
- is_bare_repository();
+ is_bare_repository() &&
+ keep_pack_list.nr == 0 &&
+ !has_pack_keep_file();
+ }
if (pack_kept_objects < 0)
pack_kept_objects = write_bitmaps;
@@ -422,7 +426,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (!names.nr && !po_args.quiet)
printf_ln(_("Nothing new to pack."));
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
/*
* Ok we have prepared all new packfiles.
diff --git a/builtin/reset.c b/builtin/reset.c
index 26ef9a7bd0..fdd572168b 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -79,13 +79,13 @@ static int reset_index(const struct object_id *oid, int reset_type, int quiet)
struct object_id head_oid;
if (get_oid("HEAD", &head_oid))
return error(_("You do not have a valid HEAD."));
- if (!fill_tree_descriptor(desc + nr, &head_oid))
+ if (!fill_tree_descriptor(the_repository, desc + nr, &head_oid))
return error(_("Failed to find tree of HEAD."));
nr++;
opts.fn = twoway_merge;
}
- if (!fill_tree_descriptor(desc + nr, oid)) {
+ if (!fill_tree_descriptor(the_repository, desc + nr, oid)) {
error(_("Failed to find tree of %s."), oid_to_hex(oid));
goto out;
}
@@ -421,7 +421,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
print_new_head_line(lookup_commit_reference(the_repository, &oid));
}
if (!pathspec.nr)
- remove_branch_state(the_repository);
+ remove_branch_state(the_repository, 0);
return update_ref_status;
}
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 660172b014..301ccb970b 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -49,6 +49,7 @@ static const char rev_list_usage[] =
" --objects | --objects-edge\n"
" --unpacked\n"
" --header | --pretty\n"
+" --[no-]object-names\n"
" --abbrev=<n> | --no-abbrev\n"
" --abbrev-commit\n"
" --left-right\n"
@@ -75,6 +76,9 @@ enum missing_action {
};
static enum missing_action arg_missing_action;
+/* display only the oid of each object encountered */
+static int arg_show_object_names = 1;
+
#define DEFAULT_OIDSET_SIZE (16*1024)
static void finish_commit(struct commit *commit);
@@ -255,7 +259,10 @@ static void show_object(struct object *obj, const char *name, void *cb_data)
display_progress(progress, ++progress_counter);
if (info->flags & REV_LIST_QUIET)
return;
- show_object_with_name(stdout, obj, name);
+ if (arg_show_object_names)
+ show_object_with_name(stdout, obj, name);
+ else
+ printf("%s\n", oid_to_hex(&obj->oid));
}
static void show_edge(struct commit *commit)
@@ -484,6 +491,16 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (skip_prefix(arg, "--missing=", &arg))
continue; /* already handled above */
+ if (!strcmp(arg, ("--no-object-names"))) {
+ arg_show_object_names = 0;
+ continue;
+ }
+
+ if (!strcmp(arg, ("--object-names"))) {
+ arg_show_object_names = 1;
+ continue;
+ }
+
usage(rev_list_usage);
}
diff --git a/builtin/revert.c b/builtin/revert.c
index d4dcedbdc6..f61cc5d82c 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -102,6 +102,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'),
OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'),
+ OPT_CMDMODE(0, "skip", &cmd, N_("skip current commit and continue"), 's'),
OPT_CLEANUP(&cleanup_arg),
OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")),
@@ -151,6 +152,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
this_operation = "--quit";
else if (cmd == 'c')
this_operation = "--continue";
+ else if (cmd == 's')
+ this_operation = "--skip";
else {
assert(cmd == 'a');
this_operation = "--abort";
@@ -203,13 +206,15 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
if (cmd == 'q') {
int ret = sequencer_remove_state(opts);
if (!ret)
- remove_branch_state(the_repository);
+ remove_branch_state(the_repository, 0);
return ret;
}
if (cmd == 'c')
return sequencer_continue(the_repository, opts);
if (cmd == 'a')
return sequencer_rollback(the_repository, opts);
+ if (cmd == 's')
+ return sequencer_skip(the_repository, opts);
return sequencer_pick_revisions(the_repository, opts);
}
diff --git a/builtin/rm.c b/builtin/rm.c
index be8edc6d1e..19ce95a901 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -179,7 +179,7 @@ static int check_local_mod(struct object_id *head, int index_only)
* way as changed from the HEAD.
*/
if (no_head
- || get_tree_entry(head, name, &oid, &mode)
+ || get_tree_entry(the_repository, head, name, &oid, &mode)
|| ce->ce_mode != create_ce_mode(mode)
|| !oideq(&ce->oid, &oid))
staged_changes = 1;
@@ -273,7 +273,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_CWD,
prefix, argv);
- refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);
+ refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
seen = xcalloc(pathspec.nr, 1);
diff --git a/builtin/stash.c b/builtin/stash.c
index 2a8e6d09b4..b5a301f24d 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -713,11 +713,11 @@ static int git_stash_config(const char *var, const char *value, void *cb)
static int show_stash(int argc, const char **argv, const char *prefix)
{
int i;
- int opts = 0;
int ret = 0;
struct stash_info info;
struct rev_info rev;
struct argv_array stash_args = ARGV_ARRAY_INIT;
+ struct argv_array revision_args = ARGV_ARRAY_INIT;
struct option options[] = {
OPT_END()
};
@@ -726,11 +726,12 @@ static int show_stash(int argc, const char **argv, const char *prefix)
git_config(git_diff_ui_config, NULL);
init_revisions(&rev, prefix);
+ argv_array_push(&revision_args, argv[0]);
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-')
argv_array_push(&stash_args, argv[i]);
else
- opts++;
+ argv_array_push(&revision_args, argv[i]);
}
ret = get_stash_info(&info, stash_args.argc, stash_args.argv);
@@ -742,7 +743,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
* The config settings are applied only if there are not passed
* any options.
*/
- if (!opts) {
+ if (revision_args.argc == 1) {
git_config(git_stash_config, NULL);
if (show_stat)
rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;
@@ -756,7 +757,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
}
}
- argc = setup_revisions(argc, argv, &rev, NULL);
+ argc = setup_revisions(revision_args.argc, revision_args.argv, &rev, NULL);
if (argc > 1) {
free_stash_info(&info);
usage_with_options(git_stash_show_usage, options);
@@ -1390,30 +1391,16 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
}
if (keep_index == 1 && !is_null_oid(&info.i_tree)) {
- struct child_process cp_ls = CHILD_PROCESS_INIT;
- struct child_process cp_checkout = CHILD_PROCESS_INIT;
- struct strbuf out = STRBUF_INIT;
-
- if (reset_tree(&info.i_tree, 0, 1)) {
- ret = -1;
- goto done;
- }
-
- cp_ls.git_cmd = 1;
- argv_array_pushl(&cp_ls.args, "ls-files", "-z",
- "--modified", "--", NULL);
-
- add_pathspecs(&cp_ls.args, ps);
- if (pipe_command(&cp_ls, NULL, 0, &out, 0, NULL, 0)) {
- ret = -1;
- goto done;
- }
+ struct child_process cp = CHILD_PROCESS_INIT;
- cp_checkout.git_cmd = 1;
- argv_array_pushl(&cp_checkout.args, "checkout-index",
- "-z", "--force", "--stdin", NULL);
- if (pipe_command(&cp_checkout, out.buf, out.len, NULL,
- 0, NULL, 0)) {
+ cp.git_cmd = 1;
+ argv_array_pushl(&cp.args, "checkout", "--no-overlay",
+ oid_to_hex(&info.i_tree), "--", NULL);
+ if (!ps->nr)
+ argv_array_push(&cp.args, ":/");
+ else
+ add_pathspecs(&cp.args, ps);
+ if (run_command(&cp)) {
ret = -1;
goto done;
}
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 13da32d3b7..909e77e802 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -540,6 +540,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
if (info->quiet)
argv_array_push(&cpr.args, "--quiet");
+ argv_array_push(&cpr.args, "--");
argv_array_pushv(&cpr.args, info->argv);
if (run_command(&cpr))
diff --git a/builtin/tag.c b/builtin/tag.c
index ef37dccf86..e0a4c25382 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -33,6 +33,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 list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
struct ref_format *format)
@@ -144,6 +145,11 @@ static int git_tag_config(const char *var, const char *value, void *cb)
int status;
struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
+ if (!strcmp(var, "tag.gpgsign")) {
+ config_sign_tag = git_config_bool(var, value);
+ return 0;
+ }
+
if (!strcmp(var, "tag.sort")) {
if (!value)
return config_error_nonbool(var);
@@ -442,15 +448,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
memset(&opt, 0, sizeof(opt));
memset(&filter, 0, sizeof(filter));
filter.lines = -1;
+ opt.sign = -1;
argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
- if (keyid) {
- opt.sign = 1;
- set_signing_key(keyid);
- }
- create_tag_object = (opt.sign || annotate || msg.given || msgfile);
-
if (!cmdmode) {
if (argc == 0)
cmdmode = 'l';
@@ -463,6 +464,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (cmdmode == 'l')
setup_auto_pager("tag", 1);
+ if (opt.sign == -1)
+ opt.sign = cmdmode ? 0 : config_sign_tag > 0;
+
+ if (keyid) {
+ opt.sign = 1;
+ set_signing_key(keyid);
+ }
+ create_tag_object = (opt.sign || annotate || msg.given || msgfile);
+
if ((create_tag_object || force) && (cmdmode != 0))
usage_with_options(git_tag_usage, options);
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 80478808b3..a87a4bfd2c 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -332,7 +332,7 @@ static int resolve_against_held(unsigned nr, const struct object_id *base,
{
struct object *obj;
struct obj_buffer *obj_buffer;
- obj = lookup_object(the_repository, base->hash);
+ obj = lookup_object(the_repository, base);
if (!obj)
return 0;
obj_buffer = lookup_object_buffer(obj);
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 3f8cc6ccb4..dff2f4b837 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -601,7 +601,7 @@ static struct cache_entry *read_one_ent(const char *which,
struct object_id oid;
struct cache_entry *ce;
- if (get_tree_entry(ent, path, &oid, &mode)) {
+ if (get_tree_entry(the_repository, ent, path, &oid, &mode)) {
if (which)
error("%s: not in %s branch.", path, which);
return NULL;
diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c
index 4b9e823f8f..40c69a0bed 100644
--- a/builtin/verify-commit.c
+++ b/builtin/verify-commit.c
@@ -12,7 +12,6 @@
#include "repository.h"
#include "commit.h"
#include "run-command.h"
-#include <signal.h>
#include "parse-options.h"
#include "gpg-interface.h"
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index 6fa04b751a..f45136a06b 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -10,7 +10,6 @@
#include "builtin.h"
#include "tag.h"
#include "run-command.h"
-#include <signal.h>
#include "parse-options.h"
#include "gpg-interface.h"
#include "ref-filter.h"