summaryrefslogtreecommitdiff
path: root/builtin/checkout.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/checkout.c')
-rw-r--r--builtin/checkout.c126
1 files changed, 77 insertions, 49 deletions
diff --git a/builtin/checkout.c b/builtin/checkout.c
index cbf73b8c9f..f6e65fedfa 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -9,6 +9,7 @@
#include "config.h"
#include "diff.h"
#include "dir.h"
+#include "hook.h"
#include "ll-merge.h"
#include "lockfile.h"
#include "merge-recursive.h"
@@ -91,8 +92,8 @@ struct checkout_opts {
};
struct branch_info {
- const char *name; /* The short name used */
- const char *path; /* The full name of a real branch */
+ char *name; /* The short name used */
+ char *path; /* The full name of a real branch */
struct commit *commit; /* The named commit */
char *refname; /* The full name of the ref being checked out. */
struct object_id oid; /* The object ID of the commit being checked out. */
@@ -103,10 +104,18 @@ struct branch_info {
char *checkout;
};
+static void branch_info_release(struct branch_info *info)
+{
+ free(info->name);
+ free(info->path);
+ free(info->refname);
+ free(info->checkout);
+}
+
static int post_checkout_hook(struct commit *old_commit, struct commit *new_commit,
int changed)
{
- return run_hook_le(NULL, "post-checkout",
+ return run_hooks_l("post-checkout",
oid_to_hex(old_commit ? &old_commit->object.oid : null_oid()),
oid_to_hex(new_commit ? &new_commit->object.oid : null_oid()),
changed ? "1" : "0", NULL);
@@ -237,6 +246,7 @@ static int checkout_merged(int pos, const struct checkout *state,
struct cache_entry *ce = active_cache[pos];
const char *path = ce->name;
mmfile_t ancestor, ours, theirs;
+ enum ll_merge_result merge_status;
int status;
struct object_id oid;
mmbuffer_t result_buf;
@@ -267,13 +277,16 @@ static int checkout_merged(int pos, const struct checkout *state,
memset(&ll_opts, 0, sizeof(ll_opts));
git_config_get_bool("merge.renormalize", &renormalize);
ll_opts.renormalize = renormalize;
- status = ll_merge(&result_buf, path, &ancestor, "base",
- &ours, "ours", &theirs, "theirs",
- state->istate, &ll_opts);
+ merge_status = ll_merge(&result_buf, path, &ancestor, "base",
+ &ours, "ours", &theirs, "theirs",
+ state->istate, &ll_opts);
free(ancestor.ptr);
free(ours.ptr);
free(theirs.ptr);
- if (status < 0 || !result_buf.ptr) {
+ if (merge_status == LL_MERGE_BINARY_CONFLICT)
+ warning("Cannot merge binary files: %s (%s vs. %s)",
+ path, "ours", "theirs");
+ if (merge_status < 0 || !result_buf.ptr) {
free(result_buf.ptr);
return error(_("path '%s': cannot merge"), path);
}
@@ -456,10 +469,10 @@ static int checkout_paths(const struct checkout_opts *opts,
die(_("'%s' cannot be used with updating paths"), "--detach");
if (opts->merge && opts->patch_mode)
- die(_("'%s' cannot be used with %s"), "--merge", "--patch");
+ die(_("options '%s' and '%s' cannot be used together"), "--merge", "--patch");
if (opts->ignore_unmerged && opts->merge)
- die(_("'%s' cannot be used with %s"),
+ die(_("options '%s' and '%s' cannot be used together"),
opts->ignore_unmerged_opt, "-m");
if (opts->new_branch)
@@ -688,9 +701,12 @@ static void setup_branch_path(struct branch_info *branch)
repo_get_oid_committish(the_repository, branch->name, &branch->oid);
strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
- if (strcmp(buf.buf, branch->name))
+ if (strcmp(buf.buf, branch->name)) {
+ free(branch->name);
branch->name = xstrdup(buf.buf);
+ }
strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
+ free(branch->path);
branch->path = strbuf_detach(&buf, NULL);
}
@@ -874,7 +890,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
int ret;
struct strbuf err = STRBUF_INIT;
- ret = safe_create_reflog(refname, 1, &err);
+ ret = safe_create_reflog(refname, &err);
if (ret) {
fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
opts->new_orphan_branch, err.buf);
@@ -894,7 +910,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
opts->new_branch_log,
opts->quiet,
opts->track);
- new_branch_info->name = opts->new_branch;
+ free(new_branch_info->name);
+ free(new_branch_info->refname);
+ new_branch_info->name = xstrdup(opts->new_branch);
setup_branch_path(new_branch_info);
}
@@ -1062,8 +1080,7 @@ static int switch_branches(const struct checkout_opts *opts,
struct branch_info *new_branch_info)
{
int ret = 0;
- struct branch_info old_branch_info;
- void *path_to_free;
+ struct branch_info old_branch_info = { 0 };
struct object_id rev;
int flag, writeout_error = 0;
int do_merge = 1;
@@ -1071,25 +1088,29 @@ static int switch_branches(const struct checkout_opts *opts,
trace2_cmd_mode("branch");
memset(&old_branch_info, 0, sizeof(old_branch_info));
- old_branch_info.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag);
+ old_branch_info.path = resolve_refdup("HEAD", 0, &rev, &flag);
if (old_branch_info.path)
old_branch_info.commit = lookup_commit_reference_gently(the_repository, &rev, 1);
if (!(flag & REF_ISSYMREF))
- old_branch_info.path = NULL;
+ FREE_AND_NULL(old_branch_info.path);
- if (old_branch_info.path)
- skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name);
+ if (old_branch_info.path) {
+ const char *const prefix = "refs/heads/";
+ const char *p;
+ if (skip_prefix(old_branch_info.path, prefix, &p))
+ old_branch_info.name = xstrdup(p);
+ }
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)";
+ new_branch_info->name = xstrdup("(empty)");
do_merge = 1;
}
if (!new_branch_info->name) {
- new_branch_info->name = "HEAD";
+ new_branch_info->name = xstrdup("HEAD");
new_branch_info->commit = old_branch_info.commit;
if (!new_branch_info->commit)
die(_("You are on a branch yet to be born"));
@@ -1102,7 +1123,7 @@ static int switch_branches(const struct checkout_opts *opts,
if (do_merge) {
ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
if (ret) {
- free(path_to_free);
+ branch_info_release(&old_branch_info);
return ret;
}
}
@@ -1113,7 +1134,8 @@ static int switch_branches(const struct checkout_opts *opts,
update_refs_for_switch(opts, &old_branch_info, new_branch_info);
ret = post_checkout_hook(old_branch_info.commit, new_branch_info->commit, 1);
- free(path_to_free);
+ branch_info_release(&old_branch_info);
+
return ret || writeout_error;
}
@@ -1145,16 +1167,15 @@ static void setup_new_branch_info_and_source_tree(
struct tree **source_tree = &opts->source_tree;
struct object_id branch_rev;
- new_branch_info->name = arg;
+ new_branch_info->name = xstrdup(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 {
- free((char *)new_branch_info->path);
- new_branch_info->path = NULL; /* not an existing branch */
- }
+ else
+ /* not an existing branch */
+ FREE_AND_NULL(new_branch_info->path);
new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
if (!new_branch_info->commit) {
@@ -1517,7 +1538,7 @@ static struct option *add_common_options(struct checkout_opts *opts,
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)")),
+ N_("conflict style (merge, diff3, or zdiff3)")),
OPT_END()
};
struct option *newopts = parse_options_concat(prevopts, options);
@@ -1530,8 +1551,10 @@ static struct option *add_common_switch_branch_options(
{
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_CALLBACK_F('t', "track", &opts->track, "(direct|inherit)",
+ N_("set branch tracking configuration"),
+ PARSE_OPT_OPTARG,
+ parse_opt_tracking_mode),
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")),
@@ -1574,12 +1597,11 @@ static char cb_option = 'b';
static int checkout_main(int argc, const char **argv, const char *prefix,
struct checkout_opts *opts, struct option *options,
- const char * const usagestr[])
+ const char * const usagestr[],
+ struct branch_info *new_branch_info)
{
- struct branch_info new_branch_info;
int parseopt_flags = 0;
- memset(&new_branch_info, 0, sizeof(new_branch_info));
opts->overwrite_ignore = 1;
opts->prefix = prefix;
opts->show_progress = -1;
@@ -1617,11 +1639,11 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
}
if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
- die(_("-%c, -%c and --orphan are mutually exclusive"),
- cb_option, toupper(cb_option));
+ die(_("options '-%c', '-%c', and '%s' cannot be used together"),
+ cb_option, toupper(cb_option), "--orphan");
if (opts->overlay_mode == 1 && opts->patch_mode)
- die(_("-p and --overlay are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "-p", "--overlay");
if (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {
if (opts->checkout_index < 0)
@@ -1688,7 +1710,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
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);
argv += n;
argc -= n;
} else if (!opts->accept_ref && opts->from_treeish) {
@@ -1697,7 +1719,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
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,
+ setup_new_branch_info_and_source_tree(new_branch_info,
opts, &rev,
opts->from_treeish);
@@ -1717,7 +1739,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
* Try to give more helpful suggestion.
* new_branch && argc > 1 will be caught later.
*/
- if (opts->new_branch && argc == 1 && !new_branch_info.commit)
+ if (opts->new_branch && argc == 1 && !new_branch_info->commit)
die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
argv[0], opts->new_branch);
@@ -1728,19 +1750,19 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
if (opts->pathspec_from_file) {
if (opts->pathspec.nr)
- die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+ die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
if (opts->force_detach)
- die(_("--pathspec-from-file is incompatible with --detach"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--detach");
if (opts->patch_mode)
- die(_("--pathspec-from-file is incompatible with --patch"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch");
parse_pathspec_file(&opts->pathspec, 0,
0,
prefix, opts->pathspec_from_file, opts->pathspec_file_nul);
} else if (opts->pathspec_file_nul) {
- die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
opts->pathspec.recursive = 1;
@@ -1766,11 +1788,10 @@ static int checkout_main(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_branch_info);
+ return checkout_paths(opts, new_branch_info);
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)
@@ -1789,6 +1810,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
OPT_END()
};
int ret;
+ struct branch_info new_branch_info = { 0 };
memset(&opts, 0, sizeof(opts));
opts.dwim_new_local_branch = 1;
@@ -1819,7 +1841,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
options = add_checkout_path_options(&opts, options);
ret = checkout_main(argc, argv, prefix, &opts,
- options, checkout_usage);
+ options, checkout_usage, &new_branch_info);
+ branch_info_release(&new_branch_info);
+ clear_pathspec(&opts.pathspec);
FREE_AND_NULL(options);
return ret;
}
@@ -1840,6 +1864,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
OPT_END()
};
int ret;
+ struct branch_info new_branch_info = { 0 };
memset(&opts, 0, sizeof(opts));
opts.dwim_new_local_branch = 1;
@@ -1859,7 +1884,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
cb_option = 'c';
ret = checkout_main(argc, argv, prefix, &opts,
- options, switch_branch_usage);
+ options, switch_branch_usage, &new_branch_info);
+ branch_info_release(&new_branch_info);
FREE_AND_NULL(options);
return ret;
}
@@ -1881,6 +1907,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
OPT_END()
};
int ret;
+ struct branch_info new_branch_info = { 0 };
memset(&opts, 0, sizeof(opts));
opts.accept_ref = 0;
@@ -1896,7 +1923,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
options = add_checkout_path_options(&opts, options);
ret = checkout_main(argc, argv, prefix, &opts,
- options, restore_usage);
+ options, restore_usage, &new_branch_info);
+ branch_info_release(&new_branch_info);
FREE_AND_NULL(options);
return ret;
}