Beispiel #1
0
Datei: reset.c Projekt: guban/git
int cmd_reset(int argc, const char **argv, const char *prefix)
{
	int reset_type = NONE, update_ref_status = 0, quiet = 0;
	int patch_mode = 0, nul_term_line = 0, read_from_stdin = 0, unborn;
	char **stdin_paths = NULL;
	int stdin_nr = 0, stdin_alloc = 0;
	const char *rev;
	struct object_id oid;
	struct pathspec pathspec;
	int intent_to_add = 0;
	const struct option options[] = {
		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
		OPT_SET_INT(0, "mixed", &reset_type,
						N_("reset HEAD and index"), MIXED),
		OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT),
		OPT_SET_INT(0, "hard", &reset_type,
				N_("reset HEAD, index and working tree"), HARD),
		OPT_SET_INT(0, "merge", &reset_type,
				N_("reset HEAD, index and working tree"), MERGE),
		OPT_SET_INT(0, "keep", &reset_type,
				N_("reset HEAD but keep local changes"), KEEP),
		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
			    "reset", "control recursive updating of submodules",
			    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
		OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
		OPT_BOOL('N', "intent-to-add", &intent_to_add,
				N_("record only the fact that removed paths will be added later")),
		OPT_BOOL('z', NULL, &nul_term_line,
			N_("EXPERIMENTAL: paths are separated with NUL character")),
		OPT_BOOL(0, "stdin", &read_from_stdin,
				N_("EXPERIMENTAL: read paths from <stdin>")),
		OPT_END()
	};

	git_config(git_reset_config, NULL);

	argc = parse_options(argc, argv, prefix, options, git_reset_usage,
						PARSE_OPT_KEEP_DASHDASH);
	parse_args(&pathspec, argv, prefix, patch_mode, &rev);

	if (read_from_stdin) {
		strbuf_getline_fn getline_fn = nul_term_line ?
			strbuf_getline_nul : strbuf_getline_lf;
		int flags = PATHSPEC_PREFER_FULL;
		struct strbuf buf = STRBUF_INIT;
		struct strbuf unquoted = STRBUF_INIT;

		if (patch_mode)
			die(_("--stdin is incompatible with --patch"));

		if (pathspec.nr)
			die(_("--stdin is incompatible with path arguments"));

		while (getline_fn(&buf, stdin) != EOF) {
			if (!nul_term_line && buf.buf[0] == '"') {
				strbuf_reset(&unquoted);
				if (unquote_c_style(&unquoted, buf.buf, NULL))
					die(_("line is badly quoted"));
				strbuf_swap(&buf, &unquoted);
			}
			ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc);
			stdin_paths[stdin_nr++] = xstrdup(buf.buf);
			strbuf_reset(&buf);
		}
		strbuf_release(&unquoted);
		strbuf_release(&buf);

		ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc);
		stdin_paths[stdin_nr++] = NULL;
		flags |= PATHSPEC_LITERAL_PATH;
		parse_pathspec(&pathspec, 0, flags, prefix,
			       (const char **)stdin_paths);

	} else if (nul_term_line)
		die(_("-z requires --stdin"));

	unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
	if (unborn) {
		/* reset on unborn branch: treat as reset to empty tree */
		hashcpy(oid.hash, EMPTY_TREE_SHA1_BIN);
	} else if (!pathspec.nr) {
		struct commit *commit;
		if (get_oid_committish(rev, &oid))
			die(_("Failed to resolve '%s' as a valid revision."), rev);
		commit = lookup_commit_reference(&oid);
		if (!commit)
			die(_("Could not parse object '%s'."), rev);
		oidcpy(&oid, &commit->object.oid);
	} else {
		struct tree *tree;
		if (get_oid_treeish(rev, &oid))
			die(_("Failed to resolve '%s' as a valid tree."), rev);
		tree = parse_tree_indirect(&oid);
		if (!tree)
			die(_("Could not parse object '%s'."), rev);
		oidcpy(&oid, &tree->object.oid);
	}

	if (patch_mode) {
		if (reset_type != NONE)
			die(_("--patch is incompatible with --{hard,mixed,soft}"));
		return run_add_interactive(rev, "--patch=reset", &pathspec);
	}

	/* git reset tree [--] paths... can be used to
	 * load chosen paths from the tree into the index without
	 * affecting the working tree nor HEAD. */
	if (pathspec.nr) {
		if (reset_type == MIXED)
			warning(_("--mixed with paths is deprecated; use 'git reset -- <paths>' instead."));
		else if (reset_type != NONE)
			die(_("Cannot do %s reset with paths."),
					_(reset_type_names[reset_type]));
	}
	if (reset_type == NONE)
		reset_type = MIXED; /* by default */

	if (reset_type != SOFT && (reset_type != MIXED || get_git_work_tree()))
		setup_work_tree();

	if (reset_type == MIXED && is_bare_repository())
		die(_("%s reset is not allowed in a bare repository"),
		    _(reset_type_names[reset_type]));

	if (intent_to_add && reset_type != MIXED)
		die(_("-N can only be used with --mixed"));

	/* Soft reset does not touch the index file nor the working tree
	 * at all, but requires them in a good order.  Other resets reset
	 * the index file to the tree object we are switching to. */
	if (reset_type == SOFT || reset_type == KEEP)
		die_if_unmerged_cache(reset_type);

	if (reset_type != SOFT) {
		struct lock_file lock = LOCK_INIT;
		hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
		if (reset_type == MIXED) {
			int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
			if (read_from_tree(&pathspec, &oid, intent_to_add))
				return 1;
			if (get_git_work_tree())
				refresh_index(&the_index, flags, NULL, NULL,
					      _("Unstaged changes after reset:"));
		} else {
			int err = reset_index(&oid, reset_type, quiet);
			if (reset_type == KEEP && !err)
				err = reset_index(&oid, MIXED, quiet);
			if (err)
				die(_("Could not reset index file to revision '%s'."), rev);
		}

		if (write_locked_index(&the_index, &lock, COMMIT_LOCK))
			die(_("Could not write new index file."));
	}

	if (!pathspec.nr && !unborn) {
		/* Any resets without paths update HEAD to the head being
		 * switched to, saving the previous head in ORIG_HEAD before. */
		update_ref_status = reset_refs(rev, &oid);

		if (reset_type == HARD && !update_ref_status && !quiet)
			print_new_head_line(lookup_commit_reference(&oid));
	}
	if (!pathspec.nr)
		remove_branch_state();

	if (stdin_paths) {
		while (stdin_nr)
			free(stdin_paths[--stdin_nr]);
		free(stdin_paths);
	}

	return update_ref_status;
}
Beispiel #2
0
int cmd_mv(int argc, const char **argv, const char *prefix)
{
	int i, newfd, gitmodules_modified = 0;
	int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
	struct option builtin_mv_options[] = {
		OPT__VERBOSE(&verbose, N_("be verbose")),
		OPT__DRY_RUN(&show_only, N_("dry run")),
		OPT__FORCE(&force, N_("force move/rename even if target exists")),
		OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")),
		OPT_END(),
	};
	const char **source, **destination, **dest_path, **submodule_gitfile;
	enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
	struct stat st;
	struct string_list src_for_dst = STRING_LIST_INIT_NODUP;

	gitmodules_config();
	git_config(git_default_config, NULL);

	argc = parse_options(argc, argv, prefix, builtin_mv_options,
			     builtin_mv_usage, 0);
	if (--argc < 1)
		usage_with_options(builtin_mv_usage, builtin_mv_options);

	newfd = hold_locked_index(&lock_file, 1);
	if (read_cache() < 0)
		die(_("index file corrupt"));

	source = internal_copy_pathspec(prefix, argv, argc, 0);
	modes = xcalloc(argc, sizeof(enum update_mode));
	/*
	 * Keep trailing slash, needed to let
	 * "git mv file no-such-dir/" error out.
	 */
	dest_path = internal_copy_pathspec(prefix, argv + argc, 1,
					   KEEP_TRAILING_SLASH);
	submodule_gitfile = xcalloc(argc, sizeof(char *));

	if (dest_path[0][0] == '\0')
		/* special case: "." was normalized to "" */
		destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
	else if (!lstat(dest_path[0], &st) &&
			S_ISDIR(st.st_mode)) {
		dest_path[0] = add_slash(dest_path[0]);
		destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
	} else {
		if (argc != 1)
			die("destination '%s' is not a directory", dest_path[0]);
		destination = dest_path;
	}

	/* Checking */
	for (i = 0; i < argc; i++) {
		const char *src = source[i], *dst = destination[i];
		int length, src_is_dir;
		const char *bad = NULL;

		if (show_only)
			printf(_("Checking rename of '%s' to '%s'\n"), src, dst);

		length = strlen(src);
		if (lstat(src, &st) < 0)
			bad = _("bad source");
		else if (!strncmp(src, dst, length) &&
				(dst[length] == 0 || dst[length] == '/')) {
			bad = _("can not move directory into itself");
		} else if ((src_is_dir = S_ISDIR(st.st_mode))
				&& lstat(dst, &st) == 0)
			bad = _("cannot move directory over file");
		else if (src_is_dir) {
			int first = cache_name_pos(src, length);
			if (first >= 0) {
				struct strbuf submodule_dotgit = STRBUF_INIT;
				if (!S_ISGITLINK(active_cache[first]->ce_mode))
					die (_("Huh? Directory %s is in index and no submodule?"), src);
				if (!is_staging_gitmodules_ok())
					die (_("Please, stage your changes to .gitmodules or stash them to proceed"));
				strbuf_addf(&submodule_dotgit, "%s/.git", src);
				submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf);
				if (submodule_gitfile[i])
					submodule_gitfile[i] = xstrdup(submodule_gitfile[i]);
				else
					submodule_gitfile[i] = SUBMODULE_WITH_GITDIR;
				strbuf_release(&submodule_dotgit);
			} else {
				const char *src_w_slash = add_slash(src);
				int last, len_w_slash = length + 1;

				modes[i] = WORKING_DIRECTORY;

				first = cache_name_pos(src_w_slash, len_w_slash);
				if (first >= 0)
					die (_("Huh? %.*s is in index?"),
							len_w_slash, src_w_slash);

				first = -1 - first;
				for (last = first; last < active_nr; last++) {
					const char *path = active_cache[last]->name;
					if (strncmp(path, src_w_slash, len_w_slash))
						break;
				}
				free((char *)src_w_slash);

				if (last - first < 1)
					bad = _("source directory is empty");
				else {
					int j, dst_len;

					if (last - first > 0) {
						source = xrealloc(source,
								(argc + last - first)
								* sizeof(char *));
						destination = xrealloc(destination,
								(argc + last - first)
								* sizeof(char *));
						modes = xrealloc(modes,
								(argc + last - first)
								* sizeof(enum update_mode));
					}

					dst = add_slash(dst);
					dst_len = strlen(dst);

					for (j = 0; j < last - first; j++) {
						const char *path =
							active_cache[first + j]->name;
						source[argc + j] = path;
						destination[argc + j] =
							prefix_path(dst, dst_len,
								path + length + 1);
						modes[argc + j] = INDEX;
					}
					argc += last - first;
				}
			}
		} else if (cache_name_pos(src, length) < 0)
			bad = _("not under version control");
		else if (lstat(dst, &st) == 0) {
			bad = _("destination exists");
			if (force) {
				/*
				 * only files can overwrite each other:
				 * check both source and destination
				 */
				if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
					if (verbose)
						warning(_("overwriting '%s'"), dst);
					bad = NULL;
				} else
					bad = _("Cannot overwrite");
			}
		} else if (string_list_has_string(&src_for_dst, dst))
			bad = _("multiple sources for the same target");
		else if (is_dir_sep(dst[strlen(dst) - 1]))
			bad = _("destination directory does not exist");
		else
			string_list_insert(&src_for_dst, dst);

		if (bad) {
			if (ignore_errors) {
				if (--argc > 0) {
					memmove(source + i, source + i + 1,
						(argc - i) * sizeof(char *));
					memmove(destination + i,
						destination + i + 1,
						(argc - i) * sizeof(char *));
					i--;
				}
			} else
				die (_("%s, source=%s, destination=%s"),
				     bad, src, dst);
		}
	}

	for (i = 0; i < argc; i++) {
		const char *src = source[i], *dst = destination[i];
		enum update_mode mode = modes[i];
		int pos;
		if (show_only || verbose)
			printf(_("Renaming %s to %s\n"), src, dst);
		if (!show_only && mode != INDEX) {
			if (rename(src, dst) < 0 && !ignore_errors)
				die_errno (_("renaming '%s' failed"), src);
			if (submodule_gitfile[i]) {
				if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR)
					connect_work_tree_and_git_dir(dst, submodule_gitfile[i]);
				if (!update_path_in_gitmodules(src, dst))
					gitmodules_modified = 1;
			}
		}

		if (mode == WORKING_DIRECTORY)
			continue;

		pos = cache_name_pos(src, strlen(src));
		assert(pos >= 0);
		if (!show_only)
			rename_cache_entry_at(pos, dst);
	}

	if (gitmodules_modified)
		stage_updated_gitmodules();

	if (active_cache_changed) {
		if (write_cache(newfd, active_cache, active_nr) ||
		    commit_locked_index(&lock_file))
			die(_("Unable to write new index file"));
	}

	return 0;
}
Beispiel #3
0
static int merge_working_tree(const struct checkout_opts *opts,
			      struct branch_info *old_branch_info,
			      struct branch_info *new_branch_info,
			      int *writeout_error)
{
	int ret;
	struct lock_file lock_file = LOCK_INIT;

	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 (ret)
			return ret;
	} else {
		struct tree_desc trees[2];
		struct tree *tree;
		struct unpack_trees_options topts;

		memset(&topts, 0, sizeof(topts));
		topts.head_idx = -1;
		topts.src_index = &the_index;
		topts.dst_index = &the_index;

		setup_unpack_trees_porcelain(&topts, "checkout");

		refresh_cache(REFRESH_QUIET);

		if (unmerged_cache()) {
			error(_("you need to resolve your current index first"));
			return 1;
		}

		/* 2-way merge to the new branch */
		topts.initial_checkout = is_cache_unborn();
		topts.update = 1;
		topts.merge = 1;
		topts.gently = opts->merge && old_branch_info->commit;
		topts.verbose_update = opts->show_progress;
		topts.fn = twoway_merge;
		if (opts->overwrite_ignore) {
			topts.dir = xcalloc(1, sizeof(*topts.dir));
			topts.dir->flags |= DIR_SHOW_IGNORED;
			setup_standard_excludes(topts.dir);
		}
		tree = parse_tree_indirect(old_branch_info->commit ?
					   &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);
		init_tree_desc(&trees[1], tree->buffer, tree->size);

		ret = unpack_trees(2, trees, &topts);
		if (ret == -1) {
			/*
			 * Unpack couldn't do a trivial merge; either
			 * give up or do a real merge, depending on
			 * whether the merge flag was used.
			 */
			struct tree *result;
			struct tree *work;
			struct merge_options o;
			if (!opts->merge)
				return 1;

			/*
			 * Without old_branch_info->commit, the below is the same as
			 * the two-tree unpack we already tried and failed.
			 */
			if (!old_branch_info->commit)
				return 1;

			/* Do more real merge */

			/*
			 * We update the index fully, then write the
			 * tree from the index, then merge the new
			 * branch with the current tree, with the old
			 * branch as the base. Then we reset the index
			 * (but not the working tree) to the new
			 * branch, leaving the working tree as the
			 * merged version, but skipping unmerged
			 * entries in the index.
			 */

			add_files_to_cache(NULL, NULL, 0);
			/*
			 * NEEDSWORK: carrying over local changes
			 * when branches have different end-of-line
			 * normalization (or clean+smudge rules) is
			 * a pain; plumb in an option to set
			 * o.renormalize?
			 */
			init_merge_options(&o);
			o.verbosity = 0;
			work = write_tree_from_memory(&o);

			ret = reset_tree(get_commit_tree(new_branch_info->commit),
					 opts, 1,
					 writeout_error);
			if (ret)
				return ret;
			o.ancestor = old_branch_info->name;
			o.branch1 = new_branch_info->name;
			o.branch2 = "local";
			ret = merge_trees(&o,
					  get_commit_tree(new_branch_info->commit),
					  work,
					  get_commit_tree(old_branch_info->commit),
					  &result);
			if (ret < 0)
				exit(128);
			ret = reset_tree(get_commit_tree(new_branch_info->commit),
					 opts, 0,
					 writeout_error);
			strbuf_release(&o.obuf);
			if (ret)
				return ret;
		}
	}

	if (!active_cache_tree)
		active_cache_tree = cache_tree();

	if (!cache_tree_fully_valid(active_cache_tree))
		cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);

	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
		die(_("unable to write new index file"));

	if (!opts->force && !opts->quiet)
		show_local_changes(&new_branch_info->commit->object, &opts->diff_options);

	return 0;
}
Beispiel #4
0
int cmd_mv(int argc, const char **argv, const char *prefix)
{
	int i, flags, gitmodules_modified = 0;
	int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
	struct option builtin_mv_options[] = {
		OPT__VERBOSE(&verbose, N_("be verbose")),
		OPT__DRY_RUN(&show_only, N_("dry run")),
		OPT__FORCE(&force, N_("force move/rename even if target exists"),
			   PARSE_OPT_NOCOMPLETE),
		OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")),
		OPT_END(),
	};
	const char **source, **destination, **dest_path, **submodule_gitfile;
	enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
	struct stat st;
	struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
	struct lock_file lock_file = LOCK_INIT;

	git_config(git_default_config, NULL);

	argc = parse_options(argc, argv, prefix, builtin_mv_options,
			     builtin_mv_usage, 0);
	if (--argc < 1)
		usage_with_options(builtin_mv_usage, builtin_mv_options);

	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
	if (read_cache() < 0)
		die(_("index file corrupt"));

	source = internal_prefix_pathspec(prefix, argv, argc, 0);
	modes = xcalloc(argc, sizeof(enum update_mode));
	/*
	 * Keep trailing slash, needed to let
	 * "git mv file no-such-dir/" error out, except in the case
	 * "git mv directory no-such-dir/".
	 */
	flags = KEEP_TRAILING_SLASH;
	if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1]))
		flags = 0;
	dest_path = internal_prefix_pathspec(prefix, argv + argc, 1, flags);
	submodule_gitfile = xcalloc(argc, sizeof(char *));

	if (dest_path[0][0] == '\0')
		/* special case: "." was normalized to "" */
		destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
	else if (!lstat(dest_path[0], &st) &&
			S_ISDIR(st.st_mode)) {
		dest_path[0] = add_slash(dest_path[0]);
		destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
	} else {
		if (argc != 1)
			die(_("destination '%s' is not a directory"), dest_path[0]);
		destination = dest_path;
	}

	/* Checking */
	for (i = 0; i < argc; i++) {
		const char *src = source[i], *dst = destination[i];
		int length, src_is_dir;
		const char *bad = NULL;

		if (show_only)
			printf(_("Checking rename of '%s' to '%s'\n"), src, dst);

		length = strlen(src);
		if (lstat(src, &st) < 0)
			bad = _("bad source");
		else if (!strncmp(src, dst, length) &&
				(dst[length] == 0 || dst[length] == '/')) {
			bad = _("can not move directory into itself");
		} else if ((src_is_dir = S_ISDIR(st.st_mode))
				&& lstat(dst, &st) == 0)
			bad = _("cannot move directory over file");
		else if (src_is_dir) {
			int first = cache_name_pos(src, length), last;

			if (first >= 0)
				prepare_move_submodule(src, first,
						       submodule_gitfile + i);
			else if (index_range_of_same_dir(src, length,
							 &first, &last) < 1)
				bad = _("source directory is empty");
			else { /* last - first >= 1 */
				int j, dst_len, n;

				modes[i] = WORKING_DIRECTORY;
				n = argc + last - first;
				REALLOC_ARRAY(source, n);
				REALLOC_ARRAY(destination, n);
				REALLOC_ARRAY(modes, n);
				REALLOC_ARRAY(submodule_gitfile, n);

				dst = add_slash(dst);
				dst_len = strlen(dst);

				for (j = 0; j < last - first; j++) {
					const char *path = active_cache[first + j]->name;
					source[argc + j] = path;
					destination[argc + j] =
						prefix_path(dst, dst_len, path + length + 1);
					modes[argc + j] = INDEX;
					submodule_gitfile[argc + j] = NULL;
				}
				argc += last - first;
			}
		} else if (cache_name_pos(src, length) < 0)
			bad = _("not under version control");
		else if (lstat(dst, &st) == 0 &&
			 (!ignore_case || strcasecmp(src, dst))) {
			bad = _("destination exists");
			if (force) {
				/*
				 * only files can overwrite each other:
				 * check both source and destination
				 */
				if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
					if (verbose)
						warning(_("overwriting '%s'"), dst);
					bad = NULL;
				} else
					bad = _("Cannot overwrite");
			}
		} else if (string_list_has_string(&src_for_dst, dst))
			bad = _("multiple sources for the same target");
		else if (is_dir_sep(dst[strlen(dst) - 1]))
			bad = _("destination directory does not exist");
		else
			string_list_insert(&src_for_dst, dst);

		if (!bad)
			continue;
		if (!ignore_errors)
			die(_("%s, source=%s, destination=%s"),
			     bad, src, dst);
		if (--argc > 0) {
			int n = argc - i;
			memmove(source + i, source + i + 1,
				n * sizeof(char *));
			memmove(destination + i, destination + i + 1,
				n * sizeof(char *));
			memmove(modes + i, modes + i + 1,
				n * sizeof(enum update_mode));
			memmove(submodule_gitfile + i, submodule_gitfile + i + 1,
				n * sizeof(char *));
			i--;
		}
	}

	for (i = 0; i < argc; i++) {
		const char *src = source[i], *dst = destination[i];
		enum update_mode mode = modes[i];
		int pos;
		if (show_only || verbose)
			printf(_("Renaming %s to %s\n"), src, dst);
		if (show_only)
			continue;
		if (mode != INDEX && rename(src, dst) < 0) {
			if (ignore_errors)
				continue;
			die_errno(_("renaming '%s' failed"), src);
		}
		if (submodule_gitfile[i]) {
			if (!update_path_in_gitmodules(src, dst))
				gitmodules_modified = 1;
			if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR)
				connect_work_tree_and_git_dir(dst,
							      submodule_gitfile[i],
							      1);
		}

		if (mode == WORKING_DIRECTORY)
			continue;

		pos = cache_name_pos(src, strlen(src));
		assert(pos >= 0);
		rename_cache_entry_at(pos, dst);
	}

	if (gitmodules_modified)
		stage_updated_gitmodules(&the_index);

	if (write_locked_index(&the_index, &lock_file,
			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
		die(_("Unable to write new index file"));

	return 0;
}
Beispiel #5
0
int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
{
	int i, stage = 0;
	unsigned char sha1[20];
	struct tree_desc t[MAX_UNPACK_TREES];
	struct unpack_trees_options opts;
	int prefix_set = 0;
	const struct option read_tree_options[] = {
		{ OPTION_CALLBACK, 0, "index-output", NULL, N_("file"),
		  N_("write resulting index to <file>"),
		  PARSE_OPT_NONEG, index_output_cb },
		OPT_SET_INT(0, "empty", &read_empty,
			    N_("only empty the index"), 1),
		OPT__VERBOSE(&opts.verbose_update, N_("be verbose")),
		OPT_GROUP(N_("Merging")),
		OPT_SET_INT('m', NULL, &opts.merge,
			    N_("perform a merge in addition to a read"), 1),
		OPT_SET_INT(0, "trivial", &opts.trivial_merges_only,
			    N_("3-way merge if no file level merging required"), 1),
		OPT_SET_INT(0, "aggressive", &opts.aggressive,
			    N_("3-way merge in presence of adds and removes"), 1),
		OPT_SET_INT(0, "reset", &opts.reset,
			    N_("same as -m, but discard unmerged entries"), 1),
		{ OPTION_STRING, 0, "prefix", &opts.prefix, N_("<subdirectory>/"),
		  N_("read the tree into the index under <subdirectory>/"),
		  PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP },
		OPT_SET_INT('u', NULL, &opts.update,
			    N_("update working tree with merge result"), 1),
		{ OPTION_CALLBACK, 0, "exclude-per-directory", &opts,
		  N_("gitignore"),
		  N_("allow explicitly ignored files to be overwritten"),
		  PARSE_OPT_NONEG, exclude_per_directory_cb },
		OPT_SET_INT('i', NULL, &opts.index_only,
			    N_("don't check the working tree after merging"), 1),
		OPT__DRY_RUN(&opts.dry_run, N_("don't update the index or the work tree")),
		OPT_SET_INT(0, "no-sparse-checkout", &opts.skip_sparse_checkout,
			    N_("skip applying sparse checkout filter"), 1),
		OPT_SET_INT(0, "debug-unpack", &opts.debug_unpack,
			    N_("debug unpack-trees"), 1),
		OPT_END()
	};

	memset(&opts, 0, sizeof(opts));
	opts.head_idx = -1;
	opts.src_index = &the_index;
	opts.dst_index = &the_index;

	git_config(git_default_config, NULL);

	argc = parse_options(argc, argv, unused_prefix, read_tree_options,
			     read_tree_usage, 0);

	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);

	prefix_set = opts.prefix ? 1 : 0;
	if (1 < opts.merge + opts.reset + prefix_set)
		die("Which one? -m, --reset, or --prefix?");

	/*
	 * NEEDSWORK
	 *
	 * The old index should be read anyway even if we're going to
	 * destroy all index entries because we still need to preserve
	 * certain information such as index version or split-index
	 * mode.
	 */

	if (opts.reset || opts.merge || opts.prefix) {
		if (read_cache_unmerged() && (opts.prefix || opts.merge))
			die("You need to resolve your current index first");
		stage = opts.merge = 1;
	}
	resolve_undo_clear();

	for (i = 0; i < argc; i++) {
		const char *arg = argv[i];

		if (get_sha1(arg, sha1))
			die("Not a valid object name %s", arg);
		if (list_tree(sha1) < 0)
			die("failed to unpack tree object %s", arg);
		stage++;
	}
	if (nr_trees == 0 && !read_empty)
		warning("read-tree: emptying the index with no arguments is deprecated; use --empty");
	else if (nr_trees > 0 && read_empty)
		die("passing trees as arguments contradicts --empty");

	if (1 < opts.index_only + opts.update)
		die("-u and -i at the same time makes no sense");
	if ((opts.update || opts.index_only) && !opts.merge)
		die("%s is meaningless without -m, --reset, or --prefix",
		    opts.update ? "-u" : "-i");
	if ((opts.dir && !opts.update))
		die("--exclude-per-directory is meaningless unless -u");
	if (opts.merge && !opts.index_only)
		setup_work_tree();

	if (opts.merge) {
		if (stage < 2)
			die("just how do you expect me to merge %d trees?", stage-1);
		switch (stage - 1) {
		case 1:
			opts.fn = opts.prefix ? bind_merge : oneway_merge;
			break;
		case 2:
			opts.fn = twoway_merge;
			opts.initial_checkout = is_cache_unborn();
			break;
		case 3:
		default:
			opts.fn = threeway_merge;
			break;
		}

		if (stage - 1 >= 3)
			opts.head_idx = stage - 2;
		else
			opts.head_idx = 1;
	}

	if (opts.debug_unpack)
		opts.fn = debug_merge;

	cache_tree_free(&active_cache_tree);
	for (i = 0; i < nr_trees; i++) {
		struct tree *tree = trees[i];
		parse_tree(tree);
		init_tree_desc(t+i, tree->buffer, tree->size);
	}
	if (unpack_trees(nr_trees, t, &opts))
		return 128;

	if (opts.debug_unpack || opts.dry_run)
		return 0; /* do not write the index out */

	/*
	 * When reading only one tree (either the most basic form,
	 * "-m ent" or "--reset ent" form), we can obtain a fully
	 * valid cache-tree because the index must match exactly
	 * what came from the tree.
	 */
	if (nr_trees == 1 && !opts.prefix)
		prime_cache_tree(&the_index, trees[0]);

	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
		die("unable to write new index file");
	return 0;
}
Beispiel #6
0
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;

	if (opts->track != BRANCH_TRACK_UNSPECIFIED)
		die(_("'%s' cannot be used with updating paths"), "--track");

	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->force_detach)
		die(_("'%s' cannot be used with updating paths"), "--detach");

	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->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);

	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
	if (read_cache_preload(&opts->pathspec) < 0)
		return error(_("index file corrupt"));

	if (opts->source_tree)
		read_tree_some(opts->source_tree, &opts->pathspec);

	ps_matched = xcalloc(opts->pathspec.nr, 1);

	/*
	 * Make sure all pathspecs participated in locating the paths
	 * to be checked out.
	 */
	for (pos = 0; pos < active_nr; pos++) {
		struct cache_entry *ce = active_cache[pos];
		ce->ce_flags &= ~CE_MATCHED;
		if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
			continue;
		if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
			/*
			 * "git checkout tree-ish -- path", but this entry
			 * is in the original index; it will not be checked
			 * out to the working tree and it does not matter
			 * if pathspec matched this entry.  We will not do
			 * anything to this entry at all.
			 */
			continue;
		/*
		 * Either this entry came from the tree-ish we are
		 * checking the paths out of, or we are checking out
		 * of the index.
		 *
		 * If it comes from the tree-ish, we already know it
		 * matches the pathspec and could just stamp
		 * CE_MATCHED to it from update_some(). But we still
		 * need ps_matched and read_tree_recursive (and
		 * eventually tree_entry_interesting) cannot fill
		 * ps_matched yet. Once it can, we can avoid calling
		 * match_pathspec() for _all_ entries when
		 * opts->source_tree != NULL.
		 */
		if (ce_path_match(ce, &opts->pathspec, ps_matched))
			ce->ce_flags |= CE_MATCHED;
	}

	if (report_path_error(ps_matched, &opts->pathspec, opts->prefix)) {
		free(ps_matched);
		return 1;
	}
	free(ps_matched);

	/* "checkout -m path" to recreate conflicted state */
	if (opts->merge)
		unmerge_marked_index(&the_index);

	/* Any unmerged paths? */
	for (pos = 0; pos < active_nr; pos++) {
		const struct cache_entry *ce = active_cache[pos];
		if (ce->ce_flags & CE_MATCHED) {
			if (!ce_stage(ce))
				continue;
			if (opts->force) {
				warning(_("path '%s' is unmerged"), ce->name);
			} else if (opts->writeout_stage) {
				errs |= check_stage(opts->writeout_stage, ce, pos);
			} else if (opts->merge) {
				errs |= check_stages((1<<2) | (1<<3), ce, pos);
			} else {
				errs = 1;
				error(_("path '%s' is unmerged"), ce->name);
			}
			pos = skip_same_name(ce, pos) - 1;
		}
	}
	if (errs)
		return 1;

	/* Now we are committed to check them out */
	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);
				continue;
			}
			if (opts->writeout_stage)
				errs |= checkout_stage(opts->writeout_stage, ce, pos, &state);
			else if (opts->merge)
				errs |= checkout_merged(pos, &state);
			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, NULL);
	head = lookup_commit_reference_gently(&rev, 1);

	errs |= post_checkout_hook(head, head, 0);
	return errs;
}
Beispiel #7
0
int cmd_status(int argc, const char **argv, const char *prefix)
{
	static struct wt_status s;
	int fd;
	unsigned char sha1[20];
	static struct option builtin_status_options[] = {
		OPT__VERBOSE(&verbose, N_("be verbose")),
		OPT_SET_INT('s', "short", &status_format,
			    N_("show status concisely"), STATUS_FORMAT_SHORT),
		OPT_BOOLEAN('b', "branch", &s.show_branch,
			    N_("show branch information")),
		OPT_SET_INT(0, "porcelain", &status_format,
			    N_("machine-readable output"),
			    STATUS_FORMAT_PORCELAIN),
		OPT_SET_INT(0, "long", &status_format,
			    N_("show status in long format (default)"),
			    STATUS_FORMAT_LONG),
		OPT_BOOLEAN('z', "null", &s.null_termination,
			    N_("terminate entries with NUL")),
		{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg,
		  N_("mode"),
		  N_("show untracked files, optional modes: all, normal, no. (Default: all)"),
		  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
		OPT_BOOLEAN(0, "ignored", &show_ignored_in_status,
			    N_("show ignored files")),
		{ OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"),
		  N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"),
		  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
		OPT_COLUMN(0, "column", &s.colopts, N_("list untracked files in columns")),
		OPT_END(),
	};

	if (argc == 2 && !strcmp(argv[1], "-h"))
		usage_with_options(builtin_status_usage, builtin_status_options);

	wt_status_prepare(&s);
	gitmodules_config();
	git_config(git_status_config, &s);
	determine_whence(&s);
	argc = parse_options(argc, argv, prefix,
			     builtin_status_options,
			     builtin_status_usage, 0);
	finalize_colopts(&s.colopts, -1);

	if (s.null_termination) {
		if (status_format == STATUS_FORMAT_NONE)
			status_format = STATUS_FORMAT_PORCELAIN;
		else if (status_format == STATUS_FORMAT_LONG)
			die(_("--long and -z are incompatible"));
	}

	handle_untracked_files_arg(&s);
	if (show_ignored_in_status)
		s.show_ignored_files = 1;
	if (*argv)
		s.pathspec = get_pathspec(prefix, argv);

	read_cache_preload(s.pathspec);
	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);

	fd = hold_locked_index(&index_lock, 0);
	if (0 <= fd)
		update_index_if_able(&the_index, &index_lock);

	s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
	s.ignore_submodule_arg = ignore_submodule_arg;
	wt_status_collect(&s);

	if (s.relative_paths)
		s.prefix = prefix;

	switch (status_format) {
	case STATUS_FORMAT_SHORT:
		wt_shortstatus_print(&s);
		break;
	case STATUS_FORMAT_PORCELAIN:
		wt_porcelain_print(&s);
		break;
	case STATUS_FORMAT_NONE:
	case STATUS_FORMAT_LONG:
		s.verbose = verbose;
		s.ignore_submodule_arg = ignore_submodule_arg;
		wt_status_print(&s);
		break;
	}
	return 0;
}
Beispiel #8
0
static char *prepare_index(int argc, const char **argv, const char *prefix,
			   const struct commit *current_head, int is_status)
{
	int fd;
	struct string_list partial;
	const char **pathspec = NULL;
	char *old_index_env = NULL;
	int refresh_flags = REFRESH_QUIET;

	if (is_status)
		refresh_flags |= REFRESH_UNMERGED;

	if (*argv)
		pathspec = get_pathspec(prefix, argv);

	if (read_cache_preload(pathspec) < 0)
		die(_("index file corrupt"));

	if (interactive) {
		fd = hold_locked_index(&index_lock, 1);

		refresh_cache_or_die(refresh_flags);

		if (write_cache(fd, active_cache, active_nr) ||
		    close_lock_file(&index_lock))
			die(_("unable to create temporary index"));

		old_index_env = getenv(INDEX_ENVIRONMENT);
		setenv(INDEX_ENVIRONMENT, index_lock.filename, 1);

		if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
			die(_("interactive add failed"));

		if (old_index_env && *old_index_env)
			setenv(INDEX_ENVIRONMENT, old_index_env, 1);
		else
			unsetenv(INDEX_ENVIRONMENT);

		discard_cache();
		read_cache_from(index_lock.filename);

		commit_style = COMMIT_NORMAL;
		return index_lock.filename;
	}

	/*
	 * Non partial, non as-is commit.
	 *
	 * (1) get the real index;
	 * (2) update the_index as necessary;
	 * (3) write the_index out to the real index (still locked);
	 * (4) return the name of the locked index file.
	 *
	 * The caller should run hooks on the locked real index, and
	 * (A) if all goes well, commit the real index;
	 * (B) on failure, rollback the real index.
	 */
	if (all || (also && pathspec && *pathspec)) {
		fd = hold_locked_index(&index_lock, 1);
		add_files_to_cache(also ? prefix : NULL, pathspec, 0);
		refresh_cache_or_die(refresh_flags);
		update_main_cache_tree(WRITE_TREE_SILENT);
		if (write_cache(fd, active_cache, active_nr) ||
		    close_lock_file(&index_lock))
			die(_("unable to write new_index file"));
		commit_style = COMMIT_NORMAL;
		return index_lock.filename;
	}

	/*
	 * As-is commit.
	 *
	 * (1) return the name of the real index file.
	 *
	 * The caller should run hooks on the real index,
	 * and create commit from the_index.
	 * We still need to refresh the index here.
	 */
	if (!only && (!pathspec || !*pathspec)) {
		fd = hold_locked_index(&index_lock, 1);
		refresh_cache_or_die(refresh_flags);
		if (active_cache_changed) {
			update_main_cache_tree(WRITE_TREE_SILENT);
			if (write_cache(fd, active_cache, active_nr) ||
			    commit_locked_index(&index_lock))
				die(_("unable to write new_index file"));
		} else {
			rollback_lock_file(&index_lock);
		}
		commit_style = COMMIT_AS_IS;
		return get_index_file();
	}

	/*
	 * A partial commit.
	 *
	 * (0) find the set of affected paths;
	 * (1) get lock on the real index file;
	 * (2) update the_index with the given paths;
	 * (3) write the_index out to the real index (still locked);
	 * (4) get lock on the false index file;
	 * (5) reset the_index from HEAD;
	 * (6) update the_index the same way as (2);
	 * (7) write the_index out to the false index file;
	 * (8) return the name of the false index file (still locked);
	 *
	 * The caller should run hooks on the locked false index, and
	 * create commit from it.  Then
	 * (A) if all goes well, commit the real index;
	 * (B) on failure, rollback the real index;
	 * In either case, rollback the false index.
	 */
	commit_style = COMMIT_PARTIAL;

	if (whence != FROM_COMMIT) {
		if (whence == FROM_MERGE)
			die(_("cannot do a partial commit during a merge."));
		else if (whence == FROM_CHERRY_PICK)
			die(_("cannot do a partial commit during a cherry-pick."));
	}

	memset(&partial, 0, sizeof(partial));
	partial.strdup_strings = 1;
	if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, pathspec))
		exit(1);

	discard_cache();
	if (read_cache() < 0)
		die(_("cannot read the index"));

	fd = hold_locked_index(&index_lock, 1);
	add_remove_files(&partial);
	refresh_cache(REFRESH_QUIET);
	if (write_cache(fd, active_cache, active_nr) ||
	    close_lock_file(&index_lock))
		die(_("unable to write new_index file"));

	fd = hold_lock_file_for_update(&false_lock,
				       git_path("next-index-%"PRIuMAX,
						(uintmax_t) getpid()),
				       LOCK_DIE_ON_ERROR);

	create_base_index(current_head);
	add_remove_files(&partial);
	refresh_cache(REFRESH_QUIET);

	if (write_cache(fd, active_cache, active_nr) ||
	    close_lock_file(&false_lock))
		die(_("unable to write temporary index file"));

	discard_cache();
	read_cache_from(false_lock.filename);

	return false_lock.filename;
}
Beispiel #9
0
int cmd_clone(int argc, const char **argv, const char *prefix)
{
	int is_bundle = 0, is_local;
	struct stat buf;
	const char *repo_name, *repo, *work_tree, *git_dir;
	char *path, *dir;
	int dest_exists;
	const struct ref *refs, *remote_head;
	const struct ref *remote_head_points_at;
	const struct ref *our_head_points_at;
	struct ref *mapped_refs;
	struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
	struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
	struct transport *transport = NULL;
	char *src_ref_prefix = "refs/heads/";
	int err = 0;

	struct refspec *refspec;
	const char *fetch_pattern;

	junk_pid = getpid();

	packet_trace_identity("clone");
	argc = parse_options(argc, argv, prefix, builtin_clone_options,
			     builtin_clone_usage, 0);

	if (argc > 2)
		usage_msg_opt(_("Too many arguments."),
			builtin_clone_usage, builtin_clone_options);

	if (argc == 0)
		usage_msg_opt(_("You must specify a repository to clone."),
			builtin_clone_usage, builtin_clone_options);

	if (option_mirror)
		option_bare = 1;

	if (option_bare) {
		if (option_origin)
			die(_("--bare and --origin %s options are incompatible."),
			    option_origin);
		option_no_checkout = 1;
	}

	if (!option_origin)
		option_origin = "origin";

	repo_name = argv[0];

	path = get_repo_path(repo_name, &is_bundle);
	if (path)
		repo = xstrdup(absolute_path(repo_name));
	else if (!strchr(repo_name, ':'))
		die(_("repository '%s' does not exist"), repo_name);
	else
		repo = repo_name;
	is_local = path && !is_bundle;
	if (is_local && option_depth)
		warning(_("--depth is ignored in local clones; use file:// instead."));

	if (argc == 2)
		dir = xstrdup(argv[1]);
	else
		dir = guess_dir_name(repo_name, is_bundle, option_bare);
	strip_trailing_slashes(dir);

	dest_exists = !stat(dir, &buf);
	if (dest_exists && !is_empty_dir(dir))
		die(_("destination path '%s' already exists and is not "
			"an empty directory."), dir);

	strbuf_addf(&reflog_msg, "clone: from %s", repo);

	if (option_bare)
		work_tree = NULL;
	else {
		work_tree = getenv("GIT_WORK_TREE");
		if (work_tree && !stat(work_tree, &buf))
			die(_("working tree '%s' already exists."), work_tree);
	}

	if (option_bare || work_tree)
		git_dir = xstrdup(dir);
	else {
		work_tree = dir;
		git_dir = xstrdup(mkpath("%s/.git", dir));
	}

	if (!option_bare) {
		junk_work_tree = work_tree;
		if (safe_create_leading_directories_const(work_tree) < 0)
			die_errno(_("could not create leading directories of '%s'"),
				  work_tree);
		if (!dest_exists && mkdir(work_tree, 0755))
			die_errno(_("could not create work tree dir '%s'."),
				  work_tree);
		set_git_work_tree(work_tree);
	}
	junk_git_dir = git_dir;
	atexit(remove_junk);
	sigchain_push_common(remove_junk_on_signal);

	setenv(CONFIG_ENVIRONMENT, mkpath("%s/config", git_dir), 1);

	if (safe_create_leading_directories_const(git_dir) < 0)
		die(_("could not create leading directories of '%s'"), git_dir);

	set_git_dir_init(git_dir, real_git_dir, 0);
	if (real_git_dir)
		git_dir = real_git_dir;

	if (0 <= option_verbosity) {
		if (option_bare)
			printf(_("Cloning into bare repository %s...\n"), dir);
		else
			printf(_("Cloning into %s...\n"), dir);
	}
	init_db(option_template, INIT_DB_QUIET);

	/*
	 * At this point, the config exists, so we do not need the
	 * environment variable.  We actually need to unset it, too, to
	 * re-enable parsing of the global configs.
	 */
	unsetenv(CONFIG_ENVIRONMENT);

	git_config(git_default_config, NULL);

	if (option_bare) {
		if (option_mirror)
			src_ref_prefix = "refs/";
		strbuf_addstr(&branch_top, src_ref_prefix);

		git_config_set("core.bare", "true");
	} else {
		strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin);
	}

	strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf);

	if (option_mirror || !option_bare) {
		/* Configure the remote */
		strbuf_addf(&key, "remote.%s.fetch", option_origin);
		git_config_set_multivar(key.buf, value.buf, "^$", 0);
		strbuf_reset(&key);

		if (option_mirror) {
			strbuf_addf(&key, "remote.%s.mirror", option_origin);
			git_config_set(key.buf, "true");
			strbuf_reset(&key);
		}
	}

	strbuf_addf(&key, "remote.%s.url", option_origin);
	git_config_set(key.buf, repo);
	strbuf_reset(&key);

	if (option_reference)
		setup_reference(git_dir);

	fetch_pattern = value.buf;
	refspec = parse_fetch_refspec(1, &fetch_pattern);

	strbuf_reset(&value);

	if (is_local) {
		refs = clone_local(path, git_dir);
		mapped_refs = wanted_peer_refs(refs, refspec);
	} else {
		struct remote *remote = remote_get(option_origin);
		transport = transport_get(remote, remote->url[0]);

		if (!transport->get_refs_list || !transport->fetch)
			die(_("Don't know how to clone %s"), transport->url);

		transport_set_option(transport, TRANS_OPT_KEEP, "yes");

		if (option_depth)
			transport_set_option(transport, TRANS_OPT_DEPTH,
					     option_depth);

		transport_set_verbosity(transport, option_verbosity, option_progress);

		if (option_upload_pack)
			transport_set_option(transport, TRANS_OPT_UPLOADPACK,
					     option_upload_pack);

		refs = transport_get_remote_refs(transport);
		if (refs) {
			mapped_refs = wanted_peer_refs(refs, refspec);
			transport_fetch_refs(transport, mapped_refs);
		}
	}

	if (refs) {
		clear_extra_refs();

		write_remote_refs(mapped_refs);

		remote_head = find_ref_by_name(refs, "HEAD");
		remote_head_points_at =
			guess_remote_head(remote_head, mapped_refs, 0);

		if (option_branch) {
			struct strbuf head = STRBUF_INIT;
			strbuf_addstr(&head, src_ref_prefix);
			strbuf_addstr(&head, option_branch);
			our_head_points_at =
				find_ref_by_name(mapped_refs, head.buf);
			strbuf_release(&head);

			if (!our_head_points_at) {
				warning(_("Remote branch %s not found in "
					"upstream %s, using HEAD instead"),
					option_branch, option_origin);
				our_head_points_at = remote_head_points_at;
			}
		}
		else
			our_head_points_at = remote_head_points_at;
	}
	else {
		warning(_("You appear to have cloned an empty repository."));
		our_head_points_at = NULL;
		remote_head_points_at = NULL;
		remote_head = NULL;
		option_no_checkout = 1;
		if (!option_bare)
			install_branch_config(0, "master", option_origin,
					      "refs/heads/master");
	}

	if (remote_head_points_at && !option_bare) {
		struct strbuf head_ref = STRBUF_INIT;
		strbuf_addstr(&head_ref, branch_top.buf);
		strbuf_addstr(&head_ref, "HEAD");
		create_symref(head_ref.buf,
			      remote_head_points_at->peer_ref->name,
			      reflog_msg.buf);
	}

	if (our_head_points_at) {
		/* Local default branch link */
		create_symref("HEAD", our_head_points_at->name, NULL);
		if (!option_bare) {
			const char *head = skip_prefix(our_head_points_at->name,
						       "refs/heads/");
			update_ref(reflog_msg.buf, "HEAD",
				   our_head_points_at->old_sha1,
				   NULL, 0, DIE_ON_ERR);
			install_branch_config(0, head, option_origin,
					      our_head_points_at->name);
		}
	} else if (remote_head) {
		/* Source had detached HEAD pointing somewhere. */
		if (!option_bare) {
			update_ref(reflog_msg.buf, "HEAD",
				   remote_head->old_sha1,
				   NULL, REF_NODEREF, DIE_ON_ERR);
			our_head_points_at = remote_head;
		}
	} else {
		/* Nothing to checkout out */
		if (!option_no_checkout)
			warning(_("remote HEAD refers to nonexistent ref, "
				"unable to checkout.\n"));
		option_no_checkout = 1;
	}

	if (transport) {
		transport_unlock_pack(transport);
		transport_disconnect(transport);
	}

	if (!option_no_checkout) {
		struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
		struct unpack_trees_options opts;
		struct tree *tree;
		struct tree_desc t;
		int fd;

		/* We need to be in the new work tree for the checkout */
		setup_work_tree();

		fd = hold_locked_index(lock_file, 1);

		memset(&opts, 0, sizeof opts);
		opts.update = 1;
		opts.merge = 1;
		opts.fn = oneway_merge;
		opts.verbose_update = (option_verbosity > 0);
		opts.src_index = &the_index;
		opts.dst_index = &the_index;

		tree = parse_tree_indirect(our_head_points_at->old_sha1);
		parse_tree(tree);
		init_tree_desc(&t, tree->buffer, tree->size);
		unpack_trees(1, &t, &opts);

		if (write_cache(fd, active_cache, active_nr) ||
		    commit_locked_index(lock_file))
			die(_("unable to write new index file"));

		err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
				sha1_to_hex(our_head_points_at->old_sha1), "1",
				NULL);

		if (!err && option_recursive)
			err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
	}

	strbuf_release(&reflog_msg);
	strbuf_release(&branch_top);
	strbuf_release(&key);
	strbuf_release(&value);
	junk_pid = 0;
	return err;
}
Beispiel #10
0
int cmd_rm(int argc, const char **argv, const char *prefix)
{
	int i, newfd;
	struct pathspec pathspec;
	char *seen;

	gitmodules_config();
	git_config(git_default_config, NULL);

	argc = parse_options(argc, argv, prefix, builtin_rm_options,
			     builtin_rm_usage, 0);
	if (!argc)
		usage_with_options(builtin_rm_usage, builtin_rm_options);

	if (!index_only)
		setup_work_tree();

	newfd = hold_locked_index(&lock_file, 1);

	if (read_cache() < 0)
		die(_("index file corrupt"));

	parse_pathspec(&pathspec, 0,
		       PATHSPEC_PREFER_CWD |
		       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
		       prefix, argv);
	refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);

	seen = xcalloc(pathspec.nr, 1);

	for (i = 0; i < active_nr; i++) {
		const struct cache_entry *ce = active_cache[i];
		if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), 0, seen))
			continue;
		ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
		list.entry[list.nr].name = ce->name;
		list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
		if (list.entry[list.nr++].is_submodule &&
		    !is_staging_gitmodules_ok())
			die (_("Please, stage your changes to .gitmodules or stash them to proceed"));
	}

	if (pathspec.nr) {
		const char *original;
		int seen_any = 0;
		for (i = 0; i < pathspec.nr; i++) {
			original = pathspec.items[i].original;
			if (!seen[i]) {
				if (!ignore_unmatch) {
					die(_("pathspec '%s' did not match any files"),
					    original);
				}
			}
			else {
				seen_any = 1;
			}
			if (!recursive && seen[i] == MATCHED_RECURSIVELY)
				die(_("not removing '%s' recursively without -r"),
				    *original ? original : ".");
		}

		if (!seen_any)
			exit(0);
	}

	/*
	 * If not forced, the file, the index and the HEAD (if exists)
	 * must match; but the file can already been removed, since
	 * this sequence is a natural "novice" way:
	 *
	 *	rm F; git rm F
	 *
	 * Further, if HEAD commit exists, "diff-index --cached" must
	 * report no changes unless forced.
	 */
	if (!force) {
		unsigned char sha1[20];
		if (get_sha1("HEAD", sha1))
			hashclr(sha1);
		if (check_local_mod(sha1, index_only))
			exit(1);
	} else if (!index_only) {
		if (check_submodules_use_gitfiles())
			exit(1);
	}

	/*
	 * First remove the names from the index: we won't commit
	 * the index unless all of them succeed.
	 */
	for (i = 0; i < list.nr; i++) {
		const char *path = list.entry[i].name;
		if (!quiet)
			printf("rm '%s'\n", path);

		if (remove_file_from_cache(path))
			die(_("git rm: unable to remove %s"), path);
	}

	if (show_only)
		return 0;

	/*
	 * Then, unless we used "--cached", remove the filenames from
	 * the workspace. If we fail to remove the first one, we
	 * abort the "git rm" (but once we've successfully removed
	 * any file at all, we'll go ahead and commit to it all:
	 * by then we've already committed ourselves and can't fail
	 * in the middle)
	 */
	if (!index_only) {
		int removed = 0, gitmodules_modified = 0;
		for (i = 0; i < list.nr; i++) {
			const char *path = list.entry[i].name;
			if (list.entry[i].is_submodule) {
				if (is_empty_dir(path)) {
					if (!rmdir(path)) {
						removed = 1;
						if (!remove_path_from_gitmodules(path))
							gitmodules_modified = 1;
						continue;
					}
				} else {
					struct strbuf buf = STRBUF_INIT;
					strbuf_addstr(&buf, path);
					if (!remove_dir_recursively(&buf, 0)) {
						removed = 1;
						if (!remove_path_from_gitmodules(path))
							gitmodules_modified = 1;
						strbuf_release(&buf);
						continue;
					} else if (!file_exists(path))
						/* Submodule was removed by user */
						if (!remove_path_from_gitmodules(path))
							gitmodules_modified = 1;
					strbuf_release(&buf);
					/* Fallthrough and let remove_path() fail. */
				}
			}
			if (!remove_path(path)) {
				removed = 1;
				continue;
			}
			if (!removed)
				die_errno("git rm: '%s'", path);
		}
		if (gitmodules_modified)
			stage_updated_gitmodules();
	}

	if (active_cache_changed) {
		if (write_cache(newfd, active_cache, active_nr) ||
		    commit_locked_index(&lock_file))
			die(_("Unable to write new index file"));
	}

	return 0;
}
Beispiel #11
0
int cmd_add(int argc, const char **argv, const char *prefix)
{
	int exit_status = 0;
	int newfd;
	const char **pathspec;
	struct dir_struct dir;
	int flags;
	int add_new_files;
	int require_pathspec;
	char *seen = NULL;

	git_config(add_config, NULL);

	argc = parse_options(argc, argv, prefix, builtin_add_options,
			  builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
	if (patch_interactive)
		add_interactive = 1;
	if (add_interactive)
		exit(interactive_add(argc - 1, argv + 1, prefix));

	if (edit_interactive)
		return(edit_patch(argc, argv, prefix));
	argc--;
	argv++;

	if (addremove && take_worktree_changes)
		die("-A and -u are mutually incompatible");
	if (!show_only && ignore_missing)
		die("Option --ignore-missing can only be used together with --dry-run");
	if ((addremove || take_worktree_changes) && !argc) {
		static const char *here[2] = { ".", NULL };
		argc = 1;
		argv = here;
	}

	add_new_files = !take_worktree_changes && !refresh_only;
	require_pathspec = !take_worktree_changes;

	newfd = hold_locked_index(&lock_file, 1);

	flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
		 (show_only ? ADD_CACHE_PRETEND : 0) |
		 (intent_to_add ? ADD_CACHE_INTENT : 0) |
		 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
		 (!(addremove || take_worktree_changes)
		  ? ADD_CACHE_IGNORE_REMOVAL : 0));

	if (require_pathspec && argc == 0) {
		fprintf(stderr, "Nothing specified, nothing added.\n");
		fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
		return 0;
	}
	pathspec = validate_pathspec(argc, argv, prefix);

	if (read_cache() < 0)
		die("index file corrupt");
	treat_gitlinks(pathspec);

	if (add_new_files) {
		int baselen;

		/* Set up the default git porcelain excludes */
		memset(&dir, 0, sizeof(dir));
		if (!ignored_too) {
			dir.flags |= DIR_COLLECT_IGNORED;
			setup_standard_excludes(&dir);
		}

		/* This picks up the paths that are not tracked */
		baselen = fill_directory(&dir, pathspec);
		if (pathspec)
			seen = prune_directory(&dir, pathspec, baselen);
	}

	if (refresh_only) {
		refresh(verbose, pathspec);
		goto finish;
	}

	if (pathspec) {
		int i;
		if (!seen)
			seen = find_used_pathspec(pathspec);
		for (i = 0; pathspec[i]; i++) {
			if (!seen[i] && pathspec[i][0]
			    && !file_exists(pathspec[i])) {
				if (ignore_missing) {
					int dtype = DT_UNKNOWN;
					if (excluded(&dir, pathspec[i], &dtype))
						dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
				} else
					die("pathspec '%s' did not match any files",
					    pathspec[i]);
			}
		}
		free(seen);
	}

	exit_status |= add_files_to_cache(prefix, pathspec, flags);

	if (add_new_files)
		exit_status |= add_files(&dir, flags);

 finish:
	if (active_cache_changed) {
		if (write_cache(newfd, active_cache, active_nr) ||
		    commit_locked_index(&lock_file))
			die("Unable to write new index file");
	}

	return exit_status;
}
Beispiel #12
0
int cmd_mv(int argc, const char **argv, const char *prefix)
{
	int i, newfd;
	int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
	struct option builtin_mv_options[] = {
		OPT__DRY_RUN(&show_only),
		OPT_BOOLEAN('f', "force", &force, "force move/rename even if target exists"),
		OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
		OPT_END(),
	};
	const char **source, **destination, **dest_path;
	enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
	struct stat st;
	struct string_list src_for_dst = {NULL, 0, 0, 0};

	git_config(git_default_config, NULL);

	argc = parse_options(argc, argv, prefix, builtin_mv_options,
			     builtin_mv_usage, 0);
	if (--argc < 1)
		usage_with_options(builtin_mv_usage, builtin_mv_options);

	newfd = hold_locked_index(&lock_file, 1);
	if (read_cache() < 0)
		die("index file corrupt");

	source = copy_pathspec(prefix, argv, argc, 0);
	modes = xcalloc(argc, sizeof(enum update_mode));
	dest_path = copy_pathspec(prefix, argv + argc, 1, 0);

	if (dest_path[0][0] == '\0')
		/* special case: "." was normalized to "" */
		destination = copy_pathspec(dest_path[0], argv, argc, 1);
	else if (!lstat(dest_path[0], &st) &&
			S_ISDIR(st.st_mode)) {
		dest_path[0] = add_slash(dest_path[0]);
		destination = copy_pathspec(dest_path[0], argv, argc, 1);
	} else {
		if (argc != 1)
			usage_with_options(builtin_mv_usage, builtin_mv_options);
		destination = dest_path;
	}

	/* Checking */
	for (i = 0; i < argc; i++) {
		const char *src = source[i], *dst = destination[i];
		int length, src_is_dir;
		const char *bad = NULL;

		if (show_only)
			printf("Checking rename of '%s' to '%s'\n", src, dst);

		length = strlen(src);
		if (lstat(src, &st) < 0)
			bad = "bad source";
		else if (!strncmp(src, dst, length) &&
				(dst[length] == 0 || dst[length] == '/')) {
			bad = "can not move directory into itself";
		} else if ((src_is_dir = S_ISDIR(st.st_mode))
				&& lstat(dst, &st) == 0)
			bad = "cannot move directory over file";
		else if (src_is_dir) {
			const char *src_w_slash = add_slash(src);
			int len_w_slash = length + 1;
			int first, last;

			modes[i] = WORKING_DIRECTORY;

			first = cache_name_pos(src_w_slash, len_w_slash);
			if (first >= 0)
				die ("Huh? %.*s is in index?",
						len_w_slash, src_w_slash);

			first = -1 - first;
			for (last = first; last < active_nr; last++) {
				const char *path = active_cache[last]->name;
				if (strncmp(path, src_w_slash, len_w_slash))
					break;
			}
			free((char *)src_w_slash);

			if (last - first < 1)
				bad = "source directory is empty";
			else {
				int j, dst_len;

				if (last - first > 0) {
					source = xrealloc(source,
							(argc + last - first)
							* sizeof(char *));
					destination = xrealloc(destination,
							(argc + last - first)
							* sizeof(char *));
					modes = xrealloc(modes,
							(argc + last - first)
							* sizeof(enum update_mode));
				}

				dst = add_slash(dst);
				dst_len = strlen(dst);

				for (j = 0; j < last - first; j++) {
					const char *path =
						active_cache[first + j]->name;
					source[argc + j] = path;
					destination[argc + j] =
						prefix_path(dst, dst_len,
							path + length + 1);
					modes[argc + j] = INDEX;
				}
				argc += last - first;
			}
		} else if (cache_name_pos(src, length) < 0)
			bad = "not under version control";
		else if (lstat(dst, &st) == 0) {
			bad = "destination exists";
			if (force) {
				/*
				 * only files can overwrite each other:
				 * check both source and destination
				 */
				if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
					warning("%s; will overwrite!", bad);
					bad = NULL;
				} else
					bad = "Cannot overwrite";
			}
		} else if (string_list_has_string(&src_for_dst, dst))
			bad = "multiple sources for the same target";
		else
			string_list_insert(dst, &src_for_dst);

		if (bad) {
			if (ignore_errors) {
				if (--argc > 0) {
					memmove(source + i, source + i + 1,
						(argc - i) * sizeof(char *));
					memmove(destination + i,
						destination + i + 1,
						(argc - i) * sizeof(char *));
					i--;
				}
			} else
				die ("%s, source=%s, destination=%s",
				     bad, src, dst);
		}
	}

	for (i = 0; i < argc; i++) {
		const char *src = source[i], *dst = destination[i];
		enum update_mode mode = modes[i];
		int pos;
		if (show_only || verbose)
			printf("Renaming %s to %s\n", src, dst);
		if (!show_only && mode != INDEX &&
				rename(src, dst) < 0 && !ignore_errors)
			die_errno ("renaming '%s' failed", src);

		if (mode == WORKING_DIRECTORY)
			continue;

		pos = cache_name_pos(src, strlen(src));
		assert(pos >= 0);
		if (!show_only)
			rename_cache_entry_at(pos, dst);
	}

	if (active_cache_changed) {
		if (write_cache(newfd, active_cache, active_nr) ||
		    commit_locked_index(&lock_file))
			die("Unable to write new index file");
	}

	return 0;
}
Beispiel #13
0
int cmd_describe(int argc, const char **argv, const char *prefix)
{
	int contains = 0;
	struct option options[] = {
		OPT_BOOL(0, "contains",   &contains, N_("find the tag that comes after the commit")),
		OPT_BOOL(0, "debug",      &debug, N_("debug search strategy on stderr")),
		OPT_BOOL(0, "all",        &all, N_("use any ref")),
		OPT_BOOL(0, "tags",       &tags, N_("use any tag, even unannotated")),
		OPT_BOOL(0, "long",       &longformat, N_("always use long format")),
		OPT_BOOL(0, "first-parent", &first_parent, N_("only follow first parent")),
		OPT__ABBREV(&abbrev),
		OPT_SET_INT(0, "exact-match", &max_candidates,
			    N_("only output exact matches"), 0),
		OPT_INTEGER(0, "candidates", &max_candidates,
			    N_("consider <n> most recent tags (default: 10)")),
		OPT_STRING(0, "match",       &pattern, N_("pattern"),
			   N_("only consider tags matching <pattern>")),
		OPT_BOOL(0, "always",        &always,
			N_("show abbreviated commit object as fallback")),
		{OPTION_STRING, 0, "dirty",  &dirty, N_("mark"),
			N_("append <mark> on dirty working tree (default: \"-dirty\")"),
			PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
		OPT_END(),
	};

	git_config(git_default_config, NULL);
	argc = parse_options(argc, argv, prefix, options, describe_usage, 0);
	if (abbrev < 0)
		abbrev = DEFAULT_ABBREV;

	if (max_candidates < 0)
		max_candidates = 0;
	else if (max_candidates > MAX_TAGS)
		max_candidates = MAX_TAGS;

	save_commit_buffer = 0;

	if (longformat && abbrev == 0)
		die(_("--long is incompatible with --abbrev=0"));

	if (contains) {
		struct argv_array args;

		argv_array_init(&args);
		argv_array_pushl(&args, "name-rev",
				 "--peel-tag", "--name-only", "--no-undefined",
				 NULL);
		if (always)
			argv_array_push(&args, "--always");
		if (!all) {
			argv_array_push(&args, "--tags");
			if (pattern)
				argv_array_pushf(&args, "--refs=refs/tags/%s", pattern);
		}
		while (*argv) {
			argv_array_push(&args, *argv);
			argv++;
		}
		return cmd_name_rev(args.argc, args.argv, prefix);
	}

	hashmap_init(&names, (hashmap_cmp_fn) commit_name_cmp, 0);
	for_each_rawref(get_name, NULL);
	if (!names.size && !always)
		die(_("No names found, cannot describe anything."));

	if (argc == 0) {
		if (dirty) {
			static struct lock_file index_lock;
			int fd;

			read_cache_preload(NULL);
			refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
				      NULL, NULL, NULL);
			fd = hold_locked_index(&index_lock, 0);
			if (0 <= fd)
				update_index_if_able(&the_index, &index_lock);

			if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1,
					    diff_index_args, prefix))
				dirty = NULL;
		}
		describe("HEAD", 1);
	} else if (dirty) {
		die(_("--dirty is incompatible with commit-ishes"));
	} else {
		while (argc-- > 0)
			describe(*argv++, argc == 0);
	}
	return 0;
}
Beispiel #14
0
int cmd_rm(int argc, const char **argv, const char *prefix)
{
	int i, newfd;
	const char **pathspec;
	char *seen;

	git_config(git_default_config, NULL);

	argc = parse_options(argc, argv, prefix, builtin_rm_options,
			     builtin_rm_usage, 0);
	if (!argc)
		usage_with_options(builtin_rm_usage, builtin_rm_options);

	if (!index_only)
		setup_work_tree();

	newfd = hold_locked_index(&lock_file, 1);

	if (read_cache() < 0)
		die(_("index file corrupt"));

	pathspec = get_pathspec(prefix, argv);
	refresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL);

	seen = NULL;
	for (i = 0; pathspec[i] ; i++)
		/* nothing */;
	seen = xcalloc(i, 1);

	for (i = 0; i < active_nr; i++) {
		struct cache_entry *ce = active_cache[i];
		if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
			continue;
		ALLOC_GROW(list.name, list.nr + 1, list.alloc);
		list.name[list.nr++] = ce->name;
	}

	if (pathspec) {
		const char *match;
		int seen_any = 0;
		for (i = 0; (match = pathspec[i]) != NULL ; i++) {
			if (!seen[i]) {
				if (!ignore_unmatch) {
					die(_("pathspec '%s' did not match any files"),
					    match);
				}
			}
			else {
				seen_any = 1;
			}
			if (!recursive && seen[i] == MATCHED_RECURSIVELY)
				die(_("not removing '%s' recursively without -r"),
				    *match ? match : ".");
		}

		if (! seen_any)
			exit(0);
	}

	/*
	 * If not forced, the file, the index and the HEAD (if exists)
	 * must match; but the file can already been removed, since
	 * this sequence is a natural "novice" way:
	 *
	 *	rm F; git rm F
	 *
	 * Further, if HEAD commit exists, "diff-index --cached" must
	 * report no changes unless forced.
	 */
	if (!force) {
		unsigned char sha1[20];
		if (get_sha1("HEAD", sha1))
			hashclr(sha1);
		if (check_local_mod(sha1, index_only))
			exit(1);
	}

	/*
	 * First remove the names from the index: we won't commit
	 * the index unless all of them succeed.
	 */
	for (i = 0; i < list.nr; i++) {
		const char *path = list.name[i];
		if (!quiet)
			printf("rm '%s'\n", path);

		if (remove_file_from_cache(path))
			die(_("git rm: unable to remove %s"), path);
	}

	if (show_only)
		return 0;

	/*
	 * Then, unless we used "--cached", remove the filenames from
	 * the workspace. If we fail to remove the first one, we
	 * abort the "git rm" (but once we've successfully removed
	 * any file at all, we'll go ahead and commit to it all:
	 * by then we've already committed ourselves and can't fail
	 * in the middle)
	 */
	if (!index_only) {
		int removed = eeex file"));
	}
