Esempio n. 1
0
static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch_options *opts, const git_checkout_options *co_opts, const char *branch)
{
	int error;
	git_buf reflog_message = GIT_BUF_INIT;
	git_fetch_options fetch_opts;
	git_remote *remote;

	assert(repo && _remote);

	if (!git_repository_is_empty(repo)) {
		git_error_set(GIT_ERROR_INVALID, "the repository is not empty");
		return -1;
	}

	if ((error = git_remote_dup(&remote, _remote)) < 0)
		return error;

	memcpy(&fetch_opts, opts, sizeof(git_fetch_options));
	fetch_opts.update_fetchhead = 0;
	fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
	git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));

	if ((error = git_remote_fetch(remote, NULL, &fetch_opts, git_buf_cstr(&reflog_message))) != 0)
		goto cleanup;

	error = checkout_branch(repo, remote, co_opts, branch, git_buf_cstr(&reflog_message));

cleanup:
	git_remote_free(remote);
	git_buf_dispose(&reflog_message);

	return error;
}
Esempio n. 2
0
static int clone_local_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, int link, const git_signature *signature)
{
	int error, flags;
	git_repository *src;
	git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT;
	git_buf reflog_message = GIT_BUF_INIT;

	assert(repo && remote);

	if (!git_repository_is_empty(repo)) {
		giterr_set(GITERR_INVALID, "the repository is not empty");
		return -1;
	}

	/*
	 * Let's figure out what path we should use for the source
	 * repo, if it's not rooted, the path should be relative to
	 * the repository's worktree/gitdir.
	 */
	if ((error = git_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0)
		return error;

	/* Copy .git/objects/ from the source to the target */
	if ((error = git_repository_open(&src, git_buf_cstr(&src_path))) < 0) {
		git_buf_free(&src_path);
		return error;
	}

	git_buf_joinpath(&src_odb, git_repository_path(src), GIT_OBJECTS_DIR);
	git_buf_joinpath(&dst_odb, git_repository_path(repo), GIT_OBJECTS_DIR);
	if (git_buf_oom(&src_odb) || git_buf_oom(&dst_odb)) {
		error = -1;
		goto cleanup;
	}

	flags = 0;
	if (can_link(git_repository_path(src), git_repository_path(repo), link))
		flags |= GIT_CPDIR_LINK_FILES;

	if ((error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
				     flags, GIT_OBJECT_DIR_MODE)) < 0)
		goto cleanup;

	git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));

	if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
		goto cleanup;

	error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));

cleanup:
	git_buf_free(&reflog_message);
	git_buf_free(&src_path);
	git_buf_free(&src_odb);
	git_buf_free(&dst_odb);
	git_repository_free(src);
	return error;
}
Esempio n. 3
0
static int clone_into(git_repository *repo, git_remote *_remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature)
{
	int error;
	git_buf reflog_message = GIT_BUF_INIT;
	git_remote *remote;
	const git_remote_callbacks *callbacks;

	assert(repo && _remote);

	if (!git_repository_is_empty(repo)) {
		giterr_set(GITERR_INVALID, "the repository is not empty");
		return -1;
	}

	if ((error = git_remote_dup(&remote, _remote)) < 0)
		return error;

	callbacks = git_remote_get_callbacks(_remote);
	if (!giterr__check_version(callbacks, 1, "git_remote_callbacks") &&
	    (error = git_remote_set_callbacks(remote, callbacks)) < 0)
		goto cleanup;

	if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
		goto cleanup;

	git_remote_set_update_fetchhead(remote, 0);
	git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));

	if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
		goto cleanup;

	error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));

