summaryrefslogtreecommitdiff
path: root/builtin/checkout.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/checkout.c')
-rw-r--r--builtin/checkout.c71
1 files changed, 43 insertions, 28 deletions
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 81f07c3ef2..fc4f8fd2ea 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "config.h"
#include "lockfile.h"
#include "parse-options.h"
#include "refs.h"
@@ -216,22 +217,24 @@ static int checkout_merged(int pos, const struct checkout *state)
/*
* NEEDSWORK:
* There is absolutely no reason to write this as a blob object
- * and create a phony cache entry just to leak. This hack is
- * primarily to get to the write_entry() machinery that massages
- * the contents to work-tree format and writes out which only
- * allows it for a cache entry. The code in write_entry() needs
- * to be refactored to allow us to feed a <buffer, size, mode>
- * instead of a cache entry. Such a refactoring would help
- * merge_recursive as well (it also writes the merge result to the
- * object database even when it may contain conflicts).
+ * and create a phony cache entry. This hack is primarily to get
+ * to the write_entry() machinery that massages the contents to
+ * work-tree format and writes out which only allows it for a
+ * cache entry. The code in write_entry() needs to be refactored
+ * to allow us to feed a <buffer, size, mode> instead of a cache
+ * entry. Such a refactoring would help merge_recursive as well
+ * (it also writes the merge result to the object database even
+ * when it may contain conflicts).
*/
if (write_sha1_file(result_buf.ptr, result_buf.size,
blob_type, oid.hash))
die(_("Unable to add merge result for '%s'"), path);
+ free(result_buf.ptr);
ce = make_cache_entry(mode, oid.hash, path, 2, 0);
if (!ce)
die(_("make_cache_entry failed for path '%s'"), path);
status = checkout_entry(ce, state, NULL);
+ free(ce);
return status;
}
@@ -355,6 +358,8 @@ static int checkout_paths(const struct checkout_opts *opts,
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) {
@@ -369,12 +374,13 @@ static int checkout_paths(const struct checkout_opts *opts,
pos = skip_same_name(ce, pos) - 1;
}
}
+ errs |= finish_delayed_checkout(&state);
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
read_ref_full("HEAD", 0, rev.hash, NULL);
- head = lookup_commit_reference_gently(rev.hash, 1);
+ head = lookup_commit_reference_gently(&rev, 1);
errs |= post_checkout_hook(head, head, 0);
return errs;
@@ -430,6 +436,7 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
* update paths in the work tree, and we cannot revert
* them.
*/
+ /* fallthrough */
case 0:
return 0;
default:
@@ -508,10 +515,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
setup_standard_excludes(topts.dir);
}
tree = parse_tree_indirect(old->commit ?
- old->commit->object.oid.hash :
- EMPTY_TREE_SHA1_BIN);
+ &old->commit->object.oid :
+ &empty_tree_oid);
init_tree_desc(&trees[0], tree->buffer, tree->size);
- tree = parse_tree_indirect(new->commit->object.oid.hash);
+ tree = parse_tree_indirect(&new->commit->object.oid);
init_tree_desc(&trees[1], tree->buffer, tree->size);
ret = unpack_trees(2, trees, &topts);
@@ -702,7 +709,7 @@ static int add_pending_uninteresting_ref(const char *refname,
const struct object_id *oid,
int flags, void *cb_data)
{
- add_pending_sha1(cb_data, refname, oid->hash, UNINTERESTING);
+ add_pending_oid(cb_data, refname, oid, UNINTERESTING);
return 0;
}
@@ -788,11 +795,16 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new)
add_pending_object(&revs, object, oid_to_hex(&object->oid));
for_each_ref(add_pending_uninteresting_ref, &revs);
- add_pending_sha1(&revs, "HEAD", new->object.oid.hash, UNINTERESTING);
+ add_pending_oid(&revs, "HEAD", &new->object.oid, UNINTERESTING);
+ /* Save pending objects, so they can be cleaned up later. */
refs = revs.pending;
revs.leak_pending = 1;
+ /*
+ * prepare_revision_walk (together with .leak_pending = 1) makes us
+ * the sole owner of the list of pending objects.
+ */
if (prepare_revision_walk(&revs))
die(_("internal error in revision walk"));
if (!(old->object.flags & UNINTERESTING))
@@ -800,8 +812,10 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new)
else
describe_detached_head(_("Previous HEAD position was"), old);
+ /* Clean up objects used, as they will be reused. */
clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
- free(refs.objects);
+
+ object_array_clear(&refs);
}
static int switch_branches(const struct checkout_opts *opts,
@@ -814,7 +828,8 @@ static int switch_branches(const struct checkout_opts *opts,
int flag, writeout_error = 0;
memset(&old, 0, sizeof(old));
old.path = path_to_free = resolve_refdup("HEAD", 0, rev.hash, &flag);
- old.commit = lookup_commit_reference_gently(rev.hash, 1);
+ if (old.path)
+ old.commit = lookup_commit_reference_gently(&rev, 1);
if (!(flag & REF_ISSYMREF))
old.path = NULL;
@@ -854,7 +869,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
}
if (starts_with(var, "submodule."))
- return parse_submodule_config_option(var, value);
+ return git_default_submodule_config(var, value, NULL);
return git_xmerge_config(var, value, NULL);
}
@@ -889,11 +904,10 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
static const char *unique_tracking_name(const char *name, struct object_id *oid)
{
struct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };
- char src_ref[PATH_MAX];
- snprintf(src_ref, PATH_MAX, "refs/heads/%s", name);
- cb_data.src_ref = src_ref;
+ cb_data.src_ref = xstrfmt("refs/heads/%s", name);
cb_data.dst_oid = oid;
for_each_remote(check_tracking_name, &cb_data);
+ free(cb_data.src_ref);
if (cb_data.unique)
return cb_data.dst_ref;
free(cb_data.dst_ref);
@@ -1029,10 +1043,10 @@ static int parse_branchname_arg(int argc, const char **argv,
else
new->path = NULL; /* not an existing branch */
- new->commit = lookup_commit_reference_gently(rev->hash, 1);
+ new->commit = lookup_commit_reference_gently(rev, 1);
if (!new->commit) {
/* not a commit */
- *source_tree = parse_tree_indirect(rev->hash);
+ *source_tree = parse_tree_indirect(rev);
} else {
parse_commit_or_die(new->commit);
*source_tree = new->commit->tree;
@@ -1110,9 +1124,8 @@ static int checkout_branch(struct checkout_opts *opts,
if (new->path && !opts->force_detach && !opts->new_branch &&
!opts->ignore_other_worktrees) {
- struct object_id oid;
int flag;
- char *head_ref = resolve_refdup("HEAD", 0, oid.hash, &flag);
+ char *head_ref = resolve_refdup("HEAD", 0, NULL, &flag);
if (head_ref &&
(!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
die_if_checked_out(new->path, 1);
@@ -1163,6 +1176,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
N_("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_END(),
};
@@ -1173,7 +1189,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
opts.prefix = prefix;
opts.show_progress = -1;
- gitmodules_config();
git_config(git_checkout_config, &opts);
opts.track = BRANCH_TRACK_UNSPECIFIED;
@@ -1259,9 +1274,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
* new_branch && argc > 1 will be caught later.
*/
if (opts.new_branch && argc == 1)
- die(_("Cannot update paths and switch to branch '%s' at the same time.\n"
- "Did you intend to checkout '%s' which can not be resolved as commit?"),
- opts.new_branch, argv[0]);
+ die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
+ argv[0], opts.new_branch);
if (opts.force_detach)
die(_("git checkout: --detach does not take a path argument '%s'"),
@@ -1283,6 +1297,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
strbuf_release(&buf);
}
+ UNLEAK(opts);
if (opts.patch_mode || opts.pathspec.nr)
return checkout_paths(&opts, new.name);
else