Beispiel #15
0
int cmd_clone(int argc, const char **argv, const char *prefix)
{
	int use_local_hardlinks = 1;
	int use_separate_remote = 1;
	int is_bundle = 0;
	struct stat buf;
	const char *repo_name, *repo, *work_tree, *git_dir;
	char *path, *dir;
	const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
	char branch_top[256], key[256], value[256];
	struct strbuf reflog_msg = STRBUF_INIT;
	struct transport *transport = NULL;
	char *src_ref_prefix = "refs/heads/";

	struct refspec refspec;

	junk_pid = getpid();

	argc = parse_options(argc, argv, builtin_clone_options,
			     builtin_clone_usage, 0);

	if (argc == 0)
		die("You must specify a repository to clone.");

	if (option_no_hardlinks)
		use_local_hardlinks = 0;

	if (option_mirror)
		option_bare = 1;

	if (option_bare) {
		if (option_origin)
			die("--bare and --origin %s options are incompatible.",
			    option_origin);
		option_no_checkout = 1;
		use_separate_remote = 0;
	}

	if (!option_origin)
		option_origin = "origin";

	repo_name = argv[0];

	path = get_repo_path(repo_name, &is_bundle);
	if (path)
		repo = xstrdup(make_nonrelative_path(repo_name));
	else if (!strchr(repo_name, ':'))
		repo = xstrdup(make_absolute_path(repo_name));
	else
		repo = repo_name;

	if (argc == 2)
		dir = xstrdup(argv[1]);
	else
		dir = guess_dir_name(repo_name, is_bundle, option_bare);
	strip_trailing_slashes(dir);

	if (!stat(dir, &buf))
		die("destination directory '%s' already exists.", dir);

	strbuf_addf(&reflog_msg, "clone: from %s", repo);

	if (option_bare)
		work_tree = NULL;
	else {
		work_tree = getenv("GIT_WORK_TREE");
		if (work_tree && !stat(work_tree, &buf))
			die("working tree '%s' already exists.", work_tree);
	}

	if (option_bare || work_tree)
		git_dir = xstrdup(dir);
	else {
		work_tree = dir;
		git_dir = xstrdup(mkpath("%s/.git", dir));
	}

	if (!option_bare) {
		junk_work_tree = work_tree;
		if (safe_create_leading_directories_const(work_tree) < 0)
			die("could not create leading directories of '%s': %s",
					work_tree, strerror(errno));
		if (mkdir(work_tree, 0755))
			die("could not create work tree dir '%s': %s.",
					work_tree, strerror(errno));
		set_git_work_tree(work_tree);
	}
	junk_git_dir = git_dir;
	atexit(remove_junk);
	signal(SIGINT, remove_junk_on_signal);

	setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1);

	if (safe_create_leading_directories_const(git_dir) < 0)
		die("could not create leading directories of '%s'", git_dir);
	set_git_dir(make_absolute_path(git_dir));

	init_db(option_template, option_quiet ? INIT_DB_QUIET : 0);

	/*
	 * At this point, the config exists, so we do not need the
	 * environment variable.  We actually need to unset it, too, to
	 * re-enable parsing of the global configs.
	 */
	unsetenv(CONFIG_ENVIRONMENT);

	if (option_reference)
		setup_reference(git_dir);

	git_config(git_default_config, NULL);

	if (option_bare) {
		if (option_mirror)
			src_ref_prefix = "refs/";
		strcpy(branch_top, src_ref_prefix);

		git_config_set("core.bare", "true");
	} else {
		snprintf(branch_top, sizeof(branch_top),
			 "refs/remotes/%s/", option_origin);
	}

	if (option_mirror || !option_bare) {
		/* Configure the remote */
		if (option_mirror) {
			snprintf(key, sizeof(key),
					"remote.%s.mirror", option_origin);
			git_config_set(key, "true");
		}

		snprintf(key, sizeof(key), "remote.%s.url", option_origin);
		git_config_set(key, repo);

		snprintf(key, sizeof(key), "remote.%s.fetch", option_origin);
		snprintf(value, sizeof(value),
				"+%s*:%s*", src_ref_prefix, branch_top);
		git_config_set_multivar(key, value, "^$", 0);
	}

	refspec.force = 0;
	refspec.pattern = 1;
	refspec.src = src_ref_prefix;
	refspec.dst = branch_top;

	if (path && !is_bundle)
		refs = clone_local(path, git_dir);
	else {
		struct remote *remote = remote_get(argv[0]);
		transport = transport_get(remote, remote->url[0]);

		if (!transport->get_refs_list || !transport->fetch)
			die("Don't know how to clone %s", transport->url);

		transport_set_option(transport, TRANS_OPT_KEEP, "yes");

		if (option_depth)
			transport_set_option(transport, TRANS_OPT_DEPTH,
					     option_depth);

		if (option_quiet)
			transport->verbose = -1;
		else if (option_verbose)
			transport->progress = 1;

		if (option_upload_pack)
			transport_set_option(transport, TRANS_OPT_UPLOADPACK,
					     option_upload_pack);

		refs = transport_get_remote_refs(transport);
		transport_fetch_refs(transport, refs);
	}

	clear_extra_refs();

	mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf);

	head_points_at = locate_head(refs, mapped_refs, &remote_head);

	if (head_points_at) {
		/* Local default branch link */
		create_symref("HEAD", head_points_at->name, NULL);

		if (!option_bare) {
			struct strbuf head_ref = STRBUF_INIT;
			const char *head = head_points_at->name;

			if (!prefixcmp(head, "refs/heads/"))
				head += 11;

			/* Set up the initial local branch */

			/* Local branch initial value */
			update_ref(reflog_msg.buf, "HEAD",
				   head_points_at->old_sha1,
				   NULL, 0, DIE_ON_ERR);

			strbuf_addstr(&head_ref, branch_top);
			strbuf_addstr(&head_ref, "HEAD");

			/* Remote branch link */
			create_symref(head_ref.buf,
				      head_points_at->peer_ref->name,
				      reflog_msg.buf);

			snprintf(key, sizeof(key), "branch.%s.remote", head);
			git_config_set(key, option_origin);
			snprintf(key, sizeof(key), "branch.%s.merge", head);
			git_config_set(key, head_points_at->name);
		}
	} else if (remote_head) {
		/* Source had detached HEAD pointing somewhere. */
		if (!option_bare)
			update_ref(reflog_msg.buf, "HEAD",
				   remote_head->old_sha1,
				   NULL, REF_NODEREF, DIE_ON_ERR);
	} else {
		/* Nothing to checkout out */
		if (!option_no_checkout)
			warning("remote HEAD refers to nonexistent ref, "
				"unable to checkout.\n");
		option_no_checkout = 1;
	}

	if (transport)
		transport_unlock_pack(transport);

	if (!option_no_checkout) {
		struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
		struct unpack_trees_options opts;
		struct tree *tree;
		struct tree_desc t;
		int fd;

		/* We need to be in the new work tree for the checkout */
		setup_work_tree();

		fd = hold_locked_index(lock_file, 1);

		memset(&opts, 0, sizeof opts);
		opts.update = 1;
		opts.merge = 1;
		opts.fn = oneway_merge;
		opts.verbose_update = !option_quiet;
		opts.src_index = &the_index;
		opts.dst_index = &the_index;

		tree = parse_tree_indirect(remote_head->old_sha1);
		parse_tree(tree);
		init_tree_desc(&t, tree->buffer, tree->size);
		unpack_trees(1, &t, &opts);

		if (write_cache(fd, active_cache, active_nr) ||
		    commit_locked_index(lock_file))
			die("unable to write new index file");
	}

	strbuf_release(&reflog_msg);
	junk_pid = 0;
	return 0;
}