cleanup:
	git_remote_free(remote);
	git_buf_free(&reflog_message);

	return error;
}
Esempio n. 4
0
static int clone_local_into(git_repository *repo, git_remote *remote, const git_fetch_options *fetch_opts, const git_checkout_options *co_opts, const char *branch, int link)
{
	int error, flags;
	git_repository *src;
	git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT;
	git_buf reflog_message = GIT_BUF_INIT;

	assert(repo && remote);

	if (!git_repository_is_empty(repo)) {
		git_error_set(GIT_ERROR_INVALID, "the repository is not empty");
		return -1;
	}

	/*
	 * Let's figure out what path we should use for the source
	 * repo, if it's not rooted, the path should be relative to
	 * the repository's worktree/gitdir.
	 */
	if ((error = git_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0)
		return error;

	/* Copy .git/objects/ from the source to the target */
	if ((error = git_repository_open(&src, git_buf_cstr(&src_path))) < 0) {
		git_buf_dispose(&src_path);
		return error;
	}

	if (git_repository_item_path(&src_odb, src, GIT_REPOSITORY_ITEM_OBJECTS) < 0
		|| git_repository_item_path(&dst_odb, repo, GIT_REPOSITORY_ITEM_OBJECTS) < 0) {
		error = -1;
		goto cleanup;
	}

	flags = 0;
	if (can_link(git_repository_path(src), git_repository_path(repo), link))
		flags |= GIT_CPDIR_LINK_FILES;

	error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
				flags, GIT_OBJECT_DIR_MODE);

	/*
	 * can_link() doesn't catch all variations, so if we hit an
	 * error and did want to link, let's try again without trying
	 * to link.
	 */
	if (error < 0 && link) {
		flags &= ~GIT_CPDIR_LINK_FILES;
		error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
					flags, GIT_OBJECT_DIR_MODE);
	}

	if (error < 0)
		goto cleanup;

	git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));

	if ((error = git_remote_fetch(remote, NULL, fetch_opts, git_buf_cstr(&reflog_message))) != 0)
		goto cleanup;

	error = checkout_branch(repo, remote, co_opts, branch, git_buf_cstr(&reflog_message));

cleanup:
	git_buf_dispose(&reflog_message);
	git_buf_dispose(&src_path);
	git_buf_dispose(&src_odb);
	git_buf_dispose(&dst_odb);
	git_repository_free(src);
	return error;
}
Esempio n. 5
0
int cmd_checkout(int argc, const char **argv, const char *prefix)
{
	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"),
			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,
			      N_("checkout our version for unmerged files"),
			      2, PARSE_OPT_NONEG),
		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,
			 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(),
	};

	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;

	git_config(git_checkout_config, &opts);

	opts.track = BRANCH_TRACK_UNSPECIFIED;

	argc = parse_options(argc, argv, prefix, options, checkout_usage,
			     PARSE_OPT_KEEP_DASHDASH);

	dwim_new_local_branch = !no_dwim_new_local_branch;
	if (opts.show_progress < 0) {
		if (opts.quiet)
			opts.show_progress = 0;
		else
			opts.show_progress = isatty(2);
	}

	if (conflict_style) {
		opts.merge = 1; /* implied */
		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
	}

	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)
		die(_("-p and --overlay are mutually exclusive"));

	/*
	 * 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_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) {
		const char *argv0 = argv[0];
		if (!argc || !strcmp(argv0, "--"))
			die(_("--track needs a branch name"));
		skip_prefix(argv0, "refs/", &argv0);
		skip_prefix(argv0, "remotes/", &argv0);
		argv0 = strchr(argv0, '/');
		if (!argv0 || !argv0[1])
			die(_("missing branch name; try -b"));
		opts.new_branch = argv0 + 1;
	}

	/*
	 * Extract branch name from command line arguments, so
	 * all that is left is pathspecs.
	 *
	 * Handle
	 *
	 *  1) git checkout <tree> -- [<paths>]
	 *  2) git checkout -- [<paths>]
	 *  3) git checkout <something> [<paths>]
	 *
	 * including "last branch" syntax and DWIM-ery for names of
	 * remote branches, erroring out for invalid or ambiguous cases.
	 */
	if (argc) {
		struct object_id rev;
		int dwim_ok =
			!opts.patch_mode &&
			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,
					     &dwim_remotes_matched);
		argv += n;
		argc -= n;
	}

	if (argc) {
		parse_pathspec(&opts.pathspec, 0,
			       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
			       prefix, argv);

		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)
			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'"),
			    argv[0]);

		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) {
		struct strbuf buf = STRBUF_INIT;

		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);
		strbuf_release(&buf);
	}

	UNLEAK(opts);
	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"
				 "We found %d remotes with a reference that matched. So we fell back\n"
				 "on trying to resolve the argument as a path, but failed there too!\n"
				 "\n"
				 "If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
				 "you can do so by fully qualifying the name with the --track option:\n"
				 "\n"
				 "    git checkout --track origin/<name>\n"
				 "\n"
				 "If you'd like to always have checkouts of an ambiguous <name> prefer\n"
				 "one remote, e.g. the 'origin' remote, consider setting\n"
				 "checkout.defaultRemote=origin in your config."),
			       argv[0],
			       dwim_remotes_matched);
		return ret;
	} else {
		return checkout_branch(&opts, &new_branch_info);
	}
}