about summary refs log tree commit diff
path: root/builtin
diff options
context:
space:
mode:
authorVincent Ambo <Vincent Ambo>2020-01-11T23·36+0000
committerVincent Ambo <Vincent Ambo>2020-01-11T23·36+0000
commit1b593e1ea4d2af0f6444d9a7788d5d99abd6fde5 (patch)
treee3accb9beed5c4c1b5a05c99db71ab2841f0ed04 /builtin
Squashed 'third_party/git/' content from commit cb71568594
git-subtree-dir: third_party/git
git-subtree-split: cb715685942260375e1eb8153b0768a376e4ece7
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c545
-rw-r--r--builtin/am.c2373
-rw-r--r--builtin/annotate.c22
-rw-r--r--builtin/apply.c34
-rw-r--r--builtin/archive.c112
-rw-r--r--builtin/bisect--helper.c714
-rw-r--r--builtin/blame.c1176
-rw-r--r--builtin/branch.c855
-rw-r--r--builtin/bundle.c67
-rw-r--r--builtin/cat-file.c717
-rw-r--r--builtin/check-attr.c189
-rw-r--r--builtin/check-ignore.c193
-rw-r--r--builtin/check-mailmap.c67
-rw-r--r--builtin/check-ref-format.c91
-rw-r--r--builtin/checkout-index.c262
-rw-r--r--builtin/checkout.c1801
-rw-r--r--builtin/clean.c1051
-rw-r--r--builtin/clone.c1282
-rw-r--r--builtin/column.c59
-rw-r--r--builtin/commit-graph.c263
-rw-r--r--builtin/commit-tree.c152
-rw-r--r--builtin/commit.c1716
-rw-r--r--builtin/config.c872
-rw-r--r--builtin/count-objects.c172
-rw-r--r--builtin/credential.c31
-rw-r--r--builtin/describe.c662
-rw-r--r--builtin/diff-files.c75
-rw-r--r--builtin/diff-index.c62
-rw-r--r--builtin/diff-tree.c196
-rw-r--r--builtin/diff.c465
-rw-r--r--builtin/difftool.c764
-rw-r--r--builtin/env--helper.c95
-rw-r--r--builtin/fast-export.c1222
-rw-r--r--builtin/fetch-pack.c270
-rw-r--r--builtin/fetch.c1736
-rw-r--r--builtin/fmt-merge-msg.c719
-rw-r--r--builtin/for-each-ref.c87
-rw-r--r--builtin/fsck.c976
-rw-r--r--builtin/gc.c702
-rw-r--r--builtin/get-tar-commit-id.c52
-rw-r--r--builtin/grep.c1149
-rw-r--r--builtin/hash-object.c162
-rw-r--r--builtin/help.c537
-rw-r--r--builtin/index-pack.c1849
-rw-r--r--builtin/init-db.c601
-rw-r--r--builtin/interpret-trailers.c140
-rw-r--r--builtin/log.c2134
-rw-r--r--builtin/ls-files.c692
-rw-r--r--builtin/ls-remote.c152
-rw-r--r--builtin/ls-tree.c190
-rw-r--r--builtin/mailinfo.c65
-rw-r--r--builtin/mailsplit.c369
-rw-r--r--builtin/merge-base.c198
-rw-r--r--builtin/merge-file.c118
-rw-r--r--builtin/merge-index.c111
-rw-r--r--builtin/merge-ours.c33
-rw-r--r--builtin/merge-recursive.c88
-rw-r--r--builtin/merge-tree.c389
-rw-r--r--builtin/merge.c1680
-rw-r--r--builtin/mktag.c176
-rw-r--r--builtin/mktree.c193
-rw-r--r--builtin/multi-pack-index.c62
-rw-r--r--builtin/mv.c304
-rw-r--r--builtin/name-rev.c517
-rw-r--r--builtin/notes.c1039
-rw-r--r--builtin/pack-objects.c3531
-rw-r--r--builtin/pack-redundant.c653
-rw-r--r--builtin/pack-refs.c24
-rw-r--r--builtin/patch-id.c184
-rw-r--r--builtin/prune-packed.c73
-rw-r--r--builtin/prune.c187
-rw-r--r--builtin/pull.c1016
-rw-r--r--builtin/push.c646
-rw-r--r--builtin/range-diff.c87
-rw-r--r--builtin/read-tree.c270
-rw-r--r--builtin/rebase.c2169
-rw-r--r--builtin/receive-pack.c2061
-rw-r--r--builtin/reflog.c788
-rw-r--r--builtin/remote-ext.c202
-rw-r--r--builtin/remote-fd.c82
-rw-r--r--builtin/remote.c1646
-rw-r--r--builtin/repack.c572
-rw-r--r--builtin/replace.c622
-rw-r--r--builtin/rerere.c120
-rw-r--r--builtin/reset.c427
-rw-r--r--builtin/rev-list.c619
-rw-r--r--builtin/rev-parse.c965
-rw-r--r--builtin/revert.c247
-rw-r--r--builtin/rm.c393
-rw-r--r--builtin/send-pack.c311
-rw-r--r--builtin/shortlog.c379
-rw-r--r--builtin/show-branch.c954
-rw-r--r--builtin/show-index.c86
-rw-r--r--builtin/show-ref.c226
-rw-r--r--builtin/stash.c1635
-rw-r--r--builtin/stripspace.c65
-rw-r--r--builtin/submodule--helper.c2243
-rw-r--r--builtin/symbolic-ref.c78
-rw-r--r--builtin/tag.c593
-rw-r--r--builtin/unpack-file.c38
-rw-r--r--builtin/unpack-objects.c597
-rw-r--r--builtin/update-index.c1239
-rw-r--r--builtin/update-ref.c447
-rw-r--r--builtin/update-server-info.c26
-rw-r--r--builtin/upload-archive.c133
-rw-r--r--builtin/upload-pack.c74
-rw-r--r--builtin/var.c94
-rw-r--r--builtin/verify-commit.c90
-rw-r--r--builtin/verify-pack.c83
-rw-r--r--builtin/verify-tag.c77
-rw-r--r--builtin/worktree.c987
-rw-r--r--builtin/write-tree.c57
112 files changed, 63943 insertions, 0 deletions
diff --git a/builtin/add.c b/builtin/add.c
new file mode 100644
index 0000000000..dd18e5c9b6
--- /dev/null
+++ b/builtin/add.c
@@ -0,0 +1,545 @@
+/*
+ * "git add" builtin command
+ *
+ * Copyright (C) 2006 Linus Torvalds
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "builtin.h"
+#include "lockfile.h"
+#include "dir.h"
+#include "pathspec.h"
+#include "exec-cmd.h"
+#include "cache-tree.h"
+#include "run-command.h"
+#include "parse-options.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "bulk-checkin.h"
+#include "argv-array.h"
+#include "submodule.h"
+
+static const char * const builtin_add_usage[] = {
+	N_("git add [<options>] [--] <pathspec>..."),
+	NULL
+};
+static int patch_interactive, add_interactive, edit_interactive;
+static int take_worktree_changes;
+static int add_renormalize;
+
+struct update_callback_data {
+	int flags;
+	int add_errors;
+};
+
+static void chmod_pathspec(struct pathspec *pathspec, char flip)
+{
+	int i;
+
+	for (i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+
+		if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
+			continue;
+
+		if (chmod_cache_entry(ce, flip) < 0)
+			fprintf(stderr, "cannot chmod %cx '%s'\n", flip, ce->name);
+	}
+}
+
+static int fix_unmerged_status(struct diff_filepair *p,
+			       struct update_callback_data *data)
+{
+	if (p->status != DIFF_STATUS_UNMERGED)
+		return p->status;
+	if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode)
+		/*
+		 * This is not an explicit add request, and the
+		 * path is missing from the working tree (deleted)
+		 */
+		return DIFF_STATUS_DELETED;
+	else
+		/*
+		 * Either an explicit add request, or path exists
+		 * in the working tree.  An attempt to explicitly
+		 * add a path that does not exist in the working tree
+		 * will be caught as an error by the caller immediately.
+		 */
+		return DIFF_STATUS_MODIFIED;
+}
+
+static void update_callback(struct diff_queue_struct *q,
+			    struct diff_options *opt, void *cbdata)
+{
+	int i;
+	struct update_callback_data *data = cbdata;
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		const char *path = p->one->path;
+		switch (fix_unmerged_status(p, data)) {
+		default:
+			die(_("unexpected diff status %c"), p->status);
+		case DIFF_STATUS_MODIFIED:
+		case DIFF_STATUS_TYPE_CHANGED:
+			if (add_file_to_index(&the_index, path,	data->flags)) {
+				if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
+					die(_("updating files failed"));
+				data->add_errors++;
+			}
+			break;
+		case DIFF_STATUS_DELETED:
+			if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
+				break;
+			if (!(data->flags & ADD_CACHE_PRETEND))
+				remove_file_from_index(&the_index, path);
+			if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
+				printf(_("remove '%s'\n"), path);
+			break;
+		}
+	}
+}
+
+int add_files_to_cache(const char *prefix,
+		       const struct pathspec *pathspec, int flags)
+{
+	struct update_callback_data data;
+	struct rev_info rev;
+
+	memset(&data, 0, sizeof(data));
+	data.flags = flags;
+
+	repo_init_revisions(the_repository, &rev, prefix);
+	setup_revisions(0, NULL, &rev, NULL);
+	if (pathspec)
+		copy_pathspec(&rev.prune_data, pathspec);
+	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = update_callback;
+	rev.diffopt.format_callback_data = &data;
+	rev.diffopt.flags.override_submodule_config = 1;
+	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
+	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
+	clear_pathspec(&rev.prune_data);
+	return !!data.add_errors;
+}
+
+static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
+{
+	int i, retval = 0;
+
+	for (i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+
+		if (ce_stage(ce))
+			continue; /* do not touch unmerged paths */
+		if (!S_ISREG(ce->ce_mode) && !S_ISLNK(ce->ce_mode))
+			continue; /* do not touch non blobs */
+		if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
+			continue;
+		retval |= add_file_to_cache(ce->name, flags | ADD_CACHE_RENORMALIZE);
+	}
+
+	return retval;
+}
+
+static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix)
+{
+	char *seen;
+	int i;
+	struct dir_entry **src, **dst;
+
+	seen = xcalloc(pathspec->nr, 1);
+
+	src = dst = dir->entries;
+	i = dir->nr;
+	while (--i >= 0) {
+		struct dir_entry *entry = *src++;
+		if (dir_path_match(&the_index, entry, pathspec, prefix, seen))
+			*dst++ = entry;
+	}
+	dir->nr = dst - dir->entries;
+	add_pathspec_matches_against_index(pathspec, &the_index, seen);
+	return seen;
+}
+
+static void refresh(int verbose, const struct pathspec *pathspec)
+{
+	char *seen;
+	int i;
+
+	seen = xcalloc(pathspec->nr, 1);
+	refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET,
+		      pathspec, seen, _("Unstaged changes after refreshing the index:"));
+	for (i = 0; i < pathspec->nr; i++) {
+		if (!seen[i])
+			die(_("pathspec '%s' did not match any files"),
+			    pathspec->items[i].match);
+	}
+	free(seen);
+}
+
+int run_add_interactive(const char *revision, const char *patch_mode,
+			const struct pathspec *pathspec)
+{
+	int status, i;
+	struct argv_array argv = ARGV_ARRAY_INIT;
+
+	argv_array_push(&argv, "add--interactive");
+	if (patch_mode)
+		argv_array_push(&argv, patch_mode);
+	if (revision)
+		argv_array_push(&argv, revision);
+	argv_array_push(&argv, "--");
+	for (i = 0; i < pathspec->nr; i++)
+		/* pass original pathspec, to be re-parsed */
+		argv_array_push(&argv, pathspec->items[i].original);
+
+	status = run_command_v_opt(argv.argv, RUN_GIT_CMD);
+	argv_array_clear(&argv);
+	return status;
+}
+
+int interactive_add(int argc, const char **argv, const char *prefix, int patch)
+{
+	struct pathspec pathspec;
+
+	parse_pathspec(&pathspec, 0,
+		       PATHSPEC_PREFER_FULL |
+		       PATHSPEC_SYMLINK_LEADING_PATH |
+		       PATHSPEC_PREFIX_ORIGIN,
+		       prefix, argv);
+
+	return run_add_interactive(NULL,
+				   patch ? "--patch" : NULL,
+				   &pathspec);
+}
+
+static int edit_patch(int argc, const char **argv, const char *prefix)
+{
+	char *file = git_pathdup("ADD_EDIT.patch");
+	const char *apply_argv[] = { "apply", "--recount", "--cached",
+		NULL, NULL };
+	struct child_process child = CHILD_PROCESS_INIT;
+	struct rev_info rev;
+	int out;
+	struct stat st;
+
+	apply_argv[3] = file;
+
+	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+
+	if (read_cache() < 0)
+		die(_("Could not read the index"));
+
+	repo_init_revisions(the_repository, &rev, prefix);
+	rev.diffopt.context = 7;
+
+	argc = setup_revisions(argc, argv, &rev, NULL);
+	rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+	rev.diffopt.use_color = 0;
+	rev.diffopt.flags.ignore_dirty_submodules = 1;
+	out = open(file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+	if (out < 0)
+		die(_("Could not open '%s' for writing."), file);
+	rev.diffopt.file = xfdopen(out, "w");
+	rev.diffopt.close_file = 1;
+	if (run_diff_files(&rev, 0))
+		die(_("Could not write patch"));
+
+	if (launch_editor(file, NULL, NULL))
+		die(_("editing patch failed"));
+
+	if (stat(file, &st))
+		die_errno(_("Could not stat '%s'"), file);
+	if (!st.st_size)
+		die(_("Empty patch. Aborted."));
+
+	child.git_cmd = 1;
+	child.argv = apply_argv;
+	if (run_command(&child))
+		die(_("Could not apply '%s'"), file);
+
+	unlink(file);
+	free(file);
+	return 0;
+}
+
+static const char ignore_error[] =
+N_("The following paths are ignored by one of your .gitignore files:\n");
+
+static int verbose, show_only, ignored_too, refresh_only;
+static int ignore_add_errors, intent_to_add, ignore_missing;
+static int warn_on_embedded_repo = 1;
+
+#define ADDREMOVE_DEFAULT 1
+static int addremove = ADDREMOVE_DEFAULT;
+static int addremove_explicit = -1; /* unspecified */
+
+static char *chmod_arg;
+
+static int ignore_removal_cb(const struct option *opt, const char *arg, int unset)
+{
+	/* if we are told to ignore, we are not adding removals */
+	*(int *)opt->value = !unset ? 0 : 1;
+	return 0;
+}
+
+static struct option builtin_add_options[] = {
+	OPT__DRY_RUN(&show_only, N_("dry run")),
+	OPT__VERBOSE(&verbose, N_("be verbose")),
+	OPT_GROUP(""),
+	OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")),
+	OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")),
+	OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
+	OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0),
+	OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")),
+	OPT_BOOL(0, "renormalize", &add_renormalize, N_("renormalize EOL of tracked files (implies -u)")),
+	OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")),
+	OPT_BOOL('A', "all", &addremove_explicit, N_("add changes from all tracked and untracked files")),
+	{ OPTION_CALLBACK, 0, "ignore-removal", &addremove_explicit,
+	  NULL /* takes no arguments */,
+	  N_("ignore paths removed in the working tree (same as --no-all)"),
+	  PARSE_OPT_NOARG, ignore_removal_cb },
+	OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
+	OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
+	OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
+	OPT_STRING(0, "chmod", &chmod_arg, "(+|-)x",
+		   N_("override the executable bit of the listed files")),
+	OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
+			N_("warn when adding an embedded repository")),
+	OPT_END(),
+};
+
+static int add_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "add.ignoreerrors") ||
+	    !strcmp(var, "add.ignore-errors")) {
+		ignore_add_errors = git_config_bool(var, value);
+		return 0;
+	}
+	return git_default_config(var, value, cb);
+}
+
+static const char embedded_advice[] = N_(
+"You've added another git repository inside your current repository.\n"
+"Clones of the outer repository will not contain the contents of\n"
+"the embedded repository and will not know how to obtain it.\n"
+"If you meant to add a submodule, use:\n"
+"\n"
+"	git submodule add <url> %s\n"
+"\n"
+"If you added this path by mistake, you can remove it from the\n"
+"index with:\n"
+"\n"
+"	git rm --cached %s\n"
+"\n"
+"See \"git help submodule\" for more information."
+);
+
+static void check_embedded_repo(const char *path)
+{
+	struct strbuf name = STRBUF_INIT;
+
+	if (!warn_on_embedded_repo)
+		return;
+	if (!ends_with(path, "/"))
+		return;
+
+	/* Drop trailing slash for aesthetics */
+	strbuf_addstr(&name, path);
+	strbuf_strip_suffix(&name, "/");
+
+	warning(_("adding embedded git repository: %s"), name.buf);
+	if (advice_add_embedded_repo) {
+		advise(embedded_advice, name.buf, name.buf);
+		/* there may be multiple entries; advise only once */
+		advice_add_embedded_repo = 0;
+	}
+
+	strbuf_release(&name);
+}
+
+static int add_files(struct dir_struct *dir, int flags)
+{
+	int i, exit_status = 0;
+
+	if (dir->ignored_nr) {
+		fprintf(stderr, _(ignore_error));
+		for (i = 0; i < dir->ignored_nr; i++)
+			fprintf(stderr, "%s\n", dir->ignored[i]->name);
+		fprintf(stderr, _("Use -f if you really want to add them.\n"));
+		exit_status = 1;
+	}
+
+	for (i = 0; i < dir->nr; i++) {
+		if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
+			if (!ignore_add_errors)
+				die(_("adding files failed"));
+			exit_status = 1;
+		} else {
+			check_embedded_repo(dir->entries[i]->name);
+		}
+	}
+	return exit_status;
+}
+
+int cmd_add(int argc, const char **argv, const char *prefix)
+{
+	int exit_status = 0;
+	struct pathspec pathspec;
+	struct dir_struct dir;
+	int flags;
+	int add_new_files;
+	int require_pathspec;
+	char *seen = NULL;
+	struct lock_file lock_file = LOCK_INIT;
+
+	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, patch_interactive));
+
+	if (edit_interactive)
+		return(edit_patch(argc, argv, prefix));
+	argc--;
+	argv++;
+
+	if (0 <= addremove_explicit)
+		addremove = addremove_explicit;
+	else if (take_worktree_changes && ADDREMOVE_DEFAULT)
+		addremove = 0; /* "-u" was given but not "-A" */
+
+	if (addremove && take_worktree_changes)
+		die(_("-A and -u are mutually incompatible"));
+
+	if (!take_worktree_changes && addremove_explicit < 0 && argc)
+		/* Turn "git add pathspec..." to "git add -A pathspec..." */
+		addremove = 1;
+
+	if (!show_only && ignore_missing)
+		die(_("Option --ignore-missing can only be used together with --dry-run"));
+
+	if (chmod_arg && ((chmod_arg[0] != '-' && chmod_arg[0] != '+') ||
+			  chmod_arg[1] != 'x' || chmod_arg[2]))
+		die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
+
+	add_new_files = !take_worktree_changes && !refresh_only && !add_renormalize;
+	require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
+
+	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+
+	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;
+	}
+
+	/*
+	 * Check the "pathspec '%s' did not match any files" block
+	 * below before enabling new magic.
+	 */
+	parse_pathspec(&pathspec, PATHSPEC_ATTR,
+		       PATHSPEC_PREFER_FULL |
+		       PATHSPEC_SYMLINK_LEADING_PATH,
+		       prefix, argv);
+
+	if (read_cache_preload(&pathspec) < 0)
+		die(_("index file corrupt"));
+
+	die_in_unpopulated_submodule(&the_index, prefix);
+	die_path_inside_submodule(&the_index, &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, &the_index, &pathspec);
+		if (pathspec.nr)
+			seen = prune_directory(&dir, &pathspec, baselen);
+	}
+
+	if (refresh_only) {
+		refresh(verbose, &pathspec);
+		goto finish;
+	}
+
+	if (pathspec.nr) {
+		int i;
+
+		if (!seen)
+			seen = find_pathspecs_matching_against_index(&pathspec, &the_index);
+
+		/*
+		 * file_exists() assumes exact match
+		 */
+		GUARD_PATHSPEC(&pathspec,
+			       PATHSPEC_FROMTOP |
+			       PATHSPEC_LITERAL |
+			       PATHSPEC_GLOB |
+			       PATHSPEC_ICASE |
+			       PATHSPEC_EXCLUDE);
+
+		for (i = 0; i < pathspec.nr; i++) {
+			const char *path = pathspec.items[i].match;
+			if (pathspec.items[i].magic & PATHSPEC_EXCLUDE)
+				continue;
+			if (!seen[i] && path[0] &&
+			    ((pathspec.items[i].magic &
+			      (PATHSPEC_GLOB | PATHSPEC_ICASE)) ||
+			     !file_exists(path))) {
+				if (ignore_missing) {
+					int dtype = DT_UNKNOWN;
+					if (is_excluded(&dir, &the_index, path, &dtype))
+						dir_add_ignored(&dir, &the_index,
+								path, pathspec.items[i].len);
+				} else
+					die(_("pathspec '%s' did not match any files"),
+					    pathspec.items[i].original);
+			}
+		}
+		free(seen);
+	}
+
+	plug_bulk_checkin();
+
+	if (add_renormalize)
+		exit_status |= renormalize_tracked_files(&pathspec, flags);
+	else
+		exit_status |= add_files_to_cache(prefix, &pathspec, flags);
+
+	if (add_new_files)
+		exit_status |= add_files(&dir, flags);
+
+	if (chmod_arg && pathspec.nr)
+		chmod_pathspec(&pathspec, chmod_arg[0]);
+	unplug_bulk_checkin();
+
+finish:
+	if (write_locked_index(&the_index, &lock_file,
+			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
+		die(_("Unable to write new index file"));
+
+	UNLEAK(pathspec);
+	UNLEAK(dir);
+	return exit_status;
+}
diff --git a/builtin/am.c b/builtin/am.c
new file mode 100644
index 0000000000..1aea657a7f
--- /dev/null
+++ b/builtin/am.c
@@ -0,0 +1,2373 @@
+/*
+ * Builtin "git am"
+ *
+ * Based on git-am.sh by Junio C Hamano.
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "builtin.h"
+#include "exec-cmd.h"
+#include "parse-options.h"
+#include "dir.h"
+#include "run-command.h"
+#include "quote.h"
+#include "tempfile.h"
+#include "lockfile.h"
+#include "cache-tree.h"
+#include "refs.h"
+#include "commit.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "unpack-trees.h"
+#include "branch.h"
+#include "sequencer.h"
+#include "revision.h"
+#include "merge-recursive.h"
+#include "revision.h"
+#include "log-tree.h"
+#include "notes-utils.h"
+#include "rerere.h"
+#include "prompt.h"
+#include "mailinfo.h"
+#include "apply.h"
+#include "string-list.h"
+#include "packfile.h"
+#include "repository.h"
+
+/**
+ * Returns the length of the first line of msg.
+ */
+static int linelen(const char *msg)
+{
+	return strchrnul(msg, '\n') - msg;
+}
+
+/**
+ * Returns true if `str` consists of only whitespace, false otherwise.
+ */
+static int str_isspace(const char *str)
+{
+	for (; *str; str++)
+		if (!isspace(*str))
+			return 0;
+
+	return 1;
+}
+
+enum patch_format {
+	PATCH_FORMAT_UNKNOWN = 0,
+	PATCH_FORMAT_MBOX,
+	PATCH_FORMAT_STGIT,
+	PATCH_FORMAT_STGIT_SERIES,
+	PATCH_FORMAT_HG,
+	PATCH_FORMAT_MBOXRD
+};
+
+enum keep_type {
+	KEEP_FALSE = 0,
+	KEEP_TRUE,      /* pass -k flag to git-mailinfo */
+	KEEP_NON_PATCH  /* pass -b flag to git-mailinfo */
+};
+
+enum scissors_type {
+	SCISSORS_UNSET = -1,
+	SCISSORS_FALSE = 0,  /* pass --no-scissors to git-mailinfo */
+	SCISSORS_TRUE        /* pass --scissors to git-mailinfo */
+};
+
+enum signoff_type {
+	SIGNOFF_FALSE = 0,
+	SIGNOFF_TRUE = 1,
+	SIGNOFF_EXPLICIT /* --signoff was set on the command-line */
+};
+
+struct am_state {
+	/* state directory path */
+	char *dir;
+
+	/* current and last patch numbers, 1-indexed */
+	int cur;
+	int last;
+
+	/* commit metadata and message */
+	char *author_name;
+	char *author_email;
+	char *author_date;
+	char *msg;
+	size_t msg_len;
+
+	/* when --rebasing, records the original commit the patch came from */
+	struct object_id orig_commit;
+
+	/* number of digits in patch filename */
+	int prec;
+
+	/* various operating modes and command line options */
+	int interactive;
+	int threeway;
+	int quiet;
+	int signoff; /* enum signoff_type */
+	int utf8;
+	int keep; /* enum keep_type */
+	int message_id;
+	int scissors; /* enum scissors_type */
+	struct argv_array git_apply_opts;
+	const char *resolvemsg;
+	int committer_date_is_author_date;
+	int ignore_date;
+	int allow_rerere_autoupdate;
+	const char *sign_commit;
+	int rebasing;
+};
+
+/**
+ * Initializes am_state with the default values.
+ */
+static void am_state_init(struct am_state *state)
+{
+	int gpgsign;
+
+	memset(state, 0, sizeof(*state));
+
+	state->dir = git_pathdup("rebase-apply");
+
+	state->prec = 4;
+
+	git_config_get_bool("am.threeway", &state->threeway);
+
+	state->utf8 = 1;
+
+	git_config_get_bool("am.messageid", &state->message_id);
+
+	state->scissors = SCISSORS_UNSET;
+
+	argv_array_init(&state->git_apply_opts);
+
+	if (!git_config_get_bool("commit.gpgsign", &gpgsign))
+		state->sign_commit = gpgsign ? "" : NULL;
+}
+
+/**
+ * Releases memory allocated by an am_state.
+ */
+static void am_state_release(struct am_state *state)
+{
+	free(state->dir);
+	free(state->author_name);
+	free(state->author_email);
+	free(state->author_date);
+	free(state->msg);
+	argv_array_clear(&state->git_apply_opts);
+}
+
+/**
+ * Returns path relative to the am_state directory.
+ */
+static inline const char *am_path(const struct am_state *state, const char *path)
+{
+	return mkpath("%s/%s", state->dir, path);
+}
+
+/**
+ * For convenience to call write_file()
+ */
+static void write_state_text(const struct am_state *state,
+			     const char *name, const char *string)
+{
+	write_file(am_path(state, name), "%s", string);
+}
+
+static void write_state_count(const struct am_state *state,
+			      const char *name, int value)
+{
+	write_file(am_path(state, name), "%d", value);
+}
+
+static void write_state_bool(const struct am_state *state,
+			     const char *name, int value)
+{
+	write_state_text(state, name, value ? "t" : "f");
+}
+
+/**
+ * If state->quiet is false, calls fprintf(fp, fmt, ...), and appends a newline
+ * at the end.
+ */
+static void say(const struct am_state *state, FILE *fp, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	if (!state->quiet) {
+		vfprintf(fp, fmt, ap);
+		putc('\n', fp);
+	}
+	va_end(ap);
+}
+
+/**
+ * Returns 1 if there is an am session in progress, 0 otherwise.
+ */
+static int am_in_progress(const struct am_state *state)
+{
+	struct stat st;
+
+	if (lstat(state->dir, &st) < 0 || !S_ISDIR(st.st_mode))
+		return 0;
+	if (lstat(am_path(state, "last"), &st) || !S_ISREG(st.st_mode))
+		return 0;
+	if (lstat(am_path(state, "next"), &st) || !S_ISREG(st.st_mode))
+		return 0;
+	return 1;
+}
+
+/**
+ * Reads the contents of `file` in the `state` directory into `sb`. Returns the
+ * number of bytes read on success, -1 if the file does not exist. If `trim` is
+ * set, trailing whitespace will be removed.
+ */
+static int read_state_file(struct strbuf *sb, const struct am_state *state,
+			const char *file, int trim)
+{
+	strbuf_reset(sb);
+
+	if (strbuf_read_file(sb, am_path(state, file), 0) >= 0) {
+		if (trim)
+			strbuf_trim(sb);
+
+		return sb->len;
+	}
+
+	if (errno == ENOENT)
+		return -1;
+
+	die_errno(_("could not read '%s'"), am_path(state, file));
+}
+
+/**
+ * Reads and parses the state directory's "author-script" file, and sets
+ * state->author_name, state->author_email and state->author_date accordingly.
+ * Returns 0 on success, -1 if the file could not be parsed.
+ *
+ * The author script is of the format:
+ *
+ *	GIT_AUTHOR_NAME='$author_name'
+ *	GIT_AUTHOR_EMAIL='$author_email'
+ *	GIT_AUTHOR_DATE='$author_date'
+ *
+ * where $author_name, $author_email and $author_date are quoted. We are strict
+ * with our parsing, as the file was meant to be eval'd in the old git-am.sh
+ * script, and thus if the file differs from what this function expects, it is
+ * better to bail out than to do something that the user does not expect.
+ */
+static int read_am_author_script(struct am_state *state)
+{
+	const char *filename = am_path(state, "author-script");
+
+	assert(!state->author_name);
+	assert(!state->author_email);
+	assert(!state->author_date);
+
+	return read_author_script(filename, &state->author_name,
+				  &state->author_email, &state->author_date, 1);
+}
+
+/**
+ * Saves state->author_name, state->author_email and state->author_date in the
+ * state directory's "author-script" file.
+ */
+static void write_author_script(const struct am_state *state)
+{
+	struct strbuf sb = STRBUF_INIT;
+
+	strbuf_addstr(&sb, "GIT_AUTHOR_NAME=");
+	sq_quote_buf(&sb, state->author_name);
+	strbuf_addch(&sb, '\n');
+
+	strbuf_addstr(&sb, "GIT_AUTHOR_EMAIL=");
+	sq_quote_buf(&sb, state->author_email);
+	strbuf_addch(&sb, '\n');
+
+	strbuf_addstr(&sb, "GIT_AUTHOR_DATE=");
+	sq_quote_buf(&sb, state->author_date);
+	strbuf_addch(&sb, '\n');
+
+	write_state_text(state, "author-script", sb.buf);
+
+	strbuf_release(&sb);
+}
+
+/**
+ * Reads the commit message from the state directory's "final-commit" file,
+ * setting state->msg to its contents and state->msg_len to the length of its
+ * contents in bytes.
+ *
+ * Returns 0 on success, -1 if the file does not exist.
+ */
+static int read_commit_msg(struct am_state *state)
+{
+	struct strbuf sb = STRBUF_INIT;
+
+	assert(!state->msg);
+
+	if (read_state_file(&sb, state, "final-commit", 0) < 0) {
+		strbuf_release(&sb);
+		return -1;
+	}
+
+	state->msg = strbuf_detach(&sb, &state->msg_len);
+	return 0;
+}
+
+/**
+ * Saves state->msg in the state directory's "final-commit" file.
+ */
+static void write_commit_msg(const struct am_state *state)
+{
+	const char *filename = am_path(state, "final-commit");
+	write_file_buf(filename, state->msg, state->msg_len);
+}
+
+/**
+ * Loads state from disk.
+ */
+static void am_load(struct am_state *state)
+{
+	struct strbuf sb = STRBUF_INIT;
+
+	if (read_state_file(&sb, state, "next", 1) < 0)
+		BUG("state file 'next' does not exist");
+	state->cur = strtol(sb.buf, NULL, 10);
+
+	if (read_state_file(&sb, state, "last", 1) < 0)
+		BUG("state file 'last' does not exist");
+	state->last = strtol(sb.buf, NULL, 10);
+
+	if (read_am_author_script(state) < 0)
+		die(_("could not parse author script"));
+
+	read_commit_msg(state);
+
+	if (read_state_file(&sb, state, "original-commit", 1) < 0)
+		oidclr(&state->orig_commit);
+	else if (get_oid_hex(sb.buf, &state->orig_commit) < 0)
+		die(_("could not parse %s"), am_path(state, "original-commit"));
+
+	read_state_file(&sb, state, "threeway", 1);
+	state->threeway = !strcmp(sb.buf, "t");
+
+	read_state_file(&sb, state, "quiet", 1);
+	state->quiet = !strcmp(sb.buf, "t");
+
+	read_state_file(&sb, state, "sign", 1);
+	state->signoff = !strcmp(sb.buf, "t");
+
+	read_state_file(&sb, state, "utf8", 1);
+	state->utf8 = !strcmp(sb.buf, "t");
+
+	if (file_exists(am_path(state, "rerere-autoupdate"))) {
+		read_state_file(&sb, state, "rerere-autoupdate", 1);
+		state->allow_rerere_autoupdate = strcmp(sb.buf, "t") ?
+			RERERE_NOAUTOUPDATE : RERERE_AUTOUPDATE;
+	} else {
+		state->allow_rerere_autoupdate = 0;
+	}
+
+	read_state_file(&sb, state, "keep", 1);
+	if (!strcmp(sb.buf, "t"))
+		state->keep = KEEP_TRUE;
+	else if (!strcmp(sb.buf, "b"))
+		state->keep = KEEP_NON_PATCH;
+	else
+		state->keep = KEEP_FALSE;
+
+	read_state_file(&sb, state, "messageid", 1);
+	state->message_id = !strcmp(sb.buf, "t");
+
+	read_state_file(&sb, state, "scissors", 1);
+	if (!strcmp(sb.buf, "t"))
+		state->scissors = SCISSORS_TRUE;
+	else if (!strcmp(sb.buf, "f"))
+		state->scissors = SCISSORS_FALSE;
+	else
+		state->scissors = SCISSORS_UNSET;
+
+	read_state_file(&sb, state, "apply-opt", 1);
+	argv_array_clear(&state->git_apply_opts);
+	if (sq_dequote_to_argv_array(sb.buf, &state->git_apply_opts) < 0)
+		die(_("could not parse %s"), am_path(state, "apply-opt"));
+
+	state->rebasing = !!file_exists(am_path(state, "rebasing"));
+
+	strbuf_release(&sb);
+}
+
+/**
+ * Removes the am_state directory, forcefully terminating the current am
+ * session.
+ */
+static void am_destroy(const struct am_state *state)
+{
+	struct strbuf sb = STRBUF_INIT;
+
+	strbuf_addstr(&sb, state->dir);
+	remove_dir_recursively(&sb, 0);
+	strbuf_release(&sb);
+}
+
+/**
+ * Runs applypatch-msg hook. Returns its exit code.
+ */
+static int run_applypatch_msg_hook(struct am_state *state)
+{
+	int ret;
+
+	assert(state->msg);
+	ret = run_hook_le(NULL, "applypatch-msg", am_path(state, "final-commit"), NULL);
+
+	if (!ret) {
+		FREE_AND_NULL(state->msg);
+		if (read_commit_msg(state) < 0)
+			die(_("'%s' was deleted by the applypatch-msg hook"),
+				am_path(state, "final-commit"));
+	}
+
+	return ret;
+}
+
+/**
+ * Runs post-rewrite hook. Returns it exit code.
+ */
+static int run_post_rewrite_hook(const struct am_state *state)
+{
+	struct child_process cp = CHILD_PROCESS_INIT;
+	const char *hook = find_hook("post-rewrite");
+	int ret;
+
+	if (!hook)
+		return 0;
+
+	argv_array_push(&cp.args, hook);
+	argv_array_push(&cp.args, "rebase");
+
+	cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
+	cp.stdout_to_stderr = 1;
+	cp.trace2_hook_name = "post-rewrite";
+
+	ret = run_command(&cp);
+
+	close(cp.in);
+	return ret;
+}
+
+/**
+ * Reads the state directory's "rewritten" file, and copies notes from the old
+ * commits listed in the file to their rewritten commits.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int copy_notes_for_rebase(const struct am_state *state)
+{
+	struct notes_rewrite_cfg *c;
+	struct strbuf sb = STRBUF_INIT;
+	const char *invalid_line = _("Malformed input line: '%s'.");
+	const char *msg = "Notes added by 'git rebase'";
+	FILE *fp;
+	int ret = 0;
+
+	assert(state->rebasing);
+
+	c = init_copy_notes_for_rewrite("rebase");
+	if (!c)
+		return 0;
+
+	fp = xfopen(am_path(state, "rewritten"), "r");
+
+	while (!strbuf_getline_lf(&sb, fp)) {
+		struct object_id from_obj, to_obj;
+		const char *p;
+
+		if (sb.len != the_hash_algo->hexsz * 2 + 1) {
+			ret = error(invalid_line, sb.buf);
+			goto finish;
+		}
+
+		if (parse_oid_hex(sb.buf, &from_obj, &p)) {
+			ret = error(invalid_line, sb.buf);
+			goto finish;
+		}
+
+		if (*p != ' ') {
+			ret = error(invalid_line, sb.buf);
+			goto finish;
+		}
+
+		if (get_oid_hex(p + 1, &to_obj)) {
+			ret = error(invalid_line, sb.buf);
+			goto finish;
+		}
+
+		if (copy_note_for_rewrite(c, &from_obj, &to_obj))
+			ret = error(_("Failed to copy notes from '%s' to '%s'"),
+					oid_to_hex(&from_obj), oid_to_hex(&to_obj));
+	}
+
+finish:
+	finish_copy_notes_for_rewrite(the_repository, c, msg);
+	fclose(fp);
+	strbuf_release(&sb);
+	return ret;
+}
+
+/**
+ * Determines if the file looks like a piece of RFC2822 mail by grabbing all
+ * non-indented lines and checking if they look like they begin with valid
+ * header field names.
+ *
+ * Returns 1 if the file looks like a piece of mail, 0 otherwise.
+ */
+static int is_mail(FILE *fp)
+{
+	const char *header_regex = "^[!-9;-~]+:";
+	struct strbuf sb = STRBUF_INIT;
+	regex_t regex;
+	int ret = 1;
+
+	if (fseek(fp, 0L, SEEK_SET))
+		die_errno(_("fseek failed"));
+
+	if (regcomp(&regex, header_regex, REG_NOSUB | REG_EXTENDED))
+		die("invalid pattern: %s", header_regex);
+
+	while (!strbuf_getline(&sb, fp)) {
+		if (!sb.len)
+			break; /* End of header */
+
+		/* Ignore indented folded lines */
+		if (*sb.buf == '\t' || *sb.buf == ' ')
+			continue;
+
+		/* It's a header if it matches header_regex */
+		if (regexec(&regex, sb.buf, 0, NULL, 0)) {
+			ret = 0;
+			goto done;
+		}
+	}
+
+done:
+	regfree(&regex);
+	strbuf_release(&sb);
+	return ret;
+}
+
+/**
+ * Attempts to detect the patch_format of the patches contained in `paths`,
+ * returning the PATCH_FORMAT_* enum value. Returns PATCH_FORMAT_UNKNOWN if
+ * detection fails.
+ */
+static int detect_patch_format(const char **paths)
+{
+	enum patch_format ret = PATCH_FORMAT_UNKNOWN;
+	struct strbuf l1 = STRBUF_INIT;
+	struct strbuf l2 = STRBUF_INIT;
+	struct strbuf l3 = STRBUF_INIT;
+	FILE *fp;
+
+	/*
+	 * We default to mbox format if input is from stdin and for directories
+	 */
+	if (!*paths || !strcmp(*paths, "-") || is_directory(*paths))
+		return PATCH_FORMAT_MBOX;
+
+	/*
+	 * Otherwise, check the first few lines of the first patch, starting
+	 * from the first non-blank line, to try to detect its format.
+	 */
+
+	fp = xfopen(*paths, "r");
+
+	while (!strbuf_getline(&l1, fp)) {
+		if (l1.len)
+			break;
+	}
+
+	if (starts_with(l1.buf, "From ") || starts_with(l1.buf, "From: ")) {
+		ret = PATCH_FORMAT_MBOX;
+		goto done;
+	}
+
+	if (starts_with(l1.buf, "# This series applies on GIT commit")) {
+		ret = PATCH_FORMAT_STGIT_SERIES;
+		goto done;
+	}
+
+	if (!strcmp(l1.buf, "# HG changeset patch")) {
+		ret = PATCH_FORMAT_HG;
+		goto done;
+	}
+
+	strbuf_getline(&l2, fp);
+	strbuf_getline(&l3, fp);
+
+	/*
+	 * If the second line is empty and the third is a From, Author or Date
+	 * entry, this is likely an StGit patch.
+	 */
+	if (l1.len && !l2.len &&
+		(starts_with(l3.buf, "From:") ||
+		 starts_with(l3.buf, "Author:") ||
+		 starts_with(l3.buf, "Date:"))) {
+		ret = PATCH_FORMAT_STGIT;
+		goto done;
+	}
+
+	if (l1.len && is_mail(fp)) {
+		ret = PATCH_FORMAT_MBOX;
+		goto done;
+	}
+
+done:
+	fclose(fp);
+	strbuf_release(&l1);
+	strbuf_release(&l2);
+	strbuf_release(&l3);
+	return ret;
+}
+
+/**
+ * Splits out individual email patches from `paths`, where each path is either
+ * a mbox file or a Maildir. Returns 0 on success, -1 on failure.
+ */
+static int split_mail_mbox(struct am_state *state, const char **paths,
+				int keep_cr, int mboxrd)
+{
+	struct child_process cp = CHILD_PROCESS_INIT;
+	struct strbuf last = STRBUF_INIT;
+	int ret;
+
+	cp.git_cmd = 1;
+	argv_array_push(&cp.args, "mailsplit");
+	argv_array_pushf(&cp.args, "-d%d", state->prec);
+	argv_array_pushf(&cp.args, "-o%s", state->dir);
+	argv_array_push(&cp.args, "-b");
+	if (keep_cr)
+		argv_array_push(&cp.args, "--keep-cr");
+	if (mboxrd)
+		argv_array_push(&cp.args, "--mboxrd");
+	argv_array_push(&cp.args, "--");
+	argv_array_pushv(&cp.args, paths);
+
+	ret = capture_command(&cp, &last, 8);
+	if (ret)
+		goto exit;
+
+	state->cur = 1;
+	state->last = strtol(last.buf, NULL, 10);
+
+exit:
+	strbuf_release(&last);
+	return ret ? -1 : 0;
+}
+
+/**
+ * Callback signature for split_mail_conv(). The foreign patch should be
+ * read from `in`, and the converted patch (in RFC2822 mail format) should be
+ * written to `out`. Return 0 on success, or -1 on failure.
+ */
+typedef int (*mail_conv_fn)(FILE *out, FILE *in, int keep_cr);
+
+/**
+ * Calls `fn` for each file in `paths` to convert the foreign patch to the
+ * RFC2822 mail format suitable for parsing with git-mailinfo.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int split_mail_conv(mail_conv_fn fn, struct am_state *state,
+			const char **paths, int keep_cr)
+{
+	static const char *stdin_only[] = {"-", NULL};
+	int i;
+
+	if (!*paths)
+		paths = stdin_only;
+
+	for (i = 0; *paths; paths++, i++) {
+		FILE *in, *out;
+		const char *mail;
+		int ret;
+
+		if (!strcmp(*paths, "-"))
+			in = stdin;
+		else
+			in = fopen(*paths, "r");
+
+		if (!in)
+			return error_errno(_("could not open '%s' for reading"),
+					   *paths);
+
+		mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1);
+
+		out = fopen(mail, "w");
+		if (!out) {
+			if (in != stdin)
+				fclose(in);
+			return error_errno(_("could not open '%s' for writing"),
+					   mail);
+		}
+
+		ret = fn(out, in, keep_cr);
+
+		fclose(out);
+		if (in != stdin)
+			fclose(in);
+
+		if (ret)
+			return error(_("could not parse patch '%s'"), *paths);
+	}
+
+	state->cur = 1;
+	state->last = i;
+	return 0;
+}
+
+/**
+ * A split_mail_conv() callback that converts an StGit patch to an RFC2822
+ * message suitable for parsing with git-mailinfo.
+ */
+static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr)
+{
+	struct strbuf sb = STRBUF_INIT;
+	int subject_printed = 0;
+
+	while (!strbuf_getline_lf(&sb, in)) {
+		const char *str;
+
+		if (str_isspace(sb.buf))
+			continue;
+		else if (skip_prefix(sb.buf, "Author:", &str))
+			fprintf(out, "From:%s\n", str);
+		else if (starts_with(sb.buf, "From") || starts_with(sb.buf, "Date"))
+			fprintf(out, "%s\n", sb.buf);
+		else if (!subject_printed) {
+			fprintf(out, "Subject: %s\n", sb.buf);
+			subject_printed = 1;
+		} else {
+			fprintf(out, "\n%s\n", sb.buf);
+			break;
+		}
+	}
+
+	strbuf_reset(&sb);
+	while (strbuf_fread(&sb, 8192, in) > 0) {
+		fwrite(sb.buf, 1, sb.len, out);
+		strbuf_reset(&sb);
+	}
+
+	strbuf_release(&sb);
+	return 0;
+}
+
+/**
+ * This function only supports a single StGit series file in `paths`.
+ *
+ * Given an StGit series file, converts the StGit patches in the series into
+ * RFC2822 messages suitable for parsing with git-mailinfo, and queues them in
+ * the state directory.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int split_mail_stgit_series(struct am_state *state, const char **paths,
+					int keep_cr)
+{
+	const char *series_dir;
+	char *series_dir_buf;
+	FILE *fp;
+	struct argv_array patches = ARGV_ARRAY_INIT;
+	struct strbuf sb = STRBUF_INIT;
+	int ret;
+
+	if (!paths[0] || paths[1])
+		return error(_("Only one StGIT patch series can be applied at once"));
+
+	series_dir_buf = xstrdup(*paths);
+	series_dir = dirname(series_dir_buf);
+
+	fp = fopen(*paths, "r");
+	if (!fp)
+		return error_errno(_("could not open '%s' for reading"), *paths);
+
+	while (!strbuf_getline_lf(&sb, fp)) {
+		if (*sb.buf == '#')
+			continue; /* skip comment lines */
+
+		argv_array_push(&patches, mkpath("%s/%s", series_dir, sb.buf));
+	}
+
+	fclose(fp);
+	strbuf_release(&sb);
+	free(series_dir_buf);
+
+	ret = split_mail_conv(stgit_patch_to_mail, state, patches.argv, keep_cr);
+
+	argv_array_clear(&patches);
+	return ret;
+}
+
+/**
+ * A split_patches_conv() callback that converts a mercurial patch to a RFC2822
+ * message suitable for parsing with git-mailinfo.
+ */
+static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
+{
+	struct strbuf sb = STRBUF_INIT;
+	int rc = 0;
+
+	while (!strbuf_getline_lf(&sb, in)) {
+		const char *str;
+
+		if (skip_prefix(sb.buf, "# User ", &str))
+			fprintf(out, "From: %s\n", str);
+		else if (skip_prefix(sb.buf, "# Date ", &str)) {
+			timestamp_t timestamp;
+			long tz, tz2;
+			char *end;
+
+			errno = 0;
+			timestamp = parse_timestamp(str, &end, 10);
+			if (errno) {
+				rc = error(_("invalid timestamp"));
+				goto exit;
+			}
+
+			if (!skip_prefix(end, " ", &str)) {
+				rc = error(_("invalid Date line"));
+				goto exit;
+			}
+
+			errno = 0;
+			tz = strtol(str, &end, 10);
+			if (errno) {
+				rc = error(_("invalid timezone offset"));
+				goto exit;
+			}
+
+			if (*end) {
+				rc = error(_("invalid Date line"));
+				goto exit;
+			}
+
+			/*
+			 * mercurial's timezone is in seconds west of UTC,
+			 * however git's timezone is in hours + minutes east of
+			 * UTC. Convert it.
+			 */
+			tz2 = labs(tz) / 3600 * 100 + labs(tz) % 3600 / 60;
+			if (tz > 0)
+				tz2 = -tz2;
+
+			fprintf(out, "Date: %s\n", show_date(timestamp, tz2, DATE_MODE(RFC2822)));
+		} else if (starts_with(sb.buf, "# ")) {
+			continue;
+		} else {
+			fprintf(out, "\n%s\n", sb.buf);
+			break;
+		}
+	}
+
+	strbuf_reset(&sb);
+	while (strbuf_fread(&sb, 8192, in) > 0) {
+		fwrite(sb.buf, 1, sb.len, out);
+		strbuf_reset(&sb);
+	}
+exit:
+	strbuf_release(&sb);
+	return rc;
+}
+
+/**
+ * Splits a list of files/directories into individual email patches. Each path
+ * in `paths` must be a file/directory that is formatted according to
+ * `patch_format`.
+ *
+ * Once split out, the individual email patches will be stored in the state
+ * directory, with each patch's filename being its index, padded to state->prec
+ * digits.
+ *
+ * state->cur will be set to the index of the first mail, and state->last will
+ * be set to the index of the last mail.
+ *
+ * Set keep_cr to 0 to convert all lines ending with \r\n to end with \n, 1
+ * to disable this behavior, -1 to use the default configured setting.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int split_mail(struct am_state *state, enum patch_format patch_format,
+			const char **paths, int keep_cr)
+{
+	if (keep_cr < 0) {
+		keep_cr = 0;
+		git_config_get_bool("am.keepcr", &keep_cr);
+	}
+
+	switch (patch_format) {
+	case PATCH_FORMAT_MBOX:
+		return split_mail_mbox(state, paths, keep_cr, 0);
+	case PATCH_FORMAT_STGIT:
+		return split_mail_conv(stgit_patch_to_mail, state, paths, keep_cr);
+	case PATCH_FORMAT_STGIT_SERIES:
+		return split_mail_stgit_series(state, paths, keep_cr);
+	case PATCH_FORMAT_HG:
+		return split_mail_conv(hg_patch_to_mail, state, paths, keep_cr);
+	case PATCH_FORMAT_MBOXRD:
+		return split_mail_mbox(state, paths, keep_cr, 1);
+	default:
+		BUG("invalid patch_format");
+	}
+	return -1;
+}
+
+/**
+ * Setup a new am session for applying patches
+ */
+static void am_setup(struct am_state *state, enum patch_format patch_format,
+			const char **paths, int keep_cr)
+{
+	struct object_id curr_head;
+	const char *str;
+	struct strbuf sb = STRBUF_INIT;
+
+	if (!patch_format)
+		patch_format = detect_patch_format(paths);
+
+	if (!patch_format) {
+		fprintf_ln(stderr, _("Patch format detection failed."));
+		exit(128);
+	}
+
+	if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
+		die_errno(_("failed to create directory '%s'"), state->dir);
+	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+
+	if (split_mail(state, patch_format, paths, keep_cr) < 0) {
+		am_destroy(state);
+		die(_("Failed to split patches."));
+	}
+
+	if (state->rebasing)
+		state->threeway = 1;
+
+	write_state_bool(state, "threeway", state->threeway);
+	write_state_bool(state, "quiet", state->quiet);
+	write_state_bool(state, "sign", state->signoff);
+	write_state_bool(state, "utf8", state->utf8);
+
+	if (state->allow_rerere_autoupdate)
+		write_state_bool(state, "rerere-autoupdate",
+			 state->allow_rerere_autoupdate == RERERE_AUTOUPDATE);
+
+	switch (state->keep) {
+	case KEEP_FALSE:
+		str = "f";
+		break;
+	case KEEP_TRUE:
+		str = "t";
+		break;
+	case KEEP_NON_PATCH:
+		str = "b";
+		break;
+	default:
+		BUG("invalid value for state->keep");
+	}
+
+	write_state_text(state, "keep", str);
+	write_state_bool(state, "messageid", state->message_id);
+
+	switch (state->scissors) {
+	case SCISSORS_UNSET:
+		str = "";
+		break;
+	case SCISSORS_FALSE:
+		str = "f";
+		break;
+	case SCISSORS_TRUE:
+		str = "t";
+		break;
+	default:
+		BUG("invalid value for state->scissors");
+	}
+	write_state_text(state, "scissors", str);
+
+	sq_quote_argv(&sb, state->git_apply_opts.argv);
+	write_state_text(state, "apply-opt", sb.buf);
+
+	if (state->rebasing)
+		write_state_text(state, "rebasing", "");
+	else
+		write_state_text(state, "applying", "");
+
+	if (!get_oid("HEAD", &curr_head)) {
+		write_state_text(state, "abort-safety", oid_to_hex(&curr_head));
+		if (!state->rebasing)
+			update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0,
+				   UPDATE_REFS_DIE_ON_ERR);
+	} else {
+		write_state_text(state, "abort-safety", "");
+		if (!state->rebasing)
+			delete_ref(NULL, "ORIG_HEAD", NULL, 0);
+	}
+
+	/*
+	 * NOTE: Since the "next" and "last" files determine if an am_state
+	 * session is in progress, they should be written last.
+	 */
+
+	write_state_count(state, "next", state->cur);
+	write_state_count(state, "last", state->last);
+
+	strbuf_release(&sb);
+}
+
+/**
+ * Increments the patch pointer, and cleans am_state for the application of the
+ * next patch.
+ */
+static void am_next(struct am_state *state)
+{
+	struct object_id head;
+
+	FREE_AND_NULL(state->author_name);
+	FREE_AND_NULL(state->author_email);
+	FREE_AND_NULL(state->author_date);
+	FREE_AND_NULL(state->msg);
+	state->msg_len = 0;
+
+	unlink(am_path(state, "author-script"));
+	unlink(am_path(state, "final-commit"));
+
+	oidclr(&state->orig_commit);
+	unlink(am_path(state, "original-commit"));
+	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+
+	if (!get_oid("HEAD", &head))
+		write_state_text(state, "abort-safety", oid_to_hex(&head));
+	else
+		write_state_text(state, "abort-safety", "");
+
+	state->cur++;
+	write_state_count(state, "next", state->cur);
+}
+
+/**
+ * Returns the filename of the current patch email.
+ */
+static const char *msgnum(const struct am_state *state)
+{
+	static struct strbuf sb = STRBUF_INIT;
+
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%0*d", state->prec, state->cur);
+
+	return sb.buf;
+}
+
+/**
+ * Refresh and write index.
+ */
+static void refresh_and_write_cache(void)
+{
+	struct lock_file lock_file = LOCK_INIT;
+
+	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+	refresh_cache(REFRESH_QUIET);
+	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+		die(_("unable to write index file"));
+}
+
+/**
+ * Dies with a user-friendly message on how to proceed after resolving the
+ * problem. This message can be overridden with state->resolvemsg.
+ */
+static void NORETURN die_user_resolve(const struct am_state *state)
+{
+	if (state->resolvemsg) {
+		printf_ln("%s", state->resolvemsg);
+	} else {
+		const char *cmdline = state->interactive ? "git am -i" : "git am";
+
+		printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
+		printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
+		printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
+	}
+
+	exit(128);
+}
+
+/**
+ * Appends signoff to the "msg" field of the am_state.
+ */
+static void am_append_signoff(struct am_state *state)
+{
+	struct strbuf sb = STRBUF_INIT;
+
+	strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
+	append_signoff(&sb, 0, 0);
+	state->msg = strbuf_detach(&sb, &state->msg_len);
+}
+
+/**
+ * Parses `mail` using git-mailinfo, extracting its patch and authorship info.
+ * state->msg will be set to the patch message. state->author_name,
+ * state->author_email and state->author_date will be set to the patch author's
+ * name, email and date respectively. The patch body will be written to the
+ * state directory's "patch" file.
+ *
+ * Returns 1 if the patch should be skipped, 0 otherwise.
+ */
+static int parse_mail(struct am_state *state, const char *mail)
+{
+	FILE *fp;
+	struct strbuf sb = STRBUF_INIT;
+	struct strbuf msg = STRBUF_INIT;
+	struct strbuf author_name = STRBUF_INIT;
+	struct strbuf author_date = STRBUF_INIT;
+	struct strbuf author_email = STRBUF_INIT;
+	int ret = 0;
+	struct mailinfo mi;
+
+	setup_mailinfo(&mi);
+
+	if (state->utf8)
+		mi.metainfo_charset = get_commit_output_encoding();
+	else
+		mi.metainfo_charset = NULL;
+
+	switch (state->keep) {
+	case KEEP_FALSE:
+		break;
+	case KEEP_TRUE:
+		mi.keep_subject = 1;
+		break;
+	case KEEP_NON_PATCH:
+		mi.keep_non_patch_brackets_in_subject = 1;
+		break;
+	default:
+		BUG("invalid value for state->keep");
+	}
+
+	if (state->message_id)
+		mi.add_message_id = 1;
+
+	switch (state->scissors) {
+	case SCISSORS_UNSET:
+		break;
+	case SCISSORS_FALSE:
+		mi.use_scissors = 0;
+		break;
+	case SCISSORS_TRUE:
+		mi.use_scissors = 1;
+		break;
+	default:
+		BUG("invalid value for state->scissors");
+	}
+
+	mi.input = xfopen(mail, "r");
+	mi.output = xfopen(am_path(state, "info"), "w");
+	if (mailinfo(&mi, am_path(state, "msg"), am_path(state, "patch")))
+		die("could not parse patch");
+
+	fclose(mi.input);
+	fclose(mi.output);
+
+	if (mi.format_flowed)
+		warning(_("Patch sent with format=flowed; "
+			  "space at the end of lines might be lost."));
+
+	/* Extract message and author information */
+	fp = xfopen(am_path(state, "info"), "r");
+	while (!strbuf_getline_lf(&sb, fp)) {
+		const char *x;
+
+		if (skip_prefix(sb.buf, "Subject: ", &x)) {
+			if (msg.len)
+				strbuf_addch(&msg, '\n');
+			strbuf_addstr(&msg, x);
+		} else if (skip_prefix(sb.buf, "Author: ", &x))
+			strbuf_addstr(&author_name, x);
+		else if (skip_prefix(sb.buf, "Email: ", &x))
+			strbuf_addstr(&author_email, x);
+		else if (skip_prefix(sb.buf, "Date: ", &x))
+			strbuf_addstr(&author_date, x);
+	}
+	fclose(fp);
+
+	/* Skip pine's internal folder data */
+	if (!strcmp(author_name.buf, "Mail System Internal Data")) {
+		ret = 1;
+		goto finish;
+	}
+
+	if (is_empty_or_missing_file(am_path(state, "patch"))) {
+		printf_ln(_("Patch is empty."));
+		die_user_resolve(state);
+	}
+
+	strbuf_addstr(&msg, "\n\n");
+	strbuf_addbuf(&msg, &mi.log_message);
+	strbuf_stripspace(&msg, 0);
+
+	assert(!state->author_name);
+	state->author_name = strbuf_detach(&author_name, NULL);
+
+	assert(!state->author_email);
+	state->author_email = strbuf_detach(&author_email, NULL);
+
+	assert(!state->author_date);
+	state->author_date = strbuf_detach(&author_date, NULL);
+
+	assert(!state->msg);
+	state->msg = strbuf_detach(&msg, &state->msg_len);
+
+finish:
+	strbuf_release(&msg);
+	strbuf_release(&author_date);
+	strbuf_release(&author_email);
+	strbuf_release(&author_name);
+	strbuf_release(&sb);
+	clear_mailinfo(&mi);
+	return ret;
+}
+
+/**
+ * Sets commit_id to the commit hash where the mail was generated from.
+ * Returns 0 on success, -1 on failure.
+ */
+static int get_mail_commit_oid(struct object_id *commit_id, const char *mail)
+{
+	struct strbuf sb = STRBUF_INIT;
+	FILE *fp = xfopen(mail, "r");
+	const char *x;
+	int ret = 0;
+
+	if (strbuf_getline_lf(&sb, fp) ||
+	    !skip_prefix(sb.buf, "From ", &x) ||
+	    get_oid_hex(x, commit_id) < 0)
+		ret = -1;
+
+	strbuf_release(&sb);
+	fclose(fp);
+	return ret;
+}
+
+/**
+ * Sets state->msg, state->author_name, state->author_email, state->author_date
+ * to the commit's respective info.
+ */
+static void get_commit_info(struct am_state *state, struct commit *commit)
+{
+	const char *buffer, *ident_line, *msg;
+	size_t ident_len;
+	struct ident_split id;
+
+	buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding());
+
+	ident_line = find_commit_header(buffer, "author", &ident_len);
+
+	if (split_ident_line(&id, ident_line, ident_len) < 0)
+		die(_("invalid ident line: %.*s"), (int)ident_len, ident_line);
+
+	assert(!state->author_name);
+	if (id.name_begin)
+		state->author_name =
+			xmemdupz(id.name_begin, id.name_end - id.name_begin);
+	else
+		state->author_name = xstrdup("");
+
+	assert(!state->author_email);
+	if (id.mail_begin)
+		state->author_email =
+			xmemdupz(id.mail_begin, id.mail_end - id.mail_begin);
+	else
+		state->author_email = xstrdup("");
+
+	assert(!state->author_date);
+	state->author_date = xstrdup(show_ident_date(&id, DATE_MODE(NORMAL)));
+
+	assert(!state->msg);
+	msg = strstr(buffer, "\n\n");
+	if (!msg)
+		die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid));
+	state->msg = xstrdup(msg + 2);
+	state->msg_len = strlen(state->msg);
+	unuse_commit_buffer(commit, buffer);
+}
+
+/**
+ * Writes `commit` as a patch to the state directory's "patch" file.
+ */
+static void write_commit_patch(const struct am_state *state, struct commit *commit)
+{
+	struct rev_info rev_info;
+	FILE *fp;
+
+	fp = xfopen(am_path(state, "patch"), "w");
+	repo_init_revisions(the_repository, &rev_info, NULL);
+	rev_info.diff = 1;
+	rev_info.abbrev = 0;
+	rev_info.disable_stdin = 1;
+	rev_info.show_root_diff = 1;
+	rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
+	rev_info.no_commit_id = 1;
+	rev_info.diffopt.flags.binary = 1;
+	rev_info.diffopt.flags.full_index = 1;
+	rev_info.diffopt.use_color = 0;
+	rev_info.diffopt.file = fp;
+	rev_info.diffopt.close_file = 1;
+	add_pending_object(&rev_info, &commit->object, "");
+	diff_setup_done(&rev_info.diffopt);
+	log_tree_commit(&rev_info, commit);
+}
+
+/**
+ * Writes the diff of the index against HEAD as a patch to the state
+ * directory's "patch" file.
+ */
+static void write_index_patch(const struct am_state *state)
+{
+	struct tree *tree;
+	struct object_id head;
+	struct rev_info rev_info;
+	FILE *fp;
+
+	if (!get_oid("HEAD", &head)) {
+		struct commit *commit = lookup_commit_or_die(&head, "HEAD");
+		tree = get_commit_tree(commit);
+	} else
+		tree = lookup_tree(the_repository,
+				   the_repository->hash_algo->empty_tree);
+
+	fp = xfopen(am_path(state, "patch"), "w");
+	repo_init_revisions(the_repository, &rev_info, NULL);
+	rev_info.diff = 1;
+	rev_info.disable_stdin = 1;
+	rev_info.no_commit_id = 1;
+	rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
+	rev_info.diffopt.use_color = 0;
+	rev_info.diffopt.file = fp;
+	rev_info.diffopt.close_file = 1;
+	add_pending_object(&rev_info, &tree->object, "");
+	diff_setup_done(&rev_info.diffopt);
+	run_diff_index(&rev_info, 1);
+}
+
+/**
+ * Like parse_mail(), but parses the mail by looking up its commit ID
+ * directly. This is used in --rebasing mode to bypass git-mailinfo's munging
+ * of patches.
+ *
+ * state->orig_commit will be set to the original commit ID.
+ *
+ * Will always return 0 as the patch should never be skipped.
+ */
+static int parse_mail_rebase(struct am_state *state, const char *mail)
+{
+	struct commit *commit;
+	struct object_id commit_oid;
+
+	if (get_mail_commit_oid(&commit_oid, mail) < 0)
+		die(_("could not parse %s"), mail);
+
+	commit = lookup_commit_or_die(&commit_oid, mail);
+
+	get_commit_info(state, commit);
+
+	write_commit_patch(state, commit);
+
+	oidcpy(&state->orig_commit, &commit_oid);
+	write_state_text(state, "original-commit", oid_to_hex(&commit_oid));
+	update_ref("am", "REBASE_HEAD", &commit_oid,
+		   NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
+
+	return 0;
+}
+
+/**
+ * Applies current patch with git-apply. Returns 0 on success, -1 otherwise. If
+ * `index_file` is not NULL, the patch will be applied to that index.
+ */
+static int run_apply(const struct am_state *state, const char *index_file)
+{
+	struct argv_array apply_paths = ARGV_ARRAY_INIT;
+	struct argv_array apply_opts = ARGV_ARRAY_INIT;
+	struct apply_state apply_state;
+	int res, opts_left;
+	int force_apply = 0;
+	int options = 0;
+
+	if (init_apply_state(&apply_state, the_repository, NULL))
+		BUG("init_apply_state() failed");
+
+	argv_array_push(&apply_opts, "apply");
+	argv_array_pushv(&apply_opts, state->git_apply_opts.argv);
+
+	opts_left = apply_parse_options(apply_opts.argc, apply_opts.argv,
+					&apply_state, &force_apply, &options,
+					NULL);
+
+	if (opts_left != 0)
+		die("unknown option passed through to git apply");
+
+	if (index_file) {
+		apply_state.index_file = index_file;
+		apply_state.cached = 1;
+	} else
+		apply_state.check_index = 1;
+
+	/*
+	 * If we are allowed to fall back on 3-way merge, don't give false
+	 * errors during the initial attempt.
+	 */
+	if (state->threeway && !index_file)
+		apply_state.apply_verbosity = verbosity_silent;
+
+	if (check_apply_state(&apply_state, force_apply))
+		BUG("check_apply_state() failed");
+
+	argv_array_push(&apply_paths, am_path(state, "patch"));
+
+	res = apply_all_patches(&apply_state, apply_paths.argc, apply_paths.argv, options);
+
+	argv_array_clear(&apply_paths);
+	argv_array_clear(&apply_opts);
+	clear_apply_state(&apply_state);
+
+	if (res)
+		return res;
+
+	if (index_file) {
+		/* Reload index as apply_all_patches() will have modified it. */
+		discard_cache();
+		read_cache_from(index_file);
+	}
+
+	return 0;
+}
+
+/**
+ * Builds an index that contains just the blobs needed for a 3way merge.
+ */
+static int build_fake_ancestor(const struct am_state *state, const char *index_file)
+{
+	struct child_process cp = CHILD_PROCESS_INIT;
+
+	cp.git_cmd = 1;
+	argv_array_push(&cp.args, "apply");
+	argv_array_pushv(&cp.args, state->git_apply_opts.argv);
+	argv_array_pushf(&cp.args, "--build-fake-ancestor=%s", index_file);
+	argv_array_push(&cp.args, am_path(state, "patch"));
+
+	if (run_command(&cp))
+		return -1;
+
+	return 0;
+}
+
+/**
+ * Attempt a threeway merge, using index_path as the temporary index.
+ */
+static int fall_back_threeway(const struct am_state *state, const char *index_path)
+{
+	struct object_id orig_tree, their_tree, our_tree;
+	const struct object_id *bases[1] = { &orig_tree };
+	struct merge_options o;
+	struct commit *result;
+	char *their_tree_name;
+
+	if (get_oid("HEAD", &our_tree) < 0)
+		oidcpy(&our_tree, the_hash_algo->empty_tree);
+
+	if (build_fake_ancestor(state, index_path))
+		return error("could not build fake ancestor");
+
+	discard_cache();
+	read_cache_from(index_path);
+
+	if (write_index_as_tree(&orig_tree, &the_index, index_path, 0, NULL))
+		return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
+
+	say(state, stdout, _("Using index info to reconstruct a base tree..."));
+
+	if (!state->quiet) {
+		/*
+		 * List paths that needed 3-way fallback, so that the user can
+		 * review them with extra care to spot mismerges.
+		 */
+		struct rev_info rev_info;
+
+		repo_init_revisions(the_repository, &rev_info, NULL);
+		rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
+		rev_info.diffopt.filter |= diff_filter_bit('A');
+		rev_info.diffopt.filter |= diff_filter_bit('M');
+		add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
+		diff_setup_done(&rev_info.diffopt);
+		run_diff_index(&rev_info, 1);
+	}
+
+	if (run_apply(state, index_path))
+		return error(_("Did you hand edit your patch?\n"
+				"It does not apply to blobs recorded in its index."));
+
+	if (write_index_as_tree(&their_tree, &the_index, index_path, 0, NULL))
+		return error("could not write tree");
+
+	say(state, stdout, _("Falling back to patching base and 3-way merge..."));
+
+	discard_cache();
+	read_cache();
+
+	/*
+	 * This is not so wrong. Depending on which base we picked, orig_tree
+	 * may be wildly different from ours, but their_tree has the same set of
+	 * wildly different changes in parts the patch did not touch, so
+	 * recursive ends up canceling them, saying that we reverted all those
+	 * changes.
+	 */
+
+	init_merge_options(&o, the_repository);
+
+	o.branch1 = "HEAD";
+	their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
+	o.branch2 = their_tree_name;
+	o.detect_directory_renames = 0;
+
+	if (state->quiet)
+		o.verbosity = 0;
+
+	if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
+		repo_rerere(the_repository, state->allow_rerere_autoupdate);
+		free(their_tree_name);
+		return error(_("Failed to merge in the changes."));
+	}
+
+	free(their_tree_name);
+	return 0;
+}
+
+/**
+ * Commits the current index with state->msg as the commit message and
+ * state->author_name, state->author_email and state->author_date as the author
+ * information.
+ */
+static void do_commit(const struct am_state *state)
+{
+	struct object_id tree, parent, commit;
+	const struct object_id *old_oid;
+	struct commit_list *parents = NULL;
+	const char *reflog_msg, *author;
+	struct strbuf sb = STRBUF_INIT;
+
+	if (run_hook_le(NULL, "pre-applypatch", NULL))
+		exit(1);
+
+	if (write_cache_as_tree(&tree, 0, NULL))
+		die(_("git write-tree failed to write a tree"));
+
+	if (!get_oid_commit("HEAD", &parent)) {
+		old_oid = &parent;
+		commit_list_insert(lookup_commit(the_repository, &parent),
+				   &parents);
+	} else {
+		old_oid = NULL;
+		say(state, stderr, _("applying to an empty history"));
+	}
+
+	author = fmt_ident(state->author_name, state->author_email,
+		WANT_AUTHOR_IDENT,
+			state->ignore_date ? NULL : state->author_date,
+			IDENT_STRICT);
+
+	if (state->committer_date_is_author_date)
+		setenv("GIT_COMMITTER_DATE",
+			state->ignore_date ? "" : state->author_date, 1);
+
+	if (commit_tree(state->msg, state->msg_len, &tree, parents, &commit,
+			author, state->sign_commit))
+		die(_("failed to write commit object"));
+
+	reflog_msg = getenv("GIT_REFLOG_ACTION");
+	if (!reflog_msg)
+		reflog_msg = "am";
+
+	strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg),
+			state->msg);
+
+	update_ref(sb.buf, "HEAD", &commit, old_oid, 0,
+		   UPDATE_REFS_DIE_ON_ERR);
+
+	if (state->rebasing) {
+		FILE *fp = xfopen(am_path(state, "rewritten"), "a");
+
+		assert(!is_null_oid(&state->orig_commit));
+		fprintf(fp, "%s ", oid_to_hex(&state->orig_commit));
+		fprintf(fp, "%s\n", oid_to_hex(&commit));
+		fclose(fp);
+	}
+
+	run_hook_le(NULL, "post-applypatch", NULL);
+
+	strbuf_release(&sb);
+}
+
+/**
+ * Validates the am_state for resuming -- the "msg" and authorship fields must
+ * be filled up.
+ */
+static void validate_resume_state(const struct am_state *state)
+{
+	if (!state->msg)
+		die(_("cannot resume: %s does not exist."),
+			am_path(state, "final-commit"));
+
+	if (!state->author_name || !state->author_email || !state->author_date)
+		die(_("cannot resume: %s does not exist."),
+			am_path(state, "author-script"));
+}
+
+/**
+ * Interactively prompt the user on whether the current patch should be
+ * applied.
+ *
+ * Returns 0 if the user chooses to apply the patch, 1 if the user chooses to
+ * skip it.
+ */
+static int do_interactive(struct am_state *state)
+{
+	assert(state->msg);
+
+	for (;;) {
+		char reply[64];
+
+		puts(_("Commit Body is:"));
+		puts("--------------------------");
+		printf("%s", state->msg);
+		puts("--------------------------");
+
+		/*
+		 * TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+		 * in your translation. The program will only accept English
+		 * input at this point.
+		 */
+		printf(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "));
+		if (!fgets(reply, sizeof(reply), stdin))
+			die("unable to read from stdin; aborting");
+
+		if (*reply == 'y' || *reply == 'Y') {
+			return 0;
+		} else if (*reply == 'a' || *reply == 'A') {
+			state->interactive = 0;
+			return 0;
+		} else if (*reply == 'n' || *reply == 'N') {
+			return 1;
+		} else if (*reply == 'e' || *reply == 'E') {
+			struct strbuf msg = STRBUF_INIT;
+
+			if (!launch_editor(am_path(state, "final-commit"), &msg, NULL)) {
+				free(state->msg);
+				state->msg = strbuf_detach(&msg, &state->msg_len);
+			}
+			strbuf_release(&msg);
+		} else if (*reply == 'v' || *reply == 'V') {
+			const char *pager = git_pager(1);
+			struct child_process cp = CHILD_PROCESS_INIT;
+
+			if (!pager)
+				pager = "cat";
+			prepare_pager_args(&cp, pager);
+			argv_array_push(&cp.args, am_path(state, "patch"));
+			run_command(&cp);
+		}
+	}
+}
+
+/**
+ * Applies all queued mail.
+ *
+ * If `resume` is true, we are "resuming". The "msg" and authorship fields, as
+ * well as the state directory's "patch" file is used as-is for applying the
+ * patch and committing it.
+ */
+static void am_run(struct am_state *state, int resume)
+{
+	const char *argv_gc_auto[] = {"gc", "--auto", NULL};
+	struct strbuf sb = STRBUF_INIT;
+
+	unlink(am_path(state, "dirtyindex"));
+
+	refresh_and_write_cache();
+
+	if (repo_index_has_changes(the_repository, NULL, &sb)) {
+		write_state_bool(state, "dirtyindex", 1);
+		die(_("Dirty index: cannot apply patches (dirty: %s)"), sb.buf);
+	}
+
+	strbuf_release(&sb);
+
+	while (state->cur <= state->last) {
+		const char *mail = am_path(state, msgnum(state));
+		int apply_status;
+
+		reset_ident_date();
+
+		if (!file_exists(mail))
+			goto next;
+
+		if (resume) {
+			validate_resume_state(state);
+		} else {
+			int skip;
+
+			if (state->rebasing)
+				skip = parse_mail_rebase(state, mail);
+			else
+				skip = parse_mail(state, mail);
+
+			if (skip)
+				goto next; /* mail should be skipped */
+
+			if (state->signoff)
+				am_append_signoff(state);
+
+			write_author_script(state);
+			write_commit_msg(state);
+		}
+
+		if (state->interactive && do_interactive(state))
+			goto next;
+
+		if (run_applypatch_msg_hook(state))
+			exit(1);
+
+		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
+
+		apply_status = run_apply(state, NULL);
+
+		if (apply_status && state->threeway) {
+			struct strbuf sb = STRBUF_INIT;
+
+			strbuf_addstr(&sb, am_path(state, "patch-merge-index"));
+			apply_status = fall_back_threeway(state, sb.buf);
+			strbuf_release(&sb);
+
+			/*
+			 * Applying the patch to an earlier tree and merging
+			 * the result may have produced the same tree as ours.
+			 */
+			if (!apply_status &&
+			    !repo_index_has_changes(the_repository, NULL, NULL)) {
+				say(state, stdout, _("No changes -- Patch already applied."));
+				goto next;
+			}
+		}
+
+		if (apply_status) {
+			printf_ln(_("Patch failed at %s %.*s"), msgnum(state),
+				linelen(state->msg), state->msg);
+
+			if (advice_amworkdir)
+				advise(_("Use 'git am --show-current-patch' to see the failed patch"));
+
+			die_user_resolve(state);
+		}
+
+		do_commit(state);
+
+next:
+		am_next(state);
+
+		if (resume)
+			am_load(state);
+		resume = 0;
+	}
+
+	if (!is_empty_or_missing_file(am_path(state, "rewritten"))) {
+		assert(state->rebasing);
+		copy_notes_for_rebase(state);
+		run_post_rewrite_hook(state);
+	}
+
+	/*
+	 * In rebasing mode, it's up to the caller to take care of
+	 * housekeeping.
+	 */
+	if (!state->rebasing) {
+		am_destroy(state);
+		close_object_store(the_repository->objects);
+		run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+	}
+}
+
+/**
+ * Resume the current am session after patch application failure. The user did
+ * all the hard work, and we do not have to do any patch application. Just
+ * trust and commit what the user has in the index and working tree.
+ */
+static void am_resolve(struct am_state *state)
+{
+	validate_resume_state(state);
+
+	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
+
+	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
+		printf_ln(_("No changes - did you forget to use 'git add'?\n"
+			"If there is nothing left to stage, chances are that something else\n"
+			"already introduced the same changes; you might want to skip this patch."));
+		die_user_resolve(state);
+	}
+
+	if (unmerged_cache()) {
+		printf_ln(_("You still have unmerged paths in your index.\n"
+			"You should 'git add' each file with resolved conflicts to mark them as such.\n"
+			"You might run `git rm` on a file to accept \"deleted by them\" for it."));
+		die_user_resolve(state);
+	}
+
+	if (state->interactive) {
+		write_index_patch(state);
+		if (do_interactive(state))
+			goto next;
+	}
+
+	repo_rerere(the_repository, 0);
+
+	do_commit(state);
+
+next:
+	am_next(state);
+	am_load(state);
+	am_run(state, 0);
+}
+
+/**
+ * Performs a checkout fast-forward from `head` to `remote`. If `reset` is
+ * true, any unmerged entries will be discarded. Returns 0 on success, -1 on
+ * failure.
+ */
+static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
+{
+	struct lock_file lock_file = LOCK_INIT;
+	struct unpack_trees_options opts;
+	struct tree_desc t[2];
+
+	if (parse_tree(head) || parse_tree(remote))
+		return -1;
+
+	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+
+	refresh_cache(REFRESH_QUIET);
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.merge = 1;
+	opts.reset = reset;
+	opts.fn = twoway_merge;
+	init_tree_desc(&t[0], head->buffer, head->size);
+	init_tree_desc(&t[1], remote->buffer, remote->size);
+
+	if (unpack_trees(2, t, &opts)) {
+		rollback_lock_file(&lock_file);
+		return -1;
+	}
+
+	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+		die(_("unable to write new index file"));
+
+	return 0;
+}
+
+/**
+ * Merges a tree into the index. The index's stat info will take precedence
+ * over the merged tree's. Returns 0 on success, -1 on failure.
+ */
+static int merge_tree(struct tree *tree)
+{
+	struct lock_file lock_file = LOCK_INIT;
+	struct unpack_trees_options opts;
+	struct tree_desc t[1];
+
+	if (parse_tree(tree))
+		return -1;
+
+	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.merge = 1;
+	opts.fn = oneway_merge;
+	init_tree_desc(&t[0], tree->buffer, tree->size);
+
+	if (unpack_trees(1, t, &opts)) {
+		rollback_lock_file(&lock_file);
+		return -1;
+	}
+
+	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+		die(_("unable to write new index file"));
+
+	return 0;
+}
+
+/**
+ * Clean the index without touching entries that are not modified between
+ * `head` and `remote`.
+ */
+static int clean_index(const struct object_id *head, const struct object_id *remote)
+{
+	struct tree *head_tree, *remote_tree, *index_tree;
+	struct object_id index;
+
+	head_tree = parse_tree_indirect(head);
+	if (!head_tree)
+		return error(_("Could not parse object '%s'."), oid_to_hex(head));
+
+	remote_tree = parse_tree_indirect(remote);
+	if (!remote_tree)
+		return error(_("Could not parse object '%s'."), oid_to_hex(remote));
+
+	read_cache_unmerged();
+
+	if (fast_forward_to(head_tree, head_tree, 1))
+		return -1;
+
+	if (write_cache_as_tree(&index, 0, NULL))
+		return -1;
+
+	index_tree = parse_tree_indirect(&index);
+	if (!index_tree)
+		return error(_("Could not parse object '%s'."), oid_to_hex(&index));
+
+	if (fast_forward_to(index_tree, remote_tree, 0))
+		return -1;
+
+	if (merge_tree(remote_tree))
+		return -1;
+
+	remove_branch_state(the_repository, 0);
+
+	return 0;
+}
+
+/**
+ * Resets rerere's merge resolution metadata.
+ */
+static void am_rerere_clear(void)
+{
+	struct string_list merge_rr = STRING_LIST_INIT_DUP;
+	rerere_clear(the_repository, &merge_rr);
+	string_list_clear(&merge_rr, 1);
+}
+
+/**
+ * Resume the current am session by skipping the current patch.
+ */
+static void am_skip(struct am_state *state)
+{
+	struct object_id head;
+
+	am_rerere_clear();
+
+	if (get_oid("HEAD", &head))
+		oidcpy(&head, the_hash_algo->empty_tree);
+
+	if (clean_index(&head, &head))
+		die(_("failed to clean index"));
+
+	if (state->rebasing) {
+		FILE *fp = xfopen(am_path(state, "rewritten"), "a");
+
+		assert(!is_null_oid(&state->orig_commit));
+		fprintf(fp, "%s ", oid_to_hex(&state->orig_commit));
+		fprintf(fp, "%s\n", oid_to_hex(&head));
+		fclose(fp);
+	}
+
+	am_next(state);
+	am_load(state);
+	am_run(state, 0);
+}
+
+/**
+ * Returns true if it is safe to reset HEAD to the ORIG_HEAD, false otherwise.
+ *
+ * It is not safe to reset HEAD when:
+ * 1. git-am previously failed because the index was dirty.
+ * 2. HEAD has moved since git-am previously failed.
+ */
+static int safe_to_abort(const struct am_state *state)
+{
+	struct strbuf sb = STRBUF_INIT;
+	struct object_id abort_safety, head;
+
+	if (file_exists(am_path(state, "dirtyindex")))
+		return 0;
+
+	if (read_state_file(&sb, state, "abort-safety", 1) > 0) {
+		if (get_oid_hex(sb.buf, &abort_safety))
+			die(_("could not parse %s"), am_path(state, "abort-safety"));
+	} else
+		oidclr(&abort_safety);
+	strbuf_release(&sb);
+
+	if (get_oid("HEAD", &head))
+		oidclr(&head);
+
+	if (oideq(&head, &abort_safety))
+		return 1;
+
+	warning(_("You seem to have moved HEAD since the last 'am' failure.\n"
+		"Not rewinding to ORIG_HEAD"));
+
+	return 0;
+}
+
+/**
+ * Aborts the current am session if it is safe to do so.
+ */
+static void am_abort(struct am_state *state)
+{
+	struct object_id curr_head, orig_head;
+	int has_curr_head, has_orig_head;
+	char *curr_branch;
+
+	if (!safe_to_abort(state)) {
+		am_destroy(state);
+		return;
+	}
+
+	am_rerere_clear();
+
+	curr_branch = resolve_refdup("HEAD", 0, &curr_head, NULL);
+	has_curr_head = curr_branch && !is_null_oid(&curr_head);
+	if (!has_curr_head)
+		oidcpy(&curr_head, the_hash_algo->empty_tree);
+
+	has_orig_head = !get_oid("ORIG_HEAD", &orig_head);
+	if (!has_orig_head)
+		oidcpy(&orig_head, the_hash_algo->empty_tree);
+
+	clean_index(&curr_head, &orig_head);
+
+	if (has_orig_head)
+		update_ref("am --abort", "HEAD", &orig_head,
+			   has_curr_head ? &curr_head : NULL, 0,
+			   UPDATE_REFS_DIE_ON_ERR);
+	else if (curr_branch)
+		delete_ref(NULL, curr_branch, NULL, REF_NO_DEREF);
+
+	free(curr_branch);
+	am_destroy(state);
+}
+
+static int show_patch(struct am_state *state)
+{
+	struct strbuf sb = STRBUF_INIT;
+	const char *patch_path;
+	int len;
+
+	if (!is_null_oid(&state->orig_commit)) {
+		const char *av[4] = { "show", NULL, "--", NULL };
+		char *new_oid_str;
+		int ret;
+
+		av[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit));
+		ret = run_command_v_opt(av, RUN_GIT_CMD);
+		free(new_oid_str);
+		return ret;
+	}
+
+	patch_path = am_path(state, msgnum(state));
+	len = strbuf_read_file(&sb, patch_path, 0);
+	if (len < 0)
+		die_errno(_("failed to read '%s'"), patch_path);
+
+	setup_pager();
+	write_in_full(1, sb.buf, sb.len);
+	strbuf_release(&sb);
+	return 0;
+}
+
+/**
+ * parse_options() callback that validates and sets opt->value to the
+ * PATCH_FORMAT_* enum value corresponding to `arg`.
+ */
+static int parse_opt_patchformat(const struct option *opt, const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	if (unset)
+		*opt_value = PATCH_FORMAT_UNKNOWN;
+	else if (!strcmp(arg, "mbox"))
+		*opt_value = PATCH_FORMAT_MBOX;
+	else if (!strcmp(arg, "stgit"))
+		*opt_value = PATCH_FORMAT_STGIT;
+	else if (!strcmp(arg, "stgit-series"))
+		*opt_value = PATCH_FORMAT_STGIT_SERIES;
+	else if (!strcmp(arg, "hg"))
+		*opt_value = PATCH_FORMAT_HG;
+	else if (!strcmp(arg, "mboxrd"))
+		*opt_value = PATCH_FORMAT_MBOXRD;
+	/*
+	 * Please update $__git_patchformat in git-completion.bash
+	 * when you add new options
+	 */
+	else
+		return error(_("Invalid value for --patch-format: %s"), arg);
+	return 0;
+}
+
+enum resume_mode {
+	RESUME_FALSE = 0,
+	RESUME_APPLY,
+	RESUME_RESOLVED,
+	RESUME_SKIP,
+	RESUME_ABORT,
+	RESUME_QUIT,
+	RESUME_SHOW_PATCH
+};
+
+static int git_am_config(const char *k, const char *v, void *cb)
+{
+	int status;
+
+	status = git_gpg_config(k, v, NULL);
+	if (status)
+		return status;
+
+	return git_default_config(k, v, NULL);
+}
+
+int cmd_am(int argc, const char **argv, const char *prefix)
+{
+	struct am_state state;
+	int binary = -1;
+	int keep_cr = -1;
+	int patch_format = PATCH_FORMAT_UNKNOWN;
+	enum resume_mode resume = RESUME_FALSE;
+	int in_progress;
+	int ret = 0;
+
+	const char * const usage[] = {
+		N_("git am [<options>] [(<mbox> | <Maildir>)...]"),
+		N_("git am [<options>] (--continue | --skip | --abort)"),
+		NULL
+	};
+
+	struct option options[] = {
+		OPT_BOOL('i', "interactive", &state.interactive,
+			N_("run interactively")),
+		OPT_HIDDEN_BOOL('b', "binary", &binary,
+			N_("historical option -- no-op")),
+		OPT_BOOL('3', "3way", &state.threeway,
+			N_("allow fall back on 3way merging if needed")),
+		OPT__QUIET(&state.quiet, N_("be quiet")),
+		OPT_SET_INT('s', "signoff", &state.signoff,
+			N_("add a Signed-off-by line to the commit message"),
+			SIGNOFF_EXPLICIT),
+		OPT_BOOL('u', "utf8", &state.utf8,
+			N_("recode into utf8 (default)")),
+		OPT_SET_INT('k', "keep", &state.keep,
+			N_("pass -k flag to git-mailinfo"), KEEP_TRUE),
+		OPT_SET_INT(0, "keep-non-patch", &state.keep,
+			N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH),
+		OPT_BOOL('m', "message-id", &state.message_id,
+			N_("pass -m flag to git-mailinfo")),
+		OPT_SET_INT_F(0, "keep-cr", &keep_cr,
+			N_("pass --keep-cr flag to git-mailsplit for mbox format"),
+			1, PARSE_OPT_NONEG),
+		OPT_SET_INT_F(0, "no-keep-cr", &keep_cr,
+			N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"),
+			0, PARSE_OPT_NONEG),
+		OPT_BOOL('c', "scissors", &state.scissors,
+			N_("strip everything before a scissors line")),
+		OPT_PASSTHRU_ARGV(0, "whitespace", &state.git_apply_opts, N_("action"),
+			N_("pass it through git-apply"),
+			0),
+		OPT_PASSTHRU_ARGV(0, "ignore-space-change", &state.git_apply_opts, NULL,
+			N_("pass it through git-apply"),
+			PARSE_OPT_NOARG),
+		OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &state.git_apply_opts, NULL,
+			N_("pass it through git-apply"),
+			PARSE_OPT_NOARG),
+		OPT_PASSTHRU_ARGV(0, "directory", &state.git_apply_opts, N_("root"),
+			N_("pass it through git-apply"),
+			0),
+		OPT_PASSTHRU_ARGV(0, "exclude", &state.git_apply_opts, N_("path"),
+			N_("pass it through git-apply"),
+			0),
+		OPT_PASSTHRU_ARGV(0, "include", &state.git_apply_opts, N_("path"),
+			N_("pass it through git-apply"),
+			0),
+		OPT_PASSTHRU_ARGV('C', NULL, &state.git_apply_opts, N_("n"),
+			N_("pass it through git-apply"),
+			0),
+		OPT_PASSTHRU_ARGV('p', NULL, &state.git_apply_opts, N_("num"),
+			N_("pass it through git-apply"),
+			0),
+		OPT_CALLBACK(0, "patch-format", &patch_format, N_("format"),
+			N_("format the patch(es) are in"),
+			parse_opt_patchformat),
+		OPT_PASSTHRU_ARGV(0, "reject", &state.git_apply_opts, NULL,
+			N_("pass it through git-apply"),
+			PARSE_OPT_NOARG),
+		OPT_STRING(0, "resolvemsg", &state.resolvemsg, NULL,
+			N_("override error message when patch failure occurs")),
+		OPT_CMDMODE(0, "continue", &resume,
+			N_("continue applying patches after resolving a conflict"),
+			RESUME_RESOLVED),
+		OPT_CMDMODE('r', "resolved", &resume,
+			N_("synonyms for --continue"),
+			RESUME_RESOLVED),
+		OPT_CMDMODE(0, "skip", &resume,
+			N_("skip the current patch"),
+			RESUME_SKIP),
+		OPT_CMDMODE(0, "abort", &resume,
+			N_("restore the original branch and abort the patching operation."),
+			RESUME_ABORT),
+		OPT_CMDMODE(0, "quit", &resume,
+			N_("abort the patching operation but keep HEAD where it is."),
+			RESUME_QUIT),
+		OPT_CMDMODE(0, "show-current-patch", &resume,
+			N_("show the patch being applied."),
+			RESUME_SHOW_PATCH),
+		OPT_BOOL(0, "committer-date-is-author-date",
+			&state.committer_date_is_author_date,
+			N_("lie about committer date")),
+		OPT_BOOL(0, "ignore-date", &state.ignore_date,
+			N_("use current timestamp for author date")),
+		OPT_RERERE_AUTOUPDATE(&state.allow_rerere_autoupdate),
+		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
+		  N_("GPG-sign commits"),
+		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
+			N_("(internal use for git-rebase)")),
+		OPT_END()
+	};
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(usage, options);
+
+	git_config(git_am_config, NULL);
+
+	am_state_init(&state);
+
+	in_progress = am_in_progress(&state);
+	if (in_progress)
+		am_load(&state);
+
+	argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+	if (binary >= 0)
+		fprintf_ln(stderr, _("The -b/--binary option has been a no-op for long time, and\n"
+				"it will be removed. Please do not use it anymore."));
+
+	/* Ensure a valid committer ident can be constructed */
+	git_committer_info(IDENT_STRICT);
+
+	if (repo_read_index_preload(the_repository, NULL, 0) < 0)
+		die(_("failed to read the index"));
+
+	if (in_progress) {
+		/*
+		 * Catch user error to feed us patches when there is a session
+		 * in progress:
+		 *
+		 * 1. mbox path(s) are provided on the command-line.
+		 * 2. stdin is not a tty: the user is trying to feed us a patch
+		 *    from standard input. This is somewhat unreliable -- stdin
+		 *    could be /dev/null for example and the caller did not
+		 *    intend to feed us a patch but wanted to continue
+		 *    unattended.
+		 */
+		if (argc || (resume == RESUME_FALSE && !isatty(0)))
+			die(_("previous rebase directory %s still exists but mbox given."),
+				state.dir);
+
+		if (resume == RESUME_FALSE)
+			resume = RESUME_APPLY;
+
+		if (state.signoff == SIGNOFF_EXPLICIT)
+			am_append_signoff(&state);
+	} else {
+		struct argv_array paths = ARGV_ARRAY_INIT;
+		int i;
+
+		/*
+		 * Handle stray state directory in the independent-run case. In
+		 * the --rebasing case, it is up to the caller to take care of
+		 * stray directories.
+		 */
+		if (file_exists(state.dir) && !state.rebasing) {
+			if (resume == RESUME_ABORT || resume == RESUME_QUIT) {
+				am_destroy(&state);
+				am_state_release(&state);
+				return 0;
+			}
+
+			die(_("Stray %s directory found.\n"
+				"Use \"git am --abort\" to remove it."),
+				state.dir);
+		}
+
+		if (resume)
+			die(_("Resolve operation not in progress, we are not resuming."));
+
+		for (i = 0; i < argc; i++) {
+			if (is_absolute_path(argv[i]) || !prefix)
+				argv_array_push(&paths, argv[i]);
+			else
+				argv_array_push(&paths, mkpath("%s/%s", prefix, argv[i]));
+		}
+
+		if (state.interactive && !paths.argc)
+			die(_("interactive mode requires patches on the command line"));
+
+		am_setup(&state, patch_format, paths.argv, keep_cr);
+
+		argv_array_clear(&paths);
+	}
+
+	switch (resume) {
+	case RESUME_FALSE:
+		am_run(&state, 0);
+		break;
+	case RESUME_APPLY:
+		am_run(&state, 1);
+		break;
+	case RESUME_RESOLVED:
+		am_resolve(&state);
+		break;
+	case RESUME_SKIP:
+		am_skip(&state);
+		break;
+	case RESUME_ABORT:
+		am_abort(&state);
+		break;
+	case RESUME_QUIT:
+		am_rerere_clear();
+		am_destroy(&state);
+		break;
+	case RESUME_SHOW_PATCH:
+		ret = show_patch(&state);
+		break;
+	default:
+		BUG("invalid resume value");
+	}
+
+	am_state_release(&state);
+
+	return ret;
+}
diff --git a/builtin/annotate.c b/builtin/annotate.c
new file mode 100644
index 0000000000..da413ae0d1
--- /dev/null
+++ b/builtin/annotate.c
@@ -0,0 +1,22 @@
+/*
+ * "git annotate" builtin alias
+ *
+ * Copyright (C) 2006 Ryan Anderson
+ */
+#include "git-compat-util.h"
+#include "builtin.h"
+#include "argv-array.h"
+
+int cmd_annotate(int argc, const char **argv, const char *prefix)
+{
+	struct argv_array args = ARGV_ARRAY_INIT;
+	int i;
+
+	argv_array_pushl(&args, "annotate", "-c", NULL);
+
+	for (i = 1; i < argc; i++) {
+		argv_array_push(&args, argv[i]);
+	}
+
+	return cmd_blame(args.argc, args.argv, prefix);
+}
diff --git a/builtin/apply.c b/builtin/apply.c
new file mode 100644
index 0000000000..3f099b9605
--- /dev/null
+++ b/builtin/apply.c
@@ -0,0 +1,34 @@
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "lockfile.h"
+#include "apply.h"
+
+static const char * const apply_usage[] = {
+	N_("git apply [<options>] [<patch>...]"),
+	NULL
+};
+
+int cmd_apply(int argc, const char **argv, const char *prefix)
+{
+	int force_apply = 0;
+	int options = 0;
+	int ret;
+	struct apply_state state;
+
+	if (init_apply_state(&state, the_repository, prefix))
+		exit(128);
+
+	argc = apply_parse_options(argc, argv,
+				   &state, &force_apply, &options,
+				   apply_usage);
+
+	if (check_apply_state(&state, force_apply))
+		exit(128);
+
+	ret = apply_all_patches(&state, argc, argv, options);
+
+	clear_apply_state(&state);
+
+	return ret;
+}
diff --git a/builtin/archive.c b/builtin/archive.c
new file mode 100644
index 0000000000..45d11669aa
--- /dev/null
+++ b/builtin/archive.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2006 Franck Bui-Huu
+ * Copyright (c) 2006 Rene Scharfe
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "archive.h"
+#include "transport.h"
+#include "parse-options.h"
+#include "pkt-line.h"
+#include "sideband.h"
+
+static void create_output_file(const char *output_file)
+{
+	int output_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+	if (output_fd < 0)
+		die_errno(_("could not create archive file '%s'"), output_file);
+	if (output_fd != 1) {
+		if (dup2(output_fd, 1) < 0)
+			die_errno(_("could not redirect output"));
+		else
+			close(output_fd);
+	}
+}
+
+static int run_remote_archiver(int argc, const char **argv,
+			       const char *remote, const char *exec,
+			       const char *name_hint)
+{
+	int fd[2], i, rv;
+	struct transport *transport;
+	struct remote *_remote;
+	struct packet_reader reader;
+
+	_remote = remote_get(remote);
+	if (!_remote->url[0])
+		die(_("git archive: Remote with no URL"));
+	transport = transport_get(_remote, _remote->url[0]);
+	transport_connect(transport, "git-upload-archive", exec, fd);
+
+	/*
+	 * Inject a fake --format field at the beginning of the
+	 * arguments, with the format inferred from our output
+	 * filename. This way explicit --format options can override
+	 * it.
+	 */
+	if (name_hint) {
+		const char *format = archive_format_from_filename(name_hint);
+		if (format)
+			packet_write_fmt(fd[1], "argument --format=%s\n", format);
+	}
+	for (i = 1; i < argc; i++)
+		packet_write_fmt(fd[1], "argument %s\n", argv[i]);
+	packet_flush(fd[1]);
+
+	packet_reader_init(&reader, fd[0], NULL, 0,
+			   PACKET_READ_CHOMP_NEWLINE |
+			   PACKET_READ_DIE_ON_ERR_PACKET);
+
+	if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
+		die(_("git archive: expected ACK/NAK, got a flush packet"));
+	if (strcmp(reader.line, "ACK")) {
+		if (starts_with(reader.line, "NACK "))
+			die(_("git archive: NACK %s"), reader.line + 5);
+		die(_("git archive: protocol error"));
+	}
+
+	if (packet_reader_read(&reader) != PACKET_READ_FLUSH)
+		die(_("git archive: expected a flush"));
+
+	/* Now, start reading from fd[0] and spit it out to stdout */
+	rv = recv_sideband("archive", fd[0], 1);
+	rv |= transport_disconnect(transport);
+
+	return !!rv;
+}
+
+#define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | 	\
+			     PARSE_OPT_KEEP_ARGV0 | 	\
+			     PARSE_OPT_KEEP_UNKNOWN |	\
+			     PARSE_OPT_NO_INTERNAL_HELP	)
+
+int cmd_archive(int argc, const char **argv, const char *prefix)
+{
+	const char *exec = "git-upload-archive";
+	const char *output = NULL;
+	const char *remote = NULL;
+	struct option local_opts[] = {
+		OPT_FILENAME('o', "output", &output,
+			     N_("write the archive to this file")),
+		OPT_STRING(0, "remote", &remote, N_("repo"),
+			N_("retrieve the archive from remote repository <repo>")),
+		OPT_STRING(0, "exec", &exec, N_("command"),
+			N_("path to the remote git-upload-archive command")),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, local_opts, NULL,
+			     PARSE_OPT_KEEP_ALL);
+
+	init_archivers();
+
+	if (output)
+		create_output_file(output);
+
+	if (remote)
+		return run_remote_archiver(argc, argv, remote, exec, output);
+
+	setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
+
+	return write_archive(argc, argv, prefix, the_repository, output, 0);
+}
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
new file mode 100644
index 0000000000..1fbe156e67
--- /dev/null
+++ b/builtin/bisect--helper.c
@@ -0,0 +1,714 @@
+#include "builtin.h"
+#include "cache.h"
+#include "parse-options.h"
+#include "bisect.h"
+#include "refs.h"
+#include "dir.h"
+#include "argv-array.h"
+#include "run-command.h"
+#include "prompt.h"
+#include "quote.h"
+
+static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
+static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
+static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
+static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
+static GIT_PATH_FUNC(git_path_bisect_head, "BISECT_HEAD")
+static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
+static GIT_PATH_FUNC(git_path_head_name, "head-name")
+static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
+
+static const char * const git_bisect_helper_usage[] = {
+	N_("git bisect--helper --next-all [--no-checkout]"),
+	N_("git bisect--helper --write-terms <bad_term> <good_term>"),
+	N_("git bisect--helper --bisect-clean-state"),
+	N_("git bisect--helper --bisect-reset [<commit>]"),
+	N_("git bisect--helper --bisect-write [--no-log] <state> <revision> <good_term> <bad_term>"),
+	N_("git bisect--helper --bisect-check-and-set-terms <command> <good_term> <bad_term>"),
+	N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"),
+	N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
+	N_("git bisect--helper --bisect-start [--term-{old,good}=<term> --term-{new,bad}=<term>]"
+					     "[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]"),
+	NULL
+};
+
+struct bisect_terms {
+	char *term_good;
+	char *term_bad;
+};
+
+static void free_terms(struct bisect_terms *terms)
+{
+	FREE_AND_NULL(terms->term_good);
+	FREE_AND_NULL(terms->term_bad);
+}
+
+static void set_terms(struct bisect_terms *terms, const char *bad,
+		      const char *good)
+{
+	free((void *)terms->term_good);
+	terms->term_good = xstrdup(good);
+	free((void *)terms->term_bad);
+	terms->term_bad = xstrdup(bad);
+}
+
+static const char *vocab_bad = "bad|new";
+static const char *vocab_good = "good|old";
+
+/*
+ * Check whether the string `term` belongs to the set of strings
+ * included in the variable arguments.
+ */
+LAST_ARG_MUST_BE_NULL
+static int one_of(const char *term, ...)
+{
+	int res = 0;
+	va_list matches;
+	const char *match;
+
+	va_start(matches, term);
+	while (!res && (match = va_arg(matches, const char *)))
+		res = !strcmp(term, match);
+	va_end(matches);
+
+	return res;
+}
+
+static int check_term_format(const char *term, const char *orig_term)
+{
+	int res;
+	char *new_term = xstrfmt("refs/bisect/%s", term);
+
+	res = check_refname_format(new_term, 0);
+	free(new_term);
+
+	if (res)
+		return error(_("'%s' is not a valid term"), term);
+
+	if (one_of(term, "help", "start", "skip", "next", "reset",
+			"visualize", "view", "replay", "log", "run", "terms", NULL))
+		return error(_("can't use the builtin command '%s' as a term"), term);
+
+	/*
+	 * In theory, nothing prevents swapping completely good and bad,
+	 * but this situation could be confusing and hasn't been tested
+	 * enough. Forbid it for now.
+	 */
+
+	if ((strcmp(orig_term, "bad") && one_of(term, "bad", "new", NULL)) ||
+		 (strcmp(orig_term, "good") && one_of(term, "good", "old", NULL)))
+		return error(_("can't change the meaning of the term '%s'"), term);
+
+	return 0;
+}
+
+static int write_terms(const char *bad, const char *good)
+{
+	FILE *fp = NULL;
+	int res;
+
+	if (!strcmp(bad, good))
+		return error(_("please use two different terms"));
+
+	if (check_term_format(bad, "bad") || check_term_format(good, "good"))
+		return -1;
+
+	fp = fopen(git_path_bisect_terms(), "w");
+	if (!fp)
+		return error_errno(_("could not open the file BISECT_TERMS"));
+
+	res = fprintf(fp, "%s\n%s\n", bad, good);
+	res |= fclose(fp);
+	return (res < 0) ? -1 : 0;
+}
+
+static int is_expected_rev(const char *expected_hex)
+{
+	struct strbuf actual_hex = STRBUF_INIT;
+	int res = 0;
+	if (strbuf_read_file(&actual_hex, git_path_bisect_expected_rev(), 0) >= 40) {
+		strbuf_trim(&actual_hex);
+		res = !strcmp(actual_hex.buf, expected_hex);
+	}
+	strbuf_release(&actual_hex);
+	return res;
+}
+
+static void check_expected_revs(const char **revs, int rev_nr)
+{
+	int i;
+
+	for (i = 0; i < rev_nr; i++) {
+		if (!is_expected_rev(revs[i])) {
+			unlink_or_warn(git_path_bisect_ancestors_ok());
+			unlink_or_warn(git_path_bisect_expected_rev());
+		}
+	}
+}
+
+static int bisect_reset(const char *commit)
+{
+	struct strbuf branch = STRBUF_INIT;
+
+	if (!commit) {
+		if (strbuf_read_file(&branch, git_path_bisect_start(), 0) < 1) {
+			printf(_("We are not bisecting.\n"));
+			return 0;
+		}
+		strbuf_rtrim(&branch);
+	} else {
+		struct object_id oid;
+
+		if (get_oid_commit(commit, &oid))
+			return error(_("'%s' is not a valid commit"), commit);
+		strbuf_addstr(&branch, commit);
+	}
+
+	if (!file_exists(git_path_bisect_head())) {
+		struct argv_array argv = ARGV_ARRAY_INIT;
+
+		argv_array_pushl(&argv, "checkout", branch.buf, "--", NULL);
+		if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+			strbuf_release(&branch);
+			argv_array_clear(&argv);
+			return error(_("could not check out original"
+				       " HEAD '%s'. Try 'git bisect"
+				       " reset <commit>'."), branch.buf);
+		}
+		argv_array_clear(&argv);
+	}
+
+	strbuf_release(&branch);
+	return bisect_clean_state();
+}
+
+static void log_commit(FILE *fp, char *fmt, const char *state,
+		       struct commit *commit)
+{
+	struct pretty_print_context pp = {0};
+	struct strbuf commit_msg = STRBUF_INIT;
+	char *label = xstrfmt(fmt, state);
+
+	format_commit_message(commit, "%s", &commit_msg, &pp);
+
+	fprintf(fp, "# %s: [%s] %s\n", label, oid_to_hex(&commit->object.oid),
+		commit_msg.buf);
+
+	strbuf_release(&commit_msg);
+	free(label);
+}
+
+static int bisect_write(const char *state, const char *rev,
+			const struct bisect_terms *terms, int nolog)
+{
+	struct strbuf tag = STRBUF_INIT;
+	struct object_id oid;
+	struct commit *commit;
+	FILE *fp = NULL;
+	int retval = 0;
+
+	if (!strcmp(state, terms->term_bad)) {
+		strbuf_addf(&tag, "refs/bisect/%s", state);
+	} else if (one_of(state, terms->term_good, "skip", NULL)) {
+		strbuf_addf(&tag, "refs/bisect/%s-%s", state, rev);
+	} else {
+		retval = error(_("Bad bisect_write argument: %s"), state);
+		goto finish;
+	}
+
+	if (get_oid(rev, &oid)) {
+		retval = error(_("couldn't get the oid of the rev '%s'"), rev);
+		goto finish;
+	}
+
+	if (update_ref(NULL, tag.buf, &oid, NULL, 0,
+		       UPDATE_REFS_MSG_ON_ERR)) {
+		retval = -1;
+		goto finish;
+	}
+
+	fp = fopen(git_path_bisect_log(), "a");
+	if (!fp) {
+		retval = error_errno(_("couldn't open the file '%s'"), git_path_bisect_log());
+		goto finish;
+	}
+
+	commit = lookup_commit_reference(the_repository, &oid);
+	log_commit(fp, "%s", state, commit);
+
+	if (!nolog)
+		fprintf(fp, "git bisect %s %s\n", state, rev);
+
+finish:
+	if (fp)
+		fclose(fp);
+	strbuf_release(&tag);
+	return retval;
+}
+
+static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
+{
+	int has_term_file = !is_empty_or_missing_file(git_path_bisect_terms());
+
+	if (one_of(cmd, "skip", "start", "terms", NULL))
+		return 0;
+
+	if (has_term_file && strcmp(cmd, terms->term_bad) &&
+	    strcmp(cmd, terms->term_good))
+		return error(_("Invalid command: you're currently in a "
+				"%s/%s bisect"), terms->term_bad,
+				terms->term_good);
+
+	if (!has_term_file) {
+		if (one_of(cmd, "bad", "good", NULL)) {
+			set_terms(terms, "bad", "good");
+			return write_terms(terms->term_bad, terms->term_good);
+		}
+		if (one_of(cmd, "new", "old", NULL)) {
+			set_terms(terms, "new", "old");
+			return write_terms(terms->term_bad, terms->term_good);
+		}
+	}
+
+	return 0;
+}
+
+static int mark_good(const char *refname, const struct object_id *oid,
+		     int flag, void *cb_data)
+{
+	int *m_good = (int *)cb_data;
+	*m_good = 0;
+	return 1;
+}
+
+static const char *need_bad_and_good_revision_warning =
+	N_("You need to give me at least one %s and %s revision.\n"
+	   "You can use \"git bisect %s\" and \"git bisect %s\" for that.");
+
+static const char *need_bisect_start_warning =
+	N_("You need to start by \"git bisect start\".\n"
+	   "You then need to give me at least one %s and %s revision.\n"
+	   "You can use \"git bisect %s\" and \"git bisect %s\" for that.");
+
+static int bisect_next_check(const struct bisect_terms *terms,
+			     const char *current_term)
+{
+	int missing_good = 1, missing_bad = 1, retval = 0;
+	const char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
+	const char *good_glob = xstrfmt("%s-*", terms->term_good);
+
+	if (ref_exists(bad_ref))
+		missing_bad = 0;
+
+	for_each_glob_ref_in(mark_good, good_glob, "refs/bisect/",
+			     (void *) &missing_good);
+
+	if (!missing_good && !missing_bad)
+		goto finish;
+
+	if (!current_term) {
+		retval = -1;
+		goto finish;
+	}
+
+	if (missing_good && !missing_bad &&
+	    !strcmp(current_term, terms->term_good)) {
+		char *yesno;
+		/*
+		 * have bad (or new) but not good (or old). We could bisect
+		 * although this is less optimum.
+		 */
+		warning(_("bisecting only with a %s commit"), terms->term_bad);
+		if (!isatty(0))
+			goto finish;
+		/*
+		 * TRANSLATORS: Make sure to include [Y] and [n] in your
+		 * translation. The program will only accept English input
+		 * at this point.
+		 */
+		yesno = git_prompt(_("Are you sure [Y/n]? "), PROMPT_ECHO);
+		if (starts_with(yesno, "N") || starts_with(yesno, "n"))
+			retval = -1;
+		goto finish;
+	}
+	if (!is_empty_or_missing_file(git_path_bisect_start())) {
+		retval = error(_(need_bad_and_good_revision_warning),
+			       vocab_bad, vocab_good, vocab_bad, vocab_good);
+	} else {
+		retval = error(_(need_bisect_start_warning),
+			       vocab_good, vocab_bad, vocab_good, vocab_bad);
+	}
+
+finish:
+	free((void *) good_glob);
+	free((void *) bad_ref);
+	return retval;
+}
+
+static int get_terms(struct bisect_terms *terms)
+{
+	struct strbuf str = STRBUF_INIT;
+	FILE *fp = NULL;
+	int res = 0;
+
+	fp = fopen(git_path_bisect_terms(), "r");
+	if (!fp) {
+		res = -1;
+		goto finish;
+	}
+
+	free_terms(terms);
+	strbuf_getline_lf(&str, fp);
+	terms->term_bad = strbuf_detach(&str, NULL);
+	strbuf_getline_lf(&str, fp);
+	terms->term_good = strbuf_detach(&str, NULL);
+
+finish:
+	if (fp)
+		fclose(fp);
+	strbuf_release(&str);
+	return res;
+}
+
+static int bisect_terms(struct bisect_terms *terms, const char *option)
+{
+	if (get_terms(terms))
+		return error(_("no terms defined"));
+
+	if (option == NULL) {
+		printf(_("Your current terms are %s for the old state\n"
+			 "and %s for the new state.\n"),
+		       terms->term_good, terms->term_bad);
+		return 0;
+	}
+	if (one_of(option, "--term-good", "--term-old", NULL))
+		printf("%s\n", terms->term_good);
+	else if (one_of(option, "--term-bad", "--term-new", NULL))
+		printf("%s\n", terms->term_bad);
+	else
+		return error(_("invalid argument %s for 'git bisect terms'.\n"
+			       "Supported options are: "
+			       "--term-good|--term-old and "
+			       "--term-bad|--term-new."), option);
+
+	return 0;
+}
+
+static int bisect_append_log_quoted(const char **argv)
+{
+	int retval = 0;
+	FILE *fp = fopen(git_path_bisect_log(), "a");
+	struct strbuf orig_args = STRBUF_INIT;
+
+	if (!fp)
+		return -1;
+
+	if (fprintf(fp, "git bisect start") < 1) {
+		retval = -1;
+		goto finish;
+	}
+
+	sq_quote_argv(&orig_args, argv);
+	if (fprintf(fp, "%s\n", orig_args.buf) < 1)
+		retval = -1;
+
+finish:
+	fclose(fp);
+	strbuf_release(&orig_args);
+	return retval;
+}
+
+static int bisect_start(struct bisect_terms *terms, int no_checkout,
+			const char **argv, int argc)
+{
+	int i, has_double_dash = 0, must_write_terms = 0, bad_seen = 0;
+	int flags, pathspec_pos, retval = 0;
+	struct string_list revs = STRING_LIST_INIT_DUP;
+	struct string_list states = STRING_LIST_INIT_DUP;
+	struct strbuf start_head = STRBUF_INIT;
+	struct strbuf bisect_names = STRBUF_INIT;
+	struct object_id head_oid;
+	struct object_id oid;
+	const char *head;
+
+	if (is_bare_repository())
+		no_checkout = 1;
+
+	/*
+	 * Check for one bad and then some good revisions
+	 */
+	for (i = 0; i < argc; i++) {
+		if (!strcmp(argv[i], "--")) {
+			has_double_dash = 1;
+			break;
+		}
+	}
+
+	for (i = 0; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(argv[i], "--")) {
+			break;
+		} else if (!strcmp(arg, "--no-checkout")) {
+			no_checkout = 1;
+		} else if (!strcmp(arg, "--term-good") ||
+			 !strcmp(arg, "--term-old")) {
+			must_write_terms = 1;
+			free((void *) terms->term_good);
+			terms->term_good = xstrdup(argv[++i]);
+		} else if (skip_prefix(arg, "--term-good=", &arg) ||
+			   skip_prefix(arg, "--term-old=", &arg)) {
+			must_write_terms = 1;
+			free((void *) terms->term_good);
+			terms->term_good = xstrdup(arg);
+		} else if (!strcmp(arg, "--term-bad") ||
+			 !strcmp(arg, "--term-new")) {
+			must_write_terms = 1;
+			free((void *) terms->term_bad);
+			terms->term_bad = xstrdup(argv[++i]);
+		} else if (skip_prefix(arg, "--term-bad=", &arg) ||
+			   skip_prefix(arg, "--term-new=", &arg)) {
+			must_write_terms = 1;
+			free((void *) terms->term_bad);
+			terms->term_bad = xstrdup(arg);
+		} else if (starts_with(arg, "--") &&
+			 !one_of(arg, "--term-good", "--term-bad", NULL)) {
+			return error(_("unrecognized option: '%s'"), arg);
+		} else {
+			char *commit_id = xstrfmt("%s^{commit}", arg);
+			if (get_oid(commit_id, &oid) && has_double_dash)
+				die(_("'%s' does not appear to be a valid "
+				      "revision"), arg);
+
+			string_list_append(&revs, oid_to_hex(&oid));
+			free(commit_id);
+		}
+	}
+	pathspec_pos = i;
+
+	/*
+	 * The user ran "git bisect start <sha1> <sha1>", hence did not
+	 * explicitly specify the terms, but we are already starting to
+	 * set references named with the default terms, and won't be able
+	 * to change afterwards.
+	 */
+	if (revs.nr)
+		must_write_terms = 1;
+	for (i = 0; i < revs.nr; i++) {
+		if (bad_seen) {
+			string_list_append(&states, terms->term_good);
+		} else {
+			bad_seen = 1;
+			string_list_append(&states, terms->term_bad);
+		}
+	}
+
+	/*
+	 * Verify HEAD
+	 */
+	head = resolve_ref_unsafe("HEAD", 0, &head_oid, &flags);
+	if (!head)
+		if (get_oid("HEAD", &head_oid))
+			return error(_("bad HEAD - I need a HEAD"));
+
+	/*
+	 * Check if we are bisecting
+	 */
+	if (!is_empty_or_missing_file(git_path_bisect_start())) {
+		/* Reset to the rev from where we started */
+		strbuf_read_file(&start_head, git_path_bisect_start(), 0);
+		strbuf_trim(&start_head);
+		if (!no_checkout) {
+			struct argv_array argv = ARGV_ARRAY_INIT;
+
+			argv_array_pushl(&argv, "checkout", start_head.buf,
+					 "--", NULL);
+			if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+				retval = error(_("checking out '%s' failed."
+						 " Try 'git bisect start "
+						 "<valid-branch>'."),
+					       start_head.buf);
+				goto finish;
+			}
+		}
+	} else {
+		/* Get the rev from where we start. */
+		if (!get_oid(head, &head_oid) &&
+		    !starts_with(head, "refs/heads/")) {
+			strbuf_reset(&start_head);
+			strbuf_addstr(&start_head, oid_to_hex(&head_oid));
+		} else if (!get_oid(head, &head_oid) &&
+			   skip_prefix(head, "refs/heads/", &head)) {
+			/*
+			 * This error message should only be triggered by
+			 * cogito usage, and cogito users should understand
+			 * it relates to cg-seek.
+			 */
+			if (!is_empty_or_missing_file(git_path_head_name()))
+				return error(_("won't bisect on cg-seek'ed tree"));
+			strbuf_addstr(&start_head, head);
+		} else {
+			return error(_("bad HEAD - strange symbolic ref"));
+		}
+	}
+
+	/*
+	 * Get rid of any old bisect state.
+	 */
+	if (bisect_clean_state())
+		return -1;
+
+	/*
+	 * In case of mistaken revs or checkout error, or signals received,
+	 * "bisect_auto_next" below may exit or misbehave.
+	 * We have to trap this to be able to clean up using
+	 * "bisect_clean_state".
+	 */
+
+	/*
+	 * Write new start state
+	 */
+	write_file(git_path_bisect_start(), "%s\n", start_head.buf);
+
+	if (no_checkout) {
+		if (get_oid(start_head.buf, &oid) < 0) {
+			retval = error(_("invalid ref: '%s'"), start_head.buf);
+			goto finish;
+		}
+		if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0,
+			       UPDATE_REFS_MSG_ON_ERR)) {
+			retval = -1;
+			goto finish;
+		}
+	}
+
+	if (pathspec_pos < argc - 1)
+		sq_quote_argv(&bisect_names, argv + pathspec_pos);
+	write_file(git_path_bisect_names(), "%s\n", bisect_names.buf);
+
+	for (i = 0; i < states.nr; i++)
+		if (bisect_write(states.items[i].string,
+				 revs.items[i].string, terms, 1)) {
+			retval = -1;
+			goto finish;
+		}
+
+	if (must_write_terms && write_terms(terms->term_bad,
+					    terms->term_good)) {
+		retval = -1;
+		goto finish;
+	}
+
+	retval = bisect_append_log_quoted(argv);
+	if (retval)
+		retval = -1;
+
+finish:
+	string_list_clear(&revs, 0);
+	string_list_clear(&states, 0);
+	strbuf_release(&start_head);
+	strbuf_release(&bisect_names);
+	return retval;
+}
+
+int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
+{
+	enum {
+		NEXT_ALL = 1,
+		WRITE_TERMS,
+		BISECT_CLEAN_STATE,
+		CHECK_EXPECTED_REVS,
+		BISECT_RESET,
+		BISECT_WRITE,
+		CHECK_AND_SET_TERMS,
+		BISECT_NEXT_CHECK,
+		BISECT_TERMS,
+		BISECT_START
+	} cmdmode = 0;
+	int no_checkout = 0, res = 0, nolog = 0;
+	struct option options[] = {
+		OPT_CMDMODE(0, "next-all", &cmdmode,
+			 N_("perform 'git bisect next'"), NEXT_ALL),
+		OPT_CMDMODE(0, "write-terms", &cmdmode,
+			 N_("write the terms to .git/BISECT_TERMS"), WRITE_TERMS),
+		OPT_CMDMODE(0, "bisect-clean-state", &cmdmode,
+			 N_("cleanup the bisection state"), BISECT_CLEAN_STATE),
+		OPT_CMDMODE(0, "check-expected-revs", &cmdmode,
+			 N_("check for expected revs"), CHECK_EXPECTED_REVS),
+		OPT_CMDMODE(0, "bisect-reset", &cmdmode,
+			 N_("reset the bisection state"), BISECT_RESET),
+		OPT_CMDMODE(0, "bisect-write", &cmdmode,
+			 N_("write out the bisection state in BISECT_LOG"), BISECT_WRITE),
+		OPT_CMDMODE(0, "check-and-set-terms", &cmdmode,
+			 N_("check and set terms in a bisection state"), CHECK_AND_SET_TERMS),
+		OPT_CMDMODE(0, "bisect-next-check", &cmdmode,
+			 N_("check whether bad or good terms exist"), BISECT_NEXT_CHECK),
+		OPT_CMDMODE(0, "bisect-terms", &cmdmode,
+			 N_("print out the bisect terms"), BISECT_TERMS),
+		OPT_CMDMODE(0, "bisect-start", &cmdmode,
+			 N_("start the bisect session"), BISECT_START),
+		OPT_BOOL(0, "no-checkout", &no_checkout,
+			 N_("update BISECT_HEAD instead of checking out the current commit")),
+		OPT_BOOL(0, "no-log", &nolog,
+			 N_("no log for BISECT_WRITE")),
+		OPT_END()
+	};
+	struct bisect_terms terms = { .term_good = NULL, .term_bad = NULL };
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_bisect_helper_usage,
+			     PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_UNKNOWN);
+
+	if (!cmdmode)
+		usage_with_options(git_bisect_helper_usage, options);
+
+	switch (cmdmode) {
+	case NEXT_ALL:
+		return bisect_next_all(the_repository, prefix, no_checkout);
+	case WRITE_TERMS:
+		if (argc != 2)
+			return error(_("--write-terms requires two arguments"));
+		return write_terms(argv[0], argv[1]);
+	case BISECT_CLEAN_STATE:
+		if (argc != 0)
+			return error(_("--bisect-clean-state requires no arguments"));
+		return bisect_clean_state();
+	case CHECK_EXPECTED_REVS:
+		check_expected_revs(argv, argc);
+		return 0;
+	case BISECT_RESET:
+		if (argc > 1)
+			return error(_("--bisect-reset requires either no argument or a commit"));
+		return !!bisect_reset(argc ? argv[0] : NULL);
+	case BISECT_WRITE:
+		if (argc != 4 && argc != 5)
+			return error(_("--bisect-write requires either 4 or 5 arguments"));
+		set_terms(&terms, argv[3], argv[2]);
+		res = bisect_write(argv[0], argv[1], &terms, nolog);
+		break;
+	case CHECK_AND_SET_TERMS:
+		if (argc != 3)
+			return error(_("--check-and-set-terms requires 3 arguments"));
+		set_terms(&terms, argv[2], argv[1]);
+		res = check_and_set_terms(&terms, argv[0]);
+		break;
+	case BISECT_NEXT_CHECK:
+		if (argc != 2 && argc != 3)
+			return error(_("--bisect-next-check requires 2 or 3 arguments"));
+		set_terms(&terms, argv[1], argv[0]);
+		res = bisect_next_check(&terms, argc == 3 ? argv[2] : NULL);
+		break;
+	case BISECT_TERMS:
+		if (argc > 1)
+			return error(_("--bisect-terms requires 0 or 1 argument"));
+		res = bisect_terms(&terms, argc == 1 ? argv[0] : NULL);
+		break;
+	case BISECT_START:
+		set_terms(&terms, "bad", "good");
+		res = bisect_start(&terms, no_checkout, argv, argc);
+		break;
+	default:
+		return error("BUG: unknown subcommand '%d'", cmdmode);
+	}
+	free_terms(&terms);
+	return !!res;
+}
diff --git a/builtin/blame.c b/builtin/blame.c
new file mode 100644
index 0000000000..b6534d4dea
--- /dev/null
+++ b/builtin/blame.c
@@ -0,0 +1,1176 @@
+/*
+ * Blame
+ *
+ * Copyright (c) 2006, 2014 by its authors
+ * See COPYING for licensing conditions
+ */
+
+#include "cache.h"
+#include "config.h"
+#include "color.h"
+#include "builtin.h"
+#include "repository.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "quote.h"
+#include "string-list.h"
+#include "mailmap.h"
+#include "parse-options.h"
+#include "prio-queue.h"
+#include "utf8.h"
+#include "userdiff.h"
+#include "line-range.h"
+#include "line-log.h"
+#include "dir.h"
+#include "progress.h"
+#include "object-store.h"
+#include "blame.h"
+#include "string-list.h"
+#include "refs.h"
+
+static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
+
+static const char *blame_opt_usage[] = {
+	blame_usage,
+	"",
+	N_("<rev-opts> are documented in git-rev-list(1)"),
+	NULL
+};
+
+static int longest_file;
+static int longest_author;
+static int max_orig_digits;
+static int max_digits;
+static int max_score_digits;
+static int show_root;
+static int reverse;
+static int blank_boundary;
+static int incremental;
+static int xdl_opts;
+static int abbrev = -1;
+static int no_whole_file_rename;
+static int show_progress;
+static char repeated_meta_color[COLOR_MAXLEN];
+static int coloring_mode;
+static struct string_list ignore_revs_file_list = STRING_LIST_INIT_NODUP;
+static int mark_unblamable_lines;
+static int mark_ignored_lines;
+
+static struct date_mode blame_date_mode = { DATE_ISO8601 };
+static size_t blame_date_width;
+
+static struct string_list mailmap = STRING_LIST_INIT_NODUP;
+
+#ifndef DEBUG_BLAME
+#define DEBUG_BLAME 0
+#endif
+
+static unsigned blame_move_score;
+static unsigned blame_copy_score;
+
+/* Remember to update object flag allocation in object.h */
+#define METAINFO_SHOWN		(1u<<12)
+#define MORE_THAN_ONE_PATH	(1u<<13)
+
+struct progress_info {
+	struct progress *progress;
+	int blamed_lines;
+};
+
+static const char *nth_line_cb(void *data, long lno)
+{
+	return blame_nth_line((struct blame_scoreboard *)data, lno);
+}
+
+/*
+ * Information on commits, used for output.
+ */
+struct commit_info {
+	struct strbuf author;
+	struct strbuf author_mail;
+	timestamp_t author_time;
+	struct strbuf author_tz;
+
+	/* filled only when asked for details */
+	struct strbuf committer;
+	struct strbuf committer_mail;
+	timestamp_t committer_time;
+	struct strbuf committer_tz;
+
+	struct strbuf summary;
+};
+
+/*
+ * Parse author/committer line in the commit object buffer
+ */
+static void get_ac_line(const char *inbuf, const char *what,
+	struct strbuf *name, struct strbuf *mail,
+	timestamp_t *time, struct strbuf *tz)
+{
+	struct ident_split ident;
+	size_t len, maillen, namelen;
+	char *tmp, *endp;
+	const char *namebuf, *mailbuf;
+
+	tmp = strstr(inbuf, what);
+	if (!tmp)
+		goto error_out;
+	tmp += strlen(what);
+	endp = strchr(tmp, '\n');
+	if (!endp)
+		len = strlen(tmp);
+	else
+		len = endp - tmp;
+
+	if (split_ident_line(&ident, tmp, len)) {
+	error_out:
+		/* Ugh */
+		tmp = "(unknown)";
+		strbuf_addstr(name, tmp);
+		strbuf_addstr(mail, tmp);
+		strbuf_addstr(tz, tmp);
+		*time = 0;
+		return;
+	}
+
+	namelen = ident.name_end - ident.name_begin;
+	namebuf = ident.name_begin;
+
+	maillen = ident.mail_end - ident.mail_begin;
+	mailbuf = ident.mail_begin;
+
+	if (ident.date_begin && ident.date_end)
+		*time = strtoul(ident.date_begin, NULL, 10);
+	else
+		*time = 0;
+
+	if (ident.tz_begin && ident.tz_end)
+		strbuf_add(tz, ident.tz_begin, ident.tz_end - ident.tz_begin);
+	else
+		strbuf_addstr(tz, "(unknown)");
+
+	/*
+	 * Now, convert both name and e-mail using mailmap
+	 */
+	map_user(&mailmap, &mailbuf, &maillen,
+		 &namebuf, &namelen);
+
+	strbuf_addf(mail, "<%.*s>", (int)maillen, mailbuf);
+	strbuf_add(name, namebuf, namelen);
+}
+
+static void commit_info_init(struct commit_info *ci)
+{
+
+	strbuf_init(&ci->author, 0);
+	strbuf_init(&ci->author_mail, 0);
+	strbuf_init(&ci->author_tz, 0);
+	strbuf_init(&ci->committer, 0);
+	strbuf_init(&ci->committer_mail, 0);
+	strbuf_init(&ci->committer_tz, 0);
+	strbuf_init(&ci->summary, 0);
+}
+
+static void commit_info_destroy(struct commit_info *ci)
+{
+
+	strbuf_release(&ci->author);
+	strbuf_release(&ci->author_mail);
+	strbuf_release(&ci->author_tz);
+	strbuf_release(&ci->committer);
+	strbuf_release(&ci->committer_mail);
+	strbuf_release(&ci->committer_tz);
+	strbuf_release(&ci->summary);
+}
+
+static void get_commit_info(struct commit *commit,
+			    struct commit_info *ret,
+			    int detailed)
+{
+	int len;
+	const char *subject, *encoding;
+	const char *message;
+
+	commit_info_init(ret);
+
+	encoding = get_log_output_encoding();
+	message = logmsg_reencode(commit, NULL, encoding);
+	get_ac_line(message, "\nauthor ",
+		    &ret->author, &ret->author_mail,
+		    &ret->author_time, &ret->author_tz);
+
+	if (!detailed) {
+		unuse_commit_buffer(commit, message);
+		return;
+	}
+
+	get_ac_line(message, "\ncommitter ",
+		    &ret->committer, &ret->committer_mail,
+		    &ret->committer_time, &ret->committer_tz);
+
+	len = find_commit_subject(message, &subject);
+	if (len)
+		strbuf_add(&ret->summary, subject, len);
+	else
+		strbuf_addf(&ret->summary, "(%s)", oid_to_hex(&commit->object.oid));
+
+	unuse_commit_buffer(commit, message);
+}
+
+/*
+ * Write out any suspect information which depends on the path. This must be
+ * handled separately from emit_one_suspect_detail(), because a given commit
+ * may have changes in multiple paths. So this needs to appear each time
+ * we mention a new group.
+ *
+ * To allow LF and other nonportable characters in pathnames,
+ * they are c-style quoted as needed.
+ */
+static void write_filename_info(struct blame_origin *suspect)
+{
+	if (suspect->previous) {
+		struct blame_origin *prev = suspect->previous;
+		printf("previous %s ", oid_to_hex(&prev->commit->object.oid));
+		write_name_quoted(prev->path, stdout, '\n');
+	}
+	printf("filename ");
+	write_name_quoted(suspect->path, stdout, '\n');
+}
+
+/*
+ * Porcelain/Incremental format wants to show a lot of details per
+ * commit.  Instead of repeating this every line, emit it only once,
+ * the first time each commit appears in the output (unless the
+ * user has specifically asked for us to repeat).
+ */
+static int emit_one_suspect_detail(struct blame_origin *suspect, int repeat)
+{
+	struct commit_info ci;
+
+	if (!repeat && (suspect->commit->object.flags & METAINFO_SHOWN))
+		return 0;
+
+	suspect->commit->object.flags |= METAINFO_SHOWN;
+	get_commit_info(suspect->commit, &ci, 1);
+	printf("author %s\n", ci.author.buf);
+	printf("author-mail %s\n", ci.author_mail.buf);
+	printf("author-time %"PRItime"\n", ci.author_time);
+	printf("author-tz %s\n", ci.author_tz.buf);
+	printf("committer %s\n", ci.committer.buf);
+	printf("committer-mail %s\n", ci.committer_mail.buf);
+	printf("committer-time %"PRItime"\n", ci.committer_time);
+	printf("committer-tz %s\n", ci.committer_tz.buf);
+	printf("summary %s\n", ci.summary.buf);
+	if (suspect->commit->object.flags & UNINTERESTING)
+		printf("boundary\n");
+
+	commit_info_destroy(&ci);
+
+	return 1;
+}
+
+/*
+ * The blame_entry is found to be guilty for the range.
+ * Show it in incremental output.
+ */
+static void found_guilty_entry(struct blame_entry *ent, void *data)
+{
+	struct progress_info *pi = (struct progress_info *)data;
+
+	if (incremental) {
+		struct blame_origin *suspect = ent->suspect;
+
+		printf("%s %d %d %d\n",
+		       oid_to_hex(&suspect->commit->object.oid),
+		       ent->s_lno + 1, ent->lno + 1, ent->num_lines);
+		emit_one_suspect_detail(suspect, 0);
+		write_filename_info(suspect);
+		maybe_flush_or_die(stdout, "stdout");
+	}
+	pi->blamed_lines += ent->num_lines;
+	display_progress(pi->progress, pi->blamed_lines);
+}
+
+static const char *format_time(timestamp_t time, const char *tz_str,
+			       int show_raw_time)
+{
+	static struct strbuf time_buf = STRBUF_INIT;
+
+	strbuf_reset(&time_buf);
+	if (show_raw_time) {
+		strbuf_addf(&time_buf, "%"PRItime" %s", time, tz_str);
+	}
+	else {
+		const char *time_str;
+		size_t time_width;
+		int tz;
+		tz = atoi(tz_str);
+		time_str = show_date(time, tz, &blame_date_mode);
+		strbuf_addstr(&time_buf, time_str);
+		/*
+		 * Add space paddings to time_buf to display a fixed width
+		 * string, and use time_width for display width calibration.
+		 */
+		for (time_width = utf8_strwidth(time_str);
+		     time_width < blame_date_width;
+		     time_width++)
+			strbuf_addch(&time_buf, ' ');
+	}
+	return time_buf.buf;
+}
+
+#define OUTPUT_ANNOTATE_COMPAT	001
+#define OUTPUT_LONG_OBJECT_NAME	002
+#define OUTPUT_RAW_TIMESTAMP	004
+#define OUTPUT_PORCELAIN	010
+#define OUTPUT_SHOW_NAME	020
+#define OUTPUT_SHOW_NUMBER	040
+#define OUTPUT_SHOW_SCORE	0100
+#define OUTPUT_NO_AUTHOR	0200
+#define OUTPUT_SHOW_EMAIL	0400
+#define OUTPUT_LINE_PORCELAIN	01000
+#define OUTPUT_COLOR_LINE	02000
+#define OUTPUT_SHOW_AGE_WITH_COLOR	04000
+
+static void emit_porcelain_details(struct blame_origin *suspect, int repeat)
+{
+	if (emit_one_suspect_detail(suspect, repeat) ||
+	    (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
+		write_filename_info(suspect);
+}
+
+static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent,
+			   int opt)
+{
+	int repeat = opt & OUTPUT_LINE_PORCELAIN;
+	int cnt;
+	const char *cp;
+	struct blame_origin *suspect = ent->suspect;
+	char hex[GIT_MAX_HEXSZ + 1];
+
+	oid_to_hex_r(hex, &suspect->commit->object.oid);
+	printf("%s %d %d %d\n",
+	       hex,
+	       ent->s_lno + 1,
+	       ent->lno + 1,
+	       ent->num_lines);
+	emit_porcelain_details(suspect, repeat);
+
+	cp = blame_nth_line(sb, ent->lno);
+	for (cnt = 0; cnt < ent->num_lines; cnt++) {
+		char ch;
+		if (cnt) {
+			printf("%s %d %d\n", hex,
+			       ent->s_lno + 1 + cnt,
+			       ent->lno + 1 + cnt);
+			if (repeat)
+				emit_porcelain_details(suspect, 1);
+		}
+		putchar('\t');
+		do {
+			ch = *cp++;
+			putchar(ch);
+		} while (ch != '\n' &&
+			 cp < sb->final_buf + sb->final_buf_size);
+	}
+
+	if (sb->final_buf_size && cp[-1] != '\n')
+		putchar('\n');
+}
+
+static struct color_field {
+	timestamp_t hop;
+	char col[COLOR_MAXLEN];
+} *colorfield;
+static int colorfield_nr, colorfield_alloc;
+
+static void parse_color_fields(const char *s)
+{
+	struct string_list l = STRING_LIST_INIT_DUP;
+	struct string_list_item *item;
+	enum { EXPECT_DATE, EXPECT_COLOR } next = EXPECT_COLOR;
+
+	colorfield_nr = 0;
+
+	/* Ideally this would be stripped and split at the same time? */
+	string_list_split(&l, s, ',', -1);
+	ALLOC_GROW(colorfield, colorfield_nr + 1, colorfield_alloc);
+
+	for_each_string_list_item(item, &l) {
+		switch (next) {
+		case EXPECT_DATE:
+			colorfield[colorfield_nr].hop = approxidate(item->string);
+			next = EXPECT_COLOR;
+			colorfield_nr++;
+			ALLOC_GROW(colorfield, colorfield_nr + 1, colorfield_alloc);
+			break;
+		case EXPECT_COLOR:
+			if (color_parse(item->string, colorfield[colorfield_nr].col))
+				die(_("expecting a color: %s"), item->string);
+			next = EXPECT_DATE;
+			break;
+		}
+	}
+
+	if (next == EXPECT_COLOR)
+		die(_("must end with a color"));
+
+	colorfield[colorfield_nr].hop = TIME_MAX;
+	string_list_clear(&l, 0);
+}
+
+static void setup_default_color_by_age(void)
+{
+	parse_color_fields("blue,12 month ago,white,1 month ago,red");
+}
+
+static void determine_line_heat(struct blame_entry *ent, const char **dest_color)
+{
+	int i = 0;
+	struct commit_info ci;
+	get_commit_info(ent->suspect->commit, &ci, 1);
+
+	while (i < colorfield_nr && ci.author_time > colorfield[i].hop)
+		i++;
+
+	*dest_color = colorfield[i].col;
+}
+
+static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt)
+{
+	int cnt;
+	const char *cp;
+	struct blame_origin *suspect = ent->suspect;
+	struct commit_info ci;
+	char hex[GIT_MAX_HEXSZ + 1];
+	int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
+	const char *default_color = NULL, *color = NULL, *reset = NULL;
+
+	get_commit_info(suspect->commit, &ci, 1);
+	oid_to_hex_r(hex, &suspect->commit->object.oid);
+
+	cp = blame_nth_line(sb, ent->lno);
+
+	if (opt & OUTPUT_SHOW_AGE_WITH_COLOR) {
+		determine_line_heat(ent, &default_color);
+		color = default_color;
+		reset = GIT_COLOR_RESET;
+	}
+
+	for (cnt = 0; cnt < ent->num_lines; cnt++) {
+		char ch;
+		int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev;
+
+		if (opt & OUTPUT_COLOR_LINE) {
+			if (cnt > 0) {
+				color = repeated_meta_color;
+				reset = GIT_COLOR_RESET;
+			} else  {
+				color = default_color ? default_color : NULL;
+				reset = default_color ? GIT_COLOR_RESET : NULL;
+			}
+		}
+		if (color)
+			fputs(color, stdout);
+
+		if (suspect->commit->object.flags & UNINTERESTING) {
+			if (blank_boundary)
+				memset(hex, ' ', length);
+			else if (!(opt & OUTPUT_ANNOTATE_COMPAT)) {
+				length--;
+				putchar('^');
+			}
+		}
+
+		if (mark_unblamable_lines && ent->unblamable) {
+			length--;
+			putchar('*');
+		}
+		if (mark_ignored_lines && ent->ignored) {
+			length--;
+			putchar('?');
+		}
+		printf("%.*s", length, hex);
+		if (opt & OUTPUT_ANNOTATE_COMPAT) {
+			const char *name;
+			if (opt & OUTPUT_SHOW_EMAIL)
+				name = ci.author_mail.buf;
+			else
+				name = ci.author.buf;
+			printf("\t(%10s\t%10s\t%d)", name,
+			       format_time(ci.author_time, ci.author_tz.buf,
+					   show_raw_time),
+			       ent->lno + 1 + cnt);
+		} else {
+			if (opt & OUTPUT_SHOW_SCORE)
+				printf(" %*d %02d",
+				       max_score_digits, ent->score,
+				       ent->suspect->refcnt);
+			if (opt & OUTPUT_SHOW_NAME)
+				printf(" %-*.*s", longest_file, longest_file,
+				       suspect->path);
+			if (opt & OUTPUT_SHOW_NUMBER)
+				printf(" %*d", max_orig_digits,
+				       ent->s_lno + 1 + cnt);
+
+			if (!(opt & OUTPUT_NO_AUTHOR)) {
+				const char *name;
+				int pad;
+				if (opt & OUTPUT_SHOW_EMAIL)
+					name = ci.author_mail.buf;
+				else
+					name = ci.author.buf;
+				pad = longest_author - utf8_strwidth(name);
+				printf(" (%s%*s %10s",
+				       name, pad, "",
+				       format_time(ci.author_time,
+						   ci.author_tz.buf,
+						   show_raw_time));
+			}
+			printf(" %*d) ",
+			       max_digits, ent->lno + 1 + cnt);
+		}
+		if (reset)
+			fputs(reset, stdout);
+		do {
+			ch = *cp++;
+			putchar(ch);
+		} while (ch != '\n' &&
+			 cp < sb->final_buf + sb->final_buf_size);
+	}
+
+	if (sb->final_buf_size && cp[-1] != '\n')
+		putchar('\n');
+
+	commit_info_destroy(&ci);
+}
+
+static void output(struct blame_scoreboard *sb, int option)
+{
+	struct blame_entry *ent;
+
+	if (option & OUTPUT_PORCELAIN) {
+		for (ent = sb->ent; ent; ent = ent->next) {
+			int count = 0;
+			struct blame_origin *suspect;
+			struct commit *commit = ent->suspect->commit;
+			if (commit->object.flags & MORE_THAN_ONE_PATH)
+				continue;
+			for (suspect = get_blame_suspects(commit); suspect; suspect = suspect->next) {
+				if (suspect->guilty && count++) {
+					commit->object.flags |= MORE_THAN_ONE_PATH;
+					break;
+				}
+			}
+		}
+	}
+
+	for (ent = sb->ent; ent; ent = ent->next) {
+		if (option & OUTPUT_PORCELAIN)
+			emit_porcelain(sb, ent, option);
+		else {
+			emit_other(sb, ent, option);
+		}
+	}
+}
+
+/*
+ * Add phony grafts for use with -S; this is primarily to
+ * support git's cvsserver that wants to give a linear history
+ * to its clients.
+ */
+static int read_ancestry(const char *graft_file)
+{
+	FILE *fp = fopen_or_warn(graft_file, "r");
+	struct strbuf buf = STRBUF_INIT;
+	if (!fp)
+		return -1;
+	while (!strbuf_getwholeline(&buf, fp, '\n')) {
+		/* The format is just "Commit Parent1 Parent2 ...\n" */
+		struct commit_graft *graft = read_graft_line(&buf);
+		if (graft)
+			register_commit_graft(the_repository, graft, 0);
+	}
+	fclose(fp);
+	strbuf_release(&buf);
+	return 0;
+}
+
+static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect)
+{
+	const char *uniq = find_unique_abbrev(&suspect->commit->object.oid,
+					      auto_abbrev);
+	int len = strlen(uniq);
+	if (auto_abbrev < len)
+		return len;
+	return auto_abbrev;
+}
+
+/*
+ * How many columns do we need to show line numbers, authors,
+ * and filenames?
+ */
+static void find_alignment(struct blame_scoreboard *sb, int *option)
+{
+	int longest_src_lines = 0;
+	int longest_dst_lines = 0;
+	unsigned largest_score = 0;
+	struct blame_entry *e;
+	int compute_auto_abbrev = (abbrev < 0);
+	int auto_abbrev = DEFAULT_ABBREV;
+
+	for (e = sb->ent; e; e = e->next) {
+		struct blame_origin *suspect = e->suspect;
+		int num;
+
+		if (compute_auto_abbrev)
+			auto_abbrev = update_auto_abbrev(auto_abbrev, suspect);
+		if (strcmp(suspect->path, sb->path))
+			*option |= OUTPUT_SHOW_NAME;
+		num = strlen(suspect->path);
+		if (longest_file < num)
+			longest_file = num;
+		if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
+			struct commit_info ci;
+			suspect->commit->object.flags |= METAINFO_SHOWN;
+			get_commit_info(suspect->commit, &ci, 1);
+			if (*option & OUTPUT_SHOW_EMAIL)
+				num = utf8_strwidth(ci.author_mail.buf);
+			else
+				num = utf8_strwidth(ci.author.buf);
+			if (longest_author < num)
+				longest_author = num;
+			commit_info_destroy(&ci);
+		}
+		num = e->s_lno + e->num_lines;
+		if (longest_src_lines < num)
+			longest_src_lines = num;
+		num = e->lno + e->num_lines;
+		if (longest_dst_lines < num)
+			longest_dst_lines = num;
+		if (largest_score < blame_entry_score(sb, e))
+			largest_score = blame_entry_score(sb, e);
+	}
+	max_orig_digits = decimal_width(longest_src_lines);
+	max_digits = decimal_width(longest_dst_lines);
+	max_score_digits = decimal_width(largest_score);
+
+	if (compute_auto_abbrev)
+		/* one more abbrev length is needed for the boundary commit */
+		abbrev = auto_abbrev + 1;
+}
+
+static void sanity_check_on_fail(struct blame_scoreboard *sb, int baa)
+{
+	int opt = OUTPUT_SHOW_SCORE | OUTPUT_SHOW_NUMBER | OUTPUT_SHOW_NAME;
+	find_alignment(sb, &opt);
+	output(sb, opt);
+	die("Baa %d!", baa);
+}
+
+static unsigned parse_score(const char *arg)
+{
+	char *end;
+	unsigned long score = strtoul(arg, &end, 10);
+	if (*end)
+		return 0;
+	return score;
+}
+
+static const char *add_prefix(const char *prefix, const char *path)
+{
+	return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
+}
+
+static int git_blame_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "blame.showroot")) {
+		show_root = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "blame.blankboundary")) {
+		blank_boundary = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "blame.showemail")) {
+		int *output_option = cb;
+		if (git_config_bool(var, value))
+			*output_option |= OUTPUT_SHOW_EMAIL;
+		else
+			*output_option &= ~OUTPUT_SHOW_EMAIL;
+		return 0;
+	}
+	if (!strcmp(var, "blame.date")) {
+		if (!value)
+			return config_error_nonbool(var);
+		parse_date_format(value, &blame_date_mode);
+		return 0;
+	}
+	if (!strcmp(var, "blame.ignorerevsfile")) {
+		const char *str;
+		int ret;
+
+		ret = git_config_pathname(&str, var, value);
+		if (ret)
+			return ret;
+		string_list_insert(&ignore_revs_file_list, str);
+		return 0;
+	}
+	if (!strcmp(var, "blame.markunblamablelines")) {
+		mark_unblamable_lines = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "blame.markignoredlines")) {
+		mark_ignored_lines = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "color.blame.repeatedlines")) {
+		if (color_parse_mem(value, strlen(value), repeated_meta_color))
+			warning(_("invalid color '%s' in color.blame.repeatedLines"),
+				value);
+		return 0;
+	}
+	if (!strcmp(var, "color.blame.highlightrecent")) {
+		parse_color_fields(value);
+		return 0;
+	}
+
+	if (!strcmp(var, "blame.coloring")) {
+		if (!strcmp(value, "repeatedLines")) {
+			coloring_mode |= OUTPUT_COLOR_LINE;
+		} else if (!strcmp(value, "highlightRecent")) {
+			coloring_mode |= OUTPUT_SHOW_AGE_WITH_COLOR;
+		} else if (!strcmp(value, "none")) {
+			coloring_mode &= ~(OUTPUT_COLOR_LINE |
+					    OUTPUT_SHOW_AGE_WITH_COLOR);
+		} else {
+			warning(_("invalid value for blame.coloring"));
+			return 0;
+		}
+	}
+
+	if (git_diff_heuristic_config(var, value, cb) < 0)
+		return -1;
+	if (userdiff_config(var, value) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
+}
+
+static int blame_copy_callback(const struct option *option, const char *arg, int unset)
+{
+	int *opt = option->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	/*
+	 * -C enables copy from removed files;
+	 * -C -C enables copy from existing files, but only
+	 *       when blaming a new file;
+	 * -C -C -C enables copy from existing files for
+	 *          everybody
+	 */
+	if (*opt & PICKAXE_BLAME_COPY_HARDER)
+		*opt |= PICKAXE_BLAME_COPY_HARDEST;
+	if (*opt & PICKAXE_BLAME_COPY)
+		*opt |= PICKAXE_BLAME_COPY_HARDER;
+	*opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;
+
+	if (arg)
+		blame_copy_score = parse_score(arg);
+	return 0;
+}
+
+static int blame_move_callback(const struct option *option, const char *arg, int unset)
+{
+	int *opt = option->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	*opt |= PICKAXE_BLAME_MOVE;
+
+	if (arg)
+		blame_move_score = parse_score(arg);
+	return 0;
+}
+
+static int is_a_rev(const char *name)
+{
+	struct object_id oid;
+
+	if (get_oid(name, &oid))
+		return 0;
+	return OBJ_NONE < oid_object_info(the_repository, &oid, NULL);
+}
+
+static void build_ignorelist(struct blame_scoreboard *sb,
+			     struct string_list *ignore_revs_file_list,
+			     struct string_list *ignore_rev_list)
+{
+	struct string_list_item *i;
+	struct object_id oid;
+
+	oidset_init(&sb->ignore_list, 0);
+	for_each_string_list_item(i, ignore_revs_file_list) {
+		if (!strcmp(i->string, ""))
+			oidset_clear(&sb->ignore_list);
+		else
+			oidset_parse_file(&sb->ignore_list, i->string);
+	}
+	for_each_string_list_item(i, ignore_rev_list) {
+		if (get_oid_committish(i->string, &oid))
+			die(_("cannot find revision %s to ignore"), i->string);
+		oidset_insert(&sb->ignore_list, &oid);
+	}
+}
+
+int cmd_blame(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info revs;
+	const char *path;
+	struct blame_scoreboard sb;
+	struct blame_origin *o;
+	struct blame_entry *ent = NULL;
+	long dashdash_pos, lno;
+	struct progress_info pi = { NULL, 0 };
+
+	struct string_list range_list = STRING_LIST_INIT_NODUP;
+	struct string_list ignore_rev_list = STRING_LIST_INIT_NODUP;
+	int output_option = 0, opt = 0;
+	int show_stats = 0;
+	const char *revs_file = NULL;
+	const char *contents_from = NULL;
+	const struct option options[] = {
+		OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")),
+		OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")),
+		OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
+		OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost statistics")),
+		OPT_BOOL(0, "progress", &show_progress, N_("Force progress reporting")),
+		OPT_BIT(0, "score-debug", &output_option, N_("Show output score for blame entries"), OUTPUT_SHOW_SCORE),
+		OPT_BIT('f', "show-name", &output_option, N_("Show original filename (Default: auto)"), OUTPUT_SHOW_NAME),
+		OPT_BIT('n', "show-number", &output_option, N_("Show original linenumber (Default: off)"), OUTPUT_SHOW_NUMBER),
+		OPT_BIT('p', "porcelain", &output_option, N_("Show in a format designed for machine consumption"), OUTPUT_PORCELAIN),
+		OPT_BIT(0, "line-porcelain", &output_option, N_("Show porcelain format with per-line commit information"), OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN),
+		OPT_BIT('c', NULL, &output_option, N_("Use the same output mode as git-annotate (Default: off)"), OUTPUT_ANNOTATE_COMPAT),
+		OPT_BIT('t', NULL, &output_option, N_("Show raw timestamp (Default: off)"), OUTPUT_RAW_TIMESTAMP),
+		OPT_BIT('l', NULL, &output_option, N_("Show long commit SHA1 (Default: off)"), OUTPUT_LONG_OBJECT_NAME),
+		OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR),
+		OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL),
+		OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE),
+		OPT_STRING_LIST(0, "ignore-rev", &ignore_rev_list, N_("rev"), N_("Ignore <rev> when blaming")),
+		OPT_STRING_LIST(0, "ignore-revs-file", &ignore_revs_file_list, N_("file"), N_("Ignore revisions from <file>")),
+		OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE),
+		OPT_BIT(0, "color-by-age", &output_option, N_("color lines by age"), OUTPUT_SHOW_AGE_WITH_COLOR),
+
+		/*
+		 * The following two options are parsed by parse_revision_opt()
+		 * and are only included here to get included in the "-h"
+		 * output:
+		 */
+		{ OPTION_LOWLEVEL_CALLBACK, 0, "indent-heuristic", NULL, NULL, N_("Use an experimental heuristic to improve diffs"), PARSE_OPT_NOARG, NULL, 0, parse_opt_unknown_cb },
+
+		OPT_BIT(0, "minimal", &xdl_opts, N_("Spend extra cycles to find better match"), XDF_NEED_MINIMAL),
+		OPT_STRING('S', NULL, &revs_file, N_("file"), N_("Use revisions from <file> instead of calling git-rev-list")),
+		OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use <file>'s contents as the final image")),
+		{ OPTION_CALLBACK, 'C', NULL, &opt, N_("score"), N_("Find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback },
+		{ OPTION_CALLBACK, 'M', NULL, &opt, N_("score"), N_("Find line movements within and across files"), PARSE_OPT_OPTARG, blame_move_callback },
+		OPT_STRING_LIST('L', NULL, &range_list, N_("n,m"), N_("Process only line range n,m, counting from 1")),
+		OPT__ABBREV(&abbrev),
+		OPT_END()
+	};
+
+	struct parse_opt_ctx_t ctx;
+	int cmd_is_annotate = !strcmp(argv[0], "annotate");
+	struct range_set ranges;
+	unsigned int range_i;
+	long anchor;
+
+	setup_default_color_by_age();
+	git_config(git_blame_config, &output_option);
+	repo_init_revisions(the_repository, &revs, NULL);
+	revs.date_mode = blame_date_mode;
+	revs.diffopt.flags.allow_textconv = 1;
+	revs.diffopt.flags.follow_renames = 1;
+
+	save_commit_buffer = 0;
+	dashdash_pos = 0;
+	show_progress = -1;
+
+	parse_options_start(&ctx, argc, argv, prefix, options,
+			    PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
+	for (;;) {
+		switch (parse_options_step(&ctx, options, blame_opt_usage)) {
+		case PARSE_OPT_HELP:
+		case PARSE_OPT_ERROR:
+			exit(129);
+		case PARSE_OPT_COMPLETE:
+			exit(0);
+		case PARSE_OPT_DONE:
+			if (ctx.argv[0])
+				dashdash_pos = ctx.cpidx;
+			goto parse_done;
+		}
+
+		if (!strcmp(ctx.argv[0], "--reverse")) {
+			ctx.argv[0] = "--children";
+			reverse = 1;
+		}
+		parse_revision_opt(&revs, &ctx, options, blame_opt_usage);
+	}
+parse_done:
+	no_whole_file_rename = !revs.diffopt.flags.follow_renames;
+	xdl_opts |= revs.diffopt.xdl_opts & XDF_INDENT_HEURISTIC;
+	revs.diffopt.flags.follow_renames = 0;
+	argc = parse_options_end(&ctx);
+
+	if (incremental || (output_option & OUTPUT_PORCELAIN)) {
+		if (show_progress > 0)
+			die(_("--progress can't be used with --incremental or porcelain formats"));
+		show_progress = 0;
+	} else if (show_progress < 0)
+		show_progress = isatty(2);
+
+	if (0 < abbrev && abbrev < GIT_SHA1_HEXSZ)
+		/* one more abbrev length is needed for the boundary commit */
+		abbrev++;
+	else if (!abbrev)
+		abbrev = GIT_SHA1_HEXSZ;
+
+	if (revs_file && read_ancestry(revs_file))
+		die_errno("reading graft file '%s' failed", revs_file);
+
+	if (cmd_is_annotate) {
+		output_option |= OUTPUT_ANNOTATE_COMPAT;
+		blame_date_mode.type = DATE_ISO8601;
+	} else {
+		blame_date_mode = revs.date_mode;
+	}
+
+	/* The maximum width used to show the dates */
+	switch (blame_date_mode.type) {
+	case DATE_RFC2822:
+		blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700");
+		break;
+	case DATE_ISO8601_STRICT:
+		blame_date_width = sizeof("2006-10-19T16:00:04-07:00");
+		break;
+	case DATE_ISO8601:
+		blame_date_width = sizeof("2006-10-19 16:00:04 -0700");
+		break;
+	case DATE_RAW:
+		blame_date_width = sizeof("1161298804 -0700");
+		break;
+	case DATE_UNIX:
+		blame_date_width = sizeof("1161298804");
+		break;
+	case DATE_SHORT:
+		blame_date_width = sizeof("2006-10-19");
+		break;
+	case DATE_RELATIVE:
+		/*
+		 * TRANSLATORS: This string is used to tell us the
+		 * maximum display width for a relative timestamp in
+		 * "git blame" output.  For C locale, "4 years, 11
+		 * months ago", which takes 22 places, is the longest
+		 * among various forms of relative timestamps, but
+		 * your language may need more or fewer display
+		 * columns.
+		 */
+		blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
+		break;
+	case DATE_HUMAN:
+		/* If the year is shown, no time is shown */
+		blame_date_width = sizeof("Thu Oct 19 16:00");
+		break;
+	case DATE_NORMAL:
+		blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
+		break;
+	case DATE_STRFTIME:
+		blame_date_width = strlen(show_date(0, 0, &blame_date_mode)) + 1; /* add the null */
+		break;
+	}
+	blame_date_width -= 1; /* strip the null */
+
+	if (revs.diffopt.flags.find_copies_harder)
+		opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |
+			PICKAXE_BLAME_COPY_HARDER);
+
+	/*
+	 * We have collected options unknown to us in argv[1..unk]
+	 * which are to be passed to revision machinery if we are
+	 * going to do the "bottom" processing.
+	 *
+	 * The remaining are:
+	 *
+	 * (1) if dashdash_pos != 0, it is either
+	 *     "blame [revisions] -- <path>" or
+	 *     "blame -- <path> <rev>"
+	 *
+	 * (2) otherwise, it is one of the two:
+	 *     "blame [revisions] <path>"
+	 *     "blame <path> <rev>"
+	 *
+	 * Note that we must strip out <path> from the arguments: we do not
+	 * want the path pruning but we may want "bottom" processing.
+	 */
+	if (dashdash_pos) {
+		switch (argc - dashdash_pos - 1) {
+		case 2: /* (1b) */
+			if (argc != 4)
+				usage_with_options(blame_opt_usage, options);
+			/* reorder for the new way: <rev> -- <path> */
+			argv[1] = argv[3];
+			argv[3] = argv[2];
+			argv[2] = "--";
+			/* FALLTHROUGH */
+		case 1: /* (1a) */
+			path = add_prefix(prefix, argv[--argc]);
+			argv[argc] = NULL;
+			break;
+		default:
+			usage_with_options(blame_opt_usage, options);
+		}
+	} else {
+		if (argc < 2)
+			usage_with_options(blame_opt_usage, options);
+		if (argc == 3 && is_a_rev(argv[argc - 1])) { /* (2b) */
+			path = add_prefix(prefix, argv[1]);
+			argv[1] = argv[2];
+		} else {	/* (2a) */
+			if (argc == 2 && is_a_rev(argv[1]) && !get_git_work_tree())
+				die("missing <path> to blame");
+			path = add_prefix(prefix, argv[argc - 1]);
+		}
+		argv[argc - 1] = "--";
+	}
+
+	revs.disable_stdin = 1;
+	setup_revisions(argc, argv, &revs, NULL);
+	if (!revs.pending.nr && is_bare_repository()) {
+		struct commit *head_commit;
+		struct object_id head_oid;
+
+		if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+					&head_oid, NULL) ||
+		    !(head_commit = lookup_commit_reference_gently(revs.repo,
+							     &head_oid, 1)))
+			die("no such ref: HEAD");
+
+		add_pending_object(&revs, &head_commit->object, "HEAD");
+	}
+
+	init_scoreboard(&sb);
+	sb.revs = &revs;
+	sb.contents_from = contents_from;
+	sb.reverse = reverse;
+	sb.repo = the_repository;
+	build_ignorelist(&sb, &ignore_revs_file_list, &ignore_rev_list);
+	string_list_clear(&ignore_revs_file_list, 0);
+	string_list_clear(&ignore_rev_list, 0);
+	setup_scoreboard(&sb, path, &o);
+	lno = sb.num_lines;
+
+	if (lno && !range_list.nr)
+		string_list_append(&range_list, "1");
+
+	anchor = 1;
+	range_set_init(&ranges, range_list.nr);
+	for (range_i = 0; range_i < range_list.nr; ++range_i) {
+		long bottom, top;
+		if (parse_range_arg(range_list.items[range_i].string,
+				    nth_line_cb, &sb, lno, anchor,
+				    &bottom, &top, sb.path,
+				    the_repository->index))
+			usage(blame_usage);
+		if ((!lno && (top || bottom)) || lno < bottom)
+			die(Q_("file %s has only %lu line",
+			       "file %s has only %lu lines",
+			       lno), path, lno);
+		if (bottom < 1)
+			bottom = 1;
+		if (top < 1 || lno < top)
+			top = lno;
+		bottom--;
+		range_set_append_unsafe(&ranges, bottom, top);
+		anchor = top + 1;
+	}
+	sort_and_merge_range_set(&ranges);
+
+	for (range_i = ranges.nr; range_i > 0; --range_i) {
+		const struct range *r = &ranges.ranges[range_i - 1];
+		ent = blame_entry_prepend(ent, r->start, r->end, o);
+	}
+
+	o->suspects = ent;
+	prio_queue_put(&sb.commits, o->commit);
+
+	blame_origin_decref(o);
+
+	range_set_release(&ranges);
+	string_list_clear(&range_list, 0);
+
+	sb.ent = NULL;
+	sb.path = path;
+
+	if (blame_move_score)
+		sb.move_score = blame_move_score;
+	if (blame_copy_score)
+		sb.copy_score = blame_copy_score;
+
+	sb.debug = DEBUG_BLAME;
+	sb.on_sanity_fail = &sanity_check_on_fail;
+
+	sb.show_root = show_root;
+	sb.xdl_opts = xdl_opts;
+	sb.no_whole_file_rename = no_whole_file_rename;
+
+	read_mailmap(&mailmap, NULL);
+
+	sb.found_guilty_entry = &found_guilty_entry;
+	sb.found_guilty_entry_data = &pi;
+	if (show_progress)
+		pi.progress = start_delayed_progress(_("Blaming lines"), sb.num_lines);
+
+	assign_blame(&sb, opt);
+
+	stop_progress(&pi.progress);
+
+	if (!incremental)
+		setup_pager();
+	else
+		return 0;
+
+	blame_sort_final(&sb);
+
+	blame_coalesce(&sb);
+
+	if (!(output_option & (OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR)))
+		output_option |= coloring_mode;
+
+	if (!(output_option & OUTPUT_PORCELAIN)) {
+		find_alignment(&sb, &output_option);
+		if (!*repeated_meta_color &&
+		    (output_option & OUTPUT_COLOR_LINE))
+			xsnprintf(repeated_meta_color,
+				  sizeof(repeated_meta_color),
+				  "%s", GIT_COLOR_CYAN);
+	}
+	if (output_option & OUTPUT_ANNOTATE_COMPAT)
+		output_option &= ~(OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR);
+
+	output(&sb, output_option);
+	free((void *)sb.final_buf);
+	for (ent = sb.ent; ent; ) {
+		struct blame_entry *e = ent->next;
+		free(ent);
+		ent = e;
+	}
+
+	if (show_stats) {
+		printf("num read blob: %d\n", sb.num_read_blob);
+		printf("num get patch: %d\n", sb.num_get_patch);
+		printf("num commits: %d\n", sb.num_commits);
+	}
+	return 0;
+}
diff --git a/builtin/branch.c b/builtin/branch.c
new file mode 100644
index 0000000000..2ef214632f
--- /dev/null
+++ b/builtin/branch.c
@@ -0,0 +1,855 @@
+/*
+ * Builtin "git branch"
+ *
+ * Copyright (c) 2006 Kristian Høgsberg <krh@redhat.com>
+ * Based on git-branch.sh by Junio C Hamano.
+ */
+
+#include "cache.h"
+#include "config.h"
+#include "color.h"
+#include "refs.h"
+#include "commit.h"
+#include "builtin.h"
+#include "remote.h"
+#include "parse-options.h"
+#include "branch.h"
+#include "diff.h"
+#include "revision.h"
+#include "string-list.h"
+#include "column.h"
+#include "utf8.h"
+#include "wt-status.h"
+#include "ref-filter.h"
+#include "worktree.h"
+#include "help.h"
+#include "commit-reach.h"
+
+static const char * const builtin_branch_usage[] = {
+	N_("git branch [<options>] [-r | -a] [--merged | --no-merged]"),
+	N_("git branch [<options>] [-l] [-f] <branch-name> [<start-point>]"),
+	N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
+	N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
+	N_("git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"),
+	N_("git branch [<options>] [-r | -a] [--points-at]"),
+	N_("git branch [<options>] [-r | -a] [--format]"),
+	NULL
+};
+
+static const char *head;
+static struct object_id head_oid;
+
+static int branch_use_color = -1;
+static char branch_colors[][COLOR_MAXLEN] = {
+	GIT_COLOR_RESET,
+	GIT_COLOR_NORMAL,       /* PLAIN */
+	GIT_COLOR_RED,          /* REMOTE */
+	GIT_COLOR_NORMAL,       /* LOCAL */
+	GIT_COLOR_GREEN,        /* CURRENT */
+	GIT_COLOR_BLUE,         /* UPSTREAM */
+	GIT_COLOR_CYAN,         /* WORKTREE */
+};
+enum color_branch {
+	BRANCH_COLOR_RESET = 0,
+	BRANCH_COLOR_PLAIN = 1,
+	BRANCH_COLOR_REMOTE = 2,
+	BRANCH_COLOR_LOCAL = 3,
+	BRANCH_COLOR_CURRENT = 4,
+	BRANCH_COLOR_UPSTREAM = 5,
+	BRANCH_COLOR_WORKTREE = 6
+};
+
+static const char *color_branch_slots[] = {
+	[BRANCH_COLOR_RESET]	= "reset",
+	[BRANCH_COLOR_PLAIN]	= "plain",
+	[BRANCH_COLOR_REMOTE]	= "remote",
+	[BRANCH_COLOR_LOCAL]	= "local",
+	[BRANCH_COLOR_CURRENT]	= "current",
+	[BRANCH_COLOR_UPSTREAM] = "upstream",
+	[BRANCH_COLOR_WORKTREE] = "worktree",
+};
+
+static struct string_list output = STRING_LIST_INIT_DUP;
+static unsigned int colopts;
+
+define_list_config_array(color_branch_slots);
+
+static int git_branch_config(const char *var, const char *value, void *cb)
+{
+	const char *slot_name;
+	struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
+
+	if (!strcmp(var, "branch.sort")) {
+		if (!value)
+			return config_error_nonbool(var);
+		parse_ref_sorting(sorting_tail, value);
+		return 0;
+	}
+
+	if (starts_with(var, "column."))
+		return git_column_config(var, value, "branch", &colopts);
+	if (!strcmp(var, "color.branch")) {
+		branch_use_color = git_config_colorbool(var, value);
+		return 0;
+	}
+	if (skip_prefix(var, "color.branch.", &slot_name)) {
+		int slot = LOOKUP_CONFIG(color_branch_slots, slot_name);
+		if (slot < 0)
+			return 0;
+		if (!value)
+			return config_error_nonbool(var);
+		return color_parse(value, branch_colors[slot]);
+	}
+	return git_color_default_config(var, value, cb);
+}
+
+static const char *branch_get_color(enum color_branch ix)
+{
+	if (want_color(branch_use_color))
+		return branch_colors[ix];
+	return "";
+}
+
+static int branch_merged(int kind, const char *name,
+			 struct commit *rev, struct commit *head_rev)
+{
+	/*
+	 * This checks whether the merge bases of branch and HEAD (or
+	 * the other branch this branch builds upon) contains the
+	 * branch, which means that the branch has already been merged
+	 * safely to HEAD (or the other branch).
+	 */
+	struct commit *reference_rev = NULL;
+	const char *reference_name = NULL;
+	void *reference_name_to_free = NULL;
+	int merged;
+
+	if (kind == FILTER_REFS_BRANCHES) {
+		struct branch *branch = branch_get(name);
+		const char *upstream = branch_get_upstream(branch, NULL);
+		struct object_id oid;
+
+		if (upstream &&
+		    (reference_name = reference_name_to_free =
+		     resolve_refdup(upstream, RESOLVE_REF_READING,
+				    &oid, NULL)) != NULL)
+			reference_rev = lookup_commit_reference(the_repository,
+								&oid);
+	}
+	if (!reference_rev)
+		reference_rev = head_rev;
+
+	merged = in_merge_bases(rev, reference_rev);
+
+	/*
+	 * After the safety valve is fully redefined to "check with
+	 * upstream, if any, otherwise with HEAD", we should just
+	 * return the result of the in_merge_bases() above without
+	 * any of the following code, but during the transition period,
+	 * a gentle reminder is in order.
+	 */
+	if ((head_rev != reference_rev) &&
+	    in_merge_bases(rev, head_rev) != merged) {
+		if (merged)
+			warning(_("deleting branch '%s' that has been merged to\n"
+				"         '%s', but not yet merged to HEAD."),
+				name, reference_name);
+		else
+			warning(_("not deleting branch '%s' that is not yet merged to\n"
+				"         '%s', even though it is merged to HEAD."),
+				name, reference_name);
+	}
+	free(reference_name_to_free);
+	return merged;
+}
+
+static int check_branch_commit(const char *branchname, const char *refname,
+			       const struct object_id *oid, struct commit *head_rev,
+			       int kinds, int force)
+{
+	struct commit *rev = lookup_commit_reference(the_repository, oid);
+	if (!rev) {
+		error(_("Couldn't look up commit object for '%s'"), refname);
+		return -1;
+	}
+	if (!force && !branch_merged(kinds, branchname, rev, head_rev)) {
+		error(_("The branch '%s' is not fully merged.\n"
+		      "If you are sure you want to delete it, "
+		      "run 'git branch -D %s'."), branchname, branchname);
+		return -1;
+	}
+	return 0;
+}
+
+static void delete_branch_config(const char *branchname)
+{
+	struct strbuf buf = STRBUF_INIT;
+	strbuf_addf(&buf, "branch.%s", branchname);
+	if (git_config_rename_section(buf.buf, NULL) < 0)
+		warning(_("Update of config-file failed"));
+	strbuf_release(&buf);
+}
+
+static int delete_branches(int argc, const char **argv, int force, int kinds,
+			   int quiet)
+{
+	struct commit *head_rev = NULL;
+	struct object_id oid;
+	char *name = NULL;
+	const char *fmt;
+	int i;
+	int ret = 0;
+	int remote_branch = 0;
+	struct strbuf bname = STRBUF_INIT;
+	unsigned allowed_interpret;
+
+	switch (kinds) {
+	case FILTER_REFS_REMOTES:
+		fmt = "refs/remotes/%s";
+		/* For subsequent UI messages */
+		remote_branch = 1;
+		allowed_interpret = INTERPRET_BRANCH_REMOTE;
+
+		force = 1;
+		break;
+	case FILTER_REFS_BRANCHES:
+		fmt = "refs/heads/%s";
+		allowed_interpret = INTERPRET_BRANCH_LOCAL;
+		break;
+	default:
+		die(_("cannot use -a with -d"));
+	}
+
+	if (!force) {
+		head_rev = lookup_commit_reference(the_repository, &head_oid);
+		if (!head_rev)
+			die(_("Couldn't look up commit object for HEAD"));
+	}
+	for (i = 0; i < argc; i++, strbuf_reset(&bname)) {
+		char *target = NULL;
+		int flags = 0;
+
+		strbuf_branchname(&bname, argv[i], allowed_interpret);
+		free(name);
+		name = mkpathdup(fmt, bname.buf);
+
+		if (kinds == FILTER_REFS_BRANCHES) {
+			const struct worktree *wt =
+				find_shared_symref("HEAD", name);
+			if (wt) {
+				error(_("Cannot delete branch '%s' "
+					"checked out at '%s'"),
+				      bname.buf, wt->path);
+				ret = 1;
+				continue;
+			}
+		}
+
+		target = resolve_refdup(name,
+					RESOLVE_REF_READING
+					| RESOLVE_REF_NO_RECURSE
+					| RESOLVE_REF_ALLOW_BAD_NAME,
+					&oid, &flags);
+		if (!target) {
+			error(remote_branch
+			      ? _("remote-tracking branch '%s' not found.")
+			      : _("branch '%s' not found."), bname.buf);
+			ret = 1;
+			continue;
+		}
+
+		if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
+		    check_branch_commit(bname.buf, name, &oid, head_rev, kinds,
+					force)) {
+			ret = 1;
+			goto next;
+		}
+
+		if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : &oid,
+			       REF_NO_DEREF)) {
+			error(remote_branch
+			      ? _("Error deleting remote-tracking branch '%s'")
+			      : _("Error deleting branch '%s'"),
+			      bname.buf);
+			ret = 1;
+			goto next;
+		}
+		if (!quiet) {
+			printf(remote_branch
+			       ? _("Deleted remote-tracking branch %s (was %s).\n")
+			       : _("Deleted branch %s (was %s).\n"),
+			       bname.buf,
+			       (flags & REF_ISBROKEN) ? "broken"
+			       : (flags & REF_ISSYMREF) ? target
+			       : find_unique_abbrev(&oid, DEFAULT_ABBREV));
+		}
+		delete_branch_config(bname.buf);
+
+	next:
+		free(target);
+	}
+
+	free(name);
+	strbuf_release(&bname);
+
+	return ret;
+}
+
+static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
+{
+	int i, max = 0;
+	for (i = 0; i < refs->nr; i++) {
+		struct ref_array_item *it = refs->items[i];
+		const char *desc = it->refname;
+		int w;
+
+		skip_prefix(it->refname, "refs/heads/", &desc);
+		skip_prefix(it->refname, "refs/remotes/", &desc);
+		if (it->kind == FILTER_REFS_DETACHED_HEAD) {
+			char *head_desc = get_head_description();
+			w = utf8_strwidth(head_desc);
+			free(head_desc);
+		} else
+			w = utf8_strwidth(desc);
+
+		if (it->kind == FILTER_REFS_REMOTES)
+			w += remote_bonus;
+		if (w > max)
+			max = w;
+	}
+	return max;
+}
+
+static const char *quote_literal_for_format(const char *s)
+{
+	static struct strbuf buf = STRBUF_INIT;
+
+	strbuf_reset(&buf);
+	while (*s) {
+		const char *ep = strchrnul(s, '%');
+		if (s < ep)
+			strbuf_add(&buf, s, ep - s);
+		if (*ep == '%') {
+			strbuf_addstr(&buf, "%%");
+			s = ep + 1;
+		} else {
+			s = ep;
+		}
+	}
+	return buf.buf;
+}
+
+static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix)
+{
+	struct strbuf fmt = STRBUF_INIT;
+	struct strbuf local = STRBUF_INIT;
+	struct strbuf remote = STRBUF_INIT;
+
+	strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else)  %s%%(end)%%(end)",
+			branch_get_color(BRANCH_COLOR_CURRENT),
+			branch_get_color(BRANCH_COLOR_WORKTREE),
+			branch_get_color(BRANCH_COLOR_LOCAL));
+	strbuf_addf(&remote, "  %s",
+		    branch_get_color(BRANCH_COLOR_REMOTE));
+
+	if (filter->verbose) {
+		struct strbuf obname = STRBUF_INIT;
+
+		if (filter->abbrev < 0)
+			strbuf_addf(&obname, "%%(objectname:short)");
+		else if (!filter->abbrev)
+			strbuf_addf(&obname, "%%(objectname)");
+		else
+			strbuf_addf(&obname, "%%(objectname:short=%d)", filter->abbrev);
+
+		strbuf_addf(&local, "%%(align:%d,left)%%(refname:lstrip=2)%%(end)", maxwidth);
+		strbuf_addstr(&local, branch_get_color(BRANCH_COLOR_RESET));
+		strbuf_addf(&local, " %s ", obname.buf);
+
+		if (filter->verbose > 1)
+		{
+			strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)",
+				    branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET));
+			strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
+				    "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)",
+				    branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));
+		}
+		else
+			strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
+
+		strbuf_addf(&remote, "%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s"
+			    "%%(if)%%(symref)%%(then) -> %%(symref:short)"
+			    "%%(else) %s %%(contents:subject)%%(end)",
+			    maxwidth, quote_literal_for_format(remote_prefix),
+			    branch_get_color(BRANCH_COLOR_RESET), obname.buf);
+		strbuf_release(&obname);
+	} else {
+		strbuf_addf(&local, "%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
+			    branch_get_color(BRANCH_COLOR_RESET));
+		strbuf_addf(&remote, "%s%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
+			    quote_literal_for_format(remote_prefix),
+			    branch_get_color(BRANCH_COLOR_RESET));
+	}
+
+	strbuf_addf(&fmt, "%%(if:notequals=refs/remotes)%%(refname:rstrip=-2)%%(then)%s%%(else)%s%%(end)", local.buf, remote.buf);
+
+	strbuf_release(&local);
+	strbuf_release(&remote);
+	return strbuf_detach(&fmt, NULL);
+}
+
+static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, struct ref_format *format)
+{
+	int i;
+	struct ref_array array;
+	int maxwidth = 0;
+	const char *remote_prefix = "";
+	char *to_free = NULL;
+
+	/*
+	 * If we are listing more than just remote branches,
+	 * then remote branches will have a "remotes/" prefix.
+	 * We need to account for this in the width.
+	 */
+	if (filter->kind != FILTER_REFS_REMOTES)
+		remote_prefix = "remotes/";
+
+	memset(&array, 0, sizeof(array));
+
+	filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
+
+	if (filter->verbose)
+		maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
+
+	if (!format->format)
+		format->format = to_free = build_format(filter, maxwidth, remote_prefix);
+	format->use_color = branch_use_color;
+
+	if (verify_ref_format(format))
+		die(_("unable to parse format string"));
+
+	ref_array_sort(sorting, &array);
+
+	for (i = 0; i < array.nr; i++) {
+		struct strbuf out = STRBUF_INIT;
+		struct strbuf err = STRBUF_INIT;
+		if (format_ref_array_item(array.items[i], format, &out, &err))
+			die("%s", err.buf);
+		if (column_active(colopts)) {
+			assert(!filter->verbose && "--column and --verbose are incompatible");
+			 /* format to a string_list to let print_columns() do its job */
+			string_list_append(&output, out.buf);
+		} else {
+			fwrite(out.buf, 1, out.len, stdout);
+			putchar('\n');
+		}
+		strbuf_release(&err);
+		strbuf_release(&out);
+	}
+
+	ref_array_clear(&array);
+	free(to_free);
+}
+
+static void print_current_branch_name(void)
+{
+	int flags;
+	const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+	const char *shortname;
+	if (!refname)
+		die(_("could not resolve HEAD"));
+	else if (!(flags & REF_ISSYMREF))
+		return;
+	else if (skip_prefix(refname, "refs/heads/", &shortname))
+		puts(shortname);
+	else
+		die(_("HEAD (%s) points outside of refs/heads/"), refname);
+}
+
+static void reject_rebase_or_bisect_branch(const char *target)
+{
+	struct worktree **worktrees = get_worktrees(0);
+	int i;
+
+	for (i = 0; worktrees[i]; i++) {
+		struct worktree *wt = worktrees[i];
+
+		if (!wt->is_detached)
+			continue;
+
+		if (is_worktree_being_rebased(wt, target))
+			die(_("Branch %s is being rebased at %s"),
+			    target, wt->path);
+
+		if (is_worktree_being_bisected(wt, target))
+			die(_("Branch %s is being bisected at %s"),
+			    target, wt->path);
+	}
+
+	free_worktrees(worktrees);
+}
+
+static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force)
+{
+	struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
+	struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
+	const char *interpreted_oldname = NULL;
+	const char *interpreted_newname = NULL;
+	int recovery = 0;
+
+	if (!oldname) {
+		if (copy)
+			die(_("cannot copy the current branch while not on any."));
+		else
+			die(_("cannot rename the current branch while not on any."));
+	}
+
+	if (strbuf_check_branch_ref(&oldref, oldname)) {
+		/*
+		 * Bad name --- this could be an attempt to rename a
+		 * ref that we used to allow to be created by accident.
+		 */
+		if (ref_exists(oldref.buf))
+			recovery = 1;
+		else
+			die(_("Invalid branch name: '%s'"), oldname);
+	}
+
+	/*
+	 * A command like "git branch -M currentbranch currentbranch" cannot
+	 * cause the worktree to become inconsistent with HEAD, so allow it.
+	 */
+	if (!strcmp(oldname, newname))
+		validate_branchname(newname, &newref);
+	else
+		validate_new_branchname(newname, &newref, force);
+
+	reject_rebase_or_bisect_branch(oldref.buf);
+
+	if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) ||
+	    !skip_prefix(newref.buf, "refs/heads/", &interpreted_newname)) {
+		BUG("expected prefix missing for refs");
+	}
+
+	if (copy)
+		strbuf_addf(&logmsg, "Branch: copied %s to %s",
+			    oldref.buf, newref.buf);
+	else
+		strbuf_addf(&logmsg, "Branch: renamed %s to %s",
+			    oldref.buf, newref.buf);
+
+	if (!copy && rename_ref(oldref.buf, newref.buf, logmsg.buf))
+		die(_("Branch rename failed"));
+	if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))
+		die(_("Branch copy failed"));
+
+	if (recovery) {
+		if (copy)
+			warning(_("Created a copy of a misnamed branch '%s'"),
+				interpreted_oldname);
+		else
+			warning(_("Renamed a misnamed branch '%s' away"),
+				interpreted_oldname);
+	}
+
+	if (!copy &&
+	    replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
+		die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
+
+	strbuf_release(&logmsg);
+
+	strbuf_addf(&oldsection, "branch.%s", interpreted_oldname);
+	strbuf_release(&oldref);
+	strbuf_addf(&newsection, "branch.%s", interpreted_newname);
+	strbuf_release(&newref);
+	if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0)
+		die(_("Branch is renamed, but update of config-file failed"));
+	if (copy && strcmp(oldname, newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
+		die(_("Branch is copied, but update of config-file failed"));
+	strbuf_release(&oldsection);
+	strbuf_release(&newsection);
+}
+
+static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION")
+
+static int edit_branch_description(const char *branch_name)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf name = STRBUF_INIT;
+
+	read_branch_desc(&buf, branch_name);
+	if (!buf.len || buf.buf[buf.len-1] != '\n')
+		strbuf_addch(&buf, '\n');
+	strbuf_commented_addf(&buf,
+		    _("Please edit the description for the branch\n"
+		      "  %s\n"
+		      "Lines starting with '%c' will be stripped.\n"),
+		    branch_name, comment_line_char);
+	write_file_buf(edit_description(), buf.buf, buf.len);
+	strbuf_reset(&buf);
+	if (launch_editor(edit_description(), &buf, NULL)) {
+		strbuf_release(&buf);
+		return -1;
+	}
+	strbuf_stripspace(&buf, 1);
+
+	strbuf_addf(&name, "branch.%s.description", branch_name);
+	git_config_set(name.buf, buf.len ? buf.buf : NULL);
+	strbuf_release(&name);
+	strbuf_release(&buf);
+
+	return 0;
+}
+
+int cmd_branch(int argc, const char **argv, const char *prefix)
+{
+	int delete = 0, rename = 0, copy = 0, force = 0, list = 0;
+	int show_current = 0;
+	int reflog = 0, edit_description = 0;
+	int quiet = 0, unset_upstream = 0;
+	const char *new_upstream = NULL;
+	enum branch_track track;
+	struct ref_filter filter;
+	int icase = 0;
+	static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+	struct ref_format format = REF_FORMAT_INIT;
+
+	struct option options[] = {
+		OPT_GROUP(N_("Generic options")),
+		OPT__VERBOSE(&filter.verbose,
+			N_("show hash and subject, give twice for upstream branch")),
+		OPT__QUIET(&quiet, N_("suppress informational messages")),
+		OPT_SET_INT('t', "track",  &track, N_("set up tracking mode (see git-pull(1))"),
+			BRANCH_TRACK_EXPLICIT),
+		OPT_SET_INT_F(0, "set-upstream", &track, N_("do not use"),
+			BRANCH_TRACK_OVERRIDE, PARSE_OPT_HIDDEN),
+		OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
+		OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("Unset the upstream info")),
+		OPT__COLOR(&branch_use_color, N_("use colored output")),
+		OPT_SET_INT('r', "remotes",     &filter.kind, N_("act on remote-tracking branches"),
+			FILTER_REFS_REMOTES),
+		OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")),
+		OPT_NO_CONTAINS(&filter.no_commit, N_("print only branches that don't contain the commit")),
+		OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")),
+		OPT_WITHOUT(&filter.no_commit, N_("print only branches that don't contain the commit")),
+		OPT__ABBREV(&filter.abbrev),
+
+		OPT_GROUP(N_("Specific git-branch actions:")),
+		OPT_SET_INT('a', "all", &filter.kind, N_("list both remote-tracking and local branches"),
+			FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES),
+		OPT_BIT('d', "delete", &delete, N_("delete fully merged branch"), 1),
+		OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
+		OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
+		OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2),
+		OPT_BIT('c', "copy", &copy, N_("copy a branch and its reflog"), 1),
+		OPT_BIT('C', NULL, &copy, N_("copy a branch, even if target exists"), 2),
+		OPT_BOOL('l', "list", &list, N_("list branch names")),
+		OPT_BOOL(0, "show-current", &show_current, N_("show current branch name")),
+		OPT_BOOL(0, "create-reflog", &reflog, N_("create the branch's reflog")),
+		OPT_BOOL(0, "edit-description", &edit_description,
+			 N_("edit the description for the branch")),
+		OPT__FORCE(&force, N_("force creation, move/rename, deletion"), PARSE_OPT_NOCOMPLETE),
+		OPT_MERGED(&filter, N_("print only branches that are merged")),
+		OPT_NO_MERGED(&filter, N_("print only branches that are not merged")),
+		OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")),
+		OPT_REF_SORT(sorting_tail),
+		{
+			OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
+			N_("print only branches of the object"), 0, parse_opt_object_name
+		},
+		OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
+		OPT_STRING(  0 , "format", &format.format, N_("format"), N_("format to use for the output")),
+		OPT_END(),
+	};
+
+	setup_ref_filter_porcelain_msg();
+
+	memset(&filter, 0, sizeof(filter));
+	filter.kind = FILTER_REFS_BRANCHES;
+	filter.abbrev = -1;
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(builtin_branch_usage, options);
+
+	git_config(git_branch_config, sorting_tail);
+
+	track = git_branch_track;
+
+	head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+	if (!head)
+		die(_("Failed to resolve HEAD as a valid ref."));
+	if (!strcmp(head, "HEAD"))
+		filter.detached = 1;
+	else if (!skip_prefix(head, "refs/heads/", &head))
+		die(_("HEAD not found below refs/heads!"));
+
+	argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
+			     0);
+
+	if (!delete && !rename && !copy && !edit_description && !new_upstream &&
+	    !show_current && !unset_upstream && argc == 0)
+		list = 1;
+
+	if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||
+	    filter.no_commit)
+		list = 1;
+
+	if (!!delete + !!rename + !!copy + !!new_upstream + !!show_current +
+	    list + unset_upstream > 1)
+		usage_with_options(builtin_branch_usage, options);
+
+	if (filter.abbrev == -1)
+		filter.abbrev = DEFAULT_ABBREV;
+	filter.ignore_case = icase;
+
+	finalize_colopts(&colopts, -1);
+	if (filter.verbose) {
+		if (explicitly_enable_column(colopts))
+			die(_("--column and --verbose are incompatible"));
+		colopts = 0;
+	}
+
+	if (force) {
+		delete *= 2;
+		rename *= 2;
+		copy *= 2;
+	}
+
+	if (list)
+		setup_auto_pager("branch", 1);
+
+	if (delete) {
+		if (!argc)
+			die(_("branch name required"));
+		return delete_branches(argc, argv, delete > 1, filter.kind, quiet);
+	} else if (show_current) {
+		print_current_branch_name();
+		return 0;
+	} else if (list) {
+		/*  git branch --local also shows HEAD when it is detached */
+		if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
+			filter.kind |= FILTER_REFS_DETACHED_HEAD;
+		filter.name_patterns = argv;
+		/*
+		 * If no sorting parameter is given then we default to sorting
+		 * by 'refname'. This would give us an alphabetically sorted
+		 * array with the 'HEAD' ref at the beginning followed by
+		 * local branches 'refs/heads/...' and finally remote-tracking
+		 * branches 'refs/remotes/...'.
+		 */
+		if (!sorting)
+			sorting = ref_default_sorting();
+		sorting->ignore_case = icase;
+		print_ref_list(&filter, sorting, &format);
+		print_columns(&output, colopts, NULL);
+		string_list_clear(&output, 0);
+		return 0;
+	} else if (edit_description) {
+		const char *branch_name;
+		struct strbuf branch_ref = STRBUF_INIT;
+
+		if (!argc) {
+			if (filter.detached)
+				die(_("Cannot give description to detached HEAD"));
+			branch_name = head;
+		} else if (argc == 1)
+			branch_name = argv[0];
+		else
+			die(_("cannot edit description of more than one branch"));
+
+		strbuf_addf(&branch_ref, "refs/heads/%s", branch_name);
+		if (!ref_exists(branch_ref.buf)) {
+			strbuf_release(&branch_ref);
+
+			if (!argc)
+				return error(_("No commit on branch '%s' yet."),
+					     branch_name);
+			else
+				return error(_("No branch named '%s'."),
+					     branch_name);
+		}
+		strbuf_release(&branch_ref);
+
+		if (edit_branch_description(branch_name))
+			return 1;
+	} else if (copy) {
+		if (!argc)
+			die(_("branch name required"));
+		else if (argc == 1)
+			copy_or_rename_branch(head, argv[0], 1, copy > 1);
+		else if (argc == 2)
+			copy_or_rename_branch(argv[0], argv[1], 1, copy > 1);
+		else
+			die(_("too many branches for a copy operation"));
+	} else if (rename) {
+		if (!argc)
+			die(_("branch name required"));
+		else if (argc == 1)
+			copy_or_rename_branch(head, argv[0], 0, rename > 1);
+		else if (argc == 2)
+			copy_or_rename_branch(argv[0], argv[1], 0, rename > 1);
+		else
+			die(_("too many arguments for a rename operation"));
+	} else if (new_upstream) {
+		struct branch *branch = branch_get(argv[0]);
+
+		if (argc > 1)
+			die(_("too many arguments to set new upstream"));
+
+		if (!branch) {
+			if (!argc || !strcmp(argv[0], "HEAD"))
+				die(_("could not set upstream of HEAD to %s when "
+				      "it does not point to any branch."),
+				    new_upstream);
+			die(_("no such branch '%s'"), argv[0]);
+		}
+
+		if (!ref_exists(branch->refname))
+			die(_("branch '%s' does not exist"), branch->name);
+
+		/*
+		 * create_branch takes care of setting up the tracking
+		 * info and making sure new_upstream is correct
+		 */
+		create_branch(the_repository, branch->name, new_upstream,
+			      0, 0, 0, quiet, BRANCH_TRACK_OVERRIDE);
+	} else if (unset_upstream) {
+		struct branch *branch = branch_get(argv[0]);
+		struct strbuf buf = STRBUF_INIT;
+
+		if (argc > 1)
+			die(_("too many arguments to unset upstream"));
+
+		if (!branch) {
+			if (!argc || !strcmp(argv[0], "HEAD"))
+				die(_("could not unset upstream of HEAD when "
+				      "it does not point to any branch."));
+			die(_("no such branch '%s'"), argv[0]);
+		}
+
+		if (!branch_has_merge_config(branch))
+			die(_("Branch '%s' has no upstream information"), branch->name);
+
+		strbuf_addf(&buf, "branch.%s.remote", branch->name);
+		git_config_set_multivar(buf.buf, NULL, NULL, 1);
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "branch.%s.merge", branch->name);
+		git_config_set_multivar(buf.buf, NULL, NULL, 1);
+		strbuf_release(&buf);
+	} else if (argc > 0 && argc <= 2) {
+		if (filter.kind != FILTER_REFS_BRANCHES)
+			die(_("The -a, and -r, options to 'git branch' do not take a branch name.\n"
+				  "Did you mean to use: -a|-r --list <pattern>?"));
+
+		if (track == BRANCH_TRACK_OVERRIDE)
+			die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
+
+		create_branch(the_repository,
+			      argv[0], (argc == 2) ? argv[1] : head,
+			      force, 0, reflog, quiet, track);
+
+	} else
+		usage_with_options(builtin_branch_usage, options);
+
+	return 0;
+}
diff --git a/builtin/bundle.c b/builtin/bundle.c
new file mode 100644
index 0000000000..1ea4bfdfc1
--- /dev/null
+++ b/builtin/bundle.c
@@ -0,0 +1,67 @@
+#include "builtin.h"
+#include "cache.h"
+#include "bundle.h"
+
+/*
+ * Basic handler for bundle files to connect repositories via sneakernet.
+ * Invocation must include action.
+ * This function can create a bundle or provide information on an existing
+ * bundle supporting "fetch", "pull", and "ls-remote".
+ */
+
+static const char builtin_bundle_usage[] =
+  "git bundle create <file> <git-rev-list args>\n"
+  "   or: git bundle verify <file>\n"
+  "   or: git bundle list-heads <file> [<refname>...]\n"
+  "   or: git bundle unbundle <file> [<refname>...]";
+
+int cmd_bundle(int argc, const char **argv, const char *prefix)
+{
+	struct bundle_header header;
+	const char *cmd, *bundle_file;
+	int bundle_fd = -1;
+
+	if (argc < 3)
+		usage(builtin_bundle_usage);
+
+	cmd = argv[1];
+	bundle_file = prefix_filename(prefix, argv[2]);
+	argc -= 2;
+	argv += 2;
+
+	memset(&header, 0, sizeof(header));
+	if (strcmp(cmd, "create") && (bundle_fd =
+				read_bundle_header(bundle_file, &header)) < 0)
+		return 1;
+
+	if (!strcmp(cmd, "verify")) {
+		close(bundle_fd);
+		if (argc != 1) {
+			usage(builtin_bundle_usage);
+			return 1;
+		}
+		if (verify_bundle(the_repository, &header, 1))
+			return 1;
+		fprintf(stderr, _("%s is okay\n"), bundle_file);
+		return 0;
+	}
+	if (!strcmp(cmd, "list-heads")) {
+		close(bundle_fd);
+		return !!list_bundle_refs(&header, argc, argv);
+	}
+	if (!strcmp(cmd, "create")) {
+		if (argc < 2) {
+			usage(builtin_bundle_usage);
+			return 1;
+		}
+		if (!startup_info->have_repository)
+			die(_("Need a repository to create a bundle."));
+		return !!create_bundle(the_repository, bundle_file, argc, argv);
+	} else if (!strcmp(cmd, "unbundle")) {
+		if (!startup_info->have_repository)
+			die(_("Need a repository to unbundle."));
+		return !!unbundle(the_repository, &header, bundle_fd, 0) ||
+			list_bundle_refs(&header, argc, argv);
+	} else
+		usage(builtin_bundle_usage);
+}
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
new file mode 100644
index 0000000000..995d47c85a
--- /dev/null
+++ b/builtin/cat-file.c
@@ -0,0 +1,717 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "builtin.h"
+#include "diff.h"
+#include "parse-options.h"
+#include "userdiff.h"
+#include "streaming.h"
+#include "tree-walk.h"
+#include "sha1-array.h"
+#include "packfile.h"
+#include "object-store.h"
+
+struct batch_options {
+	int enabled;
+	int follow_symlinks;
+	int print_contents;
+	int buffer_output;
+	int all_objects;
+	int unordered;
+	int cmdmode; /* may be 'w' or 'c' for --filters or --textconv */
+	const char *format;
+};
+
+static const char *force_path;
+
+static int filter_object(const char *path, unsigned mode,
+			 const struct object_id *oid,
+			 char **buf, unsigned long *size)
+{
+	enum object_type type;
+
+	*buf = read_object_file(oid, &type, size);
+	if (!*buf)
+		return error(_("cannot read object %s '%s'"),
+			     oid_to_hex(oid), path);
+	if ((type == OBJ_BLOB) && S_ISREG(mode)) {
+		struct strbuf strbuf = STRBUF_INIT;
+		if (convert_to_working_tree(&the_index, path, *buf, *size, &strbuf)) {
+			free(*buf);
+			*size = strbuf.len;
+			*buf = strbuf_detach(&strbuf, NULL);
+		}
+	}
+
+	return 0;
+}
+
+static int stream_blob(const struct object_id *oid)
+{
+	if (stream_blob_to_fd(1, oid, NULL, 0))
+		die("unable to stream %s to stdout", oid_to_hex(oid));
+	return 0;
+}
+
+static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
+			int unknown_type)
+{
+	struct object_id oid;
+	enum object_type type;
+	char *buf;
+	unsigned long size;
+	struct object_context obj_context;
+	struct object_info oi = OBJECT_INFO_INIT;
+	struct strbuf sb = STRBUF_INIT;
+	unsigned flags = OBJECT_INFO_LOOKUP_REPLACE;
+	const char *path = force_path;
+
+	if (unknown_type)
+		flags |= OBJECT_INFO_ALLOW_UNKNOWN_TYPE;
+
+	if (get_oid_with_context(the_repository, obj_name,
+				 GET_OID_RECORD_PATH,
+				 &oid, &obj_context))
+		die("Not a valid object name %s", obj_name);
+
+	if (!path)
+		path = obj_context.path;
+	if (obj_context.mode == S_IFINVALID)
+		obj_context.mode = 0100644;
+
+	buf = NULL;
+	switch (opt) {
+	case 't':
+		oi.type_name = &sb;
+		if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0)
+			die("git cat-file: could not get object info");
+		if (sb.len) {
+			printf("%s\n", sb.buf);
+			strbuf_release(&sb);
+			return 0;
+		}
+		break;
+
+	case 's':
+		oi.sizep = &size;
+		if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0)
+			die("git cat-file: could not get object info");
+		printf("%"PRIuMAX"\n", (uintmax_t)size);
+		return 0;
+
+	case 'e':
+		return !has_object_file(&oid);
+
+	case 'w':
+		if (!path)
+			die("git cat-file --filters %s: <object> must be "
+			    "<sha1:path>", obj_name);
+
+		if (filter_object(path, obj_context.mode,
+				  &oid, &buf, &size))
+			return -1;
+		break;
+
+	case 'c':
+		if (!path)
+			die("git cat-file --textconv %s: <object> must be <sha1:path>",
+			    obj_name);
+
+		if (textconv_object(the_repository, path, obj_context.mode,
+				    &oid, 1, &buf, &size))
+			break;
+		/* else fallthrough */
+
+	case 'p':
+		type = oid_object_info(the_repository, &oid, NULL);
+		if (type < 0)
+			die("Not a valid object name %s", obj_name);
+
+		/* custom pretty-print here */
+		if (type == OBJ_TREE) {
+			const char *ls_args[3] = { NULL };
+			ls_args[0] =  "ls-tree";
+			ls_args[1] =  obj_name;
+			return cmd_ls_tree(2, ls_args, NULL);
+		}
+
+		if (type == OBJ_BLOB)
+			return stream_blob(&oid);
+		buf = read_object_file(&oid, &type, &size);
+		if (!buf)
+			die("Cannot read object %s", obj_name);
+
+		/* otherwise just spit out the data */
+		break;
+
+	case 0:
+		if (type_from_string(exp_type) == OBJ_BLOB) {
+			struct object_id blob_oid;
+			if (oid_object_info(the_repository, &oid, NULL) == OBJ_TAG) {
+				char *buffer = read_object_file(&oid, &type,
+								&size);
+				const char *target;
+				if (!skip_prefix(buffer, "object ", &target) ||
+				    get_oid_hex(target, &blob_oid))
+					die("%s not a valid tag", oid_to_hex(&oid));
+				free(buffer);
+			} else
+				oidcpy(&blob_oid, &oid);
+
+			if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB)
+				return stream_blob(&blob_oid);
+			/*
+			 * we attempted to dereference a tag to a blob
+			 * and failed; there may be new dereference
+			 * mechanisms this code is not aware of.
+			 * fall-back to the usual case.
+			 */
+		}
+		buf = read_object_with_reference(the_repository,
+						 &oid, exp_type, &size, NULL);
+		break;
+
+	default:
+		die("git cat-file: unknown option: %s", exp_type);
+	}
+
+	if (!buf)
+		die("git cat-file %s: bad file", obj_name);
+
+	write_or_die(1, buf, size);
+	free(buf);
+	free(obj_context.path);
+	return 0;
+}
+
+struct expand_data {
+	struct object_id oid;
+	enum object_type type;
+	unsigned long size;
+	off_t disk_size;
+	const char *rest;
+	struct object_id delta_base_oid;
+
+	/*
+	 * If mark_query is true, we do not expand anything, but rather
+	 * just mark the object_info with items we wish to query.
+	 */
+	int mark_query;
+
+	/*
+	 * Whether to split the input on whitespace before feeding it to
+	 * get_sha1; this is decided during the mark_query phase based on
+	 * whether we have a %(rest) token in our format.
+	 */
+	int split_on_whitespace;
+
+	/*
+	 * After a mark_query run, this object_info is set up to be
+	 * passed to oid_object_info_extended. It will point to the data
+	 * elements above, so you can retrieve the response from there.
+	 */
+	struct object_info info;
+
+	/*
+	 * This flag will be true if the requested batch format and options
+	 * don't require us to call oid_object_info, which can then be
+	 * optimized out.
+	 */
+	unsigned skip_object_info : 1;
+};
+
+static int is_atom(const char *atom, const char *s, int slen)
+{
+	int alen = strlen(atom);
+	return alen == slen && !memcmp(atom, s, alen);
+}
+
+static void expand_atom(struct strbuf *sb, const char *atom, int len,
+			void *vdata)
+{
+	struct expand_data *data = vdata;
+
+	if (is_atom("objectname", atom, len)) {
+		if (!data->mark_query)
+			strbuf_addstr(sb, oid_to_hex(&data->oid));
+	} else if (is_atom("objecttype", atom, len)) {
+		if (data->mark_query)
+			data->info.typep = &data->type;
+		else
+			strbuf_addstr(sb, type_name(data->type));
+	} else if (is_atom("objectsize", atom, len)) {
+		if (data->mark_query)
+			data->info.sizep = &data->size;
+		else
+			strbuf_addf(sb, "%"PRIuMAX , (uintmax_t)data->size);
+	} else if (is_atom("objectsize:disk", atom, len)) {
+		if (data->mark_query)
+			data->info.disk_sizep = &data->disk_size;
+		else
+			strbuf_addf(sb, "%"PRIuMAX, (uintmax_t)data->disk_size);
+	} else if (is_atom("rest", atom, len)) {
+		if (data->mark_query)
+			data->split_on_whitespace = 1;
+		else if (data->rest)
+			strbuf_addstr(sb, data->rest);
+	} else if (is_atom("deltabase", atom, len)) {
+		if (data->mark_query)
+			data->info.delta_base_sha1 = data->delta_base_oid.hash;
+		else
+			strbuf_addstr(sb,
+				      oid_to_hex(&data->delta_base_oid));
+	} else
+		die("unknown format element: %.*s", len, atom);
+}
+
+static size_t expand_format(struct strbuf *sb, const char *start, void *data)
+{
+	const char *end;
+
+	if (*start != '(')
+		return 0;
+	end = strchr(start + 1, ')');
+	if (!end)
+		die("format element '%s' does not end in ')'", start);
+
+	expand_atom(sb, start + 1, end - start - 1, data);
+
+	return end - start + 1;
+}
+
+static void batch_write(struct batch_options *opt, const void *data, int len)
+{
+	if (opt->buffer_output) {
+		if (fwrite(data, 1, len, stdout) != len)
+			die_errno("unable to write to stdout");
+	} else
+		write_or_die(1, data, len);
+}
+
+static void print_object_or_die(struct batch_options *opt, struct expand_data *data)
+{
+	const struct object_id *oid = &data->oid;
+
+	assert(data->info.typep);
+
+	if (data->type == OBJ_BLOB) {
+		if (opt->buffer_output)
+			fflush(stdout);
+		if (opt->cmdmode) {
+			char *contents;
+			unsigned long size;
+
+			if (!data->rest)
+				die("missing path for '%s'", oid_to_hex(oid));
+
+			if (opt->cmdmode == 'w') {
+				if (filter_object(data->rest, 0100644, oid,
+						  &contents, &size))
+					die("could not convert '%s' %s",
+					    oid_to_hex(oid), data->rest);
+			} else if (opt->cmdmode == 'c') {
+				enum object_type type;
+				if (!textconv_object(the_repository,
+						     data->rest, 0100644, oid,
+						     1, &contents, &size))
+					contents = read_object_file(oid,
+								    &type,
+								    &size);
+				if (!contents)
+					die("could not convert '%s' %s",
+					    oid_to_hex(oid), data->rest);
+			} else
+				BUG("invalid cmdmode: %c", opt->cmdmode);
+			batch_write(opt, contents, size);
+			free(contents);
+		} else {
+			stream_blob(oid);
+		}
+	}
+	else {
+		enum object_type type;
+		unsigned long size;
+		void *contents;
+
+		contents = read_object_file(oid, &type, &size);
+		if (!contents)
+			die("object %s disappeared", oid_to_hex(oid));
+		if (type != data->type)
+			die("object %s changed type!?", oid_to_hex(oid));
+		if (data->info.sizep && size != data->size)
+			die("object %s changed size!?", oid_to_hex(oid));
+
+		batch_write(opt, contents, size);
+		free(contents);
+	}
+}
+
+static void batch_object_write(const char *obj_name,
+			       struct strbuf *scratch,
+			       struct batch_options *opt,
+			       struct expand_data *data)
+{
+	if (!data->skip_object_info &&
+	    oid_object_info_extended(the_repository, &data->oid, &data->info,
+				     OBJECT_INFO_LOOKUP_REPLACE) < 0) {
+		printf("%s missing\n",
+		       obj_name ? obj_name : oid_to_hex(&data->oid));
+		fflush(stdout);
+		return;
+	}
+
+	strbuf_reset(scratch);
+	strbuf_expand(scratch, opt->format, expand_format, data);
+	strbuf_addch(scratch, '\n');
+	batch_write(opt, scratch->buf, scratch->len);
+
+	if (opt->print_contents) {
+		print_object_or_die(opt, data);
+		batch_write(opt, "\n", 1);
+	}
+}
+
+static void batch_one_object(const char *obj_name,
+			     struct strbuf *scratch,
+			     struct batch_options *opt,
+			     struct expand_data *data)
+{
+	struct object_context ctx;
+	int flags = opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0;
+	enum get_oid_result result;
+
+	result = get_oid_with_context(the_repository, obj_name,
+				      flags, &data->oid, &ctx);
+	if (result != FOUND) {
+		switch (result) {
+		case MISSING_OBJECT:
+			printf("%s missing\n", obj_name);
+			break;
+		case SHORT_NAME_AMBIGUOUS:
+			printf("%s ambiguous\n", obj_name);
+			break;
+		case DANGLING_SYMLINK:
+			printf("dangling %"PRIuMAX"\n%s\n",
+			       (uintmax_t)strlen(obj_name), obj_name);
+			break;
+		case SYMLINK_LOOP:
+			printf("loop %"PRIuMAX"\n%s\n",
+			       (uintmax_t)strlen(obj_name), obj_name);
+			break;
+		case NOT_DIR:
+			printf("notdir %"PRIuMAX"\n%s\n",
+			       (uintmax_t)strlen(obj_name), obj_name);
+			break;
+		default:
+			BUG("unknown get_sha1_with_context result %d\n",
+			       result);
+			break;
+		}
+		fflush(stdout);
+		return;
+	}
+
+	if (ctx.mode == 0) {
+		printf("symlink %"PRIuMAX"\n%s\n",
+		       (uintmax_t)ctx.symlink_path.len,
+		       ctx.symlink_path.buf);
+		fflush(stdout);
+		return;
+	}
+
+	batch_object_write(obj_name, scratch, opt, data);
+}
+
+struct object_cb_data {
+	struct batch_options *opt;
+	struct expand_data *expand;
+	struct oidset *seen;
+	struct strbuf *scratch;
+};
+
+static int batch_object_cb(const struct object_id *oid, void *vdata)
+{
+	struct object_cb_data *data = vdata;
+	oidcpy(&data->expand->oid, oid);
+	batch_object_write(NULL, data->scratch, data->opt, data->expand);
+	return 0;
+}
+
+static int collect_loose_object(const struct object_id *oid,
+				const char *path,
+				void *data)
+{
+	oid_array_append(data, oid);
+	return 0;
+}
+
+static int collect_packed_object(const struct object_id *oid,
+				 struct packed_git *pack,
+				 uint32_t pos,
+				 void *data)
+{
+	oid_array_append(data, oid);
+	return 0;
+}
+
+static int batch_unordered_object(const struct object_id *oid, void *vdata)
+{
+	struct object_cb_data *data = vdata;
+
+	if (oidset_insert(data->seen, oid))
+		return 0;
+
+	return batch_object_cb(oid, data);
+}
+
+static int batch_unordered_loose(const struct object_id *oid,
+				 const char *path,
+				 void *data)
+{
+	return batch_unordered_object(oid, data);
+}
+
+static int batch_unordered_packed(const struct object_id *oid,
+				  struct packed_git *pack,
+				  uint32_t pos,
+				  void *data)
+{
+	return batch_unordered_object(oid, data);
+}
+
+static int batch_objects(struct batch_options *opt)
+{
+	struct strbuf input = STRBUF_INIT;
+	struct strbuf output = STRBUF_INIT;
+	struct expand_data data;
+	int save_warning;
+	int retval = 0;
+
+	if (!opt->format)
+		opt->format = "%(objectname) %(objecttype) %(objectsize)";
+
+	/*
+	 * Expand once with our special mark_query flag, which will prime the
+	 * object_info to be handed to oid_object_info_extended for each
+	 * object.
+	 */
+	memset(&data, 0, sizeof(data));
+	data.mark_query = 1;
+	strbuf_expand(&output, opt->format, expand_format, &data);
+	data.mark_query = 0;
+	strbuf_release(&output);
+	if (opt->cmdmode)
+		data.split_on_whitespace = 1;
+
+	if (opt->all_objects) {
+		struct object_info empty = OBJECT_INFO_INIT;
+		if (!memcmp(&data.info, &empty, sizeof(empty)))
+			data.skip_object_info = 1;
+	}
+
+	/*
+	 * If we are printing out the object, then always fill in the type,
+	 * since we will want to decide whether or not to stream.
+	 */
+	if (opt->print_contents)
+		data.info.typep = &data.type;
+
+	if (opt->all_objects) {
+		struct object_cb_data cb;
+
+		if (repository_format_partial_clone)
+			warning("This repository has extensions.partialClone set. Some objects may not be loaded.");
+
+		cb.opt = opt;
+		cb.expand = &data;
+		cb.scratch = &output;
+
+		if (opt->unordered) {
+			struct oidset seen = OIDSET_INIT;
+
+			cb.seen = &seen;
+
+			for_each_loose_object(batch_unordered_loose, &cb, 0);
+			for_each_packed_object(batch_unordered_packed, &cb,
+					       FOR_EACH_OBJECT_PACK_ORDER);
+
+			oidset_clear(&seen);
+		} else {
+			struct oid_array sa = OID_ARRAY_INIT;
+
+			for_each_loose_object(collect_loose_object, &sa, 0);
+			for_each_packed_object(collect_packed_object, &sa, 0);
+
+			oid_array_for_each_unique(&sa, batch_object_cb, &cb);
+
+			oid_array_clear(&sa);
+		}
+
+		strbuf_release(&output);
+		return 0;
+	}
+
+	/*
+	 * We are going to call get_sha1 on a potentially very large number of
+	 * objects. In most large cases, these will be actual object sha1s. The
+	 * cost to double-check that each one is not also a ref (just so we can
+	 * warn) ends up dwarfing the actual cost of the object lookups
+	 * themselves. We can work around it by just turning off the warning.
+	 */
+	save_warning = warn_on_object_refname_ambiguity;
+	warn_on_object_refname_ambiguity = 0;
+
+	while (strbuf_getline(&input, stdin) != EOF) {
+		if (data.split_on_whitespace) {
+			/*
+			 * Split at first whitespace, tying off the beginning
+			 * of the string and saving the remainder (or NULL) in
+			 * data.rest.
+			 */
+			char *p = strpbrk(input.buf, " \t");
+			if (p) {
+				while (*p && strchr(" \t", *p))
+					*p++ = '\0';
+			}
+			data.rest = p;
+		}
+
+		batch_one_object(input.buf, &output, opt, &data);
+	}
+
+	strbuf_release(&input);
+	strbuf_release(&output);
+	warn_on_object_refname_ambiguity = save_warning;
+	return retval;
+}
+
+static const char * const cat_file_usage[] = {
+	N_("git cat-file (-t [--allow-unknown-type] | -s [--allow-unknown-type] | -e | -p | <type> | --textconv | --filters) [--path=<path>] <object>"),
+	N_("git cat-file (--batch | --batch-check) [--follow-symlinks] [--textconv | --filters]"),
+	NULL
+};
+
+static int git_cat_file_config(const char *var, const char *value, void *cb)
+{
+	if (userdiff_config(var, value) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
+}
+
+static int batch_option_callback(const struct option *opt,
+				 const char *arg,
+				 int unset)
+{
+	struct batch_options *bo = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (bo->enabled) {
+		return error(_("only one batch option may be specified"));
+	}
+
+	bo->enabled = 1;
+	bo->print_contents = !strcmp(opt->long_name, "batch");
+	bo->format = arg;
+
+	return 0;
+}
+
+int cmd_cat_file(int argc, const char **argv, const char *prefix)
+{
+	int opt = 0;
+	const char *exp_type = NULL, *obj_name = NULL;
+	struct batch_options batch = {0};
+	int unknown_type = 0;
+
+	const struct option options[] = {
+		OPT_GROUP(N_("<type> can be one of: blob, tree, commit, tag")),
+		OPT_CMDMODE('t', NULL, &opt, N_("show object type"), 't'),
+		OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'),
+		OPT_CMDMODE('e', NULL, &opt,
+			    N_("exit with zero when there's no error"), 'e'),
+		OPT_CMDMODE('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
+		OPT_CMDMODE(0, "textconv", &opt,
+			    N_("for blob objects, run textconv on object's content"), 'c'),
+		OPT_CMDMODE(0, "filters", &opt,
+			    N_("for blob objects, run filters on object's content"), 'w'),
+		OPT_STRING(0, "path", &force_path, N_("blob"),
+			   N_("use a specific path for --textconv/--filters")),
+		OPT_BOOL(0, "allow-unknown-type", &unknown_type,
+			  N_("allow -s and -t to work with broken/corrupt objects")),
+		OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")),
+		{ OPTION_CALLBACK, 0, "batch", &batch, "format",
+			N_("show info and content of objects fed from the standard input"),
+			PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
+			batch_option_callback },
+		{ OPTION_CALLBACK, 0, "batch-check", &batch, "format",
+			N_("show info about objects fed from the standard input"),
+			PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
+			batch_option_callback },
+		OPT_BOOL(0, "follow-symlinks", &batch.follow_symlinks,
+			 N_("follow in-tree symlinks (used with --batch or --batch-check)")),
+		OPT_BOOL(0, "batch-all-objects", &batch.all_objects,
+			 N_("show all objects with --batch or --batch-check")),
+		OPT_BOOL(0, "unordered", &batch.unordered,
+			 N_("do not order --batch-all-objects output")),
+		OPT_END()
+	};
+
+	git_config(git_cat_file_config, NULL);
+
+	batch.buffer_output = -1;
+	argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0);
+
+	if (opt) {
+		if (batch.enabled && (opt == 'c' || opt == 'w'))
+			batch.cmdmode = opt;
+		else if (argc == 1)
+			obj_name = argv[0];
+		else
+			usage_with_options(cat_file_usage, options);
+	}
+	if (!opt && !batch.enabled) {
+		if (argc == 2) {
+			exp_type = argv[0];
+			obj_name = argv[1];
+		} else
+			usage_with_options(cat_file_usage, options);
+	}
+	if (batch.enabled) {
+		if (batch.cmdmode != opt || argc)
+			usage_with_options(cat_file_usage, options);
+		if (batch.cmdmode && batch.all_objects)
+			die("--batch-all-objects cannot be combined with "
+			    "--textconv nor with --filters");
+	}
+
+	if ((batch.follow_symlinks || batch.all_objects) && !batch.enabled) {
+		usage_with_options(cat_file_usage, options);
+	}
+
+	if (force_path && opt != 'c' && opt != 'w') {
+		error("--path=<path> needs --textconv or --filters");
+		usage_with_options(cat_file_usage, options);
+	}
+
+	if (force_path && batch.enabled) {
+		error("--path=<path> incompatible with --batch");
+		usage_with_options(cat_file_usage, options);
+	}
+
+	if (batch.buffer_output < 0)
+		batch.buffer_output = batch.all_objects;
+
+	if (batch.enabled)
+		return batch_objects(&batch);
+
+	if (unknown_type && opt != 't' && opt != 's')
+		die("git cat-file --allow-unknown-type: use with -s or -t");
+	return cat_one_file(opt, exp_type, obj_name, unknown_type);
+}
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
new file mode 100644
index 0000000000..dd83397786
--- /dev/null
+++ b/builtin/check-attr.c
@@ -0,0 +1,189 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "attr.h"
+#include "quote.h"
+#include "parse-options.h"
+
+static int all_attrs;
+static int cached_attrs;
+static int stdin_paths;
+static const char * const check_attr_usage[] = {
+N_("git check-attr [-a | --all | <attr>...] [--] <pathname>..."),
+N_("git check-attr --stdin [-z] [-a | --all | <attr>...]"),
+NULL
+};
+
+static int nul_term_line;
+
+static const struct option check_attr_options[] = {
+	OPT_BOOL('a', "all", &all_attrs, N_("report all attributes set on file")),
+	OPT_BOOL(0,  "cached", &cached_attrs, N_("use .gitattributes only from the index")),
+	OPT_BOOL(0 , "stdin", &stdin_paths, N_("read file names from stdin")),
+	OPT_BOOL('z', NULL, &nul_term_line,
+		 N_("terminate input and output records by a NUL character")),
+	OPT_END()
+};
+
+static void output_attr(struct attr_check *check, const char *file)
+{
+	int j;
+	int cnt = check->nr;
+
+	for (j = 0; j < cnt; j++) {
+		const char *value = check->items[j].value;
+
+		if (ATTR_TRUE(value))
+			value = "set";
+		else if (ATTR_FALSE(value))
+			value = "unset";
+		else if (ATTR_UNSET(value))
+			value = "unspecified";
+
+		if (nul_term_line) {
+			printf("%s%c" /* path */
+			       "%s%c" /* attrname */
+			       "%s%c" /* attrvalue */,
+			       file, 0,
+			       git_attr_name(check->items[j].attr), 0, value, 0);
+		} else {
+			quote_c_style(file, NULL, stdout, 0);
+			printf(": %s: %s\n",
+			       git_attr_name(check->items[j].attr), value);
+		}
+	}
+}
+
+static void check_attr(const char *prefix,
+		       struct attr_check *check,
+		       int collect_all,
+		       const char *file)
+{
+	char *full_path =
+		prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
+
+	if (collect_all) {
+		git_all_attrs(&the_index, full_path, check);
+	} else {
+		git_check_attr(&the_index, full_path, check);
+	}
+	output_attr(check, file);
+
+	free(full_path);
+}
+
+static void check_attr_stdin_paths(const char *prefix,
+				   struct attr_check *check,
+				   int collect_all)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf unquoted = STRBUF_INIT;
+	strbuf_getline_fn getline_fn;
+
+	getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
+	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);
+		}
+		check_attr(prefix, check, collect_all, buf.buf);
+		maybe_flush_or_die(stdout, "attribute to stdout");
+	}
+	strbuf_release(&buf);
+	strbuf_release(&unquoted);
+}
+
+static NORETURN void error_with_usage(const char *msg)
+{
+	error("%s", msg);
+	usage_with_options(check_attr_usage, check_attr_options);
+}
+
+int cmd_check_attr(int argc, const char **argv, const char *prefix)
+{
+	struct attr_check *check;
+	int cnt, i, doubledash, filei;
+
+	if (!is_bare_repository())
+		setup_work_tree();
+
+	git_config(git_default_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, check_attr_options,
+			     check_attr_usage, PARSE_OPT_KEEP_DASHDASH);
+
+	if (read_cache() < 0) {
+		die("invalid cache");
+	}
+
+	if (cached_attrs)
+		git_attr_set_direction(GIT_ATTR_INDEX);
+
+	doubledash = -1;
+	for (i = 0; doubledash < 0 && i < argc; i++) {
+		if (!strcmp(argv[i], "--"))
+			doubledash = i;
+	}
+
+	/* Process --all and/or attribute arguments: */
+	if (all_attrs) {
+		if (doubledash >= 1)
+			error_with_usage("Attributes and --all both specified");
+
+		cnt = 0;
+		filei = doubledash + 1;
+	} else if (doubledash == 0) {
+		error_with_usage("No attribute specified");
+	} else if (doubledash < 0) {
+		if (!argc)
+			error_with_usage("No attribute specified");
+
+		if (stdin_paths) {
+			/* Treat all arguments as attribute names. */
+			cnt = argc;
+			filei = argc;
+		} else {
+			/* Treat exactly one argument as an attribute name. */
+			cnt = 1;
+			filei = 1;
+		}
+	} else {
+		cnt = doubledash;
+		filei = doubledash + 1;
+	}
+
+	/* Check file argument(s): */
+	if (stdin_paths) {
+		if (filei < argc)
+			error_with_usage("Can't specify files with --stdin");
+	} else {
+		if (filei >= argc)
+			error_with_usage("No file specified");
+	}
+
+	check = attr_check_alloc();
+	if (!all_attrs) {
+		for (i = 0; i < cnt; i++) {
+			const struct git_attr *a = git_attr(argv[i]);
+
+			if (!a)
+				return error("%s: not a valid attribute name",
+					     argv[i]);
+			attr_check_append(check, a);
+		}
+	}
+
+	if (stdin_paths)
+		check_attr_stdin_paths(prefix, check, all_attrs);
+	else {
+		for (i = filei; i < argc; i++)
+			check_attr(prefix, check, all_attrs, argv[i]);
+		maybe_flush_or_die(stdout, "attribute to stdout");
+	}
+
+	attr_check_free(check);
+	return 0;
+}
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
new file mode 100644
index 0000000000..599097304b
--- /dev/null
+++ b/builtin/check-ignore.c
@@ -0,0 +1,193 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "dir.h"
+#include "quote.h"
+#include "pathspec.h"
+#include "parse-options.h"
+#include "submodule.h"
+
+static int quiet, verbose, stdin_paths, show_non_matching, no_index;
+static const char * const check_ignore_usage[] = {
+"git check-ignore [<options>] <pathname>...",
+"git check-ignore [<options>] --stdin",
+NULL
+};
+
+static int nul_term_line;
+
+static const struct option check_ignore_options[] = {
+	OPT__QUIET(&quiet, N_("suppress progress reporting")),
+	OPT__VERBOSE(&verbose, N_("be verbose")),
+	OPT_GROUP(""),
+	OPT_BOOL(0, "stdin", &stdin_paths,
+		 N_("read file names from stdin")),
+	OPT_BOOL('z', NULL, &nul_term_line,
+		 N_("terminate input and output records by a NUL character")),
+	OPT_BOOL('n', "non-matching", &show_non_matching,
+		 N_("show non-matching input paths")),
+	OPT_BOOL(0, "no-index", &no_index,
+		 N_("ignore index when checking")),
+	OPT_END()
+};
+
+static void output_exclude(const char *path, struct exclude *exclude)
+{
+	char *bang  = (exclude && exclude->flags & EXC_FLAG_NEGATIVE)  ? "!" : "";
+	char *slash = (exclude && exclude->flags & EXC_FLAG_MUSTBEDIR) ? "/" : "";
+	if (!nul_term_line) {
+		if (!verbose) {
+			write_name_quoted(path, stdout, '\n');
+		} else {
+			if (exclude) {
+				quote_c_style(exclude->el->src, NULL, stdout, 0);
+				printf(":%d:%s%s%s\t",
+				       exclude->srcpos,
+				       bang, exclude->pattern, slash);
+			}
+			else {
+				printf("::\t");
+			}
+			quote_c_style(path, NULL, stdout, 0);
+			fputc('\n', stdout);
+		}
+	} else {
+		if (!verbose) {
+			printf("%s%c", path, '\0');
+		} else {
+			if (exclude)
+				printf("%s%c%d%c%s%s%s%c%s%c",
+				       exclude->el->src, '\0',
+				       exclude->srcpos, '\0',
+				       bang, exclude->pattern, slash, '\0',
+				       path, '\0');
+			else
+				printf("%c%c%c%s%c", '\0', '\0', '\0', path, '\0');
+		}
+	}
+}
+
+static int check_ignore(struct dir_struct *dir,
+			const char *prefix, int argc, const char **argv)
+{
+	const char *full_path;
+	char *seen;
+	int num_ignored = 0, i;
+	struct exclude *exclude;
+	struct pathspec pathspec;
+
+	if (!argc) {
+		if (!quiet)
+			fprintf(stderr, "no pathspec given.\n");
+		return 0;
+	}
+
+	/*
+	 * check-ignore just needs paths. Magic beyond :/ is really
+	 * irrelevant.
+	 */
+	parse_pathspec(&pathspec,
+		       PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP,
+		       PATHSPEC_SYMLINK_LEADING_PATH |
+		       PATHSPEC_KEEP_ORDER,
+		       prefix, argv);
+
+	die_path_inside_submodule(&the_index, &pathspec);
+
+	/*
+	 * look for pathspecs matching entries in the index, since these
+	 * should not be ignored, in order to be consistent with
+	 * 'git status', 'git add' etc.
+	 */
+	seen = find_pathspecs_matching_against_index(&pathspec, &the_index);
+	for (i = 0; i < pathspec.nr; i++) {
+		full_path = pathspec.items[i].match;
+		exclude = NULL;
+		if (!seen[i]) {
+			int dtype = DT_UNKNOWN;
+			exclude = last_exclude_matching(dir, &the_index,
+							full_path, &dtype);
+		}
+		if (!quiet && (exclude || show_non_matching))
+			output_exclude(pathspec.items[i].original, exclude);
+		if (exclude)
+			num_ignored++;
+	}
+	free(seen);
+
+	return num_ignored;
+}
+
+static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf unquoted = STRBUF_INIT;
+	char *pathspec[2] = { NULL, NULL };
+	strbuf_getline_fn getline_fn;
+	int num_ignored = 0;
+
+	getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
+	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);
+		}
+		pathspec[0] = buf.buf;
+		num_ignored += check_ignore(dir, prefix,
+					    1, (const char **)pathspec);
+		maybe_flush_or_die(stdout, "check-ignore to stdout");
+	}
+	strbuf_release(&buf);
+	strbuf_release(&unquoted);
+	return num_ignored;
+}
+
+int cmd_check_ignore(int argc, const char **argv, const char *prefix)
+{
+	int num_ignored;
+	struct dir_struct dir;
+
+	git_config(git_default_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, check_ignore_options,
+			     check_ignore_usage, 0);
+
+	if (stdin_paths) {
+		if (argc > 0)
+			die(_("cannot specify pathnames with --stdin"));
+	} else {
+		if (nul_term_line)
+			die(_("-z only makes sense with --stdin"));
+		if (argc == 0)
+			die(_("no path specified"));
+	}
+	if (quiet) {
+		if (argc > 1)
+			die(_("--quiet is only valid with a single pathname"));
+		if (verbose)
+			die(_("cannot have both --quiet and --verbose"));
+	}
+	if (show_non_matching && !verbose)
+		die(_("--non-matching is only valid with --verbose"));
+
+	/* read_cache() is only necessary so we can watch out for submodules. */
+	if (!no_index && read_cache() < 0)
+		die(_("index file corrupt"));
+
+	memset(&dir, 0, sizeof(dir));
+	setup_standard_excludes(&dir);
+
+	if (stdin_paths) {
+		num_ignored = check_ignore_stdin_paths(&dir, prefix);
+	} else {
+		num_ignored = check_ignore(&dir, prefix, argc, argv);
+		maybe_flush_or_die(stdout, "ignore to stdout");
+	}
+
+	clear_directory(&dir);
+
+	return !num_ignored;
+}
diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c
new file mode 100644
index 0000000000..cdce144f3b
--- /dev/null
+++ b/builtin/check-mailmap.c
@@ -0,0 +1,67 @@
+#include "builtin.h"
+#include "config.h"
+#include "mailmap.h"
+#include "parse-options.h"
+#include "string-list.h"
+
+static int use_stdin;
+static const char * const check_mailmap_usage[] = {
+N_("git check-mailmap [<options>] <contact>..."),
+NULL
+};
+
+static const struct option check_mailmap_options[] = {
+	OPT_BOOL(0, "stdin", &use_stdin, N_("also read contacts from stdin")),
+	OPT_END()
+};
+
+static void check_mailmap(struct string_list *mailmap, const char *contact)
+{
+	const char *name, *mail;
+	size_t namelen, maillen;
+	struct ident_split ident;
+
+	if (split_ident_line(&ident, contact, strlen(contact)))
+		die(_("unable to parse contact: %s"), contact);
+
+	name = ident.name_begin;
+	namelen = ident.name_end - ident.name_begin;
+	mail = ident.mail_begin;
+	maillen = ident.mail_end - ident.mail_begin;
+
+	map_user(mailmap, &mail, &maillen, &name, &namelen);
+
+	if (namelen)
+		printf("%.*s ", (int)namelen, name);
+	printf("<%.*s>\n", (int)maillen, mail);
+}
+
+int cmd_check_mailmap(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct string_list mailmap = STRING_LIST_INIT_NODUP;
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, check_mailmap_options,
+			     check_mailmap_usage, 0);
+	if (argc == 0 && !use_stdin)
+		die(_("no contacts specified"));
+
+	read_mailmap(&mailmap, NULL);
+
+	for (i = 0; i < argc; ++i)
+		check_mailmap(&mailmap, argv[i]);
+	maybe_flush_or_die(stdout, "stdout");
+
+	if (use_stdin) {
+		struct strbuf buf = STRBUF_INIT;
+		while (strbuf_getline_lf(&buf, stdin) != EOF) {
+			check_mailmap(&mailmap, buf.buf);
+			maybe_flush_or_die(stdout, "stdout");
+		}
+		strbuf_release(&buf);
+	}
+
+	clear_mailmap(&mailmap);
+	return 0;
+}
diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c
new file mode 100644
index 0000000000..bc67d3f0a8
--- /dev/null
+++ b/builtin/check-ref-format.c
@@ -0,0 +1,91 @@
+/*
+ * GIT - The information manager from hell
+ */
+
+#include "cache.h"
+#include "refs.h"
+#include "builtin.h"
+#include "strbuf.h"
+
+static const char builtin_check_ref_format_usage[] =
+"git check-ref-format [--normalize] [<options>] <refname>\n"
+"   or: git check-ref-format --branch <branchname-shorthand>";
+
+/*
+ * Return a copy of refname but with leading slashes removed and runs
+ * of adjacent slashes replaced with single slashes.
+ *
+ * This function is similar to normalize_path_copy(), but stripped down
+ * to meet check_ref_format's simpler needs.
+ */
+static char *collapse_slashes(const char *refname)
+{
+	char *ret = xmallocz(strlen(refname));
+	char ch;
+	char prev = '/';
+	char *cp = ret;
+
+	while ((ch = *refname++) != '\0') {
+		if (prev == '/' && ch == prev)
+			continue;
+
+		*cp++ = ch;
+		prev = ch;
+	}
+	*cp = '\0';
+	return ret;
+}
+
+static int check_ref_format_branch(const char *arg)
+{
+	struct strbuf sb = STRBUF_INIT;
+	const char *name;
+	int nongit;
+
+	setup_git_directory_gently(&nongit);
+	if (strbuf_check_branch_ref(&sb, arg) ||
+	    !skip_prefix(sb.buf, "refs/heads/", &name))
+		die("'%s' is not a valid branch name", arg);
+	printf("%s\n", name);
+	strbuf_release(&sb);
+	return 0;
+}
+
+int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	int normalize = 0;
+	int flags = 0;
+	const char *refname;
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(builtin_check_ref_format_usage);
+
+	if (argc == 3 && !strcmp(argv[1], "--branch"))
+		return check_ref_format_branch(argv[2]);
+
+	for (i = 1; i < argc && argv[i][0] == '-'; i++) {
+		if (!strcmp(argv[i], "--normalize") || !strcmp(argv[i], "--print"))
+			normalize = 1;
+		else if (!strcmp(argv[i], "--allow-onelevel"))
+			flags |= REFNAME_ALLOW_ONELEVEL;
+		else if (!strcmp(argv[i], "--no-allow-onelevel"))
+			flags &= ~REFNAME_ALLOW_ONELEVEL;
+		else if (!strcmp(argv[i], "--refspec-pattern"))
+			flags |= REFNAME_REFSPEC_PATTERN;
+		else
+			usage(builtin_check_ref_format_usage);
+	}
+	if (! (i == argc - 1))
+		usage(builtin_check_ref_format_usage);
+
+	refname = argv[i];
+	if (normalize)
+		refname = collapse_slashes(refname);
+	if (check_refname_format(refname, flags))
+		return 1;
+	if (normalize)
+		printf("%s\n", refname);
+
+	return 0;
+}
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
new file mode 100644
index 0000000000..1ac1cc290e
--- /dev/null
+++ b/builtin/checkout-index.c
@@ -0,0 +1,262 @@
+/*
+ * Check-out files from the "current cache directory"
+ *
+ * Copyright (C) 2005 Linus Torvalds
+ *
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "config.h"
+#include "lockfile.h"
+#include "quote.h"
+#include "cache-tree.h"
+#include "parse-options.h"
+
+#define CHECKOUT_ALL 4
+static int nul_term_line;
+static int checkout_stage; /* default to checkout stage0 */
+static int to_tempfile;
+static char topath[4][TEMPORARY_FILENAME_LENGTH + 1];
+
+static struct checkout state = CHECKOUT_INIT;
+
+static void write_tempfile_record(const char *name, const char *prefix)
+{
+	int i;
+
+	if (CHECKOUT_ALL == checkout_stage) {
+		for (i = 1; i < 4; i++) {
+			if (i > 1)
+				putchar(' ');
+			if (topath[i][0])
+				fputs(topath[i], stdout);
+			else
+				putchar('.');
+		}
+	} else
+		fputs(topath[checkout_stage], stdout);
+
+	putchar('\t');
+	write_name_quoted_relative(name, prefix, stdout,
+				   nul_term_line ? '\0' : '\n');
+
+	for (i = 0; i < 4; i++) {
+		topath[i][0] = 0;
+	}
+}
+
+static int checkout_file(const char *name, const char *prefix)
+{
+	int namelen = strlen(name);
+	int pos = cache_name_pos(name, namelen);
+	int has_same_name = 0;
+	int did_checkout = 0;
+	int errs = 0;
+
+	if (pos < 0)
+		pos = -pos - 1;
+
+	while (pos < active_nr) {
+		struct cache_entry *ce = active_cache[pos];
+		if (ce_namelen(ce) != namelen ||
+		    memcmp(ce->name, name, namelen))
+			break;
+		has_same_name = 1;
+		pos++;
+		if (ce_stage(ce) != checkout_stage
+		    && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
+			continue;
+		did_checkout = 1;
+		if (checkout_entry(ce, &state,
+				   to_tempfile ? topath[ce_stage(ce)] : NULL,
+				   NULL) < 0)
+			errs++;
+	}
+
+	if (did_checkout) {
+		if (to_tempfile)
+			write_tempfile_record(name, prefix);
+		return errs > 0 ? -1 : 0;
+	}
+
+	if (!state.quiet) {
+		fprintf(stderr, "git checkout-index: %s ", name);
+		if (!has_same_name)
+			fprintf(stderr, "is not in the cache");
+		else if (checkout_stage)
+			fprintf(stderr, "does not exist at stage %d",
+				checkout_stage);
+		else
+			fprintf(stderr, "is unmerged");
+		fputc('\n', stderr);
+	}
+	return -1;
+}
+
+static void checkout_all(const char *prefix, int prefix_length)
+{
+	int i, errs = 0;
+	struct cache_entry *last_ce = NULL;
+
+	for (i = 0; i < active_nr ; i++) {
+		struct cache_entry *ce = active_cache[i];
+		if (ce_stage(ce) != checkout_stage
+		    && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
+			continue;
+		if (prefix && *prefix &&
+		    (ce_namelen(ce) <= prefix_length ||
+		     memcmp(prefix, ce->name, prefix_length)))
+			continue;
+		if (last_ce && to_tempfile) {
+			if (ce_namelen(last_ce) != ce_namelen(ce)
+			    || memcmp(last_ce->name, ce->name, ce_namelen(ce)))
+				write_tempfile_record(last_ce->name, prefix);
+		}
+		if (checkout_entry(ce, &state,
+				   to_tempfile ? topath[ce_stage(ce)] : NULL,
+				   NULL) < 0)
+			errs++;
+		last_ce = ce;
+	}
+	if (last_ce && to_tempfile)
+		write_tempfile_record(last_ce->name, prefix);
+	if (errs)
+		/* we have already done our error reporting.
+		 * exit with the same code as die().
+		 */
+		exit(128);
+}
+
+static const char * const builtin_checkout_index_usage[] = {
+	N_("git checkout-index [<options>] [--] [<file>...]"),
+	NULL
+};
+
+static int option_parse_stage(const struct option *opt,
+			      const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "all")) {
+		to_tempfile = 1;
+		checkout_stage = CHECKOUT_ALL;
+	} else {
+		int ch = arg[0];
+		if ('1' <= ch && ch <= '3')
+			checkout_stage = arg[0] - '0';
+		else
+			die(_("stage should be between 1 and 3 or all"));
+	}
+	return 0;
+}
+
+int cmd_checkout_index(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct lock_file lock_file = LOCK_INIT;
+	int all = 0;
+	int read_from_stdin = 0;
+	int prefix_length;
+	int force = 0, quiet = 0, not_new = 0;
+	int index_opt = 0;
+	struct option builtin_checkout_index_options[] = {
+		OPT_BOOL('a', "all", &all,
+			N_("check out all files in the index")),
+		OPT__FORCE(&force, N_("force overwrite of existing files"), 0),
+		OPT__QUIET(&quiet,
+			N_("no warning for existing files and files not in index")),
+		OPT_BOOL('n', "no-create", &not_new,
+			N_("don't checkout new files")),
+		OPT_BOOL('u', "index", &index_opt,
+			 N_("update stat information in the index file")),
+		OPT_BOOL('z', NULL, &nul_term_line,
+			N_("paths are separated with NUL character")),
+		OPT_BOOL(0, "stdin", &read_from_stdin,
+			N_("read list of paths from the standard input")),
+		OPT_BOOL(0, "temp", &to_tempfile,
+			N_("write the content to temporary files")),
+		OPT_STRING(0, "prefix", &state.base_dir, N_("string"),
+			N_("when creating files, prepend <string>")),
+		{ OPTION_CALLBACK, 0, "stage", NULL, "(1|2|3|all)",
+			N_("copy out the files from named stage"),
+			PARSE_OPT_NONEG, option_parse_stage },
+		OPT_END()
+	};
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(builtin_checkout_index_usage,
+				   builtin_checkout_index_options);
+	git_config(git_default_config, NULL);
+	prefix_length = prefix ? strlen(prefix) : 0;
+
+	if (read_cache() < 0) {
+		die("invalid cache");
+	}
+
+	argc = parse_options(argc, argv, prefix, builtin_checkout_index_options,
+			builtin_checkout_index_usage, 0);
+	state.istate = &the_index;
+	state.force = force;
+	state.quiet = quiet;
+	state.not_new = not_new;
+
+	if (!state.base_dir)
+		state.base_dir = "";
+	state.base_dir_len = strlen(state.base_dir);
+
+	/*
+	 * when --prefix is specified we do not want to update cache.
+	 */
+	if (index_opt && !state.base_dir_len && !to_tempfile) {
+		state.refresh_cache = 1;
+		state.istate = &the_index;
+		hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+	}
+
+	/* Check out named files first */
+	for (i = 0; i < argc; i++) {
+		const char *arg = argv[i];
+		char *p;
+
+		if (all)
+			die("git checkout-index: don't mix '--all' and explicit filenames");
+		if (read_from_stdin)
+			die("git checkout-index: don't mix '--stdin' and explicit filenames");
+		p = prefix_path(prefix, prefix_length, arg);
+		checkout_file(p, prefix);
+		free(p);
+	}
+
+	if (read_from_stdin) {
+		struct strbuf buf = STRBUF_INIT;
+		struct strbuf unquoted = STRBUF_INIT;
+		strbuf_getline_fn getline_fn;
+
+		if (all)
+			die("git checkout-index: don't mix '--all' and '--stdin'");
+
+		getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
+		while (getline_fn(&buf, stdin) != EOF) {
+			char *p;
+			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);
+			}
+			p = prefix_path(prefix, prefix_length, buf.buf);
+			checkout_file(p, prefix);
+			free(p);
+		}
+		strbuf_release(&unquoted);
+		strbuf_release(&buf);
+	}
+
+	if (all)
+		checkout_all(prefix, prefix_length);
+
+	if (is_lock_file_locked(&lock_file) &&
+	    write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+		die("Unable to write new index file");
+	return 0;
+}
diff --git a/builtin/checkout.c b/builtin/checkout.c
new file mode 100644
index 0000000000..6123f732a2
--- /dev/null
+++ b/builtin/checkout.c
@@ -0,0 +1,1801 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "advice.h"
+#include "blob.h"
+#include "branch.h"
+#include "cache-tree.h"
+#include "checkout.h"
+#include "commit.h"
+#include "config.h"
+#include "diff.h"
+#include "dir.h"
+#include "ll-merge.h"
+#include "lockfile.h"
+#include "merge-recursive.h"
+#include "object-store.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "remote.h"
+#include "resolve-undo.h"
+#include "revision.h"
+#include "run-command.h"
+#include "submodule.h"
+#include "submodule-config.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "unpack-trees.h"
+#include "wt-status.h"
+#include "xdiff-interface.h"
+
+static const char * const checkout_usage[] = {
+	N_("git checkout [<options>] <branch>"),
+	N_("git checkout [<options>] [<branch>] -- <file>..."),
+	NULL,
+};
+
+static const char * const switch_branch_usage[] = {
+	N_("git switch [<options>] [<branch>]"),
+	NULL,
+};
+
+static const char * const restore_usage[] = {
+	N_("git restore [<options>] [--source=<branch>] <file>..."),
+	NULL,
+};
+
+struct checkout_opts {
+	int patch_mode;
+	int quiet;
+	int merge;
+	int force;
+	int force_detach;
+	int implicit_detach;
+	int writeout_stage;
+	int overwrite_ignore;
+	int ignore_skipworktree;
+	int ignore_other_worktrees;
+	int show_progress;
+	int count_checkout_paths;
+	int overlay_mode;
+	int dwim_new_local_branch;
+	int discard_changes;
+	int accept_ref;
+	int accept_pathspec;
+	int switch_branch_doing_nothing_is_ok;
+	int only_merge_on_switching_branches;
+	int can_switch_when_in_progress;
+	int orphan_from_empty_tree;
+	int empty_pathspec_ok;
+	int checkout_index;
+	int checkout_worktree;
+	const char *ignore_unmerged_opt;
+	int ignore_unmerged;
+
+	const char *new_branch;
+	const char *new_branch_force;
+	const char *new_orphan_branch;
+	int new_branch_log;
+	enum branch_track track;
+	struct diff_options diff_options;
+	char *conflict_style;
+
+	int branch_exists;
+	const char *prefix;
+	struct pathspec pathspec;
+	const char *from_treeish;
+	struct tree *source_tree;
+};
+
+static int post_checkout_hook(struct commit *old_commit, struct commit *new_commit,
+			      int changed)
+{
+	return run_hook_le(NULL, "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);
+	/* "new_commit" can be NULL when checking out from the index before
+	   a commit exists. */
+
+}
+
+static int update_some(const struct object_id *oid, struct strbuf *base,
+		const char *pathname, unsigned mode, int stage, void *context)
+{
+	int len;
+	struct cache_entry *ce;
+	int pos;
+
+	if (S_ISDIR(mode))
+		return READ_TREE_RECURSIVE;
+
+	len = base->len + strlen(pathname);
+	ce = make_empty_cache_entry(&the_index, len);
+	oidcpy(&ce->oid, oid);
+	memcpy(ce->name, base->buf, base->len);
+	memcpy(ce->name + base->len, pathname, len - base->len);
+	ce->ce_flags = create_ce_flags(0) | CE_UPDATE;
+	ce->ce_namelen = len;
+	ce->ce_mode = create_ce_mode(mode);
+
+	/*
+	 * If the entry is the same as the current index, we can leave the old
+	 * entry in place. Whether it is UPTODATE or not, checkout_entry will
+	 * do the right thing.
+	 */
+	pos = cache_name_pos(ce->name, ce->ce_namelen);
+	if (pos >= 0) {
+		struct cache_entry *old = active_cache[pos];
+		if (ce->ce_mode == old->ce_mode &&
+		    oideq(&ce->oid, &old->oid)) {
+			old->ce_flags |= CE_UPDATE;
+			discard_cache_entry(ce);
+			return 0;
+		}
+	}
+
+	add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
+	return 0;
+}
+
+static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
+{
+	read_tree_recursive(the_repository, tree, "", 0, 0,
+			    pathspec, update_some, NULL);
+
+	/* update the index with the given tree's info
+	 * for all args, expanding wildcards, and exit
+	 * with any non-zero return code.
+	 */
+	return 0;
+}
+
+static int skip_same_name(const struct cache_entry *ce, int pos)
+{
+	while (++pos < active_nr &&
+	       !strcmp(active_cache[pos]->name, ce->name))
+		; /* skip */
+	return pos;
+}
+
+static int check_stage(int stage, const struct cache_entry *ce, int pos,
+		       int overlay_mode)
+{
+	while (pos < active_nr &&
+	       !strcmp(active_cache[pos]->name, ce->name)) {
+		if (ce_stage(active_cache[pos]) == stage)
+			return 0;
+		pos++;
+	}
+	if (!overlay_mode)
+		return 0;
+	if (stage == 2)
+		return error(_("path '%s' does not have our version"), ce->name);
+	else
+		return error(_("path '%s' does not have their version"), ce->name);
+}
+
+static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)
+{
+	unsigned seen = 0;
+	const char *name = ce->name;
+
+	while (pos < active_nr) {
+		ce = active_cache[pos];
+		if (strcmp(name, ce->name))
+			break;
+		seen |= (1 << ce_stage(ce));
+		pos++;
+	}
+	if ((stages & seen) != stages)
+		return error(_("path '%s' does not have all necessary versions"),
+			     name);
+	return 0;
+}
+
+static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
+			  const struct checkout *state, int *nr_checkouts,
+			  int overlay_mode)
+{
+	while (pos < active_nr &&
+	       !strcmp(active_cache[pos]->name, ce->name)) {
+		if (ce_stage(active_cache[pos]) == stage)
+			return checkout_entry(active_cache[pos], state,
+					      NULL, nr_checkouts);
+		pos++;
+	}
+	if (!overlay_mode) {
+		unlink_entry(ce);
+		return 0;
+	}
+	if (stage == 2)
+		return error(_("path '%s' does not have our version"), ce->name);
+	else
+		return error(_("path '%s' does not have their version"), ce->name);
+}
+
+static int checkout_merged(int pos, const struct checkout *state, int *nr_checkouts)
+{
+	struct cache_entry *ce = active_cache[pos];
+	const char *path = ce->name;
+	mmfile_t ancestor, ours, theirs;
+	int status;
+	struct object_id oid;
+	mmbuffer_t result_buf;
+	struct object_id threeway[3];
+	unsigned mode = 0;
+
+	memset(threeway, 0, sizeof(threeway));
+	while (pos < active_nr) {
+		int stage;
+		stage = ce_stage(ce);
+		if (!stage || strcmp(path, ce->name))
+			break;
+		oidcpy(&threeway[stage - 1], &ce->oid);
+		if (stage == 2)
+			mode = create_ce_mode(ce->ce_mode);
+		pos++;
+		ce = active_cache[pos];
+	}
+	if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2]))
+		return error(_("path '%s' does not have necessary versions"), path);
+
+	read_mmblob(&ancestor, &threeway[0]);
+	read_mmblob(&ours, &threeway[1]);
+	read_mmblob(&theirs, &threeway[2]);
+
+	/*
+	 * NEEDSWORK: re-create conflicts from merges with
+	 * merge.renormalize set, too
+	 */
+	status = ll_merge(&result_buf, path, &ancestor, "base",
+			  &ours, "ours", &theirs, "theirs",
+			  state->istate, NULL);
+	free(ancestor.ptr);
+	free(ours.ptr);
+	free(theirs.ptr);
+	if (status < 0 || !result_buf.ptr) {
+		free(result_buf.ptr);
+		return error(_("path '%s': cannot merge"), path);
+	}
+
+	/*
+	 * NEEDSWORK:
+	 * There is absolutely no reason to write this as a blob object
+	 * and create a phony cache entry.  This hack is primarily to get
+	 * to the write_entry() machinery that massages the contents to
+	 * work-tree format and writes out which only allows it for a
+	 * cache entry.  The code in write_entry() needs to be refactored
+	 * to allow us to feed a <buffer, size, mode> instead of a cache
+	 * entry.  Such a refactoring would help merge_recursive as well
+	 * (it also writes the merge result to the object database even
+	 * when it may contain conflicts).
+	 */
+	if (write_object_file(result_buf.ptr, result_buf.size, blob_type, &oid))
+		die(_("Unable to add merge result for '%s'"), path);
+	free(result_buf.ptr);
+	ce = make_transient_cache_entry(mode, &oid, path, 2);
+	if (!ce)
+		die(_("make_cache_entry failed for path '%s'"), path);
+	status = checkout_entry(ce, state, NULL, nr_checkouts);
+	discard_cache_entry(ce);
+	return status;
+}
+
+static void mark_ce_for_checkout_overlay(struct cache_entry *ce,
+					 char *ps_matched,
+					 const struct checkout_opts *opts)
+{
+	ce->ce_flags &= ~CE_MATCHED;
+	if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
+		return;
+	if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
+		/*
+		 * "git checkout tree-ish -- path", but this entry
+		 * is in the original index but is not in tree-ish
+		 * or does not match the pathspec; it will not be
+		 * checked out to the working tree.  We will not do
+		 * anything to this entry at all.
+		 */
+		return;
+	/*
+	 * 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(&the_index, ce, &opts->pathspec, ps_matched))
+		ce->ce_flags |= CE_MATCHED;
+}
+
+static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,
+					    char *ps_matched,
+					    const struct checkout_opts *opts)
+{
+	ce->ce_flags &= ~CE_MATCHED;
+	if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
+		return;
+	if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) {
+		ce->ce_flags |= CE_MATCHED;
+		if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
+			/*
+			 * In overlay mode, but the path is not in
+			 * tree-ish, which means we should remove it
+			 * from the index and the working tree.
+			 */
+			ce->ce_flags |= CE_REMOVE | CE_WT_REMOVE;
+	}
+}
+
+static int checkout_worktree(const struct checkout_opts *opts)
+{
+	struct checkout state = CHECKOUT_INIT;
+	int nr_checkouts = 0, nr_unmerged = 0;
+	int errs = 0;
+	int pos;
+
+	state.force = 1;
+	state.refresh_cache = 1;
+	state.istate = &the_index;
+
+	enable_delayed_checkout(&state);
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+		if (ce->ce_flags & CE_MATCHED) {
+			if (!ce_stage(ce)) {
+				errs |= checkout_entry(ce, &state,
+						       NULL, &nr_checkouts);
+				continue;
+			}
+			if (opts->writeout_stage)
+				errs |= checkout_stage(opts->writeout_stage,
+						       ce, pos,
+						       &state,
+						       &nr_checkouts, opts->overlay_mode);
+			else if (opts->merge)
+				errs |= checkout_merged(pos, &state,
+							&nr_unmerged);
+			pos = skip_same_name(ce, pos) - 1;
+		}
+	}
+	remove_marked_cache_entries(&the_index, 1);
+	remove_scheduled_dirs();
+	errs |= finish_delayed_checkout(&state, &nr_checkouts);
+
+	if (opts->count_checkout_paths) {
+		if (nr_unmerged)
+			fprintf_ln(stderr, Q_("Recreated %d merge conflict",
+					      "Recreated %d merge conflicts",
+					      nr_unmerged),
+				   nr_unmerged);
+		if (opts->source_tree)
+			fprintf_ln(stderr, Q_("Updated %d path from %s",
+					      "Updated %d paths from %s",
+					      nr_checkouts),
+				   nr_checkouts,
+				   find_unique_abbrev(&opts->source_tree->object.oid,
+						      DEFAULT_ABBREV));
+		else if (!nr_unmerged || nr_checkouts)
+			fprintf_ln(stderr, Q_("Updated %d path from the index",
+					      "Updated %d paths from the index",
+					      nr_checkouts),
+				   nr_checkouts);
+	}
+
+	return errs;
+}
+
+static int checkout_paths(const struct checkout_opts *opts,
+			  const char *revision)
+{
+	int pos;
+	static char *ps_matched;
+	struct object_id rev;
+	struct commit *head;
+	int errs = 0;
+	struct lock_file lock_file = LOCK_INIT;
+	int checkout_index;
+
+	trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
+
+	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->ignore_unmerged && opts->patch_mode)
+		die(_("'%s' cannot be used with updating paths"),
+		    opts->ignore_unmerged_opt);
+
+	if (opts->force_detach)
+		die(_("'%s' cannot be used with updating paths"), "--detach");
+
+	if (opts->merge && opts->patch_mode)
+		die(_("'%s' cannot be used with %s"), "--merge", "--patch");
+
+	if (opts->ignore_unmerged && opts->merge)
+		die(_("'%s' cannot be used with %s"),
+		    opts->ignore_unmerged_opt, "-m");
+
+	if (opts->new_branch)
+		die(_("Cannot update paths and switch to branch '%s' at the same time."),
+		    opts->new_branch);
+
+	if (!opts->checkout_worktree && !opts->checkout_index)
+		die(_("neither '%s' or '%s' is specified"),
+		    "--staged", "--worktree");
+
+	if (!opts->checkout_worktree && !opts->from_treeish)
+		die(_("'%s' must be used when '%s' is not specified"),
+		    "--worktree", "--source");
+
+	if (opts->checkout_index && !opts->checkout_worktree &&
+	    opts->writeout_stage)
+		die(_("'%s' or '%s' cannot be used with %s"),
+		    "--ours", "--theirs", "--staged");
+
+	if (opts->checkout_index && !opts->checkout_worktree &&
+	    opts->merge)
+		die(_("'%s' or '%s' cannot be used with %s"),
+		    "--merge", "--conflict", "--staged");
+
+	if (opts->patch_mode) {
+		const char *patch_mode;
+
+		if (opts->checkout_index && opts->checkout_worktree)
+			patch_mode = "--patch=checkout";
+		else if (opts->checkout_index && !opts->checkout_worktree)
+			patch_mode = "--patch=reset";
+		else if (!opts->checkout_index && opts->checkout_worktree)
+			patch_mode = "--patch=worktree";
+		else
+			BUG("either flag must have been set, worktree=%d, index=%d",
+			    opts->checkout_worktree, opts->checkout_index);
+		return run_add_interactive(revision, patch_mode, &opts->pathspec);
+	}
+
+	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
+	if (read_cache_preload(&opts->pathspec) < 0)
+		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++)
+		if (opts->overlay_mode)
+			mark_ce_for_checkout_overlay(active_cache[pos],
+						     ps_matched,
+						     opts);
+		else
+			mark_ce_for_checkout_no_overlay(active_cache[pos],
+							ps_matched,
+							opts);
+
+	if (report_path_error(ps_matched, &opts->pathspec)) {
+		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->ignore_unmerged) {
+				if (!opts->quiet)
+					warning(_("path '%s' is unmerged"), ce->name);
+			} else if (opts->writeout_stage) {
+				errs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode);
+			} else if (opts->merge) {
+				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 */
+	if (opts->checkout_worktree)
+		errs |= checkout_worktree(opts);
+
+	/*
+	 * Allow updating the index when checking out from the index.
+	 * This is to save new stat info.
+	 */
+	if (opts->checkout_worktree && !opts->checkout_index && !opts->source_tree)
+		checkout_index = 1;
+	else
+		checkout_index = opts->checkout_index;
+
+	if (checkout_index) {
+		if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+			die(_("unable to write new index file"));
+	} else {
+		/*
+		 * NEEDSWORK: if --worktree is not specified, we
+		 * should save stat info of checked out files in the
+		 * index to avoid the next (potentially costly)
+		 * refresh. But it's a bit tricker to do...
+		 */
+		rollback_lock_file(&lock_file);
+	}
+
+	read_ref_full("HEAD", 0, &rev, NULL);
+	head = lookup_commit_reference_gently(the_repository, &rev, 1);
+
+	errs |= post_checkout_hook(head, head, 0);
+	return errs;
+}
+
+static void show_local_changes(struct object *head,
+			       const struct diff_options *opts)
+{
+	struct rev_info rev;
+	/* I think we want full paths, even if we're in a subdirectory. */
+	repo_init_revisions(the_repository, &rev, NULL);
+	rev.diffopt.flags = opts->flags;
+	rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
+	diff_setup_done(&rev.diffopt);
+	add_pending_object(&rev, head, NULL);
+	run_diff_index(&rev, 0);
+}
+
+static void describe_detached_head(const char *msg, struct commit *commit)
+{
+	struct strbuf sb = STRBUF_INIT;
+
+	if (!parse_commit(commit))
+		pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
+	if (print_sha1_ellipsis()) {
+		fprintf(stderr, "%s %s... %s\n", msg,
+			find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf);
+	} else {
+		fprintf(stderr, "%s %s %s\n", msg,
+			find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf);
+	}
+	strbuf_release(&sb);
+}
+
+static int reset_tree(struct tree *tree, const struct checkout_opts *o,
+		      int worktree, int *writeout_error)
+{
+	struct unpack_trees_options opts;
+	struct tree_desc tree_desc;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = -1;
+	opts.update = worktree;
+	opts.skip_unmerged = !worktree;
+	opts.reset = 1;
+	opts.merge = 1;
+	opts.fn = oneway_merge;
+	opts.verbose_update = o->show_progress;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	parse_tree(tree);
+	init_tree_desc(&tree_desc, tree->buffer, tree->size);
+	switch (unpack_trees(1, &tree_desc, &opts)) {
+	case -2:
+		*writeout_error = 1;
+		/*
+		 * We return 0 nevertheless, as the index is all right
+		 * and more importantly we have made best efforts to
+		 * update paths in the work tree, and we cannot revert
+		 * them.
+		 */
+		/* fallthrough */
+	case 0:
+		return 0;
+	default:
+		return 128;
+	}
+}
+
+struct branch_info {
+	const char *name; /* The short name used */
+	const char *path; /* The full name of a real branch */
+	struct commit *commit; /* The named commit */
+	/*
+	 * if not null the branch is detached because it's already
+	 * checked out in this checkout
+	 */
+	char *checkout;
+};
+
+static void setup_branch_path(struct branch_info *branch)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
+	if (strcmp(buf.buf, branch->name))
+		branch->name = xstrdup(buf.buf);
+	strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
+	branch->path = strbuf_detach(&buf, NULL);
+}
+
+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;
+	struct tree *new_tree;
+
+	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+	if (read_cache_preload(NULL) < 0)
+		return error(_("index file corrupt"));
+
+	resolve_undo_clear();
+	if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
+		if (new_branch_info->commit)
+			BUG("'switch --orphan' should never accept a commit as starting point");
+		new_tree = parse_tree_indirect(the_hash_algo->empty_tree);
+	} else
+		new_tree = get_commit_tree(new_branch_info->commit);
+	if (opts->discard_changes) {
+		ret = reset_tree(new_tree, opts, 1, writeout_error);
+		if (ret)
+			return ret;
+	} else {
+		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.quiet = 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);
+		parse_tree(new_tree);
+		tree = new_tree;
+		init_tree_desc(&trees[1], tree->buffer, tree->size);
+
+		ret = unpack_trees(2, trees, &topts);
+		clear_unpack_trees_porcelain(&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 tree *old_tree;
+			struct merge_options o;
+			struct strbuf sb = STRBUF_INIT;
+
+			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;
+			old_tree = get_commit_tree(old_branch_info->commit);
+
+			if (repo_index_has_changes(the_repository, old_tree, &sb))
+				die(_("cannot continue with staged changes in "
+				      "the following files:\n%s"), sb.buf);
+			strbuf_release(&sb);
+
+			if (repo_index_has_changes(the_repository,
+						   get_commit_tree(old_branch_info->commit),
+						   &sb))
+				warning(_("staged changes in the following files may be lost: %s"),
+					sb.buf);
+			strbuf_release(&sb);
+
+			/* 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, the_repository);
+			o.verbosity = 0;
+			work = write_tree_from_memory(&o);
+
+			ret = reset_tree(new_tree,
+					 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,
+					  new_tree,
+					  work,
+					  old_tree,
+					  &result);
+			if (ret < 0)
+				exit(128);
+			ret = reset_tree(new_tree,
+					 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->discard_changes && !opts->quiet && new_branch_info->commit)
+		show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
+
+	return 0;
+}
+
+static void report_tracking(struct branch_info *new_branch_info)
+{
+	struct strbuf sb = STRBUF_INIT;
+	struct branch *branch = branch_get(new_branch_info->name);
+
+	if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL))
+		return;
+	fputs(sb.buf, stdout);
+	strbuf_release(&sb);
+}
+
+static void update_refs_for_switch(const struct checkout_opts *opts,
+				   struct branch_info *old_branch_info,
+				   struct branch_info *new_branch_info)
+{
+	struct strbuf msg = STRBUF_INIT;
+	const char *old_desc, *reflog_msg;
+	if (opts->new_branch) {
+		if (opts->new_orphan_branch) {
+			char *refname;
+
+			refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch);
+			if (opts->new_branch_log &&
+			    !should_autocreate_reflog(refname)) {
+				int ret;
+				struct strbuf err = STRBUF_INIT;
+
+				ret = safe_create_reflog(refname, 1, &err);
+				if (ret) {
+					fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
+						opts->new_orphan_branch, err.buf);
+					strbuf_release(&err);
+					free(refname);
+					return;
+				}
+				strbuf_release(&err);
+			}
+			free(refname);
+		}
+		else
+			create_branch(the_repository,
+				      opts->new_branch, new_branch_info->name,
+				      opts->new_branch_force ? 1 : 0,
+				      opts->new_branch_force ? 1 : 0,
+				      opts->new_branch_log,
+				      opts->quiet,
+				      opts->track);
+		new_branch_info->name = opts->new_branch;
+		setup_branch_path(new_branch_info);
+	}
+
+	old_desc = old_branch_info->name;
+	if (!old_desc && old_branch_info->commit)
+		old_desc = oid_to_hex(&old_branch_info->commit->object.oid);
+
+	reflog_msg = getenv("GIT_REFLOG_ACTION");
+	if (!reflog_msg)
+		strbuf_addf(&msg, "checkout: moving from %s to %s",
+			old_desc ? old_desc : "(invalid)", new_branch_info->name);
+	else
+		strbuf_insert(&msg, 0, reflog_msg, strlen(reflog_msg));
+
+	if (!strcmp(new_branch_info->name, "HEAD") && !new_branch_info->path && !opts->force_detach) {
+		/* Nothing to do. */
+	} else if (opts->force_detach || !new_branch_info->path) {	/* No longer on any branch. */
+		update_ref(msg.buf, "HEAD", &new_branch_info->commit->object.oid, NULL,
+			   REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
+		if (!opts->quiet) {
+			if (old_branch_info->path &&
+			    advice_detached_head && !opts->force_detach)
+				detach_advice(new_branch_info->name);
+			describe_detached_head(_("HEAD is now at"), new_branch_info->commit);
+		}
+	} else if (new_branch_info->path) {	/* Switch branches. */
+		if (create_symref("HEAD", new_branch_info->path, msg.buf) < 0)
+			die(_("unable to update HEAD"));
+		if (!opts->quiet) {
+			if (old_branch_info->path && !strcmp(new_branch_info->path, old_branch_info->path)) {
+				if (opts->new_branch_force)
+					fprintf(stderr, _("Reset branch '%s'\n"),
+						new_branch_info->name);
+				else
+					fprintf(stderr, _("Already on '%s'\n"),
+						new_branch_info->name);
+			} else if (opts->new_branch) {
+				if (opts->branch_exists)
+					fprintf(stderr, _("Switched to and reset branch '%s'\n"), new_branch_info->name);
+				else
+					fprintf(stderr, _("Switched to a new branch '%s'\n"), new_branch_info->name);
+			} else {
+				fprintf(stderr, _("Switched to branch '%s'\n"),
+					new_branch_info->name);
+			}
+		}
+		if (old_branch_info->path && old_branch_info->name) {
+			if (!ref_exists(old_branch_info->path) && reflog_exists(old_branch_info->path))
+				delete_reflog(old_branch_info->path);
+		}
+	}
+	remove_branch_state(the_repository, !opts->quiet);
+	strbuf_release(&msg);
+	if (!opts->quiet &&
+	    (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD"))))
+		report_tracking(new_branch_info);
+}
+
+static int add_pending_uninteresting_ref(const char *refname,
+					 const struct object_id *oid,
+					 int flags, void *cb_data)
+{
+	add_pending_oid(cb_data, refname, oid, UNINTERESTING);
+	return 0;
+}
+
+static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
+{
+	strbuf_addstr(sb, "  ");
+	strbuf_add_unique_abbrev(sb, &commit->object.oid, DEFAULT_ABBREV);
+	strbuf_addch(sb, ' ');
+	if (!parse_commit(commit))
+		pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
+	strbuf_addch(sb, '\n');
+}
+
+#define ORPHAN_CUTOFF 4
+static void suggest_reattach(struct commit *commit, struct rev_info *revs)
+{
+	struct commit *c, *last = NULL;
+	struct strbuf sb = STRBUF_INIT;
+	int lost = 0;
+	while ((c = get_revision(revs)) != NULL) {
+		if (lost < ORPHAN_CUTOFF)
+			describe_one_orphan(&sb, c);
+		last = c;
+		lost++;
+	}
+	if (ORPHAN_CUTOFF < lost) {
+		int more = lost - ORPHAN_CUTOFF;
+		if (more == 1)
+			describe_one_orphan(&sb, last);
+		else
+			strbuf_addf(&sb, _(" ... and %d more.\n"), more);
+	}
+
+	fprintf(stderr,
+		Q_(
+		/* The singular version */
+		"Warning: you are leaving %d commit behind, "
+		"not connected to\n"
+		"any of your branches:\n\n"
+		"%s\n",
+		/* The plural version */
+		"Warning: you are leaving %d commits behind, "
+		"not connected to\n"
+		"any of your branches:\n\n"
+		"%s\n",
+		/* Give ngettext() the count */
+		lost),
+		lost,
+		sb.buf);
+	strbuf_release(&sb);
+
+	if (advice_detached_head)
+		fprintf(stderr,
+			Q_(
+			/* The singular version */
+			"If you want to keep it by creating a new branch, "
+			"this may be a good time\nto do so with:\n\n"
+			" git branch <new-branch-name> %s\n\n",
+			/* The plural version */
+			"If you want to keep them by creating a new branch, "
+			"this may be a good time\nto do so with:\n\n"
+			" git branch <new-branch-name> %s\n\n",
+			/* Give ngettext() the count */
+			lost),
+			find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+}
+
+/*
+ * We are about to leave commit that was at the tip of a detached
+ * HEAD.  If it is not reachable from any ref, this is the last chance
+ * for the user to do so without resorting to reflog.
+ */
+static void orphaned_commit_warning(struct commit *old_commit, struct commit *new_commit)
+{
+	struct rev_info revs;
+	struct object *object = &old_commit->object;
+
+	repo_init_revisions(the_repository, &revs, NULL);
+	setup_revisions(0, NULL, &revs, NULL);
+
+	object->flags &= ~UNINTERESTING;
+	add_pending_object(&revs, object, oid_to_hex(&object->oid));
+
+	for_each_ref(add_pending_uninteresting_ref, &revs);
+	if (new_commit)
+		add_pending_oid(&revs, "HEAD",
+				&new_commit->object.oid,
+				UNINTERESTING);
+
+	if (prepare_revision_walk(&revs))
+		die(_("internal error in revision walk"));
+	if (!(old_commit->object.flags & UNINTERESTING))
+		suggest_reattach(old_commit, &revs);
+	else
+		describe_detached_head(_("Previous HEAD position was"), old_commit);
+
+	/* Clean up objects used, as they will be reused. */
+	clear_commit_marks_all(ALL_REV_FLAGS);
+}
+
+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 object_id rev;
+	int flag, writeout_error = 0;
+	int do_merge = 1;
+
+	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);
+	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;
+
+	if (old_branch_info.path)
+		skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name);
+
+	if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
+		if (new_branch_info->name)
+			BUG("'switch --orphan' should never accept a commit as starting point");
+		new_branch_info->commit = NULL;
+		new_branch_info->name = "(empty)";
+		do_merge = 1;
+	}
+
+	if (!new_branch_info->name) {
+		new_branch_info->name = "HEAD";
+		new_branch_info->commit = old_branch_info.commit;
+		if (!new_branch_info->commit)
+			die(_("You are on a branch yet to be born"));
+		parse_commit_or_die(new_branch_info->commit);
+
+		if (opts->only_merge_on_switching_branches)
+			do_merge = 0;
+	}
+
+	if (do_merge) {
+		ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
+		if (ret) {
+			free(path_to_free);
+			return ret;
+		}
+	}
+
+	if (!opts->quiet && !old_branch_info.path && old_branch_info.commit && new_branch_info->commit != old_branch_info.commit)
+		orphaned_commit_warning(old_branch_info.commit, new_branch_info->commit);
+
+	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);
+	return ret || writeout_error;
+}
+
+static int git_checkout_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "diff.ignoresubmodules")) {
+		struct checkout_opts *opts = cb;
+		handle_ignore_submodules_arg(&opts->diff_options, value);
+		return 0;
+	}
+
+	if (starts_with(var, "submodule."))
+		return git_default_submodule_config(var, value, NULL);
+
+	return git_xmerge_config(var, value, NULL);
+}
+
+static void setup_new_branch_info_and_source_tree(
+	struct branch_info *new_branch_info,
+	struct checkout_opts *opts,
+	struct object_id *rev,
+	const char *arg)
+{
+	struct tree **source_tree = &opts->source_tree;
+	struct object_id branch_rev;
+
+	new_branch_info->name = arg;
+	setup_branch_path(new_branch_info);
+
+	if (!check_refname_format(new_branch_info->path, 0) &&
+	    !read_ref(new_branch_info->path, &branch_rev))
+		oidcpy(rev, &branch_rev);
+	else
+		new_branch_info->path = NULL; /* not an existing branch */
+
+	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
+	if (!new_branch_info->commit) {
+		/* not a commit */
+		*source_tree = parse_tree_indirect(rev);
+	} else {
+		parse_commit_or_die(new_branch_info->commit);
+		*source_tree = get_commit_tree(new_branch_info->commit);
+	}
+}
+
+static int parse_branchname_arg(int argc, const char **argv,
+				int dwim_new_local_branch_ok,
+				struct branch_info *new_branch_info,
+				struct checkout_opts *opts,
+				struct object_id *rev,
+				int *dwim_remotes_matched)
+{
+	const char **new_branch = &opts->new_branch;
+	int argcount = 0;
+	const char *arg;
+	int dash_dash_pos;
+	int has_dash_dash = 0;
+	int i;
+
+	/*
+	 * case 1: git checkout <ref> -- [<paths>]
+	 *
+	 *   <ref> must be a valid tree, everything after the '--' must be
+	 *   a path.
+	 *
+	 * case 2: git checkout -- [<paths>]
+	 *
+	 *   everything after the '--' must be paths.
+	 *
+	 * case 3: git checkout <something> [--]
+	 *
+	 *   (a) If <something> is a commit, that is to
+	 *       switch to the branch or detach HEAD at it.  As a special case,
+	 *       if <something> is A...B (missing A or B means HEAD but you can
+	 *       omit at most one side), and if there is a unique merge base
+	 *       between A and B, A...B names that merge base.
+	 *
+	 *   (b) If <something> is _not_ a commit, either "--" is present
+	 *       or <something> is not a path, no -t or -b was given, and
+	 *       and there is a tracking branch whose name is <something>
+	 *       in one and only one remote (or if the branch exists on the
+	 *       remote named in checkout.defaultRemote), then this is a
+	 *       short-hand to fork local <something> from that
+	 *       remote-tracking branch.
+	 *
+	 *   (c) Otherwise, if "--" is present, treat it like case (1).
+	 *
+	 *   (d) Otherwise :
+	 *       - if it's a reference, treat it like case (1)
+	 *       - else if it's a path, treat it like case (2)
+	 *       - else: fail.
+	 *
+	 * case 4: git checkout <something> <paths>
+	 *
+	 *   The first argument must not be ambiguous.
+	 *   - If it's *only* a reference, treat it like case (1).
+	 *   - If it's only a path, treat it like case (2).
+	 *   - else: fail.
+	 *
+	 */
+	if (!argc)
+		return 0;
+
+	if (!opts->accept_pathspec) {
+		if (argc > 1)
+			die(_("only one reference expected"));
+		has_dash_dash = 1; /* helps disambiguate */
+	}
+
+	arg = argv[0];
+	dash_dash_pos = -1;
+	for (i = 0; i < argc; i++) {
+		if (opts->accept_pathspec && !strcmp(argv[i], "--")) {
+			dash_dash_pos = i;
+			break;
+		}
+	}
+	if (dash_dash_pos == 0)
+		return 1; /* case (2) */
+	else if (dash_dash_pos == 1)
+		has_dash_dash = 1; /* case (3) or (1) */
+	else if (dash_dash_pos >= 2)
+		die(_("only one reference expected, %d given."), dash_dash_pos);
+	opts->count_checkout_paths = !opts->quiet && !has_dash_dash;
+
+	if (!strcmp(arg, "-"))
+		arg = "@{-1}";
+
+	if (get_oid_mb(arg, rev)) {
+		/*
+		 * Either case (3) or (4), with <something> not being
+		 * a commit, or an attempt to use case (1) with an
+		 * invalid ref.
+		 *
+		 * It's likely an error, but we need to find out if
+		 * we should auto-create the branch, case (3).(b).
+		 */
+		int recover_with_dwim = dwim_new_local_branch_ok;
+
+		int could_be_checkout_paths = !has_dash_dash &&
+			check_filename(opts->prefix, arg);
+
+		if (!has_dash_dash && !no_wildcard(arg))
+			recover_with_dwim = 0;
+
+		/*
+		 * Accept "git checkout foo", "git checkout foo --"
+		 * and "git switch foo" as candidates for dwim.
+		 */
+		if (!(argc == 1 && !has_dash_dash) &&
+		    !(argc == 2 && has_dash_dash) &&
+		    opts->accept_pathspec)
+			recover_with_dwim = 0;
+
+		if (recover_with_dwim) {
+			const char *remote = unique_tracking_name(arg, rev,
+								  dwim_remotes_matched);
+			if (remote) {
+				if (could_be_checkout_paths)
+					die(_("'%s' could be both a local file and a tracking branch.\n"
+					      "Please use -- (and optionally --no-guess) to disambiguate"),
+					    arg);
+				*new_branch = arg;
+				arg = remote;
+				/* DWIMmed to create local branch, case (3).(b) */
+			} else {
+				recover_with_dwim = 0;
+			}
+		}
+
+		if (!recover_with_dwim) {
+			if (has_dash_dash)
+				die(_("invalid reference: %s"), arg);
+			return argcount;
+		}
+	}
+
+	/* we can't end up being in (2) anymore, eat the argument */
+	argcount++;
+	argv++;
+	argc--;
+
+	setup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);
+
+	if (!opts->source_tree)                   /* case (1): want a tree */
+		die(_("reference is not a tree: %s"), arg);
+
+	if (!has_dash_dash) {	/* case (3).(d) -> (1) */
+		/*
+		 * Do not complain the most common case
+		 *	git checkout branch
+		 * even if there happen to be a file called 'branch';
+		 * it would be extremely annoying.
+		 */
+		if (argc)
+			verify_non_filename(opts->prefix, arg);
+	} else if (opts->accept_pathspec) {
+		argcount++;
+		argv++;
+		argc--;
+	}
+
+	return argcount;
+}
+
+static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
+{
+	int status;
+	struct strbuf branch_ref = STRBUF_INIT;
+
+	trace2_cmd_mode("unborn");
+
+	if (!opts->new_branch)
+		die(_("You are on a branch yet to be born"));
+	strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch);
+	status = create_symref("HEAD", branch_ref.buf, "checkout -b");
+	strbuf_release(&branch_ref);
+	if (!opts->quiet)
+		fprintf(stderr, _("Switched to a new branch '%s'\n"),
+			opts->new_branch);
+	return status;
+}
+
+static void die_expecting_a_branch(const struct branch_info *branch_info)
+{
+	struct object_id oid;
+	char *to_free;
+
+	if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free) == 1) {
+		const char *ref = to_free;
+
+		if (skip_prefix(ref, "refs/tags/", &ref))
+			die(_("a branch is expected, got tag '%s'"), ref);
+		if (skip_prefix(ref, "refs/remotes/", &ref))
+			die(_("a branch is expected, got remote branch '%s'"), ref);
+		die(_("a branch is expected, got '%s'"), ref);
+	}
+	if (branch_info->commit)
+		die(_("a branch is expected, got commit '%s'"), branch_info->name);
+	/*
+	 * This case should never happen because we already die() on
+	 * non-commit, but just in case.
+	 */
+	die(_("a branch is expected, got '%s'"), branch_info->name);
+}
+
+static void die_if_some_operation_in_progress(void)
+{
+	struct wt_status_state state;
+
+	memset(&state, 0, sizeof(state));
+	wt_status_get_state(the_repository, &state, 0);
+
+	if (state.merge_in_progress)
+		die(_("cannot switch branch while merging\n"
+		      "Consider \"git merge --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.am_in_progress)
+		die(_("cannot switch branch in the middle of an am session\n"
+		      "Consider \"git am --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.rebase_interactive_in_progress || state.rebase_in_progress)
+		die(_("cannot switch branch while rebasing\n"
+		      "Consider \"git rebase --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.cherry_pick_in_progress)
+		die(_("cannot switch branch while cherry-picking\n"
+		      "Consider \"git cherry-pick --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.revert_in_progress)
+		die(_("cannot switch branch while reverting\n"
+		      "Consider \"git revert --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.bisect_in_progress)
+		warning(_("you are switching branch while bisecting"));
+}
+
+static int checkout_branch(struct checkout_opts *opts,
+			   struct branch_info *new_branch_info)
+{
+	if (opts->pathspec.nr)
+		die(_("paths cannot be used with switching branches"));
+
+	if (opts->patch_mode)
+		die(_("'%s' cannot be used with switching branches"),
+		    "--patch");
+
+	if (opts->overlay_mode != -1)
+		die(_("'%s' cannot be used with switching branches"),
+		    "--[no]-overlay");
+
+	if (opts->writeout_stage)
+		die(_("'%s' cannot be used with switching branches"),
+		    "--ours/--theirs");
+
+	if (opts->force && opts->merge)
+		die(_("'%s' cannot be used with '%s'"), "-f", "-m");
+
+	if (opts->discard_changes && opts->merge)
+		die(_("'%s' cannot be used with '%s'"), "--discard-changes", "--merge");
+
+	if (opts->force_detach && opts->new_branch)
+		die(_("'%s' cannot be used with '%s'"),
+		    "--detach", "-b/-B/--orphan");
+
+	if (opts->new_orphan_branch) {
+		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
+			die(_("'%s' cannot be used with '%s'"), "--orphan", "-t");
+		if (opts->orphan_from_empty_tree && new_branch_info->name)
+			die(_("'%s' cannot take <start-point>"), "--orphan");
+	} else if (opts->force_detach) {
+		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
+			die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
+	} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)
+		opts->track = git_branch_track;
+
+	if (new_branch_info->name && !new_branch_info->commit)
+		die(_("Cannot switch branch to a non-commit '%s'"),
+		    new_branch_info->name);
+
+	if (!opts->switch_branch_doing_nothing_is_ok &&
+	    !new_branch_info->name &&
+	    !opts->new_branch &&
+	    !opts->force_detach)
+		die(_("missing branch or commit argument"));
+
+	if (!opts->implicit_detach &&
+	    !opts->force_detach &&
+	    !opts->new_branch &&
+	    !opts->new_branch_force &&
+	    new_branch_info->name &&
+	    !new_branch_info->path)
+		die_expecting_a_branch(new_branch_info);
+
+	if (!opts->can_switch_when_in_progress)
+		die_if_some_operation_in_progress();
+
+	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
+	    !opts->ignore_other_worktrees) {
+		int flag;
+		char *head_ref = resolve_refdup("HEAD", 0, NULL, &flag);
+		if (head_ref &&
+		    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new_branch_info->path)))
+			die_if_checked_out(new_branch_info->path, 1);
+		free(head_ref);
+	}
+
+	if (!new_branch_info->commit && opts->new_branch) {
+		struct object_id rev;
+		int flag;
+
+		if (!read_ref_full("HEAD", 0, &rev, &flag) &&
+		    (flag & REF_ISSYMREF) && is_null_oid(&rev))
+			return switch_unborn_to_new_branch(opts);
+	}
+	return switch_branches(opts, new_branch_info);
+}
+
+static struct option *add_common_options(struct checkout_opts *opts,
+					 struct option *prevopts)
+{
+	struct option options[] = {
+		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+			    "checkout", "control recursive updating of submodules",
+			    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
+		OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
+		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
+			   N_("conflict style (merge or diff3)")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_common_switch_branch_options(
+	struct checkout_opts *opts, struct option *prevopts)
+{
+	struct option options[] = {
+		OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
+			BRANCH_TRACK_EXPLICIT),
+		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
+			   N_("update ignored files (default)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
+			 N_("do not check if another worktree is holding the given ref")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_checkout_path_options(struct checkout_opts *opts,
+						struct option *prevopts)
+{
+	struct option options[] = {
+		OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
+			      N_("checkout our version for unmerged files"),
+			      2, PARSE_OPT_NONEG),
+		OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
+			      N_("checkout their version for unmerged files"),
+			      3, PARSE_OPT_NONEG),
+		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_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static int checkout_main(int argc, const char **argv, const char *prefix,
+			 struct checkout_opts *opts, struct option *options,
+			 const char * const usagestr[])
+{
+	struct branch_info new_branch_info;
+	int dwim_remotes_matched = 0;
+	int parseopt_flags = 0;
+
+	memset(&new_branch_info, 0, sizeof(new_branch_info));
+	opts->overwrite_ignore = 1;
+	opts->prefix = prefix;
+	opts->show_progress = -1;
+
+	git_config(git_checkout_config, opts);
+
+	opts->track = BRANCH_TRACK_UNSPECIFIED;
+
+	if (!opts->accept_pathspec && !opts->accept_ref)
+		BUG("make up your mind, you need to take _something_");
+	if (opts->accept_pathspec && opts->accept_ref)
+		parseopt_flags = PARSE_OPT_KEEP_DASHDASH;
+
+	argc = parse_options(argc, argv, prefix, options,
+			     usagestr, parseopt_flags);
+
+	if (opts->show_progress < 0) {
+		if (opts->quiet)
+			opts->show_progress = 0;
+		else
+			opts->show_progress = isatty(2);
+	}
+
+	if (opts->conflict_style) {
+		opts->merge = 1; /* implied */
+		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
+	}
+	if (opts->force) {
+		opts->discard_changes = 1;
+		opts->ignore_unmerged_opt = "--force";
+		opts->ignore_unmerged = 1;
+	}
+
+	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
+		die(_("-b, -B and --orphan are mutually exclusive"));
+
+	if (opts->overlay_mode == 1 && opts->patch_mode)
+		die(_("-p and --overlay are mutually exclusive"));
+
+	if (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {
+		if (opts->checkout_index < 0)
+			opts->checkout_index = 0;
+		if (opts->checkout_worktree < 0)
+			opts->checkout_worktree = 0;
+	} else {
+		if (opts->checkout_index < 0)
+			opts->checkout_index = -opts->checkout_index - 1;
+		if (opts->checkout_worktree < 0)
+			opts->checkout_worktree = -opts->checkout_worktree - 1;
+	}
+	if (opts->checkout_index < 0 || opts->checkout_worktree < 0)
+		BUG("these flags should be non-negative by now");
+	/*
+	 * convenient shortcut: "git restore --staged" equals
+	 * "git restore --staged --source HEAD"
+	 */
+	if (!opts->from_treeish && opts->checkout_index && !opts->checkout_worktree)
+		opts->from_treeish = "HEAD";
+
+	/*
+	 * From here on, new_branch will contain the branch to be checked out,
+	 * and new_branch_force and new_orphan_branch will tell us which one of
+	 * -b/-B/--orphan is being used.
+	 */
+	if (opts->new_branch_force)
+		opts->new_branch = opts->new_branch_force;
+
+	if (opts->new_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 && opts->accept_ref) {
+		struct object_id rev;
+		int dwim_ok =
+			!opts->patch_mode &&
+			opts->dwim_new_local_branch &&
+			opts->track == BRANCH_TRACK_UNSPECIFIED &&
+			!opts->new_branch;
+		int n = parse_branchname_arg(argc, argv, dwim_ok,
+					     &new_branch_info, opts, &rev,
+					     &dwim_remotes_matched);
+		argv += n;
+		argc -= n;
+	} else if (!opts->accept_ref && opts->from_treeish) {
+		struct object_id rev;
+
+		if (get_oid_mb(opts->from_treeish, &rev))
+			die(_("could not resolve %s"), opts->from_treeish);
+
+		setup_new_branch_info_and_source_tree(&new_branch_info,
+						      opts, &rev,
+						      opts->from_treeish);
+
+		if (!opts->source_tree)
+			die(_("reference is not a tree: %s"), opts->from_treeish);
+	}
+
+	if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&
+	    !opts->patch_mode)	/* patch mode is special */
+		die(_("you must specify path(s) to restore"));
+
+	if (argc) {
+		parse_pathspec(&opts->pathspec, 0,
+			       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
+			       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);
+	}
+}
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options;
+	struct option checkout_options[] = {
+		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+			   N_("create and checkout a new branch")),
+		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and checkout a branch")),
+		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
+		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git checkout <no-such-branch>' (default)")),
+		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
+		OPT_END()
+	};
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.dwim_new_local_branch = 1;
+	opts.switch_branch_doing_nothing_is_ok = 1;
+	opts.only_merge_on_switching_branches = 0;
+	opts.accept_ref = 1;
+	opts.accept_pathspec = 1;
+	opts.implicit_detach = 1;
+	opts.can_switch_when_in_progress = 1;
+	opts.orphan_from_empty_tree = 0;
+	opts.empty_pathspec_ok = 1;
+	opts.overlay_mode = -1;
+	opts.checkout_index = -2;    /* default on */
+	opts.checkout_worktree = -2; /* default on */
+
+	options = parse_options_dup(checkout_options);
+	options = add_common_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, checkout_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
+
+int cmd_switch(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	struct option switch_options[] = {
+		OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
+			   N_("create and switch to a new branch")),
+		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and switch to a branch")),
+		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git switch <no-such-branch>'")),
+		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
+			 N_("throw away local modifications")),
+		OPT_END()
+	};
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.dwim_new_local_branch = 1;
+	opts.accept_ref = 1;
+	opts.accept_pathspec = 0;
+	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.only_merge_on_switching_branches = 1;
+	opts.implicit_detach = 0;
+	opts.can_switch_when_in_progress = 0;
+	opts.orphan_from_empty_tree = 1;
+	opts.overlay_mode = -1;
+
+	options = parse_options_dup(switch_options);
+	options = add_common_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, switch_branch_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
+
+int cmd_restore(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options;
+	struct option restore_options[] = {
+		OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
+			   N_("which tree-ish to checkout from")),
+		OPT_BOOL('S', "staged", &opts.checkout_index,
+			   N_("restore the index")),
+		OPT_BOOL('W', "worktree", &opts.checkout_worktree,
+			   N_("restore the working tree (default)")),
+		OPT_BOOL(0, "ignore-unmerged", &opts.ignore_unmerged,
+			 N_("ignore unmerged entries")),
+		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
+		OPT_END()
+	};
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.accept_ref = 0;
+	opts.accept_pathspec = 1;
+	opts.empty_pathspec_ok = 0;
+	opts.overlay_mode = 0;
+	opts.checkout_index = -1;    /* default off */
+	opts.checkout_worktree = -2; /* default on */
+	opts.ignore_unmerged_opt = "--ignore-unmerged";
+
+	options = parse_options_dup(restore_options);
+	options = add_common_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, restore_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
diff --git a/builtin/clean.c b/builtin/clean.c
new file mode 100644
index 0000000000..d5579da716
--- /dev/null
+++ b/builtin/clean.c
@@ -0,0 +1,1051 @@
+/*
+ * "git clean" builtin command
+ *
+ * Copyright (C) 2007 Shawn Bohrer
+ *
+ * Based on git-clean.sh by Pavel Roskin
+ */
+
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "dir.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "quote.h"
+#include "column.h"
+#include "color.h"
+#include "pathspec.h"
+#include "help.h"
+
+static int force = -1; /* unset */
+static int interactive;
+static struct string_list del_list = STRING_LIST_INIT_DUP;
+static unsigned int colopts;
+
+static const char *const builtin_clean_usage[] = {
+	N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
+	NULL
+};
+
+static const char *msg_remove = N_("Removing %s\n");
+static const char *msg_would_remove = N_("Would remove %s\n");
+static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
+static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
+static const char *msg_warn_remove_failed = N_("failed to remove %s");
+static const char *msg_warn_lstat_failed = N_("could not lstat %s\n");
+
+enum color_clean {
+	CLEAN_COLOR_RESET = 0,
+	CLEAN_COLOR_PLAIN = 1,
+	CLEAN_COLOR_PROMPT = 2,
+	CLEAN_COLOR_HEADER = 3,
+	CLEAN_COLOR_HELP = 4,
+	CLEAN_COLOR_ERROR = 5
+};
+
+static const char *color_interactive_slots[] = {
+	[CLEAN_COLOR_ERROR]  = "error",
+	[CLEAN_COLOR_HEADER] = "header",
+	[CLEAN_COLOR_HELP]   = "help",
+	[CLEAN_COLOR_PLAIN]  = "plain",
+	[CLEAN_COLOR_PROMPT] = "prompt",
+	[CLEAN_COLOR_RESET]  = "reset",
+};
+
+static int clean_use_color = -1;
+static char clean_colors[][COLOR_MAXLEN] = {
+	[CLEAN_COLOR_ERROR] = GIT_COLOR_BOLD_RED,
+	[CLEAN_COLOR_HEADER] = GIT_COLOR_BOLD,
+	[CLEAN_COLOR_HELP] = GIT_COLOR_BOLD_RED,
+	[CLEAN_COLOR_PLAIN] = GIT_COLOR_NORMAL,
+	[CLEAN_COLOR_PROMPT] = GIT_COLOR_BOLD_BLUE,
+	[CLEAN_COLOR_RESET] = GIT_COLOR_RESET,
+};
+
+#define MENU_OPTS_SINGLETON		01
+#define MENU_OPTS_IMMEDIATE		02
+#define MENU_OPTS_LIST_ONLY		04
+
+struct menu_opts {
+	const char *header;
+	const char *prompt;
+	int flags;
+};
+
+#define MENU_RETURN_NO_LOOP		10
+
+struct menu_item {
+	char hotkey;
+	const char *title;
+	int selected;
+	int (*fn)(void);
+};
+
+enum menu_stuff_type {
+	MENU_STUFF_TYPE_STRING_LIST = 1,
+	MENU_STUFF_TYPE_MENU_ITEM
+};
+
+struct menu_stuff {
+	enum menu_stuff_type type;
+	int nr;
+	void *stuff;
+};
+
+define_list_config_array(color_interactive_slots);
+
+static int git_clean_config(const char *var, const char *value, void *cb)
+{
+	const char *slot_name;
+
+	if (starts_with(var, "column."))
+		return git_column_config(var, value, "clean", &colopts);
+
+	/* honors the color.interactive* config variables which also
+	   applied in git-add--interactive and git-stash */
+	if (!strcmp(var, "color.interactive")) {
+		clean_use_color = git_config_colorbool(var, value);
+		return 0;
+	}
+	if (skip_prefix(var, "color.interactive.", &slot_name)) {
+		int slot = LOOKUP_CONFIG(color_interactive_slots, slot_name);
+		if (slot < 0)
+			return 0;
+		if (!value)
+			return config_error_nonbool(var);
+		return color_parse(value, clean_colors[slot]);
+	}
+
+	if (!strcmp(var, "clean.requireforce")) {
+		force = !git_config_bool(var, value);
+		return 0;
+	}
+
+	/* inspect the color.ui config variable and others */
+	return git_color_default_config(var, value, cb);
+}
+
+static const char *clean_get_color(enum color_clean ix)
+{
+	if (want_color(clean_use_color))
+		return clean_colors[ix];
+	return "";
+}
+
+static void clean_print_color(enum color_clean ix)
+{
+	printf("%s", clean_get_color(ix));
+}
+
+static int exclude_cb(const struct option *opt, const char *arg, int unset)
+{
+	struct string_list *exclude_list = opt->value;
+	BUG_ON_OPT_NEG(unset);
+	string_list_append(exclude_list, arg);
+	return 0;
+}
+
+static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
+		int dry_run, int quiet, int *dir_gone)
+{
+	DIR *dir;
+	struct strbuf quoted = STRBUF_INIT;
+	struct dirent *e;
+	int res = 0, ret = 0, gone = 1, original_len = path->len, len;
+	struct string_list dels = STRING_LIST_INIT_DUP;
+
+	*dir_gone = 1;
+
+	if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_nonbare_repository_dir(path)) {
+		if (!quiet) {
+			quote_path_relative(path->buf, prefix, &quoted);
+			printf(dry_run ?  _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
+					quoted.buf);
+		}
+
+		*dir_gone = 0;
+		goto out;
+	}
+
+	dir = opendir(path->buf);
+	if (!dir) {
+		/* an empty dir could be removed even if it is unreadble */
+		res = dry_run ? 0 : rmdir(path->buf);
+		if (res) {
+			int saved_errno = errno;
+			quote_path_relative(path->buf, prefix, &quoted);
+			errno = saved_errno;
+			warning_errno(_(msg_warn_remove_failed), quoted.buf);
+			*dir_gone = 0;
+		}
+		ret = res;
+		goto out;
+	}
+
+	strbuf_complete(path, '/');
+
+	len = path->len;
+	while ((e = readdir(dir)) != NULL) {
+		struct stat st;
+		if (is_dot_or_dotdot(e->d_name))
+			continue;
+
+		strbuf_setlen(path, len);
+		strbuf_addstr(path, e->d_name);
+		if (lstat(path->buf, &st))
+			warning_errno(_(msg_warn_lstat_failed), path->buf);
+		else if (S_ISDIR(st.st_mode)) {
+			if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
+				ret = 1;
+			if (gone) {
+				quote_path_relative(path->buf, prefix, &quoted);
+				string_list_append(&dels, quoted.buf);
+			} else
+				*dir_gone = 0;
+			continue;
+		} else {
+			res = dry_run ? 0 : unlink(path->buf);
+			if (!res) {
+				quote_path_relative(path->buf, prefix, &quoted);
+				string_list_append(&dels, quoted.buf);
+			} else {
+				int saved_errno = errno;
+				quote_path_relative(path->buf, prefix, &quoted);
+				errno = saved_errno;
+				warning_errno(_(msg_warn_remove_failed), quoted.buf);
+				*dir_gone = 0;
+				ret = 1;
+			}
+			continue;
+		}
+
+		/* path too long, stat fails, or non-directory still exists */
+		*dir_gone = 0;
+		ret = 1;
+		break;
+	}
+	closedir(dir);
+
+	strbuf_setlen(path, original_len);
+
+	if (*dir_gone) {
+		res = dry_run ? 0 : rmdir(path->buf);
+		if (!res)
+			*dir_gone = 1;
+		else {
+			int saved_errno = errno;
+			quote_path_relative(path->buf, prefix, &quoted);
+			errno = saved_errno;
+			warning_errno(_(msg_warn_remove_failed), quoted.buf);
+			*dir_gone = 0;
+			ret = 1;
+		}
+	}
+
+	if (!*dir_gone && !quiet) {
+		int i;
+		for (i = 0; i < dels.nr; i++)
+			printf(dry_run ?  _(msg_would_remove) : _(msg_remove), dels.items[i].string);
+	}
+out:
+	strbuf_release(&quoted);
+	string_list_clear(&dels, 0);
+	return ret;
+}
+
+static void pretty_print_dels(void)
+{
+	struct string_list list = STRING_LIST_INIT_DUP;
+	struct string_list_item *item;
+	struct strbuf buf = STRBUF_INIT;
+	const char *qname;
+	struct column_options copts;
+
+	for_each_string_list_item(item, &del_list) {
+		qname = quote_path_relative(item->string, NULL, &buf);
+		string_list_append(&list, qname);
+	}
+
+	/*
+	 * always enable column display, we only consult column.*
+	 * about layout strategy and stuff
+	 */
+	colopts = (colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
+	memset(&copts, 0, sizeof(copts));
+	copts.indent = "  ";
+	copts.padding = 2;
+	print_columns(&list, colopts, &copts);
+	strbuf_release(&buf);
+	string_list_clear(&list, 0);
+}
+
+static void pretty_print_menus(struct string_list *menu_list)
+{
+	unsigned int local_colopts = 0;
+	struct column_options copts;
+
+	local_colopts = COL_ENABLED | COL_ROW;
+	memset(&copts, 0, sizeof(copts));
+	copts.indent = "  ";
+	copts.padding = 2;
+	print_columns(menu_list, local_colopts, &copts);
+}
+
+static void prompt_help_cmd(int singleton)
+{
+	clean_print_color(CLEAN_COLOR_HELP);
+	printf(singleton ?
+		  _("Prompt help:\n"
+		    "1          - select a numbered item\n"
+		    "foo        - select item based on unique prefix\n"
+		    "           - (empty) select nothing\n") :
+		  _("Prompt help:\n"
+		    "1          - select a single item\n"
+		    "3-5        - select a range of items\n"
+		    "2-3,6-9    - select multiple ranges\n"
+		    "foo        - select item based on unique prefix\n"
+		    "-...       - unselect specified items\n"
+		    "*          - choose all items\n"
+		    "           - (empty) finish selecting\n"));
+	clean_print_color(CLEAN_COLOR_RESET);
+}
+
+/*
+ * display menu stuff with number prefix and hotkey highlight
+ */
+static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen)
+{
+	struct string_list menu_list = STRING_LIST_INIT_DUP;
+	struct strbuf menu = STRBUF_INIT;
+	struct menu_item *menu_item;
+	struct string_list_item *string_list_item;
+	int i;
+
+	switch (stuff->type) {
+	default:
+		die("Bad type of menu_stuff when print menu");
+	case MENU_STUFF_TYPE_MENU_ITEM:
+		menu_item = (struct menu_item *)stuff->stuff;
+		for (i = 0; i < stuff->nr; i++, menu_item++) {
+			const char *p;
+			int highlighted = 0;
+
+			p = menu_item->title;
+			if ((*chosen)[i] < 0)
+				(*chosen)[i] = menu_item->selected ? 1 : 0;
+			strbuf_addf(&menu, "%s%2d: ", (*chosen)[i] ? "*" : " ", i+1);
+			for (; *p; p++) {
+				if (!highlighted && *p == menu_item->hotkey) {
+					strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_PROMPT));
+					strbuf_addch(&menu, *p);
+					strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_RESET));
+					highlighted = 1;
+				} else {
+					strbuf_addch(&menu, *p);
+				}
+			}
+			string_list_append(&menu_list, menu.buf);
+			strbuf_reset(&menu);
+		}
+		break;
+	case MENU_STUFF_TYPE_STRING_LIST:
+		i = 0;
+		for_each_string_list_item(string_list_item, (struct string_list *)stuff->stuff) {
+			if ((*chosen)[i] < 0)
+				(*chosen)[i] = 0;
+			strbuf_addf(&menu, "%s%2d: %s",
+				    (*chosen)[i] ? "*" : " ", i+1, string_list_item->string);
+			string_list_append(&menu_list, menu.buf);
+			strbuf_reset(&menu);
+			i++;
+		}
+		break;
+	}
+
+	pretty_print_menus(&menu_list);
+
+	strbuf_release(&menu);
+	string_list_clear(&menu_list, 0);
+}
+
+static int find_unique(const char *choice, struct menu_stuff *menu_stuff)
+{
+	struct menu_item *menu_item;
+	struct string_list_item *string_list_item;
+	int i, len, found = 0;
+
+	len = strlen(choice);
+	switch (menu_stuff->type) {
+	default:
+		die("Bad type of menu_stuff when parse choice");
+	case MENU_STUFF_TYPE_MENU_ITEM:
+
+		menu_item = (struct menu_item *)menu_stuff->stuff;
+		for (i = 0; i < menu_stuff->nr; i++, menu_item++) {
+			if (len == 1 && *choice == menu_item->hotkey) {
+				found = i + 1;
+				break;
+			}
+			if (!strncasecmp(choice, menu_item->title, len)) {
+				if (found) {
+					if (len == 1) {
+						/* continue for hotkey matching */
+						found = -1;
+					} else {
+						found = 0;
+						break;
+					}
+				} else {
+					found = i + 1;
+				}
+			}
+		}
+		break;
+	case MENU_STUFF_TYPE_STRING_LIST:
+		string_list_item = ((struct string_list *)menu_stuff->stuff)->items;
+		for (i = 0; i < menu_stuff->nr; i++, string_list_item++) {
+			if (!strncasecmp(choice, string_list_item->string, len)) {
+				if (found) {
+					found = 0;
+					break;
+				}
+				found = i + 1;
+			}
+		}
+		break;
+	}
+	return found;
+}
+
+
+/*
+ * Parse user input, and return choice(s) for menu (menu_stuff).
+ *
+ * Input
+ *     (for single choice)
+ *         1          - select a numbered item
+ *         foo        - select item based on menu title
+ *                    - (empty) select nothing
+ *
+ *     (for multiple choice)
+ *         1          - select a single item
+ *         3-5        - select a range of items
+ *         2-3,6-9    - select multiple ranges
+ *         foo        - select item based on menu title
+ *         -...       - unselect specified items
+ *         *          - choose all items
+ *                    - (empty) finish selecting
+ *
+ * The parse result will be saved in array **chosen, and
+ * return number of total selections.
+ */
+static int parse_choice(struct menu_stuff *menu_stuff,
+			int is_single,
+			struct strbuf input,
+			int **chosen)
+{
+	struct strbuf **choice_list, **ptr;
+	int nr = 0;
+	int i;
+
+	if (is_single) {
+		choice_list = strbuf_split_max(&input, '\n', 0);
+	} else {
+		char *p = input.buf;
+		do {
+			if (*p == ',')
+				*p = ' ';
+		} while (*p++);
+		choice_list = strbuf_split_max(&input, ' ', 0);
+	}
+
+	for (ptr = choice_list; *ptr; ptr++) {
+		char *p;
+		int choose = 1;
+		int bottom = 0, top = 0;
+		int is_range, is_number;
+
+		strbuf_trim(*ptr);
+		if (!(*ptr)->len)
+			continue;
+
+		/* Input that begins with '-'; unchoose */
+		if (*(*ptr)->buf == '-') {
+			choose = 0;
+			strbuf_remove((*ptr), 0, 1);
+		}
+
+		is_range = 0;
+		is_number = 1;
+		for (p = (*ptr)->buf; *p; p++) {
+			if ('-' == *p) {
+				if (!is_range) {
+					is_range = 1;
+					is_number = 0;
+				} else {
+					is_number = 0;
+					is_range = 0;
+					break;
+				}
+			} else if (!isdigit(*p)) {
+				is_number = 0;
+				is_range = 0;
+				break;
+			}
+		}
+
+		if (is_number) {
+			bottom = atoi((*ptr)->buf);
+			top = bottom;
+		} else if (is_range) {
+			bottom = atoi((*ptr)->buf);
+			/* a range can be specified like 5-7 or 5- */
+			if (!*(strchr((*ptr)->buf, '-') + 1))
+				top = menu_stuff->nr;
+			else
+				top = atoi(strchr((*ptr)->buf, '-') + 1);
+		} else if (!strcmp((*ptr)->buf, "*")) {
+			bottom = 1;
+			top = menu_stuff->nr;
+		} else {
+			bottom = find_unique((*ptr)->buf, menu_stuff);
+			top = bottom;
+		}
+
+		if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top ||
+		    (is_single && bottom != top)) {
+			clean_print_color(CLEAN_COLOR_ERROR);
+			printf(_("Huh (%s)?\n"), (*ptr)->buf);
+			clean_print_color(CLEAN_COLOR_RESET);
+			continue;
+		}
+
+		for (i = bottom; i <= top; i++)
+			(*chosen)[i-1] = choose;
+	}
+
+	strbuf_list_free(choice_list);
+
+	for (i = 0; i < menu_stuff->nr; i++)
+		nr += (*chosen)[i];
+	return nr;
+}
+
+/*
+ * Implement a git-add-interactive compatible UI, which is borrowed
+ * from git-add--interactive.perl.
+ *
+ * Return value:
+ *
+ *   - Return an array of integers
+ *   - , and it is up to you to free the allocated memory.
+ *   - The array ends with EOF.
+ *   - If user pressed CTRL-D (i.e. EOF), no selection returned.
+ */
+static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
+{
+	struct strbuf choice = STRBUF_INIT;
+	int *chosen, *result;
+	int nr = 0;
+	int eof = 0;
+	int i;
+
+	ALLOC_ARRAY(chosen, stuff->nr);
+	/* set chosen as uninitialized */
+	for (i = 0; i < stuff->nr; i++)
+		chosen[i] = -1;
+
+	for (;;) {
+		if (opts->header) {
+			printf_ln("%s%s%s",
+				  clean_get_color(CLEAN_COLOR_HEADER),
+				  _(opts->header),
+				  clean_get_color(CLEAN_COLOR_RESET));
+		}
+
+		/* chosen will be initialized by print_highlight_menu_stuff */
+		print_highlight_menu_stuff(stuff, &chosen);
+
+		if (opts->flags & MENU_OPTS_LIST_ONLY)
+			break;
+
+		if (opts->prompt) {
+			printf("%s%s%s%s",
+			       clean_get_color(CLEAN_COLOR_PROMPT),
+			       _(opts->prompt),
+			       opts->flags & MENU_OPTS_SINGLETON ? "> " : ">> ",
+			       clean_get_color(CLEAN_COLOR_RESET));
+		}
+
+		if (strbuf_getline_lf(&choice, stdin) != EOF) {
+			strbuf_trim(&choice);
+		} else {
+			eof = 1;
+			break;
+		}
+
+		/* help for prompt */
+		if (!strcmp(choice.buf, "?")) {
+			prompt_help_cmd(opts->flags & MENU_OPTS_SINGLETON);
+			continue;
+		}
+
+		/* for a multiple-choice menu, press ENTER (empty) will return back */
+		if (!(opts->flags & MENU_OPTS_SINGLETON) && !choice.len)
+			break;
+
+		nr = parse_choice(stuff,
+				  opts->flags & MENU_OPTS_SINGLETON,
+				  choice,
+				  &chosen);
+
+		if (opts->flags & MENU_OPTS_SINGLETON) {
+			if (nr)
+				break;
+		} else if (opts->flags & MENU_OPTS_IMMEDIATE) {
+			break;
+		}
+	}
+
+	if (eof) {
+		result = xmalloc(sizeof(int));
+		*result = EOF;
+	} else {
+		int j = 0;
+
+		/*
+		 * recalculate nr, if return back from menu directly with
+		 * default selections.
+		 */
+		if (!nr) {
+			for (i = 0; i < stuff->nr; i++)
+				nr += chosen[i];
+		}
+
+		result = xcalloc(st_add(nr, 1), sizeof(int));
+		for (i = 0; i < stuff->nr && j < nr; i++) {
+			if (chosen[i])
+				result[j++] = i;
+		}
+		result[j] = EOF;
+	}
+
+	free(chosen);
+	strbuf_release(&choice);
+	return result;
+}
+
+static int clean_cmd(void)
+{
+	return MENU_RETURN_NO_LOOP;
+}
+
+static int filter_by_patterns_cmd(void)
+{
+	struct dir_struct dir;
+	struct strbuf confirm = STRBUF_INIT;
+	struct strbuf **ignore_list;
+	struct string_list_item *item;
+	struct exclude_list *el;
+	int changed = -1, i;
+
+	for (;;) {
+		if (!del_list.nr)
+			break;
+
+		if (changed)
+			pretty_print_dels();
+
+		clean_print_color(CLEAN_COLOR_PROMPT);
+		printf(_("Input ignore patterns>> "));
+		clean_print_color(CLEAN_COLOR_RESET);
+		if (strbuf_getline_lf(&confirm, stdin) != EOF)
+			strbuf_trim(&confirm);
+		else
+			putchar('\n');
+
+		/* quit filter_by_pattern mode if press ENTER or Ctrl-D */
+		if (!confirm.len)
+			break;
+
+		memset(&dir, 0, sizeof(dir));
+		el = add_exclude_list(&dir, EXC_CMDL, "manual exclude");
+		ignore_list = strbuf_split_max(&confirm, ' ', 0);
+
+		for (i = 0; ignore_list[i]; i++) {
+			strbuf_trim(ignore_list[i]);
+			if (!ignore_list[i]->len)
+				continue;
+
+			add_exclude(ignore_list[i]->buf, "", 0, el, -(i+1));
+		}
+
+		changed = 0;
+		for_each_string_list_item(item, &del_list) {
+			int dtype = DT_UNKNOWN;
+
+			if (is_excluded(&dir, &the_index, item->string, &dtype)) {
+				*item->string = '\0';
+				changed++;
+			}
+		}
+
+		if (changed) {
+			string_list_remove_empty_items(&del_list, 0);
+		} else {
+			clean_print_color(CLEAN_COLOR_ERROR);
+			printf_ln(_("WARNING: Cannot find items matched by: %s"), confirm.buf);
+			clean_print_color(CLEAN_COLOR_RESET);
+		}
+
+		strbuf_list_free(ignore_list);
+		clear_directory(&dir);
+	}
+
+	strbuf_release(&confirm);
+	return 0;
+}
+
+static int select_by_numbers_cmd(void)
+{
+	struct menu_opts menu_opts;
+	struct menu_stuff menu_stuff;
+	struct string_list_item *items;
+	int *chosen;
+	int i, j;
+
+	menu_opts.header = NULL;
+	menu_opts.prompt = N_("Select items to delete");
+	menu_opts.flags = 0;
+
+	menu_stuff.type = MENU_STUFF_TYPE_STRING_LIST;
+	menu_stuff.stuff = &del_list;
+	menu_stuff.nr = del_list.nr;
+
+	chosen = list_and_choose(&menu_opts, &menu_stuff);
+	items = del_list.items;
+	for (i = 0, j = 0; i < del_list.nr; i++) {
+		if (i < chosen[j]) {
+			*(items[i].string) = '\0';
+		} else if (i == chosen[j]) {
+			/* delete selected item */
+			j++;
+			continue;
+		} else {
+			/* end of chosen (chosen[j] == EOF), won't delete */
+			*(items[i].string) = '\0';
+		}
+	}
+
+	string_list_remove_empty_items(&del_list, 0);
+
+	free(chosen);
+	return 0;
+}
+
+static int ask_each_cmd(void)
+{
+	struct strbuf confirm = STRBUF_INIT;
+	struct strbuf buf = STRBUF_INIT;
+	struct string_list_item *item;
+	const char *qname;
+	int changed = 0, eof = 0;
+
+	for_each_string_list_item(item, &del_list) {
+		/* Ctrl-D should stop removing files */
+		if (!eof) {
+			qname = quote_path_relative(item->string, NULL, &buf);
+			/* TRANSLATORS: Make sure to keep [y/N] as is */
+			printf(_("Remove %s [y/N]? "), qname);
+			if (strbuf_getline_lf(&confirm, stdin) != EOF) {
+				strbuf_trim(&confirm);
+			} else {
+				putchar('\n');
+				eof = 1;
+			}
+		}
+		if (!confirm.len || strncasecmp(confirm.buf, "yes", confirm.len)) {
+			*item->string = '\0';
+			changed++;
+		}
+	}
+
+	if (changed)
+		string_list_remove_empty_items(&del_list, 0);
+
+	strbuf_release(&buf);
+	strbuf_release(&confirm);
+	return MENU_RETURN_NO_LOOP;
+}
+
+static int quit_cmd(void)
+{
+	string_list_clear(&del_list, 0);
+	printf(_("Bye.\n"));
+	return MENU_RETURN_NO_LOOP;
+}
+
+static int help_cmd(void)
+{
+	clean_print_color(CLEAN_COLOR_HELP);
+	printf_ln(_(
+		    "clean               - start cleaning\n"
+		    "filter by pattern   - exclude items from deletion\n"
+		    "select by numbers   - select items to be deleted by numbers\n"
+		    "ask each            - confirm each deletion (like \"rm -i\")\n"
+		    "quit                - stop cleaning\n"
+		    "help                - this screen\n"
+		    "?                   - help for prompt selection"
+		   ));
+	clean_print_color(CLEAN_COLOR_RESET);
+	return 0;
+}
+
+static void interactive_main_loop(void)
+{
+	while (del_list.nr) {
+		struct menu_opts menu_opts;
+		struct menu_stuff menu_stuff;
+		struct menu_item menus[] = {
+			{'c', "clean",			0, clean_cmd},
+			{'f', "filter by pattern",	0, filter_by_patterns_cmd},
+			{'s', "select by numbers",	0, select_by_numbers_cmd},
+			{'a', "ask each",		0, ask_each_cmd},
+			{'q', "quit",			0, quit_cmd},
+			{'h', "help",			0, help_cmd},
+		};
+		int *chosen;
+
+		menu_opts.header = N_("*** Commands ***");
+		menu_opts.prompt = N_("What now");
+		menu_opts.flags = MENU_OPTS_SINGLETON;
+
+		menu_stuff.type = MENU_STUFF_TYPE_MENU_ITEM;
+		menu_stuff.stuff = menus;
+		menu_stuff.nr = sizeof(menus) / sizeof(struct menu_item);
+
+		clean_print_color(CLEAN_COLOR_HEADER);
+		printf_ln(Q_("Would remove the following item:",
+			     "Would remove the following items:",
+			     del_list.nr));
+		clean_print_color(CLEAN_COLOR_RESET);
+
+		pretty_print_dels();
+
+		chosen = list_and_choose(&menu_opts, &menu_stuff);
+
+		if (*chosen != EOF) {
+			int ret;
+			ret = menus[*chosen].fn();
+			if (ret != MENU_RETURN_NO_LOOP) {
+				FREE_AND_NULL(chosen);
+				if (!del_list.nr) {
+					clean_print_color(CLEAN_COLOR_ERROR);
+					printf_ln(_("No more files to clean, exiting."));
+					clean_print_color(CLEAN_COLOR_RESET);
+					break;
+				}
+				continue;
+			}
+		} else {
+			quit_cmd();
+		}
+
+		FREE_AND_NULL(chosen);
+		break;
+	}
+}
+
+static void correct_untracked_entries(struct dir_struct *dir)
+{
+	int src, dst, ign;
+
+	for (src = dst = ign = 0; src < dir->nr; src++) {
+		/* skip paths in ignored[] that cannot be inside entries[src] */
+		while (ign < dir->ignored_nr &&
+		       0 <= cmp_dir_entry(&dir->entries[src], &dir->ignored[ign]))
+			ign++;
+
+		if (ign < dir->ignored_nr &&
+		    check_dir_entry_contains(dir->entries[src], dir->ignored[ign])) {
+			/* entries[src] contains an ignored path, so we drop it */
+			free(dir->entries[src]);
+		} else {
+			struct dir_entry *ent = dir->entries[src++];
+
+			/* entries[src] does not contain an ignored path, so we keep it */
+			dir->entries[dst++] = ent;
+
+			/* then discard paths in entries[] contained inside entries[src] */
+			while (src < dir->nr &&
+			       check_dir_entry_contains(ent, dir->entries[src]))
+				free(dir->entries[src++]);
+
+			/* compensate for the outer loop's loop control */
+			src--;
+		}
+	}
+	dir->nr = dst;
+}
+
+int cmd_clean(int argc, const char **argv, const char *prefix)
+{
+	int i, res;
+	int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
+	int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
+	int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
+	struct strbuf abs_path = STRBUF_INIT;
+	struct dir_struct dir;
+	struct pathspec pathspec;
+	struct strbuf buf = STRBUF_INIT;
+	struct string_list exclude_list = STRING_LIST_INIT_NODUP;
+	struct exclude_list *el;
+	struct string_list_item *item;
+	const char *qname;
+	struct option options[] = {
+		OPT__QUIET(&quiet, N_("do not print names of files removed")),
+		OPT__DRY_RUN(&dry_run, N_("dry run")),
+		OPT__FORCE(&force, N_("force"), PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")),
+		OPT_BOOL('d', NULL, &remove_directories,
+				N_("remove whole directories")),
+		{ OPTION_CALLBACK, 'e', "exclude", &exclude_list, N_("pattern"),
+		  N_("add <pattern> to ignore rules"), PARSE_OPT_NONEG, exclude_cb },
+		OPT_BOOL('x', NULL, &ignored, N_("remove ignored files, too")),
+		OPT_BOOL('X', NULL, &ignored_only,
+				N_("remove only ignored files")),
+		OPT_END()
+	};
+
+	git_config(git_clean_config, NULL);
+	if (force < 0)
+		force = 0;
+	else
+		config_set = 1;
+
+	argc = parse_options(argc, argv, prefix, options, builtin_clean_usage,
+			     0);
+
+	memset(&dir, 0, sizeof(dir));
+	if (ignored_only)
+		dir.flags |= DIR_SHOW_IGNORED;
+
+	if (ignored && ignored_only)
+		die(_("-x and -X cannot be used together"));
+
+	if (!interactive && !dry_run && !force) {
+		if (config_set)
+			die(_("clean.requireForce set to true and neither -i, -n, nor -f given; "
+				  "refusing to clean"));
+		else
+			die(_("clean.requireForce defaults to true and neither -i, -n, nor -f given;"
+				  " refusing to clean"));
+	}
+
+	if (force > 1)
+		rm_flags = 0;
+
+	dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
+
+	if (remove_directories)
+		dir.flags |= DIR_SHOW_IGNORED_TOO | DIR_KEEP_UNTRACKED_CONTENTS;
+
+	if (read_cache() < 0)
+		die(_("index file corrupt"));
+
+	if (!ignored)
+		setup_standard_excludes(&dir);
+
+	el = add_exclude_list(&dir, EXC_CMDL, "--exclude option");
+	for (i = 0; i < exclude_list.nr; i++)
+		add_exclude(exclude_list.items[i].string, "", 0, el, -(i+1));
+
+	parse_pathspec(&pathspec, 0,
+		       PATHSPEC_PREFER_CWD,
+		       prefix, argv);
+
+	fill_directory(&dir, &the_index, &pathspec);
+	correct_untracked_entries(&dir);
+
+	for (i = 0; i < dir.nr; i++) {
+		struct dir_entry *ent = dir.entries[i];
+		int matches = 0;
+		struct stat st;
+		const char *rel;
+
+		if (!cache_name_is_other(ent->name, ent->len))
+			continue;
+
+		if (pathspec.nr)
+			matches = dir_path_match(&the_index, ent, &pathspec, 0, NULL);
+
+		if (pathspec.nr && !matches)
+			continue;
+
+		if (lstat(ent->name, &st))
+			die_errno("Cannot lstat '%s'", ent->name);
+
+		if (S_ISDIR(st.st_mode) && !remove_directories &&
+		    matches != MATCHED_EXACTLY)
+			continue;
+
+		rel = relative_path(ent->name, prefix, &buf);
+		string_list_append(&del_list, rel);
+	}
+
+	for (i = 0; i < dir.nr; i++)
+		free(dir.entries[i]);
+
+	for (i = 0; i < dir.ignored_nr; i++)
+		free(dir.ignored[i]);
+
+	if (interactive && del_list.nr > 0)
+		interactive_main_loop();
+
+	for_each_string_list_item(item, &del_list) {
+		struct stat st;
+
+		if (prefix)
+			strbuf_addstr(&abs_path, prefix);
+
+		strbuf_addstr(&abs_path, item->string);
+
+		/*
+		 * we might have removed this as part of earlier
+		 * recursive directory removal, so lstat() here could
+		 * fail with ENOENT.
+		 */
+		if (lstat(abs_path.buf, &st))
+			continue;
+
+		if (S_ISDIR(st.st_mode)) {
+			if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone))
+				errors++;
+			if (gone && !quiet) {
+				qname = quote_path_relative(item->string, NULL, &buf);
+				printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
+			}
+		} else {
+			res = dry_run ? 0 : unlink(abs_path.buf);
+			if (res) {
+				int saved_errno = errno;
+				qname = quote_path_relative(item->string, NULL, &buf);
+				errno = saved_errno;
+				warning_errno(_(msg_warn_remove_failed), qname);
+				errors++;
+			} else if (!quiet) {
+				qname = quote_path_relative(item->string, NULL, &buf);
+				printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
+			}
+		}
+		strbuf_reset(&abs_path);
+	}
+
+	strbuf_release(&abs_path);
+	strbuf_release(&buf);
+	string_list_clear(&del_list, 0);
+	string_list_clear(&exclude_list, 0);
+	return (errors != 0);
+}
diff --git a/builtin/clone.c b/builtin/clone.c
new file mode 100644
index 0000000000..f665b28ccc
--- /dev/null
+++ b/builtin/clone.c
@@ -0,0 +1,1282 @@
+/*
+ * Builtin "git clone"
+ *
+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>,
+ *		 2008 Daniel Barkalow <barkalow@iabervon.org>
+ * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
+ *
+ * Clone a repository into a different directory that does not yet exist.
+ */
+
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "config.h"
+#include "lockfile.h"
+#include "parse-options.h"
+#include "fetch-pack.h"
+#include "refs.h"
+#include "refspec.h"
+#include "object-store.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "unpack-trees.h"
+#include "transport.h"
+#include "strbuf.h"
+#include "dir.h"
+#include "dir-iterator.h"
+#include "iterator.h"
+#include "sigchain.h"
+#include "branch.h"
+#include "remote.h"
+#include "run-command.h"
+#include "connected.h"
+#include "packfile.h"
+#include "list-objects-filter-options.h"
+#include "object-store.h"
+
+/*
+ * Overall FIXMEs:
+ *  - respect DB_ENVIRONMENT for .git/objects.
+ *
+ * Implementation notes:
+ *  - dropping use-separate-remote and no-separate-remote compatibility
+ *
+ */
+static const char * const builtin_clone_usage[] = {
+	N_("git clone [<options>] [--] <repo> [<dir>]"),
+	NULL
+};
+
+static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
+static int option_local = -1, option_no_hardlinks, option_shared;
+static int option_no_tags;
+static int option_shallow_submodules;
+static int deepen;
+static char *option_template, *option_depth, *option_since;
+static char *option_origin = NULL;
+static char *option_branch = NULL;
+static struct string_list option_not = STRING_LIST_INIT_NODUP;
+static const char *real_git_dir;
+static char *option_upload_pack = "git-upload-pack";
+static int option_verbosity;
+static int option_progress = -1;
+static enum transport_family family;
+static struct string_list option_config = STRING_LIST_INIT_NODUP;
+static struct string_list option_required_reference = STRING_LIST_INIT_NODUP;
+static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP;
+static int option_dissociate;
+static int max_jobs = -1;
+static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
+static struct list_objects_filter_options filter_options;
+static struct string_list server_options = STRING_LIST_INIT_NODUP;
+static int option_remote_submodules;
+
+static int recurse_submodules_cb(const struct option *opt,
+				 const char *arg, int unset)
+{
+	if (unset)
+		string_list_clear((struct string_list *)opt->value, 0);
+	else if (arg)
+		string_list_append((struct string_list *)opt->value, arg);
+	else
+		string_list_append((struct string_list *)opt->value,
+				   (const char *)opt->defval);
+
+	return 0;
+}
+
+static struct option builtin_clone_options[] = {
+	OPT__VERBOSITY(&option_verbosity),
+	OPT_BOOL(0, "progress", &option_progress,
+		 N_("force progress reporting")),
+	OPT_BOOL('n', "no-checkout", &option_no_checkout,
+		 N_("don't create a checkout")),
+	OPT_BOOL(0, "bare", &option_bare, N_("create a bare repository")),
+	OPT_HIDDEN_BOOL(0, "naked", &option_bare,
+			N_("create a bare repository")),
+	OPT_BOOL(0, "mirror", &option_mirror,
+		 N_("create a mirror repository (implies bare)")),
+	OPT_BOOL('l', "local", &option_local,
+		N_("to clone from a local repository")),
+	OPT_BOOL(0, "no-hardlinks", &option_no_hardlinks,
+		    N_("don't use local hardlinks, always copy")),
+	OPT_BOOL('s', "shared", &option_shared,
+		    N_("setup as shared repository")),
+	OPT_ALIAS(0, "recursive", "recurse-submodules"),
+	{ OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules,
+	  N_("pathspec"), N_("initialize submodules in the clone"),
+	  PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." },
+	OPT_INTEGER('j', "jobs", &max_jobs,
+		    N_("number of submodules cloned in parallel")),
+	OPT_STRING(0, "template", &option_template, N_("template-directory"),
+		   N_("directory from which templates will be used")),
+	OPT_STRING_LIST(0, "reference", &option_required_reference, N_("repo"),
+			N_("reference repository")),
+	OPT_STRING_LIST(0, "reference-if-able", &option_optional_reference,
+			N_("repo"), N_("reference repository")),
+	OPT_BOOL(0, "dissociate", &option_dissociate,
+		 N_("use --reference only while cloning")),
+	OPT_STRING('o', "origin", &option_origin, N_("name"),
+		   N_("use <name> instead of 'origin' to track upstream")),
+	OPT_STRING('b', "branch", &option_branch, N_("branch"),
+		   N_("checkout <branch> instead of the remote's HEAD")),
+	OPT_STRING('u', "upload-pack", &option_upload_pack, N_("path"),
+		   N_("path to git-upload-pack on the remote")),
+	OPT_STRING(0, "depth", &option_depth, N_("depth"),
+		    N_("create a shallow clone of that depth")),
+	OPT_STRING(0, "shallow-since", &option_since, N_("time"),
+		    N_("create a shallow clone since a specific time")),
+	OPT_STRING_LIST(0, "shallow-exclude", &option_not, N_("revision"),
+			N_("deepen history of shallow clone, excluding rev")),
+	OPT_BOOL(0, "single-branch", &option_single_branch,
+		    N_("clone only one branch, HEAD or --branch")),
+	OPT_BOOL(0, "no-tags", &option_no_tags,
+		 N_("don't clone any tags, and make later fetches not to follow them")),
+	OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules,
+		    N_("any cloned submodules will be shallow")),
+	OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
+		   N_("separate git dir from working tree")),
+	OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
+			N_("set config inside the new repository")),
+	OPT_STRING_LIST(0, "server-option", &server_options,
+			N_("server-specific"), N_("option to transmit")),
+	OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
+			TRANSPORT_FAMILY_IPV4),
+	OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
+			TRANSPORT_FAMILY_IPV6),
+	OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+	OPT_BOOL(0, "remote-submodules", &option_remote_submodules,
+		    N_("any cloned submodules will use their remote-tracking branch")),
+	OPT_END()
+};
+
+static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
+{
+	static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
+	static char *bundle_suffix[] = { ".bundle", "" };
+	size_t baselen = path->len;
+	struct stat st;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(suffix); i++) {
+		strbuf_setlen(path, baselen);
+		strbuf_addstr(path, suffix[i]);
+		if (stat(path->buf, &st))
+			continue;
+		if (S_ISDIR(st.st_mode) && is_git_directory(path->buf)) {
+			*is_bundle = 0;
+			return path->buf;
+		} else if (S_ISREG(st.st_mode) && st.st_size > 8) {
+			/* Is it a "gitfile"? */
+			char signature[8];
+			const char *dst;
+			int len, fd = open(path->buf, O_RDONLY);
+			if (fd < 0)
+				continue;
+			len = read_in_full(fd, signature, 8);
+			close(fd);
+			if (len != 8 || strncmp(signature, "gitdir: ", 8))
+				continue;
+			dst = read_gitfile(path->buf);
+			if (dst) {
+				*is_bundle = 0;
+				return dst;
+			}
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(bundle_suffix); i++) {
+		strbuf_setlen(path, baselen);
+		strbuf_addstr(path, bundle_suffix[i]);
+		if (!stat(path->buf, &st) && S_ISREG(st.st_mode)) {
+			*is_bundle = 1;
+			return path->buf;
+		}
+	}
+
+	return NULL;
+}
+
+static char *get_repo_path(const char *repo, int *is_bundle)
+{
+	struct strbuf path = STRBUF_INIT;
+	const char *raw;
+	char *canon;
+
+	strbuf_addstr(&path, repo);
+	raw = get_repo_path_1(&path, is_bundle);
+	canon = raw ? absolute_pathdup(raw) : NULL;
+	strbuf_release(&path);
+	return canon;
+}
+
+static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
+{
+	const char *end = repo + strlen(repo), *start, *ptr;
+	size_t len;
+	char *dir;
+
+	/*
+	 * Skip scheme.
+	 */
+	start = strstr(repo, "://");
+	if (start == NULL)
+		start = repo;
+	else
+		start += 3;
+
+	/*
+	 * Skip authentication data. The stripping does happen
+	 * greedily, such that we strip up to the last '@' inside
+	 * the host part.
+	 */
+	for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
+		if (*ptr == '@')
+			start = ptr + 1;
+	}
+
+	/*
+	 * Strip trailing spaces, slashes and /.git
+	 */
+	while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
+		end--;
+	if (end - start > 5 && is_dir_sep(end[-5]) &&
+	    !strncmp(end - 4, ".git", 4)) {
+		end -= 5;
+		while (start < end && is_dir_sep(end[-1]))
+			end--;
+	}
+
+	/*
+	 * Strip trailing port number if we've got only a
+	 * hostname (that is, there is no dir separator but a
+	 * colon). This check is required such that we do not
+	 * strip URI's like '/foo/bar:2222.git', which should
+	 * result in a dir '2222' being guessed due to backwards
+	 * compatibility.
+	 */
+	if (memchr(start, '/', end - start) == NULL
+	    && memchr(start, ':', end - start) != NULL) {
+		ptr = end;
+		while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
+			ptr--;
+		if (start < ptr && ptr[-1] == ':')
+			end = ptr - 1;
+	}
+
+	/*
+	 * Find last component. To remain backwards compatible we
+	 * also regard colons as path separators, such that
+	 * cloning a repository 'foo:bar.git' would result in a
+	 * directory 'bar' being guessed.
+	 */
+	ptr = end;
+	while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
+		ptr--;
+	start = ptr;
+
+	/*
+	 * Strip .{bundle,git}.
+	 */
+	len = end - start;
+	strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
+
+	if (!len || (len == 1 && *start == '/'))
+		die(_("No directory name could be guessed.\n"
+		      "Please specify a directory on the command line"));
+
+	if (is_bare)
+		dir = xstrfmt("%.*s.git", (int)len, start);
+	else
+		dir = xstrndup(start, len);
+	/*
+	 * Replace sequences of 'control' characters and whitespace
+	 * with one ascii space, remove leading and trailing spaces.
+	 */
+	if (*dir) {
+		char *out = dir;
+		int prev_space = 1 /* strip leading whitespace */;
+		for (end = dir; *end; ++end) {
+			char ch = *end;
+			if ((unsigned char)ch < '\x20')
+				ch = '\x20';
+			if (isspace(ch)) {
+				if (prev_space)
+					continue;
+				prev_space = 1;
+			} else
+				prev_space = 0;
+			*out++ = ch;
+		}
+		*out = '\0';
+		if (out > dir && prev_space)
+			out[-1] = '\0';
+	}
+	return dir;
+}
+
+static void strip_trailing_slashes(char *dir)
+{
+	char *end = dir + strlen(dir);
+
+	while (dir < end - 1 && is_dir_sep(end[-1]))
+		end--;
+	*end = '\0';
+}
+
+static int add_one_reference(struct string_list_item *item, void *cb_data)
+{
+	struct strbuf err = STRBUF_INIT;
+	int *required = cb_data;
+	char *ref_git = compute_alternate_path(item->string, &err);
+
+	if (!ref_git) {
+		if (*required)
+			die("%s", err.buf);
+		else
+			fprintf(stderr,
+				_("info: Could not add alternate for '%s': %s\n"),
+				item->string, err.buf);
+	} else {
+		struct strbuf sb = STRBUF_INIT;
+		strbuf_addf(&sb, "%s/objects", ref_git);
+		add_to_alternates_file(sb.buf);
+		strbuf_release(&sb);
+	}
+
+	strbuf_release(&err);
+	free(ref_git);
+	return 0;
+}
+
+static void setup_reference(void)
+{
+	int required = 1;
+	for_each_string_list(&option_required_reference,
+			     add_one_reference, &required);
+	required = 0;
+	for_each_string_list(&option_optional_reference,
+			     add_one_reference, &required);
+}
+
+static void copy_alternates(struct strbuf *src, const char *src_repo)
+{
+	/*
+	 * Read from the source objects/info/alternates file
+	 * and copy the entries to corresponding file in the
+	 * destination repository with add_to_alternates_file().
+	 * Both src and dst have "$path/objects/info/alternates".
+	 *
+	 * Instead of copying bit-for-bit from the original,
+	 * we need to append to existing one so that the already
+	 * created entry via "clone -s" is not lost, and also
+	 * to turn entries with paths relative to the original
+	 * absolute, so that they can be used in the new repository.
+	 */
+	FILE *in = xfopen(src->buf, "r");
+	struct strbuf line = STRBUF_INIT;
+
+	while (strbuf_getline(&line, in) != EOF) {
+		char *abs_path;
+		if (!line.len || line.buf[0] == '#')
+			continue;
+		if (is_absolute_path(line.buf)) {
+			add_to_alternates_file(line.buf);
+			continue;
+		}
+		abs_path = mkpathdup("%s/objects/%s", src_repo, line.buf);
+		if (!normalize_path_copy(abs_path, abs_path))
+			add_to_alternates_file(abs_path);
+		else
+			warning("skipping invalid relative alternate: %s/%s",
+				src_repo, line.buf);
+		free(abs_path);
+	}
+	strbuf_release(&line);
+	fclose(in);
+}
+
+static void mkdir_if_missing(const char *pathname, mode_t mode)
+{
+	struct stat st;
+
+	if (!mkdir(pathname, mode))
+		return;
+
+	if (errno != EEXIST)
+		die_errno(_("failed to create directory '%s'"), pathname);
+	else if (stat(pathname, &st))
+		die_errno(_("failed to stat '%s'"), pathname);
+	else if (!S_ISDIR(st.st_mode))
+		die(_("%s exists and is not a directory"), pathname);
+}
+
+static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
+				   const char *src_repo)
+{
+	int src_len, dest_len;
+	struct dir_iterator *iter;
+	int iter_status;
+	unsigned int flags;
+
+	mkdir_if_missing(dest->buf, 0777);
+
+	flags = DIR_ITERATOR_PEDANTIC | DIR_ITERATOR_FOLLOW_SYMLINKS;
+	iter = dir_iterator_begin(src->buf, flags);
+
+	if (!iter)
+		die_errno(_("failed to start iterator over '%s'"), src->buf);
+
+	strbuf_addch(src, '/');
+	src_len = src->len;
+	strbuf_addch(dest, '/');
+	dest_len = dest->len;
+
+	while ((iter_status = dir_iterator_advance(iter)) == ITER_OK) {
+		strbuf_setlen(src, src_len);
+		strbuf_addstr(src, iter->relative_path);
+		strbuf_setlen(dest, dest_len);
+		strbuf_addstr(dest, iter->relative_path);
+
+		if (S_ISDIR(iter->st.st_mode)) {
+			mkdir_if_missing(dest->buf, 0777);
+			continue;
+		}
+
+		/* Files that cannot be copied bit-for-bit... */
+		if (!fspathcmp(iter->relative_path, "info/alternates")) {
+			copy_alternates(src, src_repo);
+			continue;
+		}
+
+		if (unlink(dest->buf) && errno != ENOENT)
+			die_errno(_("failed to unlink '%s'"), dest->buf);
+		if (!option_no_hardlinks) {
+			if (!link(real_path(src->buf), dest->buf))
+				continue;
+			if (option_local > 0)
+				die_errno(_("failed to create link '%s'"), dest->buf);
+			option_no_hardlinks = 1;
+		}
+		if (copy_file_with_time(dest->buf, src->buf, 0666))
+			die_errno(_("failed to copy file to '%s'"), dest->buf);
+	}
+
+	if (iter_status != ITER_DONE) {
+		strbuf_setlen(src, src_len);
+		die(_("failed to iterate over '%s'"), src->buf);
+	}
+}
+
+static void clone_local(const char *src_repo, const char *dest_repo)
+{
+	if (option_shared) {
+		struct strbuf alt = STRBUF_INIT;
+		get_common_dir(&alt, src_repo);
+		strbuf_addstr(&alt, "/objects");
+		add_to_alternates_file(alt.buf);
+		strbuf_release(&alt);
+	} else {
+		struct strbuf src = STRBUF_INIT;
+		struct strbuf dest = STRBUF_INIT;
+		get_common_dir(&src, src_repo);
+		get_common_dir(&dest, dest_repo);
+		strbuf_addstr(&src, "/objects");
+		strbuf_addstr(&dest, "/objects");
+		copy_or_link_directory(&src, &dest, src_repo);
+		strbuf_release(&src);
+		strbuf_release(&dest);
+	}
+
+	if (0 <= option_verbosity)
+		fprintf(stderr, _("done.\n"));
+}
+
+static const char *junk_work_tree;
+static int junk_work_tree_flags;
+static const char *junk_git_dir;
+static int junk_git_dir_flags;
+static enum {
+	JUNK_LEAVE_NONE,
+	JUNK_LEAVE_REPO,
+	JUNK_LEAVE_ALL
+} junk_mode = JUNK_LEAVE_NONE;
+
+static const char junk_leave_repo_msg[] =
+N_("Clone succeeded, but checkout failed.\n"
+   "You can inspect what was checked out with 'git status'\n"
+   "and retry with 'git restore --source=HEAD :/'\n");
+
+static void remove_junk(void)
+{
+	struct strbuf sb = STRBUF_INIT;
+
+	switch (junk_mode) {
+	case JUNK_LEAVE_REPO:
+		warning("%s", _(junk_leave_repo_msg));
+		/* fall-through */
+	case JUNK_LEAVE_ALL:
+		return;
+	default:
+		/* proceed to removal */
+		break;
+	}
+
+	if (junk_git_dir) {
+		strbuf_addstr(&sb, junk_git_dir);
+		remove_dir_recursively(&sb, junk_git_dir_flags);
+		strbuf_reset(&sb);
+	}
+	if (junk_work_tree) {
+		strbuf_addstr(&sb, junk_work_tree);
+		remove_dir_recursively(&sb, junk_work_tree_flags);
+	}
+	strbuf_release(&sb);
+}
+
+static void remove_junk_on_signal(int signo)
+{
+	remove_junk();
+	sigchain_pop(signo);
+	raise(signo);
+}
+
+static struct ref *find_remote_branch(const struct ref *refs, const char *branch)
+{
+	struct ref *ref;
+	struct strbuf head = STRBUF_INIT;
+	strbuf_addstr(&head, "refs/heads/");
+	strbuf_addstr(&head, branch);
+	ref = find_ref_by_name(refs, head.buf);
+	strbuf_release(&head);
+
+	if (ref)
+		return ref;
+
+	strbuf_addstr(&head, "refs/tags/");
+	strbuf_addstr(&head, branch);
+	ref = find_ref_by_name(refs, head.buf);
+	strbuf_release(&head);
+
+	return ref;
+}
+
+static struct ref *wanted_peer_refs(const struct ref *refs,
+		struct refspec *refspec)
+{
+	struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
+	struct ref *local_refs = head;
+	struct ref **tail = head ? &head->next : &local_refs;
+
+	if (option_single_branch) {
+		struct ref *remote_head = NULL;
+
+		if (!option_branch)
+			remote_head = guess_remote_head(head, refs, 0);
+		else {
+			local_refs = NULL;
+			tail = &local_refs;
+			remote_head = copy_ref(find_remote_branch(refs, option_branch));
+		}
+
+		if (!remote_head && option_branch)
+			warning(_("Could not find remote branch %s to clone."),
+				option_branch);
+		else {
+			int i;
+			for (i = 0; i < refspec->nr; i++)
+				get_fetch_map(remote_head, &refspec->items[i],
+					      &tail, 0);
+
+			/* if --branch=tag, pull the requested tag explicitly */
+			get_fetch_map(remote_head, tag_refspec, &tail, 0);
+		}
+	} else {
+		int i;
+		for (i = 0; i < refspec->nr; i++)
+			get_fetch_map(refs, &refspec->items[i], &tail, 0);
+	}
+
+	if (!option_mirror && !option_single_branch && !option_no_tags)
+		get_fetch_map(refs, tag_refspec, &tail, 0);
+
+	return local_refs;
+}
+
+static void write_remote_refs(const struct ref *local_refs)
+{
+	const struct ref *r;
+
+	struct ref_transaction *t;
+	struct strbuf err = STRBUF_INIT;
+
+	t = ref_transaction_begin(&err);
+	if (!t)
+		die("%s", err.buf);
+
+	for (r = local_refs; r; r = r->next) {
+		if (!r->peer_ref)
+			continue;
+		if (ref_transaction_create(t, r->peer_ref->name, &r->old_oid,
+					   0, NULL, &err))
+			die("%s", err.buf);
+	}
+
+	if (initial_ref_transaction_commit(t, &err))
+		die("%s", err.buf);
+
+	strbuf_release(&err);
+	ref_transaction_free(t);
+}
+
+static void write_followtags(const struct ref *refs, const char *msg)
+{
+	const struct ref *ref;
+	for (ref = refs; ref; ref = ref->next) {
+		if (!starts_with(ref->name, "refs/tags/"))
+			continue;
+		if (ends_with(ref->name, "^{}"))
+			continue;
+		if (!has_object_file(&ref->old_oid))
+			continue;
+		update_ref(msg, ref->name, &ref->old_oid, NULL, 0,
+			   UPDATE_REFS_DIE_ON_ERR);
+	}
+}
+
+static int iterate_ref_map(void *cb_data, struct object_id *oid)
+{
+	struct ref **rm = cb_data;
+	struct ref *ref = *rm;
+
+	/*
+	 * Skip anything missing a peer_ref, which we are not
+	 * actually going to write a ref for.
+	 */
+	while (ref && !ref->peer_ref)
+		ref = ref->next;
+	/* Returning -1 notes "end of list" to the caller. */
+	if (!ref)
+		return -1;
+
+	oidcpy(oid, &ref->old_oid);
+	*rm = ref->next;
+	return 0;
+}
+
+static void update_remote_refs(const struct ref *refs,
+			       const struct ref *mapped_refs,
+			       const struct ref *remote_head_points_at,
+			       const char *branch_top,
+			       const char *msg,
+			       struct transport *transport,
+			       int check_connectivity,
+			       int check_refs_only)
+{
+	const struct ref *rm = mapped_refs;
+
+	if (check_connectivity) {
+		struct check_connected_options opt = CHECK_CONNECTED_INIT;
+
+		opt.transport = transport;
+		opt.progress = transport->progress;
+		opt.check_refs_only = !!check_refs_only;
+
+		if (check_connected(iterate_ref_map, &rm, &opt))
+			die(_("remote did not send all necessary objects"));
+	}
+
+	if (refs) {
+		write_remote_refs(mapped_refs);
+		if (option_single_branch && !option_no_tags)
+			write_followtags(refs, msg);
+	}
+
+	if (remote_head_points_at && !option_bare) {
+		struct strbuf head_ref = STRBUF_INIT;
+		strbuf_addstr(&head_ref, branch_top);
+		strbuf_addstr(&head_ref, "HEAD");
+		if (create_symref(head_ref.buf,
+				  remote_head_points_at->peer_ref->name,
+				  msg) < 0)
+			die(_("unable to update %s"), head_ref.buf);
+		strbuf_release(&head_ref);
+	}
+}
+
+static void update_head(const struct ref *our, const struct ref *remote,
+			const char *msg)
+{
+	const char *head;
+	if (our && skip_prefix(our->name, "refs/heads/", &head)) {
+		/* Local default branch link */
+		if (create_symref("HEAD", our->name, NULL) < 0)
+			die(_("unable to update HEAD"));
+		if (!option_bare) {
+			update_ref(msg, "HEAD", &our->old_oid, NULL, 0,
+				   UPDATE_REFS_DIE_ON_ERR);
+			install_branch_config(0, head, option_origin, our->name);
+		}
+	} else if (our) {
+		struct commit *c = lookup_commit_reference(the_repository,
+							   &our->old_oid);
+		/* --branch specifies a non-branch (i.e. tags), detach HEAD */
+		update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF,
+			   UPDATE_REFS_DIE_ON_ERR);
+	} else if (remote) {
+		/*
+		 * We know remote HEAD points to a non-branch, or
+		 * HEAD points to a branch but we don't know which one.
+		 * Detach HEAD in all these cases.
+		 */
+		update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
+			   UPDATE_REFS_DIE_ON_ERR);
+	}
+}
+
+static int checkout(int submodule_progress)
+{
+	struct object_id oid;
+	char *head;
+	struct lock_file lock_file = LOCK_INIT;
+	struct unpack_trees_options opts;
+	struct tree *tree;
+	struct tree_desc t;
+	int err = 0;
+
+	if (option_no_checkout)
+		return 0;
+
+	head = resolve_refdup("HEAD", RESOLVE_REF_READING, &oid, NULL);
+	if (!head) {
+		warning(_("remote HEAD refers to nonexistent ref, "
+			  "unable to checkout.\n"));
+		return 0;
+	}
+	if (!strcmp(head, "HEAD")) {
+		if (advice_detached_head)
+			detach_advice(oid_to_hex(&oid));
+	} else {
+		if (!starts_with(head, "refs/heads/"))
+			die(_("HEAD not found below refs/heads!"));
+	}
+	free(head);
+
+	/* We need to be in the new work tree for the checkout */
+	setup_work_tree();
+
+	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+
+	memset(&opts, 0, sizeof opts);
+	opts.update = 1;
+	opts.merge = 1;
+	opts.clone = 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(&oid);
+	parse_tree(tree);
+	init_tree_desc(&t, tree->buffer, tree->size);
+	if (unpack_trees(1, &t, &opts) < 0)
+		die(_("unable to checkout working tree"));
+
+	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+		die(_("unable to write new index file"));
+
+	err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
+			   oid_to_hex(&oid), "1", NULL);
+
+	if (!err && (option_recurse_submodules.nr > 0)) {
+		struct argv_array args = ARGV_ARRAY_INIT;
+		argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
+
+		if (option_shallow_submodules == 1)
+			argv_array_push(&args, "--depth=1");
+
+		if (max_jobs != -1)
+			argv_array_pushf(&args, "--jobs=%d", max_jobs);
+
+		if (submodule_progress)
+			argv_array_push(&args, "--progress");
+
+		if (option_verbosity < 0)
+			argv_array_push(&args, "--quiet");
+
+		if (option_remote_submodules) {
+			argv_array_push(&args, "--remote");
+			argv_array_push(&args, "--no-fetch");
+		}
+
+		err = run_command_v_opt(args.argv, RUN_GIT_CMD);
+		argv_array_clear(&args);
+	}
+
+	return err;
+}
+
+static int write_one_config(const char *key, const char *value, void *data)
+{
+	return git_config_set_multivar_gently(key,
+					      value ? value : "true",
+					      CONFIG_REGEX_NONE, 0);
+}
+
+static void write_config(struct string_list *config)
+{
+	int i;
+
+	for (i = 0; i < config->nr; i++) {
+		if (git_config_parse_parameter(config->items[i].string,
+					       write_one_config, NULL) < 0)
+			die(_("unable to write parameters to config file"));
+	}
+}
+
+static void write_refspec_config(const char *src_ref_prefix,
+		const struct ref *our_head_points_at,
+		const struct ref *remote_head_points_at,
+		struct strbuf *branch_top)
+{
+	struct strbuf key = STRBUF_INIT;
+	struct strbuf value = STRBUF_INIT;
+
+	if (option_mirror || !option_bare) {
+		if (option_single_branch && !option_mirror) {
+			if (option_branch) {
+				if (starts_with(our_head_points_at->name, "refs/tags/"))
+					strbuf_addf(&value, "+%s:%s", our_head_points_at->name,
+						our_head_points_at->name);
+				else
+					strbuf_addf(&value, "+%s:%s%s", our_head_points_at->name,
+						branch_top->buf, option_branch);
+			} else if (remote_head_points_at) {
+				const char *head = remote_head_points_at->name;
+				if (!skip_prefix(head, "refs/heads/", &head))
+					BUG("remote HEAD points at non-head?");
+
+				strbuf_addf(&value, "+%s:%s%s", remote_head_points_at->name,
+						branch_top->buf, head);
+			}
+			/*
+			 * otherwise, the next "git fetch" will
+			 * simply fetch from HEAD without updating
+			 * any remote-tracking branch, which is what
+			 * we want.
+			 */
+		} else {
+			strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top->buf);
+		}
+		/* Configure the remote */
+		if (value.len) {
+			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_release(&key);
+	strbuf_release(&value);
+}
+
+static void dissociate_from_references(void)
+{
+	static const char* argv[] = { "repack", "-a", "-d", NULL };
+	char *alternates = git_pathdup("objects/info/alternates");
+
+	if (!access(alternates, F_OK)) {
+		if (run_command_v_opt(argv, RUN_GIT_CMD|RUN_COMMAND_NO_STDIN))
+			die(_("cannot repack to clean up"));
+		if (unlink(alternates) && errno != ENOENT)
+			die_errno(_("cannot unlink temporary alternates file"));
+	}
+	free(alternates);
+}
+
+static int dir_exists(const char *path)
+{
+	struct stat sb;
+	return !stat(path, &sb);
+}
+
+int cmd_clone(int argc, const char **argv, const char *prefix)
+{
+	int is_bundle = 0, is_local;
+	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;
+	const struct ref *ref;
+	struct strbuf key = STRBUF_INIT;
+	struct strbuf default_refspec = STRBUF_INIT;
+	struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
+	struct transport *transport = NULL;
+	const char *src_ref_prefix = "refs/heads/";
+	struct remote *remote;
+	int err = 0, complete_refs_before_fetch = 1;
+	int submodule_progress;
+
+	struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
+
+	fetch_if_missing = 0;
+
+	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_depth || option_since || option_not.nr)
+		deepen = 1;
+	if (option_single_branch == -1)
+		option_single_branch = deepen ? 1 : 0;
+
+	if (option_mirror)
+		option_bare = 1;
+
+	if (option_bare) {
+		if (option_origin)
+			die(_("--bare and --origin %s options are incompatible."),
+			    option_origin);
+		if (real_git_dir)
+			die(_("--bare and --separate-git-dir are incompatible."));
+		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 = absolute_pathdup(repo_name);
+	else if (!strchr(repo_name, ':'))
+		die(_("repository '%s' does not exist"), repo_name);
+	else
+		repo = repo_name;
+
+	/* no need to be strict, transport_set_option() will validate it again */
+	if (option_depth && atoi(option_depth) < 1)
+		die(_("depth %s is not a positive number"), option_depth);
+
+	if (argc == 2)
+		dir = xstrdup(argv[1]);
+	else
+		dir = guess_dir_name(repo_name, is_bundle, option_bare);
+	strip_trailing_slashes(dir);
+
+	dest_exists = dir_exists(dir);
+	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 && dir_exists(work_tree))
+			die(_("working tree '%s' already exists."), work_tree);
+	}
+
+	if (option_bare || work_tree)
+		git_dir = xstrdup(dir);
+	else {
+		work_tree = dir;
+		git_dir = mkpathdup("%s/.git", dir);
+	}
+
+	atexit(remove_junk);
+	sigchain_push_common(remove_junk_on_signal);
+
+	if (!option_bare) {
+		if (safe_create_leading_directories_const(work_tree) < 0)
+			die_errno(_("could not create leading directories of '%s'"),
+				  work_tree);
+		if (dest_exists)
+			junk_work_tree_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
+		else if (mkdir(work_tree, 0777))
+			die_errno(_("could not create work tree dir '%s'"),
+				  work_tree);
+		junk_work_tree = work_tree;
+		set_git_work_tree(work_tree);
+	}
+
+	if (real_git_dir) {
+		if (dir_exists(real_git_dir))
+			junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
+		junk_git_dir = real_git_dir;
+	} else {
+		if (dest_exists)
+			junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
+		junk_git_dir = git_dir;
+	}
+	if (safe_create_leading_directories_const(git_dir) < 0)
+		die(_("could not create leading directories of '%s'"), git_dir);
+
+	if (0 <= option_verbosity) {
+		if (option_bare)
+			fprintf(stderr, _("Cloning into bare repository '%s'...\n"), dir);
+		else
+			fprintf(stderr, _("Cloning into '%s'...\n"), dir);
+	}
+
+	if (option_recurse_submodules.nr > 0) {
+		struct string_list_item *item;
+		struct strbuf sb = STRBUF_INIT;
+
+		/* remove duplicates */
+		string_list_sort(&option_recurse_submodules);
+		string_list_remove_duplicates(&option_recurse_submodules, 0);
+
+		/*
+		 * NEEDSWORK: In a multi-working-tree world, this needs to be
+		 * set in the per-worktree config.
+		 */
+		for_each_string_list_item(item, &option_recurse_submodules) {
+			strbuf_addf(&sb, "submodule.active=%s",
+				    item->string);
+			string_list_append(&option_config,
+					   strbuf_detach(&sb, NULL));
+		}
+
+		if (option_required_reference.nr &&
+		    option_optional_reference.nr)
+			die(_("clone --recursive is not compatible with "
+			      "both --reference and --reference-if-able"));
+		else if (option_required_reference.nr) {
+			string_list_append(&option_config,
+				"submodule.alternateLocation=superproject");
+			string_list_append(&option_config,
+				"submodule.alternateErrorStrategy=die");
+		} else if (option_optional_reference.nr) {
+			string_list_append(&option_config,
+				"submodule.alternateLocation=superproject");
+			string_list_append(&option_config,
+				"submodule.alternateErrorStrategy=info");
+		}
+	}
+
+	init_db(git_dir, real_git_dir, option_template, INIT_DB_QUIET);
+
+	if (real_git_dir)
+		git_dir = real_git_dir;
+
+	write_config(&option_config);
+
+	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(&key, "remote.%s.url", option_origin);
+	git_config_set(key.buf, repo);
+	strbuf_reset(&key);
+
+	if (option_no_tags) {
+		strbuf_addf(&key, "remote.%s.tagOpt", option_origin);
+		git_config_set(key.buf, "--no-tags");
+		strbuf_reset(&key);
+	}
+
+	if (option_required_reference.nr || option_optional_reference.nr)
+		setup_reference();
+
+	remote = remote_get(option_origin);
+
+	strbuf_addf(&default_refspec, "+%s*:%s*", src_ref_prefix,
+		    branch_top.buf);
+	refspec_append(&remote->fetch, default_refspec.buf);
+
+	transport = transport_get(remote, remote->url[0]);
+	transport_set_verbosity(transport, option_verbosity, option_progress);
+	transport->family = family;
+
+	path = get_repo_path(remote->url[0], &is_bundle);
+	is_local = option_local != 0 && path && !is_bundle;
+	if (is_local) {
+		if (option_depth)
+			warning(_("--depth is ignored in local clones; use file:// instead."));
+		if (option_since)
+			warning(_("--shallow-since is ignored in local clones; use file:// instead."));
+		if (option_not.nr)
+			warning(_("--shallow-exclude is ignored in local clones; use file:// instead."));
+		if (filter_options.choice)
+			warning(_("--filter is ignored in local clones; use file:// instead."));
+		if (!access(mkpath("%s/shallow", path), F_OK)) {
+			if (option_local > 0)
+				warning(_("source repository is shallow, ignoring --local"));
+			is_local = 0;
+		}
+	}
+	if (option_local > 0 && !is_local)
+		warning(_("--local is ignored"));
+	transport->cloning = 1;
+
+	transport_set_option(transport, TRANS_OPT_KEEP, "yes");
+
+	if (option_depth)
+		transport_set_option(transport, TRANS_OPT_DEPTH,
+				     option_depth);
+	if (option_since)
+		transport_set_option(transport, TRANS_OPT_DEEPEN_SINCE,
+				     option_since);
+	if (option_not.nr)
+		transport_set_option(transport, TRANS_OPT_DEEPEN_NOT,
+				     (const char *)&option_not);
+	if (option_single_branch)
+		transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
+
+	if (option_upload_pack)
+		transport_set_option(transport, TRANS_OPT_UPLOADPACK,
+				     option_upload_pack);
+
+	if (server_options.nr)
+		transport->server_options = &server_options;
+
+	if (filter_options.choice) {
+		struct strbuf expanded_filter_spec = STRBUF_INIT;
+		expand_list_objects_filter_spec(&filter_options,
+						&expanded_filter_spec);
+		transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
+				     expanded_filter_spec.buf);
+		transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+		strbuf_release(&expanded_filter_spec);
+	}
+
+	if (transport->smart_options && !deepen && !filter_options.choice)
+		transport->smart_options->check_self_contained_and_connected = 1;
+
+
+	argv_array_push(&ref_prefixes, "HEAD");
+	refspec_ref_prefixes(&remote->fetch, &ref_prefixes);
+	if (option_branch)
+		expand_ref_prefix(&ref_prefixes, option_branch);
+	if (!option_no_tags)
+		argv_array_push(&ref_prefixes, "refs/tags/");
+
+	refs = transport_get_remote_refs(transport, &ref_prefixes);
+
+	if (refs) {
+		mapped_refs = wanted_peer_refs(refs, &remote->fetch);
+		/*
+		 * transport_get_remote_refs() may return refs with null sha-1
+		 * in mapped_refs (see struct transport->get_refs_list
+		 * comment). In that case we need fetch it early because
+		 * remote_head code below relies on it.
+		 *
+		 * for normal clones, transport_get_remote_refs() should
+		 * return reliable ref set, we can delay cloning until after
+		 * remote HEAD check.
+		 */
+		for (ref = refs; ref; ref = ref->next)
+			if (is_null_oid(&ref->old_oid)) {
+				complete_refs_before_fetch = 0;
+				break;
+			}
+
+		if (!is_local && !complete_refs_before_fetch)
+			transport_fetch_refs(transport, 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) {
+			our_head_points_at =
+				find_remote_branch(mapped_refs, option_branch);
+
+			if (!our_head_points_at)
+				die(_("Remote branch %s not found in upstream %s"),
+				    option_branch, option_origin);
+		}
+		else
+			our_head_points_at = remote_head_points_at;
+	}
+	else {
+		if (option_branch)
+			die(_("Remote branch %s not found in upstream %s"),
+					option_branch, option_origin);
+
+		warning(_("You appear to have cloned an empty repository."));
+		mapped_refs = NULL;
+		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");
+	}
+
+	write_refspec_config(src_ref_prefix, our_head_points_at,
+			remote_head_points_at, &branch_top);
+
+	if (filter_options.choice)
+		partial_clone_register(option_origin, &filter_options);
+
+	if (is_local)
+		clone_local(path, git_dir);
+	else if (refs && complete_refs_before_fetch)
+		transport_fetch_refs(transport, mapped_refs);
+
+	update_remote_refs(refs, mapped_refs, remote_head_points_at,
+			   branch_top.buf, reflog_msg.buf, transport,
+			   !is_local, filter_options.choice);
+
+	update_head(our_head_points_at, remote_head, reflog_msg.buf);
+
+	/*
+	 * We want to show progress for recursive submodule clones iff
+	 * we did so for the main clone. But only the transport knows
+	 * the final decision for this flag, so we need to rescue the value
+	 * before we free the transport.
+	 */
+	submodule_progress = transport->progress;
+
+	transport_unlock_pack(transport);
+	transport_disconnect(transport);
+
+	if (option_dissociate) {
+		close_object_store(the_repository->objects);
+		dissociate_from_references();
+	}
+
+	junk_mode = JUNK_LEAVE_REPO;
+	fetch_if_missing = 1;
+	err = checkout(submodule_progress);
+
+	strbuf_release(&reflog_msg);
+	strbuf_release(&branch_top);
+	strbuf_release(&key);
+	strbuf_release(&default_refspec);
+	junk_mode = JUNK_LEAVE_ALL;
+
+	argv_array_clear(&ref_prefixes);
+	return err;
+}
diff --git a/builtin/column.c b/builtin/column.c
new file mode 100644
index 0000000000..e815e148aa
--- /dev/null
+++ b/builtin/column.c
@@ -0,0 +1,59 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "strbuf.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "column.h"
+
+static const char * const builtin_column_usage[] = {
+	N_("git column [<options>]"),
+	NULL
+};
+static unsigned int colopts;
+
+static int column_config(const char *var, const char *value, void *cb)
+{
+	return git_column_config(var, value, cb, &colopts);
+}
+
+int cmd_column(int argc, const char **argv, const char *prefix)
+{
+	struct string_list list = STRING_LIST_INIT_DUP;
+	struct strbuf sb = STRBUF_INIT;
+	struct column_options copts;
+	const char *command = NULL, *real_command = NULL;
+	struct option options[] = {
+		OPT_STRING(0, "command", &real_command, N_("name"), N_("lookup config vars")),
+		OPT_COLUMN(0, "mode", &colopts, N_("layout to use")),
+		OPT_INTEGER(0, "raw-mode", &colopts, N_("layout to use")),
+		OPT_INTEGER(0, "width", &copts.width, N_("Maximum width")),
+		OPT_STRING(0, "indent", &copts.indent, N_("string"), N_("Padding space on left border")),
+		OPT_INTEGER(0, "nl", &copts.nl, N_("Padding space on right border")),
+		OPT_INTEGER(0, "padding", &copts.padding, N_("Padding space between columns")),
+		OPT_END()
+	};
+
+	/* This one is special and must be the first one */
+	if (argc > 1 && starts_with(argv[1], "--command=")) {
+		command = argv[1] + 10;
+		git_config(column_config, (void *)command);
+	} else
+		git_config(column_config, NULL);
+
+	memset(&copts, 0, sizeof(copts));
+	copts.padding = 1;
+	argc = parse_options(argc, argv, prefix, options, builtin_column_usage, 0);
+	if (argc)
+		usage_with_options(builtin_column_usage, options);
+	if (real_command || command) {
+		if (!real_command || !command || strcmp(real_command, command))
+			die(_("--command must be the first argument"));
+	}
+	finalize_colopts(&colopts, -1);
+	while (!strbuf_getline(&sb, stdin))
+		string_list_append(&list, sb.buf);
+
+	print_columns(&list, colopts, &copts);
+	return 0;
+}
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
new file mode 100644
index 0000000000..38027b83d9
--- /dev/null
+++ b/builtin/commit-graph.c
@@ -0,0 +1,263 @@
+#include "builtin.h"
+#include "config.h"
+#include "dir.h"
+#include "lockfile.h"
+#include "parse-options.h"
+#include "repository.h"
+#include "commit-graph.h"
+#include "object-store.h"
+
+static char const * const builtin_commit_graph_usage[] = {
+	N_("git commit-graph [--object-dir <objdir>]"),
+	N_("git commit-graph read [--object-dir <objdir>]"),
+	N_("git commit-graph verify [--object-dir <objdir>] [--shallow]"),
+	N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] <split options>"),
+	NULL
+};
+
+static const char * const builtin_commit_graph_verify_usage[] = {
+	N_("git commit-graph verify [--object-dir <objdir>] [--shallow]"),
+	NULL
+};
+
+static const char * const builtin_commit_graph_read_usage[] = {
+	N_("git commit-graph read [--object-dir <objdir>]"),
+	NULL
+};
+
+static const char * const builtin_commit_graph_write_usage[] = {
+	N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] <split options>"),
+	NULL
+};
+
+static struct opts_commit_graph {
+	const char *obj_dir;
+	int reachable;
+	int stdin_packs;
+	int stdin_commits;
+	int append;
+	int split;
+	int shallow;
+} opts;
+
+static int graph_verify(int argc, const char **argv)
+{
+	struct commit_graph *graph = NULL;
+	char *graph_name;
+	int open_ok;
+	int fd;
+	struct stat st;
+	int flags = 0;
+
+	static struct option builtin_commit_graph_verify_options[] = {
+		OPT_STRING(0, "object-dir", &opts.obj_dir,
+			   N_("dir"),
+			   N_("The object directory to store the graph")),
+		OPT_BOOL(0, "shallow", &opts.shallow,
+			 N_("if the commit-graph is split, only verify the tip file")),
+		OPT_END(),
+	};
+
+	argc = parse_options(argc, argv, NULL,
+			     builtin_commit_graph_verify_options,
+			     builtin_commit_graph_verify_usage, 0);
+
+	if (!opts.obj_dir)
+		opts.obj_dir = get_object_directory();
+	if (opts.shallow)
+		flags |= COMMIT_GRAPH_VERIFY_SHALLOW;
+
+	graph_name = get_commit_graph_filename(opts.obj_dir);
+	open_ok = open_commit_graph(graph_name, &fd, &st);
+	if (!open_ok && errno != ENOENT)
+		die_errno(_("Could not open commit-graph '%s'"), graph_name);
+
+	FREE_AND_NULL(graph_name);
+
+	if (open_ok)
+		graph = load_commit_graph_one_fd_st(fd, &st);
+	 else
+		graph = read_commit_graph_one(the_repository, opts.obj_dir);
+
+	/* Return failure if open_ok predicted success */
+	if (!graph)
+		return !!open_ok;
+
+	UNLEAK(graph);
+	return verify_commit_graph(the_repository, graph, flags);
+}
+
+static int graph_read(int argc, const char **argv)
+{
+	struct commit_graph *graph = NULL;
+	char *graph_name;
+	int open_ok;
+	int fd;
+	struct stat st;
+
+	static struct option builtin_commit_graph_read_options[] = {
+		OPT_STRING(0, "object-dir", &opts.obj_dir,
+			N_("dir"),
+			N_("The object directory to store the graph")),
+		OPT_END(),
+	};
+
+	argc = parse_options(argc, argv, NULL,
+			     builtin_commit_graph_read_options,
+			     builtin_commit_graph_read_usage, 0);
+
+	if (!opts.obj_dir)
+		opts.obj_dir = get_object_directory();
+
+	graph_name = get_commit_graph_filename(opts.obj_dir);
+
+	open_ok = open_commit_graph(graph_name, &fd, &st);
+	if (!open_ok)
+		die_errno(_("Could not open commit-graph '%s'"), graph_name);
+
+	graph = load_commit_graph_one_fd_st(fd, &st);
+	if (!graph)
+		return 1;
+
+	FREE_AND_NULL(graph_name);
+
+	printf("header: %08x %d %d %d %d\n",
+		ntohl(*(uint32_t*)graph->data),
+		*(unsigned char*)(graph->data + 4),
+		*(unsigned char*)(graph->data + 5),
+		*(unsigned char*)(graph->data + 6),
+		*(unsigned char*)(graph->data + 7));
+	printf("num_commits: %u\n", graph->num_commits);
+	printf("chunks:");
+
+	if (graph->chunk_oid_fanout)
+		printf(" oid_fanout");
+	if (graph->chunk_oid_lookup)
+		printf(" oid_lookup");
+	if (graph->chunk_commit_data)
+		printf(" commit_metadata");
+	if (graph->chunk_extra_edges)
+		printf(" extra_edges");
+	printf("\n");
+
+	UNLEAK(graph);
+
+	return 0;
+}
+
+extern int read_replace_refs;
+static struct split_commit_graph_opts split_opts;
+
+static int graph_write(int argc, const char **argv)
+{
+	struct string_list *pack_indexes = NULL;
+	struct string_list *commit_hex = NULL;
+	struct string_list lines;
+	int result = 0;
+	unsigned int flags = COMMIT_GRAPH_PROGRESS;
+
+	static struct option builtin_commit_graph_write_options[] = {
+		OPT_STRING(0, "object-dir", &opts.obj_dir,
+			N_("dir"),
+			N_("The object directory to store the graph")),
+		OPT_BOOL(0, "reachable", &opts.reachable,
+			N_("start walk at all refs")),
+		OPT_BOOL(0, "stdin-packs", &opts.stdin_packs,
+			N_("scan pack-indexes listed by stdin for commits")),
+		OPT_BOOL(0, "stdin-commits", &opts.stdin_commits,
+			N_("start walk at commits listed by stdin")),
+		OPT_BOOL(0, "append", &opts.append,
+			N_("include all commits already in the commit-graph file")),
+		OPT_BOOL(0, "split", &opts.split,
+			N_("allow writing an incremental commit-graph file")),
+		OPT_INTEGER(0, "max-commits", &split_opts.max_commits,
+			N_("maximum number of commits in a non-base split commit-graph")),
+		OPT_INTEGER(0, "size-multiple", &split_opts.size_multiple,
+			N_("maximum ratio between two levels of a split commit-graph")),
+		OPT_EXPIRY_DATE(0, "expire-time", &split_opts.expire_time,
+			N_("maximum number of commits in a non-base split commit-graph")),
+		OPT_END(),
+	};
+
+	split_opts.size_multiple = 2;
+	split_opts.max_commits = 0;
+	split_opts.expire_time = 0;
+
+	argc = parse_options(argc, argv, NULL,
+			     builtin_commit_graph_write_options,
+			     builtin_commit_graph_write_usage, 0);
+
+	if (opts.reachable + opts.stdin_packs + opts.stdin_commits > 1)
+		die(_("use at most one of --reachable, --stdin-commits, or --stdin-packs"));
+	if (!opts.obj_dir)
+		opts.obj_dir = get_object_directory();
+	if (opts.append)
+		flags |= COMMIT_GRAPH_APPEND;
+	if (opts.split)
+		flags |= COMMIT_GRAPH_SPLIT;
+
+	read_replace_refs = 0;
+
+	if (opts.reachable) {
+		if (write_commit_graph_reachable(opts.obj_dir, flags, &split_opts))
+			return 1;
+		return 0;
+	}
+
+	string_list_init(&lines, 0);
+	if (opts.stdin_packs || opts.stdin_commits) {
+		struct strbuf buf = STRBUF_INIT;
+
+		while (strbuf_getline(&buf, stdin) != EOF)
+			string_list_append(&lines, strbuf_detach(&buf, NULL));
+
+		if (opts.stdin_packs)
+			pack_indexes = &lines;
+		if (opts.stdin_commits)
+			commit_hex = &lines;
+
+		UNLEAK(buf);
+	}
+
+	if (write_commit_graph(opts.obj_dir,
+			       pack_indexes,
+			       commit_hex,
+			       flags,
+			       &split_opts))
+		result = 1;
+
+	UNLEAK(lines);
+	return result;
+}
+
+int cmd_commit_graph(int argc, const char **argv, const char *prefix)
+{
+	static struct option builtin_commit_graph_options[] = {
+		OPT_STRING(0, "object-dir", &opts.obj_dir,
+			N_("dir"),
+			N_("The object directory to store the graph")),
+		OPT_END(),
+	};
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(builtin_commit_graph_usage,
+				   builtin_commit_graph_options);
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix,
+			     builtin_commit_graph_options,
+			     builtin_commit_graph_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+
+	if (argc > 0) {
+		if (!strcmp(argv[0], "read"))
+			return graph_read(argc, argv);
+		if (!strcmp(argv[0], "verify"))
+			return graph_verify(argc, argv);
+		if (!strcmp(argv[0], "write"))
+			return graph_write(argc, argv);
+	}
+
+	usage_with_options(builtin_commit_graph_usage,
+			   builtin_commit_graph_options);
+}
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
new file mode 100644
index 0000000000..b866d83951
--- /dev/null
+++ b/builtin/commit-tree.c
@@ -0,0 +1,152 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+#include "config.h"
+#include "object-store.h"
+#include "repository.h"
+#include "commit.h"
+#include "tree.h"
+#include "builtin.h"
+#include "utf8.h"
+#include "gpg-interface.h"
+#include "parse-options.h"
+
+static const char * const commit_tree_usage[] = {
+	N_("git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] "
+		"[(-F <file>)...] <tree>"),
+	NULL
+};
+
+static const char *sign_commit;
+
+static void new_parent(struct commit *parent, struct commit_list **parents_p)
+{
+	struct object_id *oid = &parent->object.oid;
+	struct commit_list *parents;
+	for (parents = *parents_p; parents; parents = parents->next) {
+		if (parents->item == parent) {
+			error(_("duplicate parent %s ignored"), oid_to_hex(oid));
+			return;
+		}
+		parents_p = &parents->next;
+	}
+	commit_list_insert(parent, parents_p);
+}
+
+static int commit_tree_config(const char *var, const char *value, void *cb)
+{
+	int status = git_gpg_config(var, value, NULL);
+	if (status)
+		return status;
+	return git_default_config(var, value, cb);
+}
+
+static int parse_parent_arg_callback(const struct option *opt,
+		const char *arg, int unset)
+{
+	struct object_id oid;
+	struct commit_list **parents = opt->value;
+
+	BUG_ON_OPT_NEG_NOARG(unset, arg);
+
+	if (get_oid_commit(arg, &oid))
+		die(_("not a valid object name %s"), arg);
+
+	assert_oid_type(&oid, OBJ_COMMIT);
+	new_parent(lookup_commit(the_repository, &oid), parents);
+	return 0;
+}
+
+static int parse_message_arg_callback(const struct option *opt,
+		const char *arg, int unset)
+{
+	struct strbuf *buf = opt->value;
+
+	BUG_ON_OPT_NEG_NOARG(unset, arg);
+
+	if (buf->len)
+		strbuf_addch(buf, '\n');
+	strbuf_addstr(buf, arg);
+	strbuf_complete_line(buf);
+
+	return 0;
+}
+
+static int parse_file_arg_callback(const struct option *opt,
+		const char *arg, int unset)
+{
+	int fd;
+	struct strbuf *buf = opt->value;
+
+	BUG_ON_OPT_NEG_NOARG(unset, arg);
+
+	if (buf->len)
+		strbuf_addch(buf, '\n');
+	if (!strcmp(arg, "-"))
+		fd = 0;
+	else {
+		fd = open(arg, O_RDONLY);
+		if (fd < 0)
+			die_errno(_("git commit-tree: failed to open '%s'"), arg);
+	}
+	if (strbuf_read(buf, fd, 0) < 0)
+		die_errno(_("git commit-tree: failed to read '%s'"), arg);
+	if (fd && close(fd))
+		die_errno(_("git commit-tree: failed to close '%s'"), arg);
+
+	return 0;
+}
+
+int cmd_commit_tree(int argc, const char **argv, const char *prefix)
+{
+	static struct strbuf buffer = STRBUF_INIT;
+	struct commit_list *parents = NULL;
+	struct object_id tree_oid;
+	struct object_id commit_oid;
+
+	struct option options[] = {
+		{ OPTION_CALLBACK, 'p', NULL, &parents, N_("parent"),
+			N_("id of a parent commit object"), PARSE_OPT_NONEG,
+			parse_parent_arg_callback },
+		{ OPTION_CALLBACK, 'm', NULL, &buffer, N_("message"),
+			N_("commit message"), PARSE_OPT_NONEG,
+			parse_message_arg_callback },
+		{ OPTION_CALLBACK, 'F', NULL, &buffer, N_("file"),
+			N_("read commit log message from file"), PARSE_OPT_NONEG,
+			parse_file_arg_callback },
+		{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
+			N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_END()
+	};
+
+	git_config(commit_tree_config, NULL);
+
+	if (argc < 2 || !strcmp(argv[1], "-h"))
+		usage_with_options(commit_tree_usage, options);
+
+	argc = parse_options(argc, argv, prefix, options, commit_tree_usage, 0);
+
+	if (argc != 1)
+		die(_("must give exactly one tree"));
+
+	if (get_oid_tree(argv[0], &tree_oid))
+		die(_("not a valid object name %s"), argv[0]);
+
+	if (!buffer.len) {
+		if (strbuf_read(&buffer, 0, 0) < 0)
+			die_errno(_("git commit-tree: failed to read"));
+	}
+
+	if (commit_tree(buffer.buf, buffer.len, &tree_oid, parents, &commit_oid,
+			NULL, sign_commit)) {
+		strbuf_release(&buffer);
+		return 1;
+	}
+
+	printf("%s\n", oid_to_hex(&commit_oid));
+	strbuf_release(&buffer);
+	return 0;
+}
diff --git a/builtin/commit.c b/builtin/commit.c
new file mode 100644
index 0000000000..ae7aaf6dc6
--- /dev/null
+++ b/builtin/commit.c
@@ -0,0 +1,1716 @@
+/*
+ * Builtin "git commit"
+ *
+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
+ * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
+ */
+
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "lockfile.h"
+#include "cache-tree.h"
+#include "color.h"
+#include "dir.h"
+#include "builtin.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "commit.h"
+#include "revision.h"
+#include "wt-status.h"
+#include "run-command.h"
+#include "refs.h"
+#include "log-tree.h"
+#include "strbuf.h"
+#include "utf8.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "rerere.h"
+#include "unpack-trees.h"
+#include "quote.h"
+#include "submodule.h"
+#include "gpg-interface.h"
+#include "column.h"
+#include "sequencer.h"
+#include "mailmap.h"
+#include "help.h"
+#include "commit-reach.h"
+#include "commit-graph.h"
+
+static const char * const builtin_commit_usage[] = {
+	N_("git commit [<options>] [--] <pathspec>..."),
+	NULL
+};
+
+static const char * const builtin_status_usage[] = {
+	N_("git status [<options>] [--] <pathspec>..."),
+	NULL
+};
+
+static const char empty_amend_advice[] =
+N_("You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n");
+
+static const char empty_cherry_pick_advice[] =
+N_("The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n");
+
+static const char empty_cherry_pick_advice_single[] =
+N_("Otherwise, please use 'git cherry-pick --skip'\n");
+
+static const char empty_cherry_pick_advice_multi[] =
+N_("and then use:\n"
+"\n"
+"    git cherry-pick --continue\n"
+"\n"
+"to resume cherry-picking the remaining commits.\n"
+"If you wish to skip this commit, use:\n"
+"\n"
+"    git cherry-pick --skip\n"
+"\n");
+
+static const char *color_status_slots[] = {
+	[WT_STATUS_HEADER]	  = "header",
+	[WT_STATUS_UPDATED]	  = "updated",
+	[WT_STATUS_CHANGED]	  = "changed",
+	[WT_STATUS_UNTRACKED]	  = "untracked",
+	[WT_STATUS_NOBRANCH]	  = "noBranch",
+	[WT_STATUS_UNMERGED]	  = "unmerged",
+	[WT_STATUS_LOCAL_BRANCH]  = "localBranch",
+	[WT_STATUS_REMOTE_BRANCH] = "remoteBranch",
+	[WT_STATUS_ONBRANCH]	  = "branch",
+};
+
+static const char *use_message_buffer;
+static struct lock_file index_lock; /* real index */
+static struct lock_file false_lock; /* used only for partial commits */
+static enum {
+	COMMIT_AS_IS = 1,
+	COMMIT_NORMAL,
+	COMMIT_PARTIAL
+} commit_style;
+
+static const char *logfile, *force_author;
+static const char *template_file;
+/*
+ * The _message variables are commit names from which to take
+ * the commit message and/or authorship.
+ */
+static const char *author_message, *author_message_buffer;
+static char *edit_message, *use_message;
+static char *fixup_message, *squash_message;
+static int all, also, interactive, patch_interactive, only, amend, signoff;
+static int edit_flag = -1; /* unspecified */
+static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
+static int config_commit_verbose = -1; /* unspecified */
+static int no_post_rewrite, allow_empty_message;
+static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
+static char *sign_commit;
+
+/*
+ * The default commit message cleanup mode will remove the lines
+ * beginning with # (shell comments) and leading and trailing
+ * whitespaces (empty lines or containing only whitespaces)
+ * if editor is used, and only the whitespaces if the message
+ * is specified explicitly.
+ */
+static enum commit_msg_cleanup_mode cleanup_mode;
+static const char *cleanup_arg;
+
+static enum commit_whence whence;
+static int sequencer_in_use;
+static int use_editor = 1, include_status = 1;
+static int have_option_m;
+static struct strbuf message = STRBUF_INIT;
+
+static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
+
+static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
+{
+	enum wt_status_format *value = (enum wt_status_format *)opt->value;
+	if (unset)
+		*value = STATUS_FORMAT_NONE;
+	else if (!arg)
+		*value = STATUS_FORMAT_PORCELAIN;
+	else if (!strcmp(arg, "v1") || !strcmp(arg, "1"))
+		*value = STATUS_FORMAT_PORCELAIN;
+	else if (!strcmp(arg, "v2") || !strcmp(arg, "2"))
+		*value = STATUS_FORMAT_PORCELAIN_V2;
+	else
+		die("unsupported porcelain version '%s'", arg);
+
+	return 0;
+}
+
+static int opt_parse_m(const struct option *opt, const char *arg, int unset)
+{
+	struct strbuf *buf = opt->value;
+	if (unset) {
+		have_option_m = 0;
+		strbuf_setlen(buf, 0);
+	} else {
+		have_option_m = 1;
+		if (buf->len)
+			strbuf_addch(buf, '\n');
+		strbuf_addstr(buf, arg);
+		strbuf_complete_line(buf);
+	}
+	return 0;
+}
+
+static int opt_parse_rename_score(const struct option *opt, const char *arg, int unset)
+{
+	const char **value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (arg != NULL && *arg == '=')
+		arg = arg + 1;
+
+	*value = arg;
+	return 0;
+}
+
+static void determine_whence(struct wt_status *s)
+{
+	if (file_exists(git_path_merge_head(the_repository)))
+		whence = FROM_MERGE;
+	else if (file_exists(git_path_cherry_pick_head(the_repository))) {
+		whence = FROM_CHERRY_PICK;
+		if (file_exists(git_path_seq_dir()))
+			sequencer_in_use = 1;
+	}
+	else
+		whence = FROM_COMMIT;
+	if (s)
+		s->whence = whence;
+}
+
+static void status_init_config(struct wt_status *s, config_fn_t fn)
+{
+	wt_status_prepare(the_repository, s);
+	init_diff_ui_defaults();
+	git_config(fn, s);
+	determine_whence(s);
+	s->hints = advice_status_hints; /* must come after git_config() */
+}
+
+static void rollback_index_files(void)
+{
+	switch (commit_style) {
+	case COMMIT_AS_IS:
+		break; /* nothing to do */
+	case COMMIT_NORMAL:
+		rollback_lock_file(&index_lock);
+		break;
+	case COMMIT_PARTIAL:
+		rollback_lock_file(&index_lock);
+		rollback_lock_file(&false_lock);
+		break;
+	}
+}
+
+static int commit_index_files(void)
+{
+	int err = 0;
+
+	switch (commit_style) {
+	case COMMIT_AS_IS:
+		break; /* nothing to do */
+	case COMMIT_NORMAL:
+		err = commit_lock_file(&index_lock);
+		break;
+	case COMMIT_PARTIAL:
+		err = commit_lock_file(&index_lock);
+		rollback_lock_file(&false_lock);
+		break;
+	}
+
+	return err;
+}
+
+/*
+ * Take a union of paths in the index and the named tree (typically, "HEAD"),
+ * and return the paths that match the given pattern in list.
+ */
+static int list_paths(struct string_list *list, const char *with_tree,
+		      const struct pathspec *pattern)
+{
+	int i, ret;
+	char *m;
+
+	if (!pattern->nr)
+		return 0;
+
+	m = xcalloc(1, pattern->nr);
+
+	if (with_tree) {
+		char *max_prefix = common_prefix(pattern);
+		overlay_tree_on_index(&the_index, with_tree, max_prefix);
+		free(max_prefix);
+	}
+
+	for (i = 0; i < active_nr; i++) {
+		const struct cache_entry *ce = active_cache[i];
+		struct string_list_item *item;
+
+		if (ce->ce_flags & CE_UPDATE)
+			continue;
+		if (!ce_path_match(&the_index, ce, pattern, m))
+			continue;
+		item = string_list_insert(list, ce->name);
+		if (ce_skip_worktree(ce))
+			item->util = item; /* better a valid pointer than a fake one */
+	}
+
+	ret = report_path_error(m, pattern);
+	free(m);
+	return ret;
+}
+
+static void add_remove_files(struct string_list *list)
+{
+	int i;
+	for (i = 0; i < list->nr; i++) {
+		struct stat st;
+		struct string_list_item *p = &(list->items[i]);
+
+		/* p->util is skip-worktree */
+		if (p->util)
+			continue;
+
+		if (!lstat(p->string, &st)) {
+			if (add_to_cache(p->string, &st, 0))
+				die(_("updating files failed"));
+		} else
+			remove_file_from_cache(p->string);
+	}
+}
+
+static void create_base_index(const struct commit *current_head)
+{
+	struct tree *tree;
+	struct unpack_trees_options opts;
+	struct tree_desc t;
+
+	if (!current_head) {
+		discard_cache();
+		return;
+	}
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 1;
+	opts.index_only = 1;
+	opts.merge = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+
+	opts.fn = oneway_merge;
+	tree = parse_tree_indirect(&current_head->object.oid);
+	if (!tree)
+		die(_("failed to unpack HEAD tree object"));
+	parse_tree(tree);
+	init_tree_desc(&t, tree->buffer, tree->size);
+	if (unpack_trees(1, &t, &opts))
+		exit(128); /* We've already reported the error, finish dying */
+}
+
+static void refresh_cache_or_die(int refresh_flags)
+{
+	/*
+	 * refresh_flags contains REFRESH_QUIET, so the only errors
+	 * are for unmerged entries.
+	 */
+	if (refresh_cache(refresh_flags | REFRESH_IN_PORCELAIN))
+		die_resolve_conflict("commit");
+}
+
+static const char *prepare_index(int argc, const char **argv, const char *prefix,
+				 const struct commit *current_head, int is_status)
+{
+	struct string_list partial = STRING_LIST_INIT_DUP;
+	struct pathspec pathspec;
+	int refresh_flags = REFRESH_QUIET;
+	const char *ret;
+
+	if (is_status)
+		refresh_flags |= REFRESH_UNMERGED;
+	parse_pathspec(&pathspec, 0,
+		       PATHSPEC_PREFER_FULL,
+		       prefix, argv);
+
+	if (read_cache_preload(&pathspec) < 0)
+		die(_("index file corrupt"));
+
+	if (interactive) {
+		char *old_index_env = NULL;
+		hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+
+		refresh_cache_or_die(refresh_flags);
+
+		if (write_locked_index(&the_index, &index_lock, 0))
+			die(_("unable to create temporary index"));
+
+		old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
+		setenv(INDEX_ENVIRONMENT, get_lock_file_path(&index_lock), 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);
+		FREE_AND_NULL(old_index_env);
+
+		discard_cache();
+		read_cache_from(get_lock_file_path(&index_lock));
+		if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
+			if (reopen_lock_file(&index_lock) < 0)
+				die(_("unable to write index file"));
+			if (write_locked_index(&the_index, &index_lock, 0))
+				die(_("unable to update temporary index"));
+		} else
+			warning(_("Failed to update main cache tree"));
+
+		commit_style = COMMIT_NORMAL;
+		ret = get_lock_file_path(&index_lock);
+		goto out;
+	}
+
+	/*
+	 * 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.nr)) {
+		hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+		add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
+		refresh_cache_or_die(refresh_flags);
+		update_main_cache_tree(WRITE_TREE_SILENT);
+		if (write_locked_index(&the_index, &index_lock, 0))
+			die(_("unable to write new_index file"));
+		commit_style = COMMIT_NORMAL;
+		ret = get_lock_file_path(&index_lock);
+		goto out;
+	}
+
+	/*
+	 * 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.nr) {
+		hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+		refresh_cache_or_die(refresh_flags);
+		if (active_cache_changed
+		    || !cache_tree_fully_valid(active_cache_tree))
+			update_main_cache_tree(WRITE_TREE_SILENT);
+		if (write_locked_index(&the_index, &index_lock,
+				       COMMIT_LOCK | SKIP_IF_UNCHANGED))
+			die(_("unable to write new_index file"));
+		commit_style = COMMIT_AS_IS;
+		ret = get_index_file();
+		goto out;
+	}
+
+	/*
+	 * 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."));
+	}
+
+	if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
+		exit(1);
+
+	discard_cache();
+	if (read_cache() < 0)
+		die(_("cannot read the index"));
+
+	hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+	add_remove_files(&partial);
+	refresh_cache(REFRESH_QUIET);
+	update_main_cache_tree(WRITE_TREE_SILENT);
+	if (write_locked_index(&the_index, &index_lock, 0))
+		die(_("unable to write new_index file"));
+
+	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_locked_index(&the_index, &false_lock, 0))
+		die(_("unable to write temporary index file"));
+
+	discard_cache();
+	ret = get_lock_file_path(&false_lock);
+	read_cache_from(ret);
+out:
+	string_list_clear(&partial, 0);
+	clear_pathspec(&pathspec);
+	return ret;
+}
+
+static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
+		      struct wt_status *s)
+{
+	struct object_id oid;
+
+	if (s->relative_paths)
+		s->prefix = prefix;
+
+	if (amend) {
+		s->amend = 1;
+		s->reference = "HEAD^1";
+	}
+	s->verbose = verbose;
+	s->index_file = index_file;
+	s->fp = fp;
+	s->nowarn = nowarn;
+	s->is_initial = get_oid(s->reference, &oid) ? 1 : 0;
+	if (!s->is_initial)
+		hashcpy(s->sha1_commit, oid.hash);
+	s->status_format = status_format;
+	s->ignore_submodule_arg = ignore_submodule_arg;
+
+	wt_status_collect(s);
+	wt_status_print(s);
+	wt_status_collect_free_buffers(s);
+
+	return s->committable;
+}
+
+static int is_a_merge(const struct commit *current_head)
+{
+	return !!(current_head->parents && current_head->parents->next);
+}
+
+static void assert_split_ident(struct ident_split *id, const struct strbuf *buf)
+{
+	if (split_ident_line(id, buf->buf, buf->len) || !id->date_begin)
+		BUG("unable to parse our own ident: %s", buf->buf);
+}
+
+static void export_one(const char *var, const char *s, const char *e, int hack)
+{
+	struct strbuf buf = STRBUF_INIT;
+	if (hack)
+		strbuf_addch(&buf, hack);
+	strbuf_addf(&buf, "%.*s", (int)(e - s), s);
+	setenv(var, buf.buf, 1);
+	strbuf_release(&buf);
+}
+
+static int parse_force_date(const char *in, struct strbuf *out)
+{
+	strbuf_addch(out, '@');
+
+	if (parse_date(in, out) < 0) {
+		int errors = 0;
+		unsigned long t = approxidate_careful(in, &errors);
+		if (errors)
+			return -1;
+		strbuf_addf(out, "%lu", t);
+	}
+
+	return 0;
+}
+
+static void set_ident_var(char **buf, char *val)
+{
+	free(*buf);
+	*buf = val;
+}
+
+static void determine_author_info(struct strbuf *author_ident)
+{
+	char *name, *email, *date;
+	struct ident_split author;
+
+	name = xstrdup_or_null(getenv("GIT_AUTHOR_NAME"));
+	email = xstrdup_or_null(getenv("GIT_AUTHOR_EMAIL"));
+	date = xstrdup_or_null(getenv("GIT_AUTHOR_DATE"));
+
+	if (author_message) {
+		struct ident_split ident;
+		size_t len;
+		const char *a;
+
+		a = find_commit_header(author_message_buffer, "author", &len);
+		if (!a)
+			die(_("commit '%s' lacks author header"), author_message);
+		if (split_ident_line(&ident, a, len) < 0)
+			die(_("commit '%s' has malformed author line"), author_message);
+
+		set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin));
+		set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin));
+
+		if (ident.date_begin) {
+			struct strbuf date_buf = STRBUF_INIT;
+			strbuf_addch(&date_buf, '@');
+			strbuf_add(&date_buf, ident.date_begin, ident.date_end - ident.date_begin);
+			strbuf_addch(&date_buf, ' ');
+			strbuf_add(&date_buf, ident.tz_begin, ident.tz_end - ident.tz_begin);
+			set_ident_var(&date, strbuf_detach(&date_buf, NULL));
+		}
+	}
+
+	if (force_author) {
+		struct ident_split ident;
+
+		if (split_ident_line(&ident, force_author, strlen(force_author)) < 0)
+			die(_("malformed --author parameter"));
+		set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin));
+		set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin));
+	}
+
+	if (force_date) {
+		struct strbuf date_buf = STRBUF_INIT;
+		if (parse_force_date(force_date, &date_buf))
+			die(_("invalid date format: %s"), force_date);
+		set_ident_var(&date, strbuf_detach(&date_buf, NULL));
+	}
+
+	strbuf_addstr(author_ident, fmt_ident(name, email, WANT_AUTHOR_IDENT, date,
+				IDENT_STRICT));
+	assert_split_ident(&author, author_ident);
+	export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0);
+	export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);
+	export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@');
+	free(name);
+	free(email);
+	free(date);
+}
+
+static int author_date_is_interesting(void)
+{
+	return author_message || force_date;
+}
+
+static void adjust_comment_line_char(const struct strbuf *sb)
+{
+	char candidates[] = "#;@!$%^&|:";
+	char *candidate;
+	const char *p;
+
+	comment_line_char = candidates[0];
+	if (!memchr(sb->buf, comment_line_char, sb->len))
+		return;
+
+	p = sb->buf;
+	candidate = strchr(candidates, *p);
+	if (candidate)
+		*candidate = ' ';
+	for (p = sb->buf; *p; p++) {
+		if ((p[0] == '\n' || p[0] == '\r') && p[1]) {
+			candidate = strchr(candidates, p[1]);
+			if (candidate)
+				*candidate = ' ';
+		}
+	}
+
+	for (p = candidates; *p == ' '; p++)
+		;
+	if (!*p)
+		die(_("unable to select a comment character that is not used\n"
+		      "in the current commit message"));
+	comment_line_char = *p;
+}
+
+static int prepare_to_commit(const char *index_file, const char *prefix,
+			     struct commit *current_head,
+			     struct wt_status *s,
+			     struct strbuf *author_ident)
+{
+	struct stat statbuf;
+	struct strbuf committer_ident = STRBUF_INIT;
+	int committable;
+	struct strbuf sb = STRBUF_INIT;
+	const char *hook_arg1 = NULL;
+	const char *hook_arg2 = NULL;
+	int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
+	int old_display_comment_prefix;
+	int merge_contains_scissors = 0;
+
+	/* This checks and barfs if author is badly specified */
+	determine_author_info(author_ident);
+
+	if (!no_verify && run_commit_hook(use_editor, index_file, "pre-commit", NULL))
+		return 0;
+
+	if (squash_message) {
+		/*
+		 * Insert the proper subject line before other commit
+		 * message options add their content.
+		 */
+		if (use_message && !strcmp(use_message, squash_message))
+			strbuf_addstr(&sb, "squash! ");
+		else {
+			struct pretty_print_context ctx = {0};
+			struct commit *c;
+			c = lookup_commit_reference_by_name(squash_message);
+			if (!c)
+				die(_("could not lookup commit %s"), squash_message);
+			ctx.output_encoding = get_commit_output_encoding();
+			format_commit_message(c, "squash! %s\n\n", &sb,
+					      &ctx);
+		}
+	}
+
+	if (have_option_m && !fixup_message) {
+		strbuf_addbuf(&sb, &message);
+		hook_arg1 = "message";
+	} else if (logfile && !strcmp(logfile, "-")) {
+		if (isatty(0))
+			fprintf(stderr, _("(reading log message from standard input)\n"));
+		if (strbuf_read(&sb, 0, 0) < 0)
+			die_errno(_("could not read log from standard input"));
+		hook_arg1 = "message";
+	} else if (logfile) {
+		if (strbuf_read_file(&sb, logfile, 0) < 0)
+			die_errno(_("could not read log file '%s'"),
+				  logfile);
+		hook_arg1 = "message";
+	} else if (use_message) {
+		char *buffer;
+		buffer = strstr(use_message_buffer, "\n\n");
+		if (buffer)
+			strbuf_addstr(&sb, skip_blank_lines(buffer + 2));
+		hook_arg1 = "commit";
+		hook_arg2 = use_message;
+	} else if (fixup_message) {
+		struct pretty_print_context ctx = {0};
+		struct commit *commit;
+		commit = lookup_commit_reference_by_name(fixup_message);
+		if (!commit)
+			die(_("could not lookup commit %s"), fixup_message);
+		ctx.output_encoding = get_commit_output_encoding();
+		format_commit_message(commit, "fixup! %s\n\n",
+				      &sb, &ctx);
+		if (have_option_m)
+			strbuf_addbuf(&sb, &message);
+		hook_arg1 = "message";
+	} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
+		size_t merge_msg_start;
+
+		/*
+		 * prepend SQUASH_MSG here if it exists and a
+		 * "merge --squash" was originally performed
+		 */
+		if (!stat(git_path_squash_msg(the_repository), &statbuf)) {
+			if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)
+				die_errno(_("could not read SQUASH_MSG"));
+			hook_arg1 = "squash";
+		} else
+			hook_arg1 = "merge";
+
+		merge_msg_start = sb.len;
+		if (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)
+			die_errno(_("could not read MERGE_MSG"));
+
+		if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
+		    wt_status_locate_end(sb.buf + merge_msg_start,
+					 sb.len - merge_msg_start) <
+				sb.len - merge_msg_start)
+			merge_contains_scissors = 1;
+	} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {
+		if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)
+			die_errno(_("could not read SQUASH_MSG"));
+		hook_arg1 = "squash";
+	} else if (template_file) {
+		if (strbuf_read_file(&sb, template_file, 0) < 0)
+			die_errno(_("could not read '%s'"), template_file);
+		hook_arg1 = "template";
+		clean_message_contents = 0;
+	}
+
+	/*
+	 * The remaining cases don't modify the template message, but
+	 * just set the argument(s) to the prepare-commit-msg hook.
+	 */
+	else if (whence == FROM_MERGE)
+		hook_arg1 = "merge";
+	else if (whence == FROM_CHERRY_PICK) {
+		hook_arg1 = "commit";
+		hook_arg2 = "CHERRY_PICK_HEAD";
+	}
+
+	if (squash_message) {
+		/*
+		 * If squash_commit was used for the commit subject,
+		 * then we're possibly hijacking other commit log options.
+		 * Reset the hook args to tell the real story.
+		 */
+		hook_arg1 = "message";
+		hook_arg2 = "";
+	}
+
+	s->fp = fopen_for_writing(git_path_commit_editmsg());
+	if (s->fp == NULL)
+		die_errno(_("could not open '%s'"), git_path_commit_editmsg());
+
+	/* Ignore status.displayCommentPrefix: we do need comments in COMMIT_EDITMSG. */
+	old_display_comment_prefix = s->display_comment_prefix;
+	s->display_comment_prefix = 1;
+
+	/*
+	 * Most hints are counter-productive when the commit has
+	 * already started.
+	 */
+	s->hints = 0;
+
+	if (clean_message_contents)
+		strbuf_stripspace(&sb, 0);
+
+	if (signoff)
+		append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0);
+
+	if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
+		die_errno(_("could not write commit template"));
+
+	if (auto_comment_line_char)
+		adjust_comment_line_char(&sb);
+	strbuf_release(&sb);
+
+	/* This checks if committer ident is explicitly given */
+	strbuf_addstr(&committer_ident, git_committer_info(IDENT_STRICT));
+	if (use_editor && include_status) {
+		int ident_shown = 0;
+		int saved_color_setting;
+		struct ident_split ci, ai;
+
+		if (whence != FROM_COMMIT) {
+			if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
+				!merge_contains_scissors)
+				wt_status_add_cut_line(s->fp);
+			status_printf_ln(s, GIT_COLOR_NORMAL,
+			    whence == FROM_MERGE
+				? _("\n"
+					"It looks like you may be committing a merge.\n"
+					"If this is not correct, please remove the file\n"
+					"	%s\n"
+					"and try again.\n")
+				: _("\n"
+					"It looks like you may be committing a cherry-pick.\n"
+					"If this is not correct, please remove the file\n"
+					"	%s\n"
+					"and try again.\n"),
+				whence == FROM_MERGE ?
+					git_path_merge_head(the_repository) :
+					git_path_cherry_pick_head(the_repository));
+		}
+
+		fprintf(s->fp, "\n");
+		if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
+			status_printf(s, GIT_COLOR_NORMAL,
+				_("Please enter the commit message for your changes."
+				  " Lines starting\nwith '%c' will be ignored, and an empty"
+				  " message aborts the commit.\n"), comment_line_char);
+		else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
+			if (whence == FROM_COMMIT && !merge_contains_scissors)
+				wt_status_add_cut_line(s->fp);
+		} else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
+			status_printf(s, GIT_COLOR_NORMAL,
+				_("Please enter the commit message for your changes."
+				  " Lines starting\n"
+				  "with '%c' will be kept; you may remove them"
+				  " yourself if you want to.\n"
+				  "An empty message aborts the commit.\n"), comment_line_char);
+
+		/*
+		 * These should never fail because they come from our own
+		 * fmt_ident. They may fail the sane_ident test, but we know
+		 * that the name and mail pointers will at least be valid,
+		 * which is enough for our tests and printing here.
+		 */
+		assert_split_ident(&ai, author_ident);
+		assert_split_ident(&ci, &committer_ident);
+
+		if (ident_cmp(&ai, &ci))
+			status_printf_ln(s, GIT_COLOR_NORMAL,
+				_("%s"
+				"Author:    %.*s <%.*s>"),
+				ident_shown++ ? "" : "\n",
+				(int)(ai.name_end - ai.name_begin), ai.name_begin,
+				(int)(ai.mail_end - ai.mail_begin), ai.mail_begin);
+
+		if (author_date_is_interesting())
+			status_printf_ln(s, GIT_COLOR_NORMAL,
+				_("%s"
+				"Date:      %s"),
+				ident_shown++ ? "" : "\n",
+				show_ident_date(&ai, DATE_MODE(NORMAL)));
+
+		if (!committer_ident_sufficiently_given())
+			status_printf_ln(s, GIT_COLOR_NORMAL,
+				_("%s"
+				"Committer: %.*s <%.*s>"),
+				ident_shown++ ? "" : "\n",
+				(int)(ci.name_end - ci.name_begin), ci.name_begin,
+				(int)(ci.mail_end - ci.mail_begin), ci.mail_begin);
+
+		status_printf_ln(s, GIT_COLOR_NORMAL, "%s", ""); /* Add new line for clarity */
+
+		saved_color_setting = s->use_color;
+		s->use_color = 0;
+		committable = run_status(s->fp, index_file, prefix, 1, s);
+		s->use_color = saved_color_setting;
+		string_list_clear(&s->change, 1);
+	} else {
+		struct object_id oid;
+		const char *parent = "HEAD";
+
+		if (!active_nr && read_cache() < 0)
+			die(_("Cannot read index"));
+
+		if (amend)
+			parent = "HEAD^1";
+
+		if (get_oid(parent, &oid)) {
+			int i, ita_nr = 0;
+
+			for (i = 0; i < active_nr; i++)
+				if (ce_intent_to_add(active_cache[i]))
+					ita_nr++;
+			committable = active_nr - ita_nr > 0;
+		} else {
+			/*
+			 * Unless the user did explicitly request a submodule
+			 * ignore mode by passing a command line option we do
+			 * not ignore any changed submodule SHA-1s when
+			 * comparing index and parent, no matter what is
+			 * configured. Otherwise we won't commit any
+			 * submodules which were manually staged, which would
+			 * be really confusing.
+			 */
+			struct diff_flags flags = DIFF_FLAGS_INIT;
+			flags.override_submodule_config = 1;
+			if (ignore_submodule_arg &&
+			    !strcmp(ignore_submodule_arg, "all"))
+				flags.ignore_submodules = 1;
+			committable = index_differs_from(the_repository,
+							 parent, &flags, 1);
+		}
+	}
+	strbuf_release(&committer_ident);
+
+	fclose(s->fp);
+
+	/*
+	 * Reject an attempt to record a non-merge empty commit without
+	 * explicit --allow-empty. In the cherry-pick case, it may be
+	 * empty due to conflict resolution, which the user should okay.
+	 */
+	if (!committable && whence != FROM_MERGE && !allow_empty &&
+	    !(amend && is_a_merge(current_head))) {
+		s->display_comment_prefix = old_display_comment_prefix;
+		run_status(stdout, index_file, prefix, 0, s);
+		if (amend)
+			fputs(_(empty_amend_advice), stderr);
+		else if (whence == FROM_CHERRY_PICK) {
+			fputs(_(empty_cherry_pick_advice), stderr);
+			if (!sequencer_in_use)
+				fputs(_(empty_cherry_pick_advice_single), stderr);
+			else
+				fputs(_(empty_cherry_pick_advice_multi), stderr);
+		}
+		return 0;
+	}
+
+	if (!no_verify && find_hook("pre-commit")) {
+		/*
+		 * Re-read the index as pre-commit hook could have updated it,
+		 * and write it out as a tree.  We must do this before we invoke
+		 * the editor and after we invoke run_status above.
+		 */
+		discard_cache();
+	}
+	read_cache_from(index_file);
+
+	if (update_main_cache_tree(0)) {
+		error(_("Error building trees"));
+		return 0;
+	}
+
+	if (run_commit_hook(use_editor, index_file, "prepare-commit-msg",
+			    git_path_commit_editmsg(), hook_arg1, hook_arg2, NULL))
+		return 0;
+
+	if (use_editor) {
+		struct argv_array env = ARGV_ARRAY_INIT;
+
+		argv_array_pushf(&env, "GIT_INDEX_FILE=%s", index_file);
+		if (launch_editor(git_path_commit_editmsg(), NULL, env.argv)) {
+			fprintf(stderr,
+			_("Please supply the message using either -m or -F option.\n"));
+			exit(1);
+		}
+		argv_array_clear(&env);
+	}
+
+	if (!no_verify &&
+	    run_commit_hook(use_editor, index_file, "commit-msg", git_path_commit_editmsg(), NULL)) {
+		return 0;
+	}
+
+	return 1;
+}
+
+static const char *find_author_by_nickname(const char *name)
+{
+	struct rev_info revs;
+	struct commit *commit;
+	struct strbuf buf = STRBUF_INIT;
+	struct string_list mailmap = STRING_LIST_INIT_NODUP;
+	const char *av[20];
+	int ac = 0;
+
+	repo_init_revisions(the_repository, &revs, NULL);
+	strbuf_addf(&buf, "--author=%s", name);
+	av[++ac] = "--all";
+	av[++ac] = "-i";
+	av[++ac] = buf.buf;
+	av[++ac] = NULL;
+	setup_revisions(ac, av, &revs, NULL);
+	revs.mailmap = &mailmap;
+	read_mailmap(revs.mailmap, NULL);
+
+	if (prepare_revision_walk(&revs))
+		die(_("revision walk setup failed"));
+	commit = get_revision(&revs);
+	if (commit) {
+		struct pretty_print_context ctx = {0};
+		ctx.date_mode.type = DATE_NORMAL;
+		strbuf_release(&buf);
+		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
+		clear_mailmap(&mailmap);
+		return strbuf_detach(&buf, NULL);
+	}
+	die(_("--author '%s' is not 'Name <email>' and matches no existing author"), name);
+}
+
+static void handle_ignored_arg(struct wt_status *s)
+{
+	if (!ignored_arg)
+		; /* default already initialized */
+	else if (!strcmp(ignored_arg, "traditional"))
+		s->show_ignored_mode = SHOW_TRADITIONAL_IGNORED;
+	else if (!strcmp(ignored_arg, "no"))
+		s->show_ignored_mode = SHOW_NO_IGNORED;
+	else if (!strcmp(ignored_arg, "matching"))
+		s->show_ignored_mode = SHOW_MATCHING_IGNORED;
+	else
+		die(_("Invalid ignored mode '%s'"), ignored_arg);
+}
+
+static void handle_untracked_files_arg(struct wt_status *s)
+{
+	if (!untracked_files_arg)
+		; /* default already initialized */
+	else if (!strcmp(untracked_files_arg, "no"))
+		s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
+	else if (!strcmp(untracked_files_arg, "normal"))
+		s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+	else if (!strcmp(untracked_files_arg, "all"))
+		s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
+	/*
+	 * Please update $__git_untracked_file_modes in
+	 * git-completion.bash when you add new options
+	 */
+	else
+		die(_("Invalid untracked files mode '%s'"), untracked_files_arg);
+}
+
+static const char *read_commit_message(const char *name)
+{
+	const char *out_enc;
+	struct commit *commit;
+
+	commit = lookup_commit_reference_by_name(name);
+	if (!commit)
+		die(_("could not lookup commit %s"), name);
+	out_enc = get_commit_output_encoding();
+	return logmsg_reencode(commit, NULL, out_enc);
+}
+
+/*
+ * Enumerate what needs to be propagated when --porcelain
+ * is not in effect here.
+ */
+static struct status_deferred_config {
+	enum wt_status_format status_format;
+	int show_branch;
+	enum ahead_behind_flags ahead_behind;
+} status_deferred_config = {
+	STATUS_FORMAT_UNSPECIFIED,
+	-1, /* unspecified */
+	AHEAD_BEHIND_UNSPECIFIED,
+};
+
+static void finalize_deferred_config(struct wt_status *s)
+{
+	int use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN &&
+				   status_format != STATUS_FORMAT_PORCELAIN_V2 &&
+				   !s->null_termination);
+
+	if (s->null_termination) {
+		if (status_format == STATUS_FORMAT_NONE ||
+		    status_format == STATUS_FORMAT_UNSPECIFIED)
+			status_format = STATUS_FORMAT_PORCELAIN;
+		else if (status_format == STATUS_FORMAT_LONG)
+			die(_("--long and -z are incompatible"));
+	}
+
+	if (use_deferred_config && status_format == STATUS_FORMAT_UNSPECIFIED)
+		status_format = status_deferred_config.status_format;
+	if (status_format == STATUS_FORMAT_UNSPECIFIED)
+		status_format = STATUS_FORMAT_NONE;
+
+	if (use_deferred_config && s->show_branch < 0)
+		s->show_branch = status_deferred_config.show_branch;
+	if (s->show_branch < 0)
+		s->show_branch = 0;
+
+	/*
+	 * If the user did not give a "--[no]-ahead-behind" command
+	 * line argument *AND* we will print in a human-readable format
+	 * (short, long etc.) then we inherit from the status.aheadbehind
+	 * config setting.  In all other cases (and porcelain V[12] formats
+	 * in particular), we inherit _FULL for backwards compatibility.
+	 */
+	if (use_deferred_config &&
+	    s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)
+		s->ahead_behind_flags = status_deferred_config.ahead_behind;
+
+	if (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)
+		s->ahead_behind_flags = AHEAD_BEHIND_FULL;
+}
+
+static int parse_and_validate_options(int argc, const char *argv[],
+				      const struct option *options,
+				      const char * const usage[],
+				      const char *prefix,
+				      struct commit *current_head,
+				      struct wt_status *s)
+{
+	int f = 0;
+
+	argc = parse_options(argc, argv, prefix, options, usage, 0);
+	finalize_deferred_config(s);
+
+	if (force_author && !strchr(force_author, '>'))
+		force_author = find_author_by_nickname(force_author);
+
+	if (force_author && renew_authorship)
+		die(_("Using both --reset-author and --author does not make sense"));
+
+	if (logfile || have_option_m || use_message || fixup_message)
+		use_editor = 0;
+	if (0 <= edit_flag)
+		use_editor = edit_flag;
+
+	/* Sanity check options */
+	if (amend && !current_head)
+		die(_("You have nothing to amend."));
+	if (amend && whence != FROM_COMMIT) {
+		if (whence == FROM_MERGE)
+			die(_("You are in the middle of a merge -- cannot amend."));
+		else if (whence == FROM_CHERRY_PICK)
+			die(_("You are in the middle of a cherry-pick -- cannot amend."));
+	}
+	if (fixup_message && squash_message)
+		die(_("Options --squash and --fixup cannot be used together"));
+	if (use_message)
+		f++;
+	if (edit_message)
+		f++;
+	if (fixup_message)
+		f++;
+	if (logfile)
+		f++;
+	if (f > 1)
+		die(_("Only one of -c/-C/-F/--fixup can be used."));
+	if (have_option_m && (edit_message || use_message || logfile))
+		die((_("Option -m cannot be combined with -c/-C/-F.")));
+	if (f || have_option_m)
+		template_file = NULL;
+	if (edit_message)
+		use_message = edit_message;
+	if (amend && !use_message && !fixup_message)
+		use_message = "HEAD";
+	if (!use_message && whence != FROM_CHERRY_PICK && renew_authorship)
+		die(_("--reset-author can be used only with -C, -c or --amend."));
+	if (use_message) {
+		use_message_buffer = read_commit_message(use_message);
+		if (!renew_authorship) {
+			author_message = use_message;
+			author_message_buffer = use_message_buffer;
+		}
+	}
+	if (whence == FROM_CHERRY_PICK && !renew_authorship) {
+		author_message = "CHERRY_PICK_HEAD";
+		author_message_buffer = read_commit_message(author_message);
+	}
+
+	if (patch_interactive)
+		interactive = 1;
+
+	if (also + only + all + interactive > 1)
+		die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
+	if (argc == 0 && (also || (only && !amend && !allow_empty)))
+		die(_("No paths with --include/--only does not make sense."));
+	cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
+
+	handle_untracked_files_arg(s);
+
+	if (all && argc > 0)
+		die(_("paths '%s ...' with -a does not make sense"),
+		    argv[0]);
+
+	if (status_format != STATUS_FORMAT_NONE)
+		dry_run = 1;
+
+	return argc;
+}
+
+static int dry_run_commit(int argc, const char **argv, const char *prefix,
+			  const struct commit *current_head, struct wt_status *s)
+{
+	int committable;
+	const char *index_file;
+
+	index_file = prepare_index(argc, argv, prefix, current_head, 1);
+	committable = run_status(stdout, index_file, prefix, 0, s);
+	rollback_index_files();
+
+	return committable ? 0 : 1;
+}
+
+define_list_config_array_extra(color_status_slots, {"added"});
+
+static int parse_status_slot(const char *slot)
+{
+	if (!strcasecmp(slot, "added"))
+		return WT_STATUS_UPDATED;
+
+	return LOOKUP_CONFIG(color_status_slots, slot);
+}
+
+static int git_status_config(const char *k, const char *v, void *cb)
+{
+	struct wt_status *s = cb;
+	const char *slot_name;
+
+	if (starts_with(k, "column."))
+		return git_column_config(k, v, "status", &s->colopts);
+	if (!strcmp(k, "status.submodulesummary")) {
+		int is_bool;
+		s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
+		if (is_bool && s->submodule_summary)
+			s->submodule_summary = -1;
+		return 0;
+	}
+	if (!strcmp(k, "status.short")) {
+		if (git_config_bool(k, v))
+			status_deferred_config.status_format = STATUS_FORMAT_SHORT;
+		else
+			status_deferred_config.status_format = STATUS_FORMAT_NONE;
+		return 0;
+	}
+	if (!strcmp(k, "status.branch")) {
+		status_deferred_config.show_branch = git_config_bool(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "status.aheadbehind")) {
+		status_deferred_config.ahead_behind = git_config_bool(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "status.showstash")) {
+		s->show_stash = git_config_bool(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
+		s->use_color = git_config_colorbool(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "status.displaycommentprefix")) {
+		s->display_comment_prefix = git_config_bool(k, v);
+		return 0;
+	}
+	if (skip_prefix(k, "status.color.", &slot_name) ||
+	    skip_prefix(k, "color.status.", &slot_name)) {
+		int slot = parse_status_slot(slot_name);
+		if (slot < 0)
+			return 0;
+		if (!v)
+			return config_error_nonbool(k);
+		return color_parse(v, s->color_palette[slot]);
+	}
+	if (!strcmp(k, "status.relativepaths")) {
+		s->relative_paths = git_config_bool(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "status.showuntrackedfiles")) {
+		if (!v)
+			return config_error_nonbool(k);
+		else if (!strcmp(v, "no"))
+			s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
+		else if (!strcmp(v, "normal"))
+			s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+		else if (!strcmp(v, "all"))
+			s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
+		else
+			return error(_("Invalid untracked files mode '%s'"), v);
+		return 0;
+	}
+	if (!strcmp(k, "diff.renamelimit")) {
+		if (s->rename_limit == -1)
+			s->rename_limit = git_config_int(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "status.renamelimit")) {
+		s->rename_limit = git_config_int(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "diff.renames")) {
+		if (s->detect_rename == -1)
+			s->detect_rename = git_config_rename(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "status.renames")) {
+		s->detect_rename = git_config_rename(k, v);
+		return 0;
+	}
+	return git_diff_ui_config(k, v, NULL);
+}
+
+int cmd_status(int argc, const char **argv, const char *prefix)
+{
+	static int no_renames = -1;
+	static const char *rename_score_arg = (const char *)-1;
+	static struct wt_status s;
+	unsigned int progress_flag = 0;
+	int fd;
+	struct object_id oid;
+	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_BOOL('b', "branch", &s.show_branch,
+			 N_("show branch information")),
+		OPT_BOOL(0, "show-stash", &s.show_stash,
+			 N_("show stash information")),
+		OPT_BOOL(0, "ahead-behind", &s.ahead_behind_flags,
+			 N_("compute full ahead/behind values")),
+		{ OPTION_CALLBACK, 0, "porcelain", &status_format,
+		  N_("version"), N_("machine-readable output"),
+		  PARSE_OPT_OPTARG, opt_parse_porcelain },
+		OPT_SET_INT(0, "long", &status_format,
+			    N_("show status in long format (default)"),
+			    STATUS_FORMAT_LONG),
+		OPT_BOOL('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" },
+		{ OPTION_STRING, 0, "ignored", &ignored_arg,
+		  N_("mode"),
+		  N_("show ignored files, optional modes: traditional, matching, no. (Default: traditional)"),
+		  PARSE_OPT_OPTARG, NULL, (intptr_t)"traditional" },
+		{ 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_BOOL(0, "no-renames", &no_renames, N_("do not detect renames")),
+		{ OPTION_CALLBACK, 'M', "find-renames", &rename_score_arg,
+		  N_("n"), N_("detect renames, optionally set similarity index"),
+		  PARSE_OPT_OPTARG | PARSE_OPT_NONEG, opt_parse_rename_score },
+		OPT_END(),
+	};
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(builtin_status_usage, builtin_status_options);
+
+	status_init_config(&s, git_status_config);
+	argc = parse_options(argc, argv, prefix,
+			     builtin_status_options,
+			     builtin_status_usage, 0);
+	finalize_colopts(&s.colopts, -1);
+	finalize_deferred_config(&s);
+
+	handle_untracked_files_arg(&s);
+	handle_ignored_arg(&s);
+
+	if (s.show_ignored_mode == SHOW_MATCHING_IGNORED &&
+	    s.show_untracked_files == SHOW_NO_UNTRACKED_FILES)
+		die(_("Unsupported combination of ignored and untracked-files arguments"));
+
+	parse_pathspec(&s.pathspec, 0,
+		       PATHSPEC_PREFER_FULL,
+		       prefix, argv);
+
+	if (status_format != STATUS_FORMAT_PORCELAIN &&
+	    status_format != STATUS_FORMAT_PORCELAIN_V2)
+		progress_flag = REFRESH_PROGRESS;
+	repo_read_index(the_repository);
+	refresh_index(&the_index,
+		      REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,
+		      &s.pathspec, NULL, NULL);
+
+	if (use_optional_locks())
+		fd = hold_locked_index(&index_lock, 0);
+	else
+		fd = -1;
+
+	s.is_initial = get_oid(s.reference, &oid) ? 1 : 0;
+	if (!s.is_initial)
+		hashcpy(s.sha1_commit, oid.hash);
+
+	s.ignore_submodule_arg = ignore_submodule_arg;
+	s.status_format = status_format;
+	s.verbose = verbose;
+	if (no_renames != -1)
+		s.detect_rename = !no_renames;
+	if ((intptr_t)rename_score_arg != -1) {
+		if (s.detect_rename < DIFF_DETECT_RENAME)
+			s.detect_rename = DIFF_DETECT_RENAME;
+		if (rename_score_arg)
+			s.rename_score = parse_rename_score(&rename_score_arg);
+	}
+
+	wt_status_collect(&s);
+
+	if (0 <= fd)
+		repo_update_index_if_able(the_repository, &index_lock);
+
+	if (s.relative_paths)
+		s.prefix = prefix;
+
+	wt_status_print(&s);
+	wt_status_collect_free_buffers(&s);
+
+	return 0;
+}
+
+static int git_commit_config(const char *k, const char *v, void *cb)
+{
+	struct wt_status *s = cb;
+	int status;
+
+	if (!strcmp(k, "commit.template"))
+		return git_config_pathname(&template_file, k, v);
+	if (!strcmp(k, "commit.status")) {
+		include_status = git_config_bool(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "commit.cleanup"))
+		return git_config_string(&cleanup_arg, k, v);
+	if (!strcmp(k, "commit.gpgsign")) {
+		sign_commit = git_config_bool(k, v) ? "" : NULL;
+		return 0;
+	}
+	if (!strcmp(k, "commit.verbose")) {
+		int is_bool;
+		config_commit_verbose = git_config_bool_or_int(k, v, &is_bool);
+		return 0;
+	}
+
+	status = git_gpg_config(k, v, NULL);
+	if (status)
+		return status;
+	return git_status_config(k, v, s);
+}
+
+int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
+{
+	struct argv_array hook_env = ARGV_ARRAY_INIT;
+	va_list args;
+	int ret;
+
+	argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", index_file);
+
+	/*
+	 * Let the hook know that no editor will be launched.
+	 */
+	if (!editor_is_used)
+		argv_array_push(&hook_env, "GIT_EDITOR=:");
+
+	va_start(args, name);
+	ret = run_hook_ve(hook_env.argv,name, args);
+	va_end(args);
+	argv_array_clear(&hook_env);
+
+	return ret;
+}
+
+int cmd_commit(int argc, const char **argv, const char *prefix)
+{
+	const char *argv_gc_auto[] = {"gc", "--auto", NULL};
+	static struct wt_status s;
+	static struct option builtin_commit_options[] = {
+		OPT__QUIET(&quiet, N_("suppress summary after successful commit")),
+		OPT__VERBOSE(&verbose, N_("show diff in commit message template")),
+
+		OPT_GROUP(N_("Commit message options")),
+		OPT_FILENAME('F', "file", &logfile, N_("read message from file")),
+		OPT_STRING(0, "author", &force_author, N_("author"), N_("override author for commit")),
+		OPT_STRING(0, "date", &force_date, N_("date"), N_("override date for commit")),
+		OPT_CALLBACK('m', "message", &message, N_("message"), N_("commit message"), opt_parse_m),
+		OPT_STRING('c', "reedit-message", &edit_message, N_("commit"), N_("reuse and edit message from specified commit")),
+		OPT_STRING('C', "reuse-message", &use_message, N_("commit"), N_("reuse message from specified commit")),
+		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup specified commit")),
+		OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
+		OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
+		OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")),
+		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
+		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
+		OPT_CLEANUP(&cleanup_arg),
+		OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")),
+		{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
+		  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		/* end commit message options */
+
+		OPT_GROUP(N_("Commit contents options")),
+		OPT_BOOL('a', "all", &all, N_("commit all changed files")),
+		OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")),
+		OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")),
+		OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")),
+		OPT_BOOL('o', "only", &only, N_("commit only specified files")),
+		OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")),
+		OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")),
+		OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
+			    STATUS_FORMAT_SHORT),
+		OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")),
+		OPT_BOOL(0, "ahead-behind", &s.ahead_behind_flags,
+			 N_("compute full ahead/behind values")),
+		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_BOOL('z', "null", &s.null_termination,
+			 N_("terminate entries with NUL")),
+		OPT_BOOL(0, "amend", &amend, N_("amend previous commit")),
+		OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")),
+		{ 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" },
+		/* end commit contents options */
+
+		OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty,
+				N_("ok to record an empty change")),
+		OPT_HIDDEN_BOOL(0, "allow-empty-message", &allow_empty_message,
+				N_("ok to record a change with an empty message")),
+
+		OPT_END()
+	};
+
+	struct strbuf sb = STRBUF_INIT;
+	struct strbuf author_ident = STRBUF_INIT;
+	const char *index_file, *reflog_msg;
+	struct object_id oid;
+	struct commit_list *parents = NULL;
+	struct stat statbuf;
+	struct commit *current_head = NULL;
+	struct commit_extra_header *extra = NULL;
+	struct strbuf err = STRBUF_INIT;
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(builtin_commit_usage, builtin_commit_options);
+
+	status_init_config(&s, git_commit_config);
+	s.commit_template = 1;
+	status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
+	s.colopts = 0;
+
+	if (get_oid("HEAD", &oid))
+		current_head = NULL;
+	else {
+		current_head = lookup_commit_or_die(&oid, "HEAD");
+		if (parse_commit(current_head))
+			die(_("could not parse HEAD commit"));
+	}
+	verbose = -1; /* unspecified */
+	argc = parse_and_validate_options(argc, argv, builtin_commit_options,
+					  builtin_commit_usage,
+					  prefix, current_head, &s);
+	if (verbose == -1)
+		verbose = (config_commit_verbose < 0) ? 0 : config_commit_verbose;
+
+	if (dry_run)
+		return dry_run_commit(argc, argv, prefix, current_head, &s);
+	index_file = prepare_index(argc, argv, prefix, current_head, 0);
+
+	/* Set up everything for writing the commit object.  This includes
+	   running hooks, writing the trees, and interacting with the user.  */
+	if (!prepare_to_commit(index_file, prefix,
+			       current_head, &s, &author_ident)) {
+		rollback_index_files();
+		return 1;
+	}
+
+	/* Determine parents */
+	reflog_msg = getenv("GIT_REFLOG_ACTION");
+	if (!current_head) {
+		if (!reflog_msg)
+			reflog_msg = "commit (initial)";
+	} else if (amend) {
+		if (!reflog_msg)
+			reflog_msg = "commit (amend)";
+		parents = copy_commit_list(current_head->parents);
+	} else if (whence == FROM_MERGE) {
+		struct strbuf m = STRBUF_INIT;
+		FILE *fp;
+		int allow_fast_forward = 1;
+		struct commit_list **pptr = &parents;
+
+		if (!reflog_msg)
+			reflog_msg = "commit (merge)";
+		pptr = commit_list_append(current_head, pptr);
+		fp = xfopen(git_path_merge_head(the_repository), "r");
+		while (strbuf_getline_lf(&m, fp) != EOF) {
+			struct commit *parent;
+
+			parent = get_merge_parent(m.buf);
+			if (!parent)
+				die(_("Corrupt MERGE_HEAD file (%s)"), m.buf);
+			pptr = commit_list_append(parent, pptr);
+		}
+		fclose(fp);
+		strbuf_release(&m);
+		if (!stat(git_path_merge_mode(the_repository), &statbuf)) {
+			if (strbuf_read_file(&sb, git_path_merge_mode(the_repository), 0) < 0)
+				die_errno(_("could not read MERGE_MODE"));
+			if (!strcmp(sb.buf, "no-ff"))
+				allow_fast_forward = 0;
+		}
+		if (allow_fast_forward)
+			reduce_heads_replace(&parents);
+	} else {
+		if (!reflog_msg)
+			reflog_msg = (whence == FROM_CHERRY_PICK)
+					? "commit (cherry-pick)"
+					: "commit";
+		commit_list_insert(current_head, &parents);
+	}
+
+	/* Finally, get the commit message */
+	strbuf_reset(&sb);
+	if (strbuf_read_file(&sb, git_path_commit_editmsg(), 0) < 0) {
+		int saved_errno = errno;
+		rollback_index_files();
+		die(_("could not read commit message: %s"), strerror(saved_errno));
+	}
+
+	cleanup_message(&sb, cleanup_mode, verbose);
+
+	if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
+		rollback_index_files();
+		fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
+		exit(1);
+	}
+	if (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {
+		rollback_index_files();
+		fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
+		exit(1);
+	}
+
+	if (amend) {
+		const char *exclude_gpgsig[2] = { "gpgsig", NULL };
+		extra = read_commit_extra_headers(current_head, exclude_gpgsig);
+	} else {
+		struct commit_extra_header **tail = &extra;
+		append_merge_tag_headers(parents, &tail);
+	}
+
+	if (commit_tree_extended(sb.buf, sb.len, &active_cache_tree->oid,
+				 parents, &oid, author_ident.buf, sign_commit,
+				 extra)) {
+		rollback_index_files();
+		die(_("failed to write commit object"));
+	}
+	strbuf_release(&author_ident);
+	free_commit_extra_headers(extra);
+
+	if (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,
+				    &err)) {
+		rollback_index_files();
+		die("%s", err.buf);
+	}
+
+	sequencer_post_commit_cleanup(the_repository, 0);
+	unlink(git_path_merge_head(the_repository));
+	unlink(git_path_merge_msg(the_repository));
+	unlink(git_path_merge_mode(the_repository));
+	unlink(git_path_squash_msg(the_repository));
+
+	if (commit_index_files())
+		die(_("repository has been updated, but unable to write\n"
+		      "new_index file. Check that disk is not full and quota is\n"
+		      "not exceeded, and then \"git restore --staged :/\" to recover."));
+
+	if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
+	    write_commit_graph_reachable(get_object_directory(), 0, NULL))
+		return 1;
+
+	repo_rerere(the_repository, 0);
+	run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+	run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
+	if (amend && !no_post_rewrite) {
+		commit_post_rewrite(the_repository, current_head, &oid);
+	}
+	if (!quiet) {
+		unsigned int flags = 0;
+
+		if (!current_head)
+			flags |= SUMMARY_INITIAL_COMMIT;
+		if (author_date_is_interesting())
+			flags |= SUMMARY_SHOW_AUTHOR_DATE;
+		print_commit_summary(the_repository, prefix,
+				     &oid, flags);
+	}
+
+	UNLEAK(err);
+	UNLEAK(sb);
+	return 0;
+}
diff --git a/builtin/config.c b/builtin/config.c
new file mode 100644
index 0000000000..98d65bc0ad
--- /dev/null
+++ b/builtin/config.c
@@ -0,0 +1,872 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "color.h"
+#include "parse-options.h"
+#include "urlmatch.h"
+#include "quote.h"
+#include "worktree.h"
+
+static const char *const builtin_config_usage[] = {
+	N_("git config [<options>]"),
+	NULL
+};
+
+static char *key;
+static regex_t *key_regexp;
+static regex_t *regexp;
+static int show_keys;
+static int omit_values;
+static int use_key_regexp;
+static int do_all;
+static int do_not_match;
+static char delim = '=';
+static char key_delim = ' ';
+static char term = '\n';
+
+static int use_global_config, use_system_config, use_local_config;
+static int use_worktree_config;
+static struct git_config_source given_config_source;
+static int actions, type;
+static char *default_value;
+static int end_null;
+static int respect_includes_opt = -1;
+static struct config_options config_options;
+static int show_origin;
+
+#define ACTION_GET (1<<0)
+#define ACTION_GET_ALL (1<<1)
+#define ACTION_GET_REGEXP (1<<2)
+#define ACTION_REPLACE_ALL (1<<3)
+#define ACTION_ADD (1<<4)
+#define ACTION_UNSET (1<<5)
+#define ACTION_UNSET_ALL (1<<6)
+#define ACTION_RENAME_SECTION (1<<7)
+#define ACTION_REMOVE_SECTION (1<<8)
+#define ACTION_LIST (1<<9)
+#define ACTION_EDIT (1<<10)
+#define ACTION_SET (1<<11)
+#define ACTION_SET_ALL (1<<12)
+#define ACTION_GET_COLOR (1<<13)
+#define ACTION_GET_COLORBOOL (1<<14)
+#define ACTION_GET_URLMATCH (1<<15)
+
+/*
+ * The actions "ACTION_LIST | ACTION_GET_*" which may produce more than
+ * one line of output and which should therefore be paged.
+ */
+#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \
+			ACTION_GET_REGEXP | ACTION_GET_URLMATCH)
+
+#define TYPE_BOOL		1
+#define TYPE_INT		2
+#define TYPE_BOOL_OR_INT	3
+#define TYPE_PATH		4
+#define TYPE_EXPIRY_DATE	5
+#define TYPE_COLOR		6
+
+#define OPT_CALLBACK_VALUE(s, l, v, h, i) \
+	{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \
+	PARSE_OPT_NONEG, option_parse_type, (i) }
+
+static NORETURN void usage_builtin_config(void);
+
+static int option_parse_type(const struct option *opt, const char *arg,
+			     int unset)
+{
+	int new_type, *to_type;
+
+	if (unset) {
+		*((int *) opt->value) = 0;
+		return 0;
+	}
+
+	/*
+	 * To support '--<type>' style flags, begin with new_type equal to
+	 * opt->defval.
+	 */
+	new_type = opt->defval;
+	if (!new_type) {
+		if (!strcmp(arg, "bool"))
+			new_type = TYPE_BOOL;
+		else if (!strcmp(arg, "int"))
+			new_type = TYPE_INT;
+		else if (!strcmp(arg, "bool-or-int"))
+			new_type = TYPE_BOOL_OR_INT;
+		else if (!strcmp(arg, "path"))
+			new_type = TYPE_PATH;
+		else if (!strcmp(arg, "expiry-date"))
+			new_type = TYPE_EXPIRY_DATE;
+		else if (!strcmp(arg, "color"))
+			new_type = TYPE_COLOR;
+		else
+			die(_("unrecognized --type argument, %s"), arg);
+	}
+
+	to_type = opt->value;
+	if (*to_type && *to_type != new_type) {
+		/*
+		 * Complain when there is a new type not equal to the old type.
+		 * This allows for combinations like '--int --type=int' and
+		 * '--type=int --type=int', but disallows ones like '--type=bool
+		 * --int' and '--type=bool
+		 * --type=int'.
+		 */
+		error(_("only one type at a time"));
+		usage_builtin_config();
+	}
+	*to_type = new_type;
+
+	return 0;
+}
+
+static struct option builtin_config_options[] = {
+	OPT_GROUP(N_("Config file location")),
+	OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
+	OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
+	OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
+	OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")),
+	OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
+	OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
+	OPT_GROUP(N_("Action")),
+	OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
+	OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
+	OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-regex]"), ACTION_GET_REGEXP),
+	OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
+	OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value_regex]"), ACTION_REPLACE_ALL),
+	OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
+	OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-regex]"), ACTION_UNSET),
+	OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-regex]"), ACTION_UNSET_ALL),
+	OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
+	OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
+	OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
+	OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
+	OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
+	OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
+	OPT_GROUP(N_("Type")),
+	OPT_CALLBACK('t', "type", &type, "", N_("value is given this type"), option_parse_type),
+	OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
+	OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT),
+	OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
+	OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH),
+	OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
+	OPT_GROUP(N_("Other")),
+	OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
+	OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
+	OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
+	OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
+	OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
+	OPT_END(),
+};
+
+static NORETURN void usage_builtin_config(void)
+{
+	usage_with_options(builtin_config_usage, builtin_config_options);
+}
+
+static void check_argc(int argc, int min, int max)
+{
+	if (argc >= min && argc <= max)
+		return;
+	if (min == max)
+		error(_("wrong number of arguments, should be %d"), min);
+	else
+		error(_("wrong number of arguments, should be from %d to %d"),
+		      min, max);
+	usage_builtin_config();
+}
+
+static void show_config_origin(struct strbuf *buf)
+{
+	const char term = end_null ? '\0' : '\t';
+
+	strbuf_addstr(buf, current_config_origin_type());
+	strbuf_addch(buf, ':');
+	if (end_null)
+		strbuf_addstr(buf, current_config_name());
+	else
+		quote_c_style(current_config_name(), buf, NULL, 0);
+	strbuf_addch(buf, term);
+}
+
+static int show_all_config(const char *key_, const char *value_, void *cb)
+{
+	if (show_origin) {
+		struct strbuf buf = STRBUF_INIT;
+		show_config_origin(&buf);
+		/* Use fwrite as "buf" can contain \0's if "end_null" is set. */
+		fwrite(buf.buf, 1, buf.len, stdout);
+		strbuf_release(&buf);
+	}
+	if (!omit_values && value_)
+		printf("%s%c%s%c", key_, delim, value_, term);
+	else
+		printf("%s%c", key_, term);
+	return 0;
+}
+
+struct strbuf_list {
+	struct strbuf *items;
+	int nr;
+	int alloc;
+};
+
+static int format_config(struct strbuf *buf, const char *key_, const char *value_)
+{
+	if (show_origin)
+		show_config_origin(buf);
+	if (show_keys)
+		strbuf_addstr(buf, key_);
+	if (!omit_values) {
+		if (show_keys)
+			strbuf_addch(buf, key_delim);
+
+		if (type == TYPE_INT)
+			strbuf_addf(buf, "%"PRId64,
+				    git_config_int64(key_, value_ ? value_ : ""));
+		else if (type == TYPE_BOOL)
+			strbuf_addstr(buf, git_config_bool(key_, value_) ?
+				      "true" : "false");
+		else if (type == TYPE_BOOL_OR_INT) {
+			int is_bool, v;
+			v = git_config_bool_or_int(key_, value_, &is_bool);
+			if (is_bool)
+				strbuf_addstr(buf, v ? "true" : "false");
+			else
+				strbuf_addf(buf, "%d", v);
+		} else if (type == TYPE_PATH) {
+			const char *v;
+			if (git_config_pathname(&v, key_, value_) < 0)
+				return -1;
+			strbuf_addstr(buf, v);
+			free((char *)v);
+		} else if (type == TYPE_EXPIRY_DATE) {
+			timestamp_t t;
+			if (git_config_expiry_date(&t, key_, value_) < 0)
+				return -1;
+			strbuf_addf(buf, "%"PRItime, t);
+		} else if (type == TYPE_COLOR) {
+			char v[COLOR_MAXLEN];
+			if (git_config_color(v, key_, value_) < 0)
+				return -1;
+			strbuf_addstr(buf, v);
+		} else if (value_) {
+			strbuf_addstr(buf, value_);
+		} else {
+			/* Just show the key name; back out delimiter */
+			if (show_keys)
+				strbuf_setlen(buf, buf->len - 1);
+		}
+	}
+	strbuf_addch(buf, term);
+	return 0;
+}
+
+static int collect_config(const char *key_, const char *value_, void *cb)
+{
+	struct strbuf_list *values = cb;
+
+	if (!use_key_regexp && strcmp(key_, key))
+		return 0;
+	if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
+		return 0;
+	if (regexp != NULL &&
+	    (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
+		return 0;
+
+	ALLOC_GROW(values->items, values->nr + 1, values->alloc);
+	strbuf_init(&values->items[values->nr], 0);
+
+	return format_config(&values->items[values->nr++], key_, value_);
+}
+
+static int get_value(const char *key_, const char *regex_)
+{
+	int ret = CONFIG_GENERIC_ERROR;
+	struct strbuf_list values = {NULL};
+	int i;
+
+	if (use_key_regexp) {
+		char *tl;
+
+		/*
+		 * NEEDSWORK: this naive pattern lowercasing obviously does not
+		 * work for more complex patterns like "^[^.]*Foo.*bar".
+		 * Perhaps we should deprecate this altogether someday.
+		 */
+
+		key = xstrdup(key_);
+		for (tl = key + strlen(key) - 1;
+		     tl >= key && *tl != '.';
+		     tl--)
+			*tl = tolower(*tl);
+		for (tl = key; *tl && *tl != '.'; tl++)
+			*tl = tolower(*tl);
+
+		key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
+		if (regcomp(key_regexp, key, REG_EXTENDED)) {
+			error(_("invalid key pattern: %s"), key_);
+			FREE_AND_NULL(key_regexp);
+			ret = CONFIG_INVALID_PATTERN;
+			goto free_strings;
+		}
+	} else {
+		if (git_config_parse_key(key_, &key, NULL)) {
+			ret = CONFIG_INVALID_KEY;
+			goto free_strings;
+		}
+	}
+
+	if (regex_) {
+		if (regex_[0] == '!') {
+			do_not_match = 1;
+			regex_++;
+		}
+
+		regexp = (regex_t*)xmalloc(sizeof(regex_t));
+		if (regcomp(regexp, regex_, REG_EXTENDED)) {
+			error(_("invalid pattern: %s"), regex_);
+			FREE_AND_NULL(regexp);
+			ret = CONFIG_INVALID_PATTERN;
+			goto free_strings;
+		}
+	}
+
+	config_with_options(collect_config, &values,
+			    &given_config_source, &config_options);
+
+	if (!values.nr && default_value) {
+		struct strbuf *item;
+		ALLOC_GROW(values.items, values.nr + 1, values.alloc);
+		item = &values.items[values.nr++];
+		strbuf_init(item, 0);
+		if (format_config(item, key_, default_value) < 0)
+			die(_("failed to format default config value: %s"),
+				default_value);
+	}
+
+	ret = !values.nr;
+
+	for (i = 0; i < values.nr; i++) {
+		struct strbuf *buf = values.items + i;
+		if (do_all || i == values.nr - 1)
+			fwrite(buf->buf, 1, buf->len, stdout);
+		strbuf_release(buf);
+	}
+	free(values.items);
+
+free_strings:
+	free(key);
+	if (key_regexp) {
+		regfree(key_regexp);
+		free(key_regexp);
+	}
+	if (regexp) {
+		regfree(regexp);
+		free(regexp);
+	}
+
+	return ret;
+}
+
+static char *normalize_value(const char *key, const char *value)
+{
+	if (!value)
+		return NULL;
+
+	if (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE)
+		/*
+		 * We don't do normalization for TYPE_PATH here: If
+		 * the path is like ~/foobar/, we prefer to store
+		 * "~/foobar/" in the config file, and to expand the ~
+		 * when retrieving the value.
+		 * Also don't do normalization for expiry dates.
+		 */
+		return xstrdup(value);
+	if (type == TYPE_INT)
+		return xstrfmt("%"PRId64, git_config_int64(key, value));
+	if (type == TYPE_BOOL)
+		return xstrdup(git_config_bool(key, value) ?  "true" : "false");
+	if (type == TYPE_BOOL_OR_INT) {
+		int is_bool, v;
+		v = git_config_bool_or_int(key, value, &is_bool);
+		if (!is_bool)
+			return xstrfmt("%d", v);
+		else
+			return xstrdup(v ? "true" : "false");
+	}
+	if (type == TYPE_COLOR) {
+		char v[COLOR_MAXLEN];
+		if (git_config_color(v, key, value))
+			die(_("cannot parse color '%s'"), value);
+
+		/*
+		 * The contents of `v` now contain an ANSI escape
+		 * sequence, not suitable for including within a
+		 * configuration file. Treat the above as a
+		 * "sanity-check", and return the given value, which we
+		 * know is representable as valid color code.
+		 */
+		return xstrdup(value);
+	}
+
+	BUG("cannot normalize type %d", type);
+}
+
+static int get_color_found;
+static const char *get_color_slot;
+static const char *get_colorbool_slot;
+static char parsed_color[COLOR_MAXLEN];
+
+static int git_get_color_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, get_color_slot)) {
+		if (!value)
+			config_error_nonbool(var);
+		if (color_parse(value, parsed_color) < 0)
+			return -1;
+		get_color_found = 1;
+	}
+	return 0;
+}
+
+static void get_color(const char *var, const char *def_color)
+{
+	get_color_slot = var;
+	get_color_found = 0;
+	parsed_color[0] = '\0';
+	config_with_options(git_get_color_config, NULL,
+			    &given_config_source, &config_options);
+
+	if (!get_color_found && def_color) {
+		if (color_parse(def_color, parsed_color) < 0)
+			die(_("unable to parse default color value"));
+	}
+
+	fputs(parsed_color, stdout);
+}
+
+static int get_colorbool_found;
+static int get_diff_color_found;
+static int get_color_ui_found;
+static int git_get_colorbool_config(const char *var, const char *value,
+		void *cb)
+{
+	if (!strcmp(var, get_colorbool_slot))
+		get_colorbool_found = git_config_colorbool(var, value);
+	else if (!strcmp(var, "diff.color"))
+		get_diff_color_found = git_config_colorbool(var, value);
+	else if (!strcmp(var, "color.ui"))
+		get_color_ui_found = git_config_colorbool(var, value);
+	return 0;
+}
+
+static int get_colorbool(const char *var, int print)
+{
+	get_colorbool_slot = var;
+	get_colorbool_found = -1;
+	get_diff_color_found = -1;
+	get_color_ui_found = -1;
+	config_with_options(git_get_colorbool_config, NULL,
+			    &given_config_source, &config_options);
+
+	if (get_colorbool_found < 0) {
+		if (!strcmp(get_colorbool_slot, "color.diff"))
+			get_colorbool_found = get_diff_color_found;
+		if (get_colorbool_found < 0)
+			get_colorbool_found = get_color_ui_found;
+	}
+
+	if (get_colorbool_found < 0)
+		/* default value if none found in config */
+		get_colorbool_found = GIT_COLOR_AUTO;
+
+	get_colorbool_found = want_color(get_colorbool_found);
+
+	if (print) {
+		printf("%s\n", get_colorbool_found ? "true" : "false");
+		return 0;
+	} else
+		return get_colorbool_found ? 0 : 1;
+}
+
+static void check_write(void)
+{
+	if (!given_config_source.file && !startup_info->have_repository)
+		die(_("not in a git directory"));
+
+	if (given_config_source.use_stdin)
+		die(_("writing to stdin is not supported"));
+
+	if (given_config_source.blob)
+		die(_("writing config blobs is not supported"));
+}
+
+struct urlmatch_current_candidate_value {
+	char value_is_null;
+	struct strbuf value;
+};
+
+static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
+{
+	struct string_list *values = cb;
+	struct string_list_item *item = string_list_insert(values, var);
+	struct urlmatch_current_candidate_value *matched = item->util;
+
+	if (!matched) {
+		matched = xmalloc(sizeof(*matched));
+		strbuf_init(&matched->value, 0);
+		item->util = matched;
+	} else {
+		strbuf_reset(&matched->value);
+	}
+
+	if (value) {
+		strbuf_addstr(&matched->value, value);
+		matched->value_is_null = 0;
+	} else {
+		matched->value_is_null = 1;
+	}
+	return 0;
+}
+
+static int get_urlmatch(const char *var, const char *url)
+{
+	int ret;
+	char *section_tail;
+	struct string_list_item *item;
+	struct urlmatch_config config = { STRING_LIST_INIT_DUP };
+	struct string_list values = STRING_LIST_INIT_DUP;
+
+	config.collect_fn = urlmatch_collect_fn;
+	config.cascade_fn = NULL;
+	config.cb = &values;
+
+	if (!url_normalize(url, &config.url))
+		die("%s", config.url.err);
+
+	config.section = xstrdup_tolower(var);
+	section_tail = strchr(config.section, '.');
+	if (section_tail) {
+		*section_tail = '\0';
+		config.key = section_tail + 1;
+		show_keys = 0;
+	} else {
+		config.key = NULL;
+		show_keys = 1;
+	}
+
+	config_with_options(urlmatch_config_entry, &config,
+			    &given_config_source, &config_options);
+
+	ret = !values.nr;
+
+	for_each_string_list_item(item, &values) {
+		struct urlmatch_current_candidate_value *matched = item->util;
+		struct strbuf buf = STRBUF_INIT;
+
+		format_config(&buf, item->string,
+			      matched->value_is_null ? NULL : matched->value.buf);
+		fwrite(buf.buf, 1, buf.len, stdout);
+		strbuf_release(&buf);
+
+		strbuf_release(&matched->value);
+	}
+	string_list_clear(&config.vars, 1);
+	string_list_clear(&values, 1);
+	free(config.url.url);
+
+	free((void *)config.section);
+	return ret;
+}
+
+static char *default_user_config(void)
+{
+	struct strbuf buf = STRBUF_INIT;
+	strbuf_addf(&buf,
+		    _("# This is Git's per-user configuration file.\n"
+		      "[user]\n"
+		      "# Please adapt and uncomment the following lines:\n"
+		      "#	name = %s\n"
+		      "#	email = %s\n"),
+		    ident_default_name(),
+		    ident_default_email());
+	return strbuf_detach(&buf, NULL);
+}
+
+int cmd_config(int argc, const char **argv, const char *prefix)
+{
+	int nongit = !startup_info->have_repository;
+	char *value;
+
+	given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
+
+	argc = parse_options(argc, argv, prefix, builtin_config_options,
+			     builtin_config_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+
+	if (use_global_config + use_system_config + use_local_config +
+	    use_worktree_config +
+	    !!given_config_source.file + !!given_config_source.blob > 1) {
+		error(_("only one config file at a time"));
+		usage_builtin_config();
+	}
+
+	if (use_local_config && nongit)
+		die(_("--local can only be used inside a git repository"));
+
+	if (given_config_source.blob && nongit)
+		die(_("--blob can only be used inside a git repository"));
+
+	if (given_config_source.file &&
+			!strcmp(given_config_source.file, "-")) {
+		given_config_source.file = NULL;
+		given_config_source.use_stdin = 1;
+	}
+
+	if (use_global_config) {
+		char *user_config = expand_user_path("~/.gitconfig", 0);
+		char *xdg_config = xdg_config_home("config");
+
+		if (!user_config)
+			/*
+			 * It is unknown if HOME/.gitconfig exists, so
+			 * we do not know if we should write to XDG
+			 * location; error out even if XDG_CONFIG_HOME
+			 * is set and points at a sane location.
+			 */
+			die(_("$HOME not set"));
+
+		if (access_or_warn(user_config, R_OK, 0) &&
+		    xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
+			given_config_source.file = xdg_config;
+			free(user_config);
+		} else {
+			given_config_source.file = user_config;
+			free(xdg_config);
+		}
+	}
+	else if (use_system_config)
+		given_config_source.file = git_etc_gitconfig();
+	else if (use_local_config)
+		given_config_source.file = git_pathdup("config");
+	else if (use_worktree_config) {
+		struct worktree **worktrees = get_worktrees(0);
+		if (repository_format_worktree_config)
+			given_config_source.file = git_pathdup("config.worktree");
+		else if (worktrees[0] && worktrees[1])
+			die(_("--worktree cannot be used with multiple "
+			      "working trees unless the config\n"
+			      "extension worktreeConfig is enabled. "
+			      "Please read \"CONFIGURATION FILE\"\n"
+			      "section in \"git help worktree\" for details"));
+		else
+			given_config_source.file = git_pathdup("config");
+		free_worktrees(worktrees);
+	} else if (given_config_source.file) {
+		if (!is_absolute_path(given_config_source.file) && prefix)
+			given_config_source.file =
+				prefix_filename(prefix, given_config_source.file);
+	}
+
+	if (respect_includes_opt == -1)
+		config_options.respect_includes = !given_config_source.file;
+	else
+		config_options.respect_includes = respect_includes_opt;
+	if (!nongit) {
+		config_options.commondir = get_git_common_dir();
+		config_options.git_dir = get_git_dir();
+	}
+
+	if (end_null) {
+		term = '\0';
+		delim = '\n';
+		key_delim = '\n';
+	}
+
+	if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {
+		error(_("--get-color and variable type are incoherent"));
+		usage_builtin_config();
+	}
+
+	if (HAS_MULTI_BITS(actions)) {
+		error(_("only one action at a time"));
+		usage_builtin_config();
+	}
+	if (actions == 0)
+		switch (argc) {
+		case 1: actions = ACTION_GET; break;
+		case 2: actions = ACTION_SET; break;
+		case 3: actions = ACTION_SET_ALL; break;
+		default:
+			usage_builtin_config();
+		}
+	if (omit_values &&
+	    !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {
+		error(_("--name-only is only applicable to --list or --get-regexp"));
+		usage_builtin_config();
+	}
+
+	if (show_origin && !(actions &
+		(ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {
+		error(_("--show-origin is only applicable to --get, --get-all, "
+			"--get-regexp, and --list"));
+		usage_builtin_config();
+	}
+
+	if (default_value && !(actions & ACTION_GET)) {
+		error(_("--default is only applicable to --get"));
+		usage_builtin_config();
+	}
+
+	if (actions & PAGING_ACTIONS)
+		setup_auto_pager("config", 1);
+
+	if (actions == ACTION_LIST) {
+		check_argc(argc, 0, 0);
+		if (config_with_options(show_all_config, NULL,
+					&given_config_source,
+					&config_options) < 0) {
+			if (given_config_source.file)
+				die_errno(_("unable to read config file '%s'"),
+					  given_config_source.file);
+			else
+				die(_("error processing config file(s)"));
+		}
+	}
+	else if (actions == ACTION_EDIT) {
+		char *config_file;
+
+		check_argc(argc, 0, 0);
+		if (!given_config_source.file && nongit)
+			die(_("not in a git directory"));
+		if (given_config_source.use_stdin)
+			die(_("editing stdin is not supported"));
+		if (given_config_source.blob)
+			die(_("editing blobs is not supported"));
+		git_config(git_default_config, NULL);
+		config_file = given_config_source.file ?
+				xstrdup(given_config_source.file) :
+				git_pathdup("config");
+		if (use_global_config) {
+			int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
+			if (fd >= 0) {
+				char *content = default_user_config();
+				write_str_in_full(fd, content);
+				free(content);
+				close(fd);
+			}
+			else if (errno != EEXIST)
+				die_errno(_("cannot create configuration file %s"), config_file);
+		}
+		launch_editor(config_file, NULL, NULL);
+		free(config_file);
+	}
+	else if (actions == ACTION_SET) {
+		int ret;
+		check_write();
+		check_argc(argc, 2, 2);
+		value = normalize_value(argv[0], argv[1]);
+		UNLEAK(value);
+		ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
+		if (ret == CONFIG_NOTHING_SET)
+			error(_("cannot overwrite multiple values with a single value\n"
+			"       Use a regexp, --add or --replace-all to change %s."), argv[0]);
+		return ret;
+	}
+	else if (actions == ACTION_SET_ALL) {
+		check_write();
+		check_argc(argc, 2, 3);
+		value = normalize_value(argv[0], argv[1]);
+		UNLEAK(value);
+		return git_config_set_multivar_in_file_gently(given_config_source.file,
+							      argv[0], value, argv[2], 0);
+	}
+	else if (actions == ACTION_ADD) {
+		check_write();
+		check_argc(argc, 2, 2);
+		value = normalize_value(argv[0], argv[1]);
+		UNLEAK(value);
+		return git_config_set_multivar_in_file_gently(given_config_source.file,
+							      argv[0], value,
+							      CONFIG_REGEX_NONE, 0);
+	}
+	else if (actions == ACTION_REPLACE_ALL) {
+		check_write();
+		check_argc(argc, 2, 3);
+		value = normalize_value(argv[0], argv[1]);
+		UNLEAK(value);
+		return git_config_set_multivar_in_file_gently(given_config_source.file,
+							      argv[0], value, argv[2], 1);
+	}
+	else if (actions == ACTION_GET) {
+		check_argc(argc, 1, 2);
+		return get_value(argv[0], argv[1]);
+	}
+	else if (actions == ACTION_GET_ALL) {
+		do_all = 1;
+		check_argc(argc, 1, 2);
+		return get_value(argv[0], argv[1]);
+	}
+	else if (actions == ACTION_GET_REGEXP) {
+		show_keys = 1;
+		use_key_regexp = 1;
+		do_all = 1;
+		check_argc(argc, 1, 2);
+		return get_value(argv[0], argv[1]);
+	}
+	else if (actions == ACTION_GET_URLMATCH) {
+		check_argc(argc, 2, 2);
+		return get_urlmatch(argv[0], argv[1]);
+	}
+	else if (actions == ACTION_UNSET) {
+		check_write();
+		check_argc(argc, 1, 2);
+		if (argc == 2)
+			return git_config_set_multivar_in_file_gently(given_config_source.file,
+								      argv[0], NULL, argv[1], 0);
+		else
+			return git_config_set_in_file_gently(given_config_source.file,
+							     argv[0], NULL);
+	}
+	else if (actions == ACTION_UNSET_ALL) {
+		check_write();
+		check_argc(argc, 1, 2);
+		return git_config_set_multivar_in_file_gently(given_config_source.file,
+							      argv[0], NULL, argv[1], 1);
+	}
+	else if (actions == ACTION_RENAME_SECTION) {
+		int ret;
+		check_write();
+		check_argc(argc, 2, 2);
+		ret = git_config_rename_section_in_file(given_config_source.file,
+							argv[0], argv[1]);
+		if (ret < 0)
+			return ret;
+		if (ret == 0)
+			die(_("no such section: %s"), argv[0]);
+	}
+	else if (actions == ACTION_REMOVE_SECTION) {
+		int ret;
+		check_write();
+		check_argc(argc, 1, 1);
+		ret = git_config_rename_section_in_file(given_config_source.file,
+							argv[0], NULL);
+		if (ret < 0)
+			return ret;
+		if (ret == 0)
+			die(_("no such section: %s"), argv[0]);
+	}
+	else if (actions == ACTION_GET_COLOR) {
+		check_argc(argc, 1, 2);
+		get_color(argv[0], argv[1]);
+	}
+	else if (actions == ACTION_GET_COLORBOOL) {
+		check_argc(argc, 1, 2);
+		if (argc == 2)
+			color_stdout_is_tty = git_config_bool("command line", argv[1]);
+		return get_colorbool(argv[0], argc == 2);
+	}
+
+	return 0;
+}
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
new file mode 100644
index 0000000000..3fae474f6f
--- /dev/null
+++ b/builtin/count-objects.c
@@ -0,0 +1,172 @@
+/*
+ * Builtin "git count-objects".
+ *
+ * Copyright (c) 2006 Junio C Hamano
+ */
+
+#include "cache.h"
+#include "config.h"
+#include "dir.h"
+#include "repository.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "quote.h"
+#include "packfile.h"
+#include "object-store.h"
+
+static unsigned long garbage;
+static off_t size_garbage;
+static int verbose;
+static unsigned long loose, packed, packed_loose;
+static off_t loose_size;
+
+static const char *bits_to_msg(unsigned seen_bits)
+{
+	switch (seen_bits) {
+	case 0:
+		return "no corresponding .idx or .pack";
+	case PACKDIR_FILE_GARBAGE:
+		return "garbage found";
+	case PACKDIR_FILE_PACK:
+		return "no corresponding .idx";
+	case PACKDIR_FILE_IDX:
+		return "no corresponding .pack";
+	case PACKDIR_FILE_PACK|PACKDIR_FILE_IDX:
+	default:
+		return NULL;
+	}
+}
+
+static void real_report_garbage(unsigned seen_bits, const char *path)
+{
+	struct stat st;
+	const char *desc = bits_to_msg(seen_bits);
+
+	if (!desc)
+		return;
+
+	if (!stat(path, &st))
+		size_garbage += st.st_size;
+	warning("%s: %s", desc, path);
+	garbage++;
+}
+
+static void loose_garbage(const char *path)
+{
+	if (verbose)
+		report_garbage(PACKDIR_FILE_GARBAGE, path);
+}
+
+static int count_loose(const struct object_id *oid, const char *path, void *data)
+{
+	struct stat st;
+
+	if (lstat(path, &st) || !S_ISREG(st.st_mode))
+		loose_garbage(path);
+	else {
+		loose_size += on_disk_bytes(st);
+		loose++;
+		if (verbose && has_object_pack(oid))
+			packed_loose++;
+	}
+	return 0;
+}
+
+static int count_cruft(const char *basename, const char *path, void *data)
+{
+	loose_garbage(path);
+	return 0;
+}
+
+static int print_alternate(struct object_directory *odb, void *data)
+{
+	printf("alternate: ");
+	quote_c_style(odb->path, NULL, stdout, 0);
+	putchar('\n');
+	return 0;
+}
+
+static char const * const count_objects_usage[] = {
+	N_("git count-objects [-v] [-H | --human-readable]"),
+	NULL
+};
+
+int cmd_count_objects(int argc, const char **argv, const char *prefix)
+{
+	int human_readable = 0;
+	struct option opts[] = {
+		OPT__VERBOSE(&verbose, N_("be verbose")),
+		OPT_BOOL('H', "human-readable", &human_readable,
+			 N_("print sizes in human readable format")),
+		OPT_END(),
+	};
+
+	git_config(git_default_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, opts, count_objects_usage, 0);
+	/* we do not take arguments other than flags for now */
+	if (argc)
+		usage_with_options(count_objects_usage, opts);
+	if (verbose) {
+		report_garbage = real_report_garbage;
+		report_linked_checkout_garbage();
+	}
+
+	for_each_loose_file_in_objdir(get_object_directory(),
+				      count_loose, count_cruft, NULL, NULL);
+
+	if (verbose) {
+		struct packed_git *p;
+		unsigned long num_pack = 0;
+		off_t size_pack = 0;
+		struct strbuf loose_buf = STRBUF_INIT;
+		struct strbuf pack_buf = STRBUF_INIT;
+		struct strbuf garbage_buf = STRBUF_INIT;
+
+		for (p = get_all_packs(the_repository); p; p = p->next) {
+			if (!p->pack_local)
+				continue;
+			if (open_pack_index(p))
+				continue;
+			packed += p->num_objects;
+			size_pack += p->pack_size + p->index_size;
+			num_pack++;
+		}
+
+		if (human_readable) {
+			strbuf_humanise_bytes(&loose_buf, loose_size);
+			strbuf_humanise_bytes(&pack_buf, size_pack);
+			strbuf_humanise_bytes(&garbage_buf, size_garbage);
+		} else {
+			strbuf_addf(&loose_buf, "%lu",
+				    (unsigned long)(loose_size / 1024));
+			strbuf_addf(&pack_buf, "%lu",
+				    (unsigned long)(size_pack / 1024));
+			strbuf_addf(&garbage_buf, "%lu",
+				    (unsigned long)(size_garbage / 1024));
+		}
+
+		printf("count: %lu\n", loose);
+		printf("size: %s\n", loose_buf.buf);
+		printf("in-pack: %lu\n", packed);
+		printf("packs: %lu\n", num_pack);
+		printf("size-pack: %s\n", pack_buf.buf);
+		printf("prune-packable: %lu\n", packed_loose);
+		printf("garbage: %lu\n", garbage);
+		printf("size-garbage: %s\n", garbage_buf.buf);
+		foreach_alt_odb(print_alternate, NULL);
+		strbuf_release(&loose_buf);
+		strbuf_release(&pack_buf);
+		strbuf_release(&garbage_buf);
+	} else {
+		struct strbuf buf = STRBUF_INIT;
+		if (human_readable)
+			strbuf_humanise_bytes(&buf, loose_size);
+		else
+			strbuf_addf(&buf, "%lu kilobytes",
+				    (unsigned long)(loose_size / 1024));
+		printf("%lu objects, %s\n", loose, buf.buf);
+		strbuf_release(&buf);
+	}
+	return 0;
+}
diff --git a/builtin/credential.c b/builtin/credential.c
new file mode 100644
index 0000000000..879acfbcda
--- /dev/null
+++ b/builtin/credential.c
@@ -0,0 +1,31 @@
+#include "git-compat-util.h"
+#include "credential.h"
+#include "builtin.h"
+
+static const char usage_msg[] =
+	"git credential [fill|approve|reject]";
+
+int cmd_credential(int argc, const char **argv, const char *prefix)
+{
+	const char *op;
+	struct credential c = CREDENTIAL_INIT;
+
+	if (argc != 2 || !strcmp(argv[1], "-h"))
+		usage(usage_msg);
+	op = argv[1];
+
+	if (credential_read(&c, stdin) < 0)
+		die("unable to read credential from stdin");
+
+	if (!strcmp(op, "fill")) {
+		credential_fill(&c);
+		credential_write(&c, stdout);
+	} else if (!strcmp(op, "approve")) {
+		credential_approve(&c);
+	} else if (!strcmp(op, "reject")) {
+		credential_reject(&c);
+	} else {
+		usage(usage_msg);
+	}
+	return 0;
+}
diff --git a/builtin/describe.c b/builtin/describe.c
new file mode 100644
index 0000000000..200154297d
--- /dev/null
+++ b/builtin/describe.c
@@ -0,0 +1,662 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "lockfile.h"
+#include "commit.h"
+#include "tag.h"
+#include "blob.h"
+#include "refs.h"
+#include "builtin.h"
+#include "exec-cmd.h"
+#include "parse-options.h"
+#include "revision.h"
+#include "diff.h"
+#include "hashmap.h"
+#include "argv-array.h"
+#include "run-command.h"
+#include "object-store.h"
+#include "revision.h"
+#include "list-objects.h"
+#include "commit-slab.h"
+
+#define MAX_TAGS	(FLAG_BITS - 1)
+
+define_commit_slab(commit_names, struct commit_name *);
+
+static const char * const describe_usage[] = {
+	N_("git describe [<options>] [<commit-ish>...]"),
+	N_("git describe [<options>] --dirty"),
+	NULL
+};
+
+static int debug;	/* Display lots of verbose info */
+static int all;	/* Any valid ref can be used */
+static int tags;	/* Allow lightweight tags */
+static int longformat;
+static int first_parent;
+static int abbrev = -1; /* unspecified */
+static int max_candidates = 10;
+static struct hashmap names;
+static int have_util;
+static struct string_list patterns = STRING_LIST_INIT_NODUP;
+static struct string_list exclude_patterns = STRING_LIST_INIT_NODUP;
+static int always;
+static const char *suffix, *dirty, *broken;
+static struct commit_names commit_names;
+
+/* diff-index command arguments to check if working tree is dirty. */
+static const char *diff_index_args[] = {
+	"diff-index", "--quiet", "HEAD", "--", NULL
+};
+
+struct commit_name {
+	struct hashmap_entry entry;
+	struct object_id peeled;
+	struct tag *tag;
+	unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
+	unsigned name_checked:1;
+	struct object_id oid;
+	char *path;
+};
+
+static const char *prio_names[] = {
+	N_("head"), N_("lightweight"), N_("annotated"),
+};
+
+static int commit_name_neq(const void *unused_cmp_data,
+			   const void *entry,
+			   const void *entry_or_key,
+			   const void *peeled)
+{
+	const struct commit_name *cn1 = entry;
+	const struct commit_name *cn2 = entry_or_key;
+
+	return !oideq(&cn1->peeled, peeled ? peeled : &cn2->peeled);
+}
+
+static inline struct commit_name *find_commit_name(const struct object_id *peeled)
+{
+	return hashmap_get_from_hash(&names, oidhash(peeled), peeled);
+}
+
+static int replace_name(struct commit_name *e,
+			       int prio,
+			       const struct object_id *oid,
+			       struct tag **tag)
+{
+	if (!e || e->prio < prio)
+		return 1;
+
+	if (e->prio == 2 && prio == 2) {
+		/* Multiple annotated tags point to the same commit.
+		 * Select one to keep based upon their tagger date.
+		 */
+		struct tag *t;
+
+		if (!e->tag) {
+			t = lookup_tag(the_repository, &e->oid);
+			if (!t || parse_tag(t))
+				return 1;
+			e->tag = t;
+		}
+
+		t = lookup_tag(the_repository, oid);
+		if (!t || parse_tag(t))
+			return 0;
+		*tag = t;
+
+		if (e->tag->date < t->date)
+			return 1;
+	}
+
+	return 0;
+}
+
+static void add_to_known_names(const char *path,
+			       const struct object_id *peeled,
+			       int prio,
+			       const struct object_id *oid)
+{
+	struct commit_name *e = find_commit_name(peeled);
+	struct tag *tag = NULL;
+	if (replace_name(e, prio, oid, &tag)) {
+		if (!e) {
+			e = xmalloc(sizeof(struct commit_name));
+			oidcpy(&e->peeled, peeled);
+			hashmap_entry_init(e, oidhash(peeled));
+			hashmap_add(&names, e);
+			e->path = NULL;
+		}
+		e->tag = tag;
+		e->prio = prio;
+		e->name_checked = 0;
+		oidcpy(&e->oid, oid);
+		free(e->path);
+		e->path = xstrdup(path);
+	}
+}
+
+static int get_name(const char *path, const struct object_id *oid, int flag, void *cb_data)
+{
+	int is_tag = 0;
+	struct object_id peeled;
+	int is_annotated, prio;
+	const char *path_to_match = NULL;
+
+	if (skip_prefix(path, "refs/tags/", &path_to_match)) {
+		is_tag = 1;
+	} else if (all) {
+		if ((exclude_patterns.nr || patterns.nr) &&
+		    !skip_prefix(path, "refs/heads/", &path_to_match) &&
+		    !skip_prefix(path, "refs/remotes/", &path_to_match)) {
+			/* Only accept reference of known type if there are match/exclude patterns */
+			return 0;
+		}
+	} else {
+		/* Reject anything outside refs/tags/ unless --all */
+		return 0;
+	}
+
+	/*
+	 * If we're given exclude patterns, first exclude any tag which match
+	 * any of the exclude pattern.
+	 */
+	if (exclude_patterns.nr) {
+		struct string_list_item *item;
+
+		for_each_string_list_item(item, &exclude_patterns) {
+			if (!wildmatch(item->string, path_to_match, 0))
+				return 0;
+		}
+	}
+
+	/*
+	 * If we're given patterns, accept only tags which match at least one
+	 * pattern.
+	 */
+	if (patterns.nr) {
+		int found = 0;
+		struct string_list_item *item;
+
+		for_each_string_list_item(item, &patterns) {
+			if (!wildmatch(item->string, path_to_match, 0)) {
+				found = 1;
+				break;
+			}
+		}
+
+		if (!found)
+			return 0;
+	}
+
+	/* Is it annotated? */
+	if (!peel_ref(path, &peeled)) {
+		is_annotated = !oideq(oid, &peeled);
+	} else {
+		oidcpy(&peeled, oid);
+		is_annotated = 0;
+	}
+
+	/*
+	 * By default, we only use annotated tags, but with --tags
+	 * we fall back to lightweight ones (even without --tags,
+	 * we still remember lightweight ones, only to give hints
+	 * in an error message).  --all allows any refs to be used.
+	 */
+	if (is_annotated)
+		prio = 2;
+	else if (is_tag)
+		prio = 1;
+	else
+		prio = 0;
+
+	add_to_known_names(all ? path + 5 : path + 10, &peeled, prio, oid);
+	return 0;
+}
+
+struct possible_tag {
+	struct commit_name *name;
+	int depth;
+	int found_order;
+	unsigned flag_within;
+};
+
+static int compare_pt(const void *a_, const void *b_)
+{
+	struct possible_tag *a = (struct possible_tag *)a_;
+	struct possible_tag *b = (struct possible_tag *)b_;
+	if (a->depth != b->depth)
+		return a->depth - b->depth;
+	if (a->found_order != b->found_order)
+		return a->found_order - b->found_order;
+	return 0;
+}
+
+static unsigned long finish_depth_computation(
+	struct commit_list **list,
+	struct possible_tag *best)
+{
+	unsigned long seen_commits = 0;
+	while (*list) {
+		struct commit *c = pop_commit(list);
+		struct commit_list *parents = c->parents;
+		seen_commits++;
+		if (c->object.flags & best->flag_within) {
+			struct commit_list *a = *list;
+			while (a) {
+				struct commit *i = a->item;
+				if (!(i->object.flags & best->flag_within))
+					break;
+				a = a->next;
+			}
+			if (!a)
+				break;
+		} else
+			best->depth++;
+		while (parents) {
+			struct commit *p = parents->item;
+			parse_commit(p);
+			if (!(p->object.flags & SEEN))
+				commit_list_insert_by_date(p, list);
+			p->object.flags |= c->object.flags;
+			parents = parents->next;
+		}
+	}
+	return seen_commits;
+}
+
+static void append_name(struct commit_name *n, struct strbuf *dst)
+{
+	if (n->prio == 2 && !n->tag) {
+		n->tag = lookup_tag(the_repository, &n->oid);
+		if (!n->tag || parse_tag(n->tag))
+			die(_("annotated tag %s not available"), n->path);
+	}
+	if (n->tag && !n->name_checked) {
+		if (!n->tag->tag)
+			die(_("annotated tag %s has no embedded name"), n->path);
+		if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
+			warning(_("tag '%s' is really '%s' here"), n->tag->tag, n->path);
+		n->name_checked = 1;
+	}
+
+	if (n->tag) {
+		if (all)
+			strbuf_addstr(dst, "tags/");
+		strbuf_addstr(dst, n->tag->tag);
+	} else {
+		strbuf_addstr(dst, n->path);
+	}
+}
+
+static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst)
+{
+	strbuf_addf(dst, "-%d-g%s", depth, find_unique_abbrev(oid, abbrev));
+}
+
+static void describe_commit(struct object_id *oid, struct strbuf *dst)
+{
+	struct commit *cmit, *gave_up_on = NULL;
+	struct commit_list *list;
+	struct commit_name *n;
+	struct possible_tag all_matches[MAX_TAGS];
+	unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
+	unsigned long seen_commits = 0;
+	unsigned int unannotated_cnt = 0;
+
+	cmit = lookup_commit_reference(the_repository, oid);
+
+	n = find_commit_name(&cmit->object.oid);
+	if (n && (tags || all || n->prio == 2)) {
+		/*
+		 * Exact match to an existing ref.
+		 */
+		append_name(n, dst);
+		if (longformat)
+			append_suffix(0, n->tag ? &n->tag->tagged->oid : oid, dst);
+		if (suffix)
+			strbuf_addstr(dst, suffix);
+		return;
+	}
+
+	if (!max_candidates)
+		die(_("no tag exactly matches '%s'"), oid_to_hex(&cmit->object.oid));
+	if (debug)
+		fprintf(stderr, _("No exact match on refs or tags, searching to describe\n"));
+
+	if (!have_util) {
+		struct hashmap_iter iter;
+		struct commit *c;
+		struct commit_name *n;
+
+		init_commit_names(&commit_names);
+		n = hashmap_iter_first(&names, &iter);
+		for (; n; n = hashmap_iter_next(&iter)) {
+			c = lookup_commit_reference_gently(the_repository,
+							   &n->peeled, 1);
+			if (c)
+				*commit_names_at(&commit_names, c) = n;
+		}
+		have_util = 1;
+	}
+
+	list = NULL;
+	cmit->object.flags = SEEN;
+	commit_list_insert(cmit, &list);
+	while (list) {
+		struct commit *c = pop_commit(&list);
+		struct commit_list *parents = c->parents;
+		struct commit_name **slot;
+
+		seen_commits++;
+		slot = commit_names_peek(&commit_names, c);
+		n = slot ? *slot : NULL;
+		if (n) {
+			if (!tags && !all && n->prio < 2) {
+				unannotated_cnt++;
+			} else if (match_cnt < max_candidates) {
+				struct possible_tag *t = &all_matches[match_cnt++];
+				t->name = n;
+				t->depth = seen_commits - 1;
+				t->flag_within = 1u << match_cnt;
+				t->found_order = match_cnt;
+				c->object.flags |= t->flag_within;
+				if (n->prio == 2)
+					annotated_cnt++;
+			}
+			else {
+				gave_up_on = c;
+				break;
+			}
+		}
+		for (cur_match = 0; cur_match < match_cnt; cur_match++) {
+			struct possible_tag *t = &all_matches[cur_match];
+			if (!(c->object.flags & t->flag_within))
+				t->depth++;
+		}
+		if (annotated_cnt && !list) {
+			if (debug)
+				fprintf(stderr, _("finished search at %s\n"),
+					oid_to_hex(&c->object.oid));
+			break;
+		}
+		while (parents) {
+			struct commit *p = parents->item;
+			parse_commit(p);
+			if (!(p->object.flags & SEEN))
+				commit_list_insert_by_date(p, &list);
+			p->object.flags |= c->object.flags;
+			parents = parents->next;
+
+			if (first_parent)
+				break;
+		}
+	}
+
+	if (!match_cnt) {
+		struct object_id *cmit_oid = &cmit->object.oid;
+		if (always) {
+			strbuf_add_unique_abbrev(dst, cmit_oid, abbrev);
+			if (suffix)
+				strbuf_addstr(dst, suffix);
+			return;
+		}
+		if (unannotated_cnt)
+			die(_("No annotated tags can describe '%s'.\n"
+			    "However, there were unannotated tags: try --tags."),
+			    oid_to_hex(cmit_oid));
+		else
+			die(_("No tags can describe '%s'.\n"
+			    "Try --always, or create some tags."),
+			    oid_to_hex(cmit_oid));
+	}
+
+	QSORT(all_matches, match_cnt, compare_pt);
+
+	if (gave_up_on) {
+		commit_list_insert_by_date(gave_up_on, &list);
+		seen_commits--;
+	}
+	seen_commits += finish_depth_computation(&list, &all_matches[0]);
+	free_commit_list(list);
+
+	if (debug) {
+		static int label_width = -1;
+		if (label_width < 0) {
+			int i, w;
+			for (i = 0; i < ARRAY_SIZE(prio_names); i++) {
+				w = strlen(_(prio_names[i]));
+				if (label_width < w)
+					label_width = w;
+			}
+		}
+		for (cur_match = 0; cur_match < match_cnt; cur_match++) {
+			struct possible_tag *t = &all_matches[cur_match];
+			fprintf(stderr, " %-*s %8d %s\n",
+				label_width, _(prio_names[t->name->prio]),
+				t->depth, t->name->path);
+		}
+		fprintf(stderr, _("traversed %lu commits\n"), seen_commits);
+		if (gave_up_on) {
+			fprintf(stderr,
+				_("more than %i tags found; listed %i most recent\n"
+				"gave up search at %s\n"),
+				max_candidates, max_candidates,
+				oid_to_hex(&gave_up_on->object.oid));
+		}
+	}
+
+	append_name(all_matches[0].name, dst);
+	if (abbrev)
+		append_suffix(all_matches[0].depth, &cmit->object.oid, dst);
+	if (suffix)
+		strbuf_addstr(dst, suffix);
+}
+
+struct process_commit_data {
+	struct object_id current_commit;
+	struct object_id looking_for;
+	struct strbuf *dst;
+	struct rev_info *revs;
+};
+
+static void process_commit(struct commit *commit, void *data)
+{
+	struct process_commit_data *pcd = data;
+	pcd->current_commit = commit->object.oid;
+}
+
+static void process_object(struct object *obj, const char *path, void *data)
+{
+	struct process_commit_data *pcd = data;
+
+	if (oideq(&pcd->looking_for, &obj->oid) && !pcd->dst->len) {
+		reset_revision_walk();
+		describe_commit(&pcd->current_commit, pcd->dst);
+		strbuf_addf(pcd->dst, ":%s", path);
+		free_commit_list(pcd->revs->commits);
+		pcd->revs->commits = NULL;
+	}
+}
+
+static void describe_blob(struct object_id oid, struct strbuf *dst)
+{
+	struct rev_info revs;
+	struct argv_array args = ARGV_ARRAY_INIT;
+	struct process_commit_data pcd = { null_oid, oid, dst, &revs};
+
+	argv_array_pushl(&args, "internal: The first arg is not parsed",
+		"--objects", "--in-commit-order", "--reverse", "HEAD",
+		NULL);
+
+	repo_init_revisions(the_repository, &revs, NULL);
+	if (setup_revisions(args.argc, args.argv, &revs, NULL) > 1)
+		BUG("setup_revisions could not handle all args?");
+
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+
+	traverse_commit_list(&revs, process_commit, process_object, &pcd);
+	reset_revision_walk();
+}
+
+static void describe(const char *arg, int last_one)
+{
+	struct object_id oid;
+	struct commit *cmit;
+	struct strbuf sb = STRBUF_INIT;
+
+	if (debug)
+		fprintf(stderr, _("describe %s\n"), arg);
+
+	if (get_oid(arg, &oid))
+		die(_("Not a valid object name %s"), arg);
+	cmit = lookup_commit_reference_gently(the_repository, &oid, 1);
+
+	if (cmit)
+		describe_commit(&oid, &sb);
+	else if (oid_object_info(the_repository, &oid, NULL) == OBJ_BLOB)
+		describe_blob(oid, &sb);
+	else
+		die(_("%s is neither a commit nor blob"), arg);
+
+	puts(sb.buf);
+
+	if (!last_one)
+		clear_commit_marks(cmit, -1);
+
+	strbuf_release(&sb);
+}
+
+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_LIST(0, "match", &patterns, N_("pattern"),
+			   N_("only consider tags matching <pattern>")),
+		OPT_STRING_LIST(0, "exclude", &exclude_patterns, N_("pattern"),
+			   N_("do not 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"},
+		{OPTION_STRING, 0, "broken",  &broken, N_("mark"),
+			N_("append <mark> on broken working tree (default: \"-broken\")"),
+			PARSE_OPT_OPTARG, NULL, (intptr_t) "-broken"},
+		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 string_list_item *item;
+		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");
+			for_each_string_list_item(item, &patterns)
+				argv_array_pushf(&args, "--refs=refs/tags/%s", item->string);
+			for_each_string_list_item(item, &exclude_patterns)
+				argv_array_pushf(&args, "--exclude=refs/tags/%s", item->string);
+		}
+		if (argc)
+			argv_array_pushv(&args, argv);
+		else
+			argv_array_push(&args, "HEAD");
+		return cmd_name_rev(args.argc, args.argv, prefix);
+	}
+
+	hashmap_init(&names, commit_name_neq, NULL, 0);
+	for_each_rawref(get_name, NULL);
+	if (!hashmap_get_size(&names) && !always)
+		die(_("No names found, cannot describe anything."));
+
+	if (argc == 0) {
+		if (broken) {
+			struct child_process cp = CHILD_PROCESS_INIT;
+			argv_array_pushv(&cp.args, diff_index_args);
+			cp.git_cmd = 1;
+			cp.no_stdin = 1;
+			cp.no_stdout = 1;
+
+			if (!dirty)
+				dirty = "-dirty";
+
+			switch (run_command(&cp)) {
+			case 0:
+				suffix = NULL;
+				break;
+			case 1:
+				suffix = dirty;
+				break;
+			default:
+				/* diff-index aborted abnormally */
+				suffix = broken;
+			}
+		} else if (dirty) {
+			struct lock_file index_lock = LOCK_INIT;
+			struct rev_info revs;
+			struct argv_array args = ARGV_ARRAY_INIT;
+			int fd, result;
+
+			setup_work_tree();
+			read_cache();
+			refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
+				      NULL, NULL, NULL);
+			fd = hold_locked_index(&index_lock, 0);
+			if (0 <= fd)
+				repo_update_index_if_able(the_repository, &index_lock);
+
+			repo_init_revisions(the_repository, &revs, prefix);
+			argv_array_pushv(&args, diff_index_args);
+			if (setup_revisions(args.argc, args.argv, &revs, NULL) != 1)
+				BUG("malformed internal diff-index command line");
+			result = run_diff_index(&revs, 0);
+
+			if (!diff_result_code(&revs.diffopt, result))
+				suffix = NULL;
+			else
+				suffix = dirty;
+		}
+		describe("HEAD", 1);
+	} else if (dirty) {
+		die(_("--dirty is incompatible with commit-ishes"));
+	} else if (broken) {
+		die(_("--broken is incompatible with commit-ishes"));
+	} else {
+		while (argc-- > 0)
+			describe(*argv++, argc == 0);
+	}
+	return 0;
+}
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
new file mode 100644
index 0000000000..86ae474fbf
--- /dev/null
+++ b/builtin/diff-files.c
@@ -0,0 +1,75 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "diff.h"
+#include "commit.h"
+#include "revision.h"
+#include "builtin.h"
+#include "submodule.h"
+
+static const char diff_files_usage[] =
+"git diff-files [-q] [-0 | -1 | -2 | -3 | -c | --cc] [<common-diff-options>] [<path>...]"
+COMMON_DIFF_OPTIONS_HELP;
+
+int cmd_diff_files(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info rev;
+	int result;
+	unsigned options = 0;
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(diff_files_usage);
+
+	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+	repo_init_revisions(the_repository, &rev, prefix);
+	rev.abbrev = 0;
+	precompose_argv(argc, argv);
+
+	argc = setup_revisions(argc, argv, &rev, NULL);
+	while (1 < argc && argv[1][0] == '-') {
+		if (!strcmp(argv[1], "--base"))
+			rev.max_count = 1;
+		else if (!strcmp(argv[1], "--ours"))
+			rev.max_count = 2;
+		else if (!strcmp(argv[1], "--theirs"))
+			rev.max_count = 3;
+		else if (!strcmp(argv[1], "-q"))
+			options |= DIFF_SILENT_ON_REMOVED;
+		else
+			usage(diff_files_usage);
+		argv++; argc--;
+	}
+	if (!rev.diffopt.output_format)
+		rev.diffopt.output_format = DIFF_FORMAT_RAW;
+
+	/*
+	 * Make sure there are NO revision (i.e. pending object) parameter,
+	 * rev.max_count is reasonable (0 <= n <= 3), and
+	 * there is no other revision filtering parameters.
+	 */
+	if (rev.pending.nr ||
+	    rev.min_age != -1 || rev.max_age != -1 ||
+	    3 < rev.max_count)
+		usage(diff_files_usage);
+
+	/*
+	 * "diff-files --base -p" should not combine merges because it
+	 * was not asked to.  "diff-files -c -p" should not densify
+	 * (the user should ask with "diff-files --cc" explicitly).
+	 */
+	if (rev.max_count == -1 && !rev.combine_merges &&
+	    (rev.diffopt.output_format & DIFF_FORMAT_PATCH))
+		rev.combine_merges = rev.dense_combined_merges = 1;
+
+	if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
+		perror("read_cache_preload");
+		return -1;
+	}
+	result = run_diff_files(&rev, options);
+	return diff_result_code(&rev.diffopt, result);
+}
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
new file mode 100644
index 0000000000..93ec642423
--- /dev/null
+++ b/builtin/diff-index.c
@@ -0,0 +1,62 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "diff.h"
+#include "commit.h"
+#include "revision.h"
+#include "builtin.h"
+#include "submodule.h"
+
+static const char diff_cache_usage[] =
+"git diff-index [-m] [--cached] "
+"[<common-diff-options>] <tree-ish> [<path>...]"
+COMMON_DIFF_OPTIONS_HELP;
+
+int cmd_diff_index(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info rev;
+	int cached = 0;
+	int i;
+	int result;
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(diff_cache_usage);
+
+	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+	repo_init_revisions(the_repository, &rev, prefix);
+	rev.abbrev = 0;
+	precompose_argv(argc, argv);
+
+	argc = setup_revisions(argc, argv, &rev, NULL);
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (!strcmp(arg, "--cached"))
+			cached = 1;
+		else
+			usage(diff_cache_usage);
+	}
+	if (!rev.diffopt.output_format)
+		rev.diffopt.output_format = DIFF_FORMAT_RAW;
+
+	/*
+	 * Make sure there is one revision (i.e. pending object),
+	 * and there is no revision filtering parameters.
+	 */
+	if (rev.pending.nr != 1 ||
+	    rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
+		usage(diff_cache_usage);
+	if (!cached) {
+		setup_work_tree();
+		if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
+			perror("read_cache_preload");
+			return -1;
+		}
+	} else if (read_cache() < 0) {
+		perror("read_cache");
+		return -1;
+	}
+	result = run_diff_index(&rev, cached);
+	UNLEAK(rev);
+	return diff_result_code(&rev.diffopt, result);
+}
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
new file mode 100644
index 0000000000..cb9ea79367
--- /dev/null
+++ b/builtin/diff-tree.c
@@ -0,0 +1,196 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "diff.h"
+#include "commit.h"
+#include "log-tree.h"
+#include "builtin.h"
+#include "submodule.h"
+#include "repository.h"
+
+static struct rev_info log_tree_opt;
+
+static int diff_tree_commit_oid(const struct object_id *oid)
+{
+	struct commit *commit = lookup_commit_reference(the_repository, oid);
+	if (!commit)
+		return -1;
+	return log_tree_commit(&log_tree_opt, commit);
+}
+
+/* Diff one or more commits. */
+static int stdin_diff_commit(struct commit *commit, const char *p)
+{
+	struct object_id oid;
+	struct commit_list **pptr = NULL;
+
+	/* Graft the fake parents locally to the commit */
+	while (isspace(*p++) && !parse_oid_hex(p, &oid, &p)) {
+		struct commit *parent = lookup_commit(the_repository, &oid);
+		if (!pptr) {
+			/* Free the real parent list */
+			free_commit_list(commit->parents);
+			commit->parents = NULL;
+			pptr = &(commit->parents);
+		}
+		if (parent) {
+			pptr = &commit_list_insert(parent, pptr)->next;
+		}
+	}
+	return log_tree_commit(&log_tree_opt, commit);
+}
+
+/* Diff two trees. */
+static int stdin_diff_trees(struct tree *tree1, const char *p)
+{
+	struct object_id oid;
+	struct tree *tree2;
+	if (!isspace(*p++) || parse_oid_hex(p, &oid, &p) || *p)
+		return error("Need exactly two trees, separated by a space");
+	tree2 = lookup_tree(the_repository, &oid);
+	if (!tree2 || parse_tree(tree2))
+		return -1;
+	printf("%s %s\n", oid_to_hex(&tree1->object.oid),
+			  oid_to_hex(&tree2->object.oid));
+	diff_tree_oid(&tree1->object.oid, &tree2->object.oid,
+		      "", &log_tree_opt.diffopt);
+	log_tree_diff_flush(&log_tree_opt);
+	return 0;
+}
+
+static int diff_tree_stdin(char *line)
+{
+	int len = strlen(line);
+	struct object_id oid;
+	struct object *obj;
+	const char *p;
+
+	if (!len || line[len-1] != '\n')
+		return -1;
+	line[len-1] = 0;
+	if (parse_oid_hex(line, &oid, &p))
+		return -1;
+	obj = parse_object(the_repository, &oid);
+	if (!obj)
+		return -1;
+	if (obj->type == OBJ_COMMIT)
+		return stdin_diff_commit((struct commit *)obj, p);
+	if (obj->type == OBJ_TREE)
+		return stdin_diff_trees((struct tree *)obj, p);
+	error("Object %s is a %s, not a commit or tree",
+	      oid_to_hex(&oid), type_name(obj->type));
+	return -1;
+}
+
+static const char diff_tree_usage[] =
+"git diff-tree [--stdin] [-m] [-c | --cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
+"[<common-diff-options>] <tree-ish> [<tree-ish>] [<path>...]\n"
+"  -r            diff recursively\n"
+"  -c            show combined diff for merge commits\n"
+"  --cc          show combined diff for merge commits removing uninteresting hunks\n"
+"  --combined-all-paths\n"
+"                show name of file in all parents for combined diffs\n"
+"  --root        include the initial commit as diff against /dev/null\n"
+COMMON_DIFF_OPTIONS_HELP;
+
+static void diff_tree_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt)
+{
+	if (!rev->diffopt.output_format) {
+		if (rev->dense_combined_merges)
+			rev->diffopt.output_format = DIFF_FORMAT_PATCH;
+		else
+			rev->diffopt.output_format = DIFF_FORMAT_RAW;
+	}
+}
+
+int cmd_diff_tree(int argc, const char **argv, const char *prefix)
+{
+	char line[1000];
+	struct object *tree1, *tree2;
+	static struct rev_info *opt = &log_tree_opt;
+	struct setup_revision_opt s_r_opt;
+	int read_stdin = 0;
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(diff_tree_usage);
+
+	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+	repo_init_revisions(the_repository, opt, prefix);
+	if (read_cache() < 0)
+		die(_("index file corrupt"));
+	opt->abbrev = 0;
+	opt->diff = 1;
+	opt->disable_stdin = 1;
+	memset(&s_r_opt, 0, sizeof(s_r_opt));
+	s_r_opt.tweak = diff_tree_tweak_rev;
+
+	precompose_argv(argc, argv);
+	argc = setup_revisions(argc, argv, opt, &s_r_opt);
+
+	while (--argc > 0) {
+		const char *arg = *++argv;
+
+		if (!strcmp(arg, "--stdin")) {
+			read_stdin = 1;
+			continue;
+		}
+		usage(diff_tree_usage);
+	}
+
+	/*
+	 * NOTE!  We expect "a..b" to expand to "^a b" but it is
+	 * perfectly valid for revision range parser to yield "b ^a",
+	 * which means the same thing. If we get the latter, i.e. the
+	 * second one is marked UNINTERESTING, we recover the original
+	 * order the user gave, i.e. "a..b", by swapping the trees.
+	 */
+	switch (opt->pending.nr) {
+	case 0:
+		if (!read_stdin)
+			usage(diff_tree_usage);
+		break;
+	case 1:
+		tree1 = opt->pending.objects[0].item;
+		diff_tree_commit_oid(&tree1->oid);
+		break;
+	case 2:
+		tree1 = opt->pending.objects[0].item;
+		tree2 = opt->pending.objects[1].item;
+		if (tree2->flags & UNINTERESTING) {
+			SWAP(tree2, tree1);
+		}
+		diff_tree_oid(&tree1->oid, &tree2->oid, "", &opt->diffopt);
+		log_tree_diff_flush(opt);
+		break;
+	}
+
+	if (read_stdin) {
+		int saved_nrl = 0;
+		int saved_dcctc = 0;
+
+		if (opt->diffopt.detect_rename) {
+			if (!the_index.cache)
+				repo_read_index(the_repository);
+			opt->diffopt.setup |= DIFF_SETUP_USE_SIZE_CACHE;
+		}
+		while (fgets(line, sizeof(line), stdin)) {
+			struct object_id oid;
+
+			if (get_oid_hex(line, &oid)) {
+				fputs(line, stdout);
+				fflush(stdout);
+			}
+			else {
+				diff_tree_stdin(line);
+				if (saved_nrl < opt->diffopt.needed_rename_limit)
+					saved_nrl = opt->diffopt.needed_rename_limit;
+				if (opt->diffopt.degraded_cc_to_c)
+					saved_dcctc = 1;
+			}
+		}
+		opt->diffopt.degraded_cc_to_c = saved_dcctc;
+		opt->diffopt.needed_rename_limit = saved_nrl;
+	}
+
+	return diff_result_code(&opt->diffopt, 0);
+}
diff --git a/builtin/diff.c b/builtin/diff.c
new file mode 100644
index 0000000000..42ac803091
--- /dev/null
+++ b/builtin/diff.c
@@ -0,0 +1,465 @@
+/*
+ * Builtin "git diff"
+ *
+ * Copyright (c) 2006 Junio C Hamano
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "lockfile.h"
+#include "color.h"
+#include "commit.h"
+#include "blob.h"
+#include "tag.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "log-tree.h"
+#include "builtin.h"
+#include "submodule.h"
+#include "sha1-array.h"
+
+#define DIFF_NO_INDEX_EXPLICIT 1
+#define DIFF_NO_INDEX_IMPLICIT 2
+
+static const char builtin_diff_usage[] =
+"git diff [<options>] [<commit> [<commit>]] [--] [<path>...]";
+
+static const char *blob_path(struct object_array_entry *entry)
+{
+	return entry->path ? entry->path : entry->name;
+}
+
+static void stuff_change(struct diff_options *opt,
+			 unsigned old_mode, unsigned new_mode,
+			 const struct object_id *old_oid,
+			 const struct object_id *new_oid,
+			 int old_oid_valid,
+			 int new_oid_valid,
+			 const char *old_path,
+			 const char *new_path)
+{
+	struct diff_filespec *one, *two;
+
+	if (!is_null_oid(old_oid) && !is_null_oid(new_oid) &&
+	    oideq(old_oid, new_oid) && (old_mode == new_mode))
+		return;
+
+	if (opt->flags.reverse_diff) {
+		SWAP(old_mode, new_mode);
+		SWAP(old_oid, new_oid);
+		SWAP(old_path, new_path);
+	}
+
+	if (opt->prefix &&
+	    (strncmp(old_path, opt->prefix, opt->prefix_length) ||
+	     strncmp(new_path, opt->prefix, opt->prefix_length)))
+		return;
+
+	one = alloc_filespec(old_path);
+	two = alloc_filespec(new_path);
+	fill_filespec(one, old_oid, old_oid_valid, old_mode);
+	fill_filespec(two, new_oid, new_oid_valid, new_mode);
+
+	diff_queue(&diff_queued_diff, one, two);
+}
+
+static int builtin_diff_b_f(struct rev_info *revs,
+			    int argc, const char **argv,
+			    struct object_array_entry **blob)
+{
+	/* Blob vs file in the working tree*/
+	struct stat st;
+	const char *path;
+
+	if (argc > 1)
+		usage(builtin_diff_usage);
+
+	GUARD_PATHSPEC(&revs->prune_data, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
+	path = revs->prune_data.items[0].match;
+
+	if (lstat(path, &st))
+		die_errno(_("failed to stat '%s'"), path);
+	if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
+		die(_("'%s': not a regular file or symlink"), path);
+
+	diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/");
+
+	if (blob[0]->mode == S_IFINVALID)
+		blob[0]->mode = canon_mode(st.st_mode);
+
+	stuff_change(&revs->diffopt,
+		     blob[0]->mode, canon_mode(st.st_mode),
+		     &blob[0]->item->oid, &null_oid,
+		     1, 0,
+		     blob[0]->path ? blob[0]->path : path,
+		     path);
+	diffcore_std(&revs->diffopt);
+	diff_flush(&revs->diffopt);
+	return 0;
+}
+
+static int builtin_diff_blobs(struct rev_info *revs,
+			      int argc, const char **argv,
+			      struct object_array_entry **blob)
+{
+	const unsigned mode = canon_mode(S_IFREG | 0644);
+
+	if (argc > 1)
+		usage(builtin_diff_usage);
+
+	if (blob[0]->mode == S_IFINVALID)
+		blob[0]->mode = mode;
+
+	if (blob[1]->mode == S_IFINVALID)
+		blob[1]->mode = mode;
+
+	stuff_change(&revs->diffopt,
+		     blob[0]->mode, blob[1]->mode,
+		     &blob[0]->item->oid, &blob[1]->item->oid,
+		     1, 1,
+		     blob_path(blob[0]), blob_path(blob[1]));
+	diffcore_std(&revs->diffopt);
+	diff_flush(&revs->diffopt);
+	return 0;
+}
+
+static int builtin_diff_index(struct rev_info *revs,
+			      int argc, const char **argv)
+{
+	int cached = 0;
+	while (1 < argc) {
+		const char *arg = argv[1];
+		if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged"))
+			cached = 1;
+		else
+			usage(builtin_diff_usage);
+		argv++; argc--;
+	}
+	/*
+	 * Make sure there is one revision (i.e. pending object),
+	 * and there is no revision filtering parameters.
+	 */
+	if (revs->pending.nr != 1 ||
+	    revs->max_count != -1 || revs->min_age != -1 ||
+	    revs->max_age != -1)
+		usage(builtin_diff_usage);
+	if (!cached) {
+		setup_work_tree();
+		if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
+			perror("read_cache_preload");
+			return -1;
+		}
+	} else if (read_cache() < 0) {
+		perror("read_cache");
+		return -1;
+	}
+	return run_diff_index(revs, cached);
+}
+
+static int builtin_diff_tree(struct rev_info *revs,
+			     int argc, const char **argv,
+			     struct object_array_entry *ent0,
+			     struct object_array_entry *ent1)
+{
+	const struct object_id *(oid[2]);
+	int swap = 0;
+
+	if (argc > 1)
+		usage(builtin_diff_usage);
+
+	/*
+	 * We saw two trees, ent0 and ent1.  If ent1 is uninteresting,
+	 * swap them.
+	 */
+	if (ent1->item->flags & UNINTERESTING)
+		swap = 1;
+	oid[swap] = &ent0->item->oid;
+	oid[1 - swap] = &ent1->item->oid;
+	diff_tree_oid(oid[0], oid[1], "", &revs->diffopt);
+	log_tree_diff_flush(revs);
+	return 0;
+}
+
+static int builtin_diff_combined(struct rev_info *revs,
+				 int argc, const char **argv,
+				 struct object_array_entry *ent,
+				 int ents)
+{
+	struct oid_array parents = OID_ARRAY_INIT;
+	int i;
+
+	if (argc > 1)
+		usage(builtin_diff_usage);
+
+	if (!revs->dense_combined_merges && !revs->combine_merges)
+		revs->dense_combined_merges = revs->combine_merges = 1;
+	for (i = 1; i < ents; i++)
+		oid_array_append(&parents, &ent[i].item->oid);
+	diff_tree_combined(&ent[0].item->oid, &parents,
+			   revs->dense_combined_merges, revs);
+	oid_array_clear(&parents);
+	return 0;
+}
+
+static void refresh_index_quietly(void)
+{
+	struct lock_file lock_file = LOCK_INIT;
+	int fd;
+
+	fd = hold_locked_index(&lock_file, 0);
+	if (fd < 0)
+		return;
+	discard_cache();
+	read_cache();
+	refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
+	repo_update_index_if_able(the_repository, &lock_file);
+}
+
+static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
+{
+	unsigned int options = 0;
+
+	while (1 < argc && argv[1][0] == '-') {
+		if (!strcmp(argv[1], "--base"))
+			revs->max_count = 1;
+		else if (!strcmp(argv[1], "--ours"))
+			revs->max_count = 2;
+		else if (!strcmp(argv[1], "--theirs"))
+			revs->max_count = 3;
+		else if (!strcmp(argv[1], "-q"))
+			options |= DIFF_SILENT_ON_REMOVED;
+		else if (!strcmp(argv[1], "-h"))
+			usage(builtin_diff_usage);
+		else
+			return error(_("invalid option: %s"), argv[1]);
+		argv++; argc--;
+	}
+
+	/*
+	 * "diff --base" should not combine merges because it was not
+	 * asked to.  "diff -c" should not densify (if the user wants
+	 * dense one, --cc can be explicitly asked for, or just rely
+	 * on the default).
+	 */
+	if (revs->max_count == -1 && !revs->combine_merges &&
+	    (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
+		revs->combine_merges = revs->dense_combined_merges = 1;
+
+	setup_work_tree();
+	if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
+		perror("read_cache_preload");
+		return -1;
+	}
+	return run_diff_files(revs, options);
+}
+
+int cmd_diff(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct rev_info rev;
+	struct object_array ent = OBJECT_ARRAY_INIT;
+	int blobs = 0, paths = 0;
+	struct object_array_entry *blob[2];
+	int nongit = 0, no_index = 0;
+	int result = 0;
+
+	/*
+	 * We could get N tree-ish in the rev.pending_objects list.
+	 * Also there could be M blobs there, and P pathspecs.
+	 *
+	 * N=0, M=0:
+	 *	cache vs files (diff-files)
+	 * N=0, M=2:
+	 *      compare two random blobs.  P must be zero.
+	 * N=0, M=1, P=1:
+	 *	compare a blob with a working tree file.
+	 *
+	 * N=1, M=0:
+	 *      tree vs cache (diff-index --cached)
+	 *
+	 * N=2, M=0:
+	 *      tree vs tree (diff-tree)
+	 *
+	 * N=0, M=0, P=2:
+	 *      compare two filesystem entities (aka --no-index).
+	 *
+	 * Other cases are errors.
+	 */
+
+	/* Were we asked to do --no-index explicitly? */
+	for (i = 1; i < argc; i++) {
+		if (!strcmp(argv[i], "--")) {
+			i++;
+			break;
+		}
+		if (!strcmp(argv[i], "--no-index"))
+			no_index = DIFF_NO_INDEX_EXPLICIT;
+		if (argv[i][0] != '-')
+			break;
+	}
+
+	prefix = setup_git_directory_gently(&nongit);
+
+	if (!no_index) {
+		/*
+		 * Treat git diff with at least one path outside of the
+		 * repo the same as if the command would have been executed
+		 * outside of a git repository.  In this case it behaves
+		 * the same way as "git diff --no-index <a> <b>", which acts
+		 * as a colourful "diff" replacement.
+		 */
+		if (nongit || ((argc == i + 2) &&
+			       (!path_inside_repo(prefix, argv[i]) ||
+				!path_inside_repo(prefix, argv[i + 1]))))
+			no_index = DIFF_NO_INDEX_IMPLICIT;
+	}
+
+	init_diff_ui_defaults();
+	git_config(git_diff_ui_config, NULL);
+	precompose_argv(argc, argv);
+
+	repo_init_revisions(the_repository, &rev, prefix);
+
+	/* Set up defaults that will apply to both no-index and regular diffs. */
+	rev.diffopt.stat_width = -1;
+	rev.diffopt.stat_graph_width = -1;
+	rev.diffopt.flags.allow_external = 1;
+	rev.diffopt.flags.allow_textconv = 1;
+
+	/* If this is a no-index diff, just run it and exit there. */
+	if (no_index)
+		exit(diff_no_index(&rev, no_index == DIFF_NO_INDEX_IMPLICIT,
+				   argc, argv));
+
+
+	/*
+	 * Otherwise, we are doing the usual "git" diff; set up any
+	 * further defaults that apply to regular diffs.
+	 */
+	rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
+
+	/*
+	 * Default to intent-to-add entries invisible in the
+	 * index. This makes them show up as new files in diff-files
+	 * and not at all in diff-cached.
+	 */
+	rev.diffopt.ita_invisible_in_index = 1;
+
+	if (nongit)
+		die(_("Not a git repository"));
+	argc = setup_revisions(argc, argv, &rev, NULL);
+	if (!rev.diffopt.output_format) {
+		rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+		diff_setup_done(&rev.diffopt);
+	}
+
+	rev.diffopt.flags.recursive = 1;
+
+	setup_diff_pager(&rev.diffopt);
+
+	/*
+	 * Do we have --cached and not have a pending object, then
+	 * default to HEAD by hand.  Eek.
+	 */
+	if (!rev.pending.nr) {
+		int i;
+		for (i = 1; i < argc; i++) {
+			const char *arg = argv[i];
+			if (!strcmp(arg, "--"))
+				break;
+			else if (!strcmp(arg, "--cached") ||
+				 !strcmp(arg, "--staged")) {
+				add_head_to_pending(&rev);
+				if (!rev.pending.nr) {
+					struct tree *tree;
+					tree = lookup_tree(the_repository,
+							   the_repository->hash_algo->empty_tree);
+					add_pending_object(&rev, &tree->object, "HEAD");
+				}
+				break;
+			}
+		}
+	}
+
+	for (i = 0; i < rev.pending.nr; i++) {
+		struct object_array_entry *entry = &rev.pending.objects[i];
+		struct object *obj = entry->item;
+		const char *name = entry->name;
+		int flags = (obj->flags & UNINTERESTING);
+		if (!obj->parsed)
+			obj = parse_object(the_repository, &obj->oid);
+		obj = deref_tag(the_repository, obj, NULL, 0);
+		if (!obj)
+			die(_("invalid object '%s' given."), name);
+		if (obj->type == OBJ_COMMIT)
+			obj = &get_commit_tree(((struct commit *)obj))->object;
+
+		if (obj->type == OBJ_TREE) {
+			obj->flags |= flags;
+			add_object_array(obj, name, &ent);
+		} else if (obj->type == OBJ_BLOB) {
+			if (2 <= blobs)
+				die(_("more than two blobs given: '%s'"), name);
+			blob[blobs] = entry;
+			blobs++;
+
+		} else {
+			die(_("unhandled object '%s' given."), name);
+		}
+	}
+	if (rev.prune_data.nr)
+		paths += rev.prune_data.nr;
+
+	/*
+	 * Now, do the arguments look reasonable?
+	 */
+	if (!ent.nr) {
+		switch (blobs) {
+		case 0:
+			result = builtin_diff_files(&rev, argc, argv);
+			break;
+		case 1:
+			if (paths != 1)
+				usage(builtin_diff_usage);
+			result = builtin_diff_b_f(&rev, argc, argv, blob);
+			break;
+		case 2:
+			if (paths)
+				usage(builtin_diff_usage);
+			result = builtin_diff_blobs(&rev, argc, argv, blob);
+			break;
+		default:
+			usage(builtin_diff_usage);
+		}
+	}
+	else if (blobs)
+		usage(builtin_diff_usage);
+	else if (ent.nr == 1)
+		result = builtin_diff_index(&rev, argc, argv);
+	else if (ent.nr == 2)
+		result = builtin_diff_tree(&rev, argc, argv,
+					   &ent.objects[0], &ent.objects[1]);
+	else if (ent.objects[0].item->flags & UNINTERESTING) {
+		/*
+		 * diff A...B where there is at least one merge base
+		 * between A and B.  We have ent.objects[0] ==
+		 * merge-base, ent.objects[ents-2] == A, and
+		 * ent.objects[ents-1] == B.  Show diff between the
+		 * base and B.  Note that we pick one merge base at
+		 * random if there are more than one.
+		 */
+		result = builtin_diff_tree(&rev, argc, argv,
+					   &ent.objects[0],
+					   &ent.objects[ent.nr-1]);
+	} else
+		result = builtin_diff_combined(&rev, argc, argv,
+					       ent.objects, ent.nr);
+	result = diff_result_code(&rev.diffopt, result);
+	if (1 < rev.diffopt.skip_stat_unmatch)
+		refresh_index_quietly();
+	UNLEAK(rev);
+	UNLEAK(ent);
+	UNLEAK(blob);
+	return result;
+}
diff --git a/builtin/difftool.c b/builtin/difftool.c
new file mode 100644
index 0000000000..16eb8b70ea
--- /dev/null
+++ b/builtin/difftool.c
@@ -0,0 +1,764 @@
+/*
+ * "git difftool" builtin command
+ *
+ * This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
+ * git-difftool--helper script.
+ *
+ * This script exports GIT_EXTERNAL_DIFF and GIT_PAGER for use by git.
+ * The GIT_DIFF* variables are exported for use by git-difftool--helper.
+ *
+ * Any arguments that are unknown to this script are forwarded to 'git diff'.
+ *
+ * Copyright (C) 2016 Johannes Schindelin
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "builtin.h"
+#include "run-command.h"
+#include "exec-cmd.h"
+#include "parse-options.h"
+#include "argv-array.h"
+#include "strbuf.h"
+#include "lockfile.h"
+#include "object-store.h"
+#include "dir.h"
+
+static int trust_exit_code;
+
+static const char *const builtin_difftool_usage[] = {
+	N_("git difftool [<options>] [<commit> [<commit>]] [--] [<path>...]"),
+	NULL
+};
+
+static int difftool_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "difftool.trustexitcode")) {
+		trust_exit_code = git_config_bool(var, value);
+		return 0;
+	}
+
+	return git_default_config(var, value, cb);
+}
+
+static int print_tool_help(void)
+{
+	const char *argv[] = { "mergetool", "--tool-help=diff", NULL };
+	return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int parse_index_info(char *p, int *mode1, int *mode2,
+			    struct object_id *oid1, struct object_id *oid2,
+			    char *status)
+{
+	if (*p != ':')
+		return error("expected ':', got '%c'", *p);
+	*mode1 = (int)strtol(p + 1, &p, 8);
+	if (*p != ' ')
+		return error("expected ' ', got '%c'", *p);
+	*mode2 = (int)strtol(p + 1, &p, 8);
+	if (*p != ' ')
+		return error("expected ' ', got '%c'", *p);
+	if (parse_oid_hex(++p, oid1, (const char **)&p))
+		return error("expected object ID, got '%s'", p);
+	if (*p != ' ')
+		return error("expected ' ', got '%c'", *p);
+	if (parse_oid_hex(++p, oid2, (const char **)&p))
+		return error("expected object ID, got '%s'", p);
+	if (*p != ' ')
+		return error("expected ' ', got '%c'", *p);
+	*status = *++p;
+	if (!*status)
+		return error("missing status");
+	if (p[1] && !isdigit(p[1]))
+		return error("unexpected trailer: '%s'", p + 1);
+	return 0;
+}
+
+/*
+ * Remove any trailing slash from $workdir
+ * before starting to avoid double slashes in symlink targets.
+ */
+static void add_path(struct strbuf *buf, size_t base_len, const char *path)
+{
+	strbuf_setlen(buf, base_len);
+	if (buf->len && buf->buf[buf->len - 1] != '/')
+		strbuf_addch(buf, '/');
+	strbuf_addstr(buf, path);
+}
+
+/*
+ * Determine whether we can simply reuse the file in the worktree.
+ */
+static int use_wt_file(const char *workdir, const char *name,
+		       struct object_id *oid)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct stat st;
+	int use = 0;
+
+	strbuf_addstr(&buf, workdir);
+	add_path(&buf, buf.len, name);
+
+	if (!lstat(buf.buf, &st) && !S_ISLNK(st.st_mode)) {
+		struct object_id wt_oid;
+		int fd = open(buf.buf, O_RDONLY);
+
+		if (fd >= 0 &&
+		    !index_fd(&the_index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
+			if (is_null_oid(oid)) {
+				oidcpy(oid, &wt_oid);
+				use = 1;
+			} else if (oideq(oid, &wt_oid))
+				use = 1;
+		}
+	}
+
+	strbuf_release(&buf);
+
+	return use;
+}
+
+struct working_tree_entry {
+	struct hashmap_entry entry;
+	char path[FLEX_ARRAY];
+};
+
+static int working_tree_entry_cmp(const void *unused_cmp_data,
+				  const void *entry,
+				  const void *entry_or_key,
+				  const void *unused_keydata)
+{
+	const struct working_tree_entry *a = entry;
+	const struct working_tree_entry *b = entry_or_key;
+	return strcmp(a->path, b->path);
+}
+
+/*
+ * The `left` and `right` entries hold paths for the symlinks hashmap,
+ * and a SHA-1 surrounded by brief text for submodules.
+ */
+struct pair_entry {
+	struct hashmap_entry entry;
+	char left[PATH_MAX], right[PATH_MAX];
+	const char path[FLEX_ARRAY];
+};
+
+static int pair_cmp(const void *unused_cmp_data,
+		    const void *entry,
+		    const void *entry_or_key,
+		    const void *unused_keydata)
+{
+	const struct pair_entry *a = entry;
+	const struct pair_entry *b = entry_or_key;
+
+	return strcmp(a->path, b->path);
+}
+
+static void add_left_or_right(struct hashmap *map, const char *path,
+			      const char *content, int is_right)
+{
+	struct pair_entry *e, *existing;
+
+	FLEX_ALLOC_STR(e, path, path);
+	hashmap_entry_init(e, strhash(path));
+	existing = hashmap_get(map, e, NULL);
+	if (existing) {
+		free(e);
+		e = existing;
+	} else {
+		e->left[0] = e->right[0] = '\0';
+		hashmap_add(map, e);
+	}
+	strlcpy(is_right ? e->right : e->left, content, PATH_MAX);
+}
+
+struct path_entry {
+	struct hashmap_entry entry;
+	char path[FLEX_ARRAY];
+};
+
+static int path_entry_cmp(const void *unused_cmp_data,
+			  const void *entry,
+			  const void *entry_or_key,
+			  const void *key)
+{
+	const struct path_entry *a = entry;
+	const struct path_entry *b = entry_or_key;
+
+	return strcmp(a->path, key ? key : b->path);
+}
+
+static void changed_files(struct hashmap *result, const char *index_path,
+			  const char *workdir)
+{
+	struct child_process update_index = CHILD_PROCESS_INIT;
+	struct child_process diff_files = CHILD_PROCESS_INIT;
+	struct strbuf index_env = STRBUF_INIT, buf = STRBUF_INIT;
+	const char *git_dir = absolute_path(get_git_dir()), *env[] = {
+		NULL, NULL
+	};
+	FILE *fp;
+
+	strbuf_addf(&index_env, "GIT_INDEX_FILE=%s", index_path);
+	env[0] = index_env.buf;
+
+	argv_array_pushl(&update_index.args,
+			 "--git-dir", git_dir, "--work-tree", workdir,
+			 "update-index", "--really-refresh", "-q",
+			 "--unmerged", NULL);
+	update_index.no_stdin = 1;
+	update_index.no_stdout = 1;
+	update_index.no_stderr = 1;
+	update_index.git_cmd = 1;
+	update_index.use_shell = 0;
+	update_index.clean_on_exit = 1;
+	update_index.dir = workdir;
+	update_index.env = env;
+	/* Ignore any errors of update-index */
+	run_command(&update_index);
+
+	argv_array_pushl(&diff_files.args,
+			 "--git-dir", git_dir, "--work-tree", workdir,
+			 "diff-files", "--name-only", "-z", NULL);
+	diff_files.no_stdin = 1;
+	diff_files.git_cmd = 1;
+	diff_files.use_shell = 0;
+	diff_files.clean_on_exit = 1;
+	diff_files.out = -1;
+	diff_files.dir = workdir;
+	diff_files.env = env;
+	if (start_command(&diff_files))
+		die("could not obtain raw diff");
+	fp = xfdopen(diff_files.out, "r");
+	while (!strbuf_getline_nul(&buf, fp)) {
+		struct path_entry *entry;
+		FLEX_ALLOC_STR(entry, path, buf.buf);
+		hashmap_entry_init(entry, strhash(buf.buf));
+		hashmap_add(result, entry);
+	}
+	fclose(fp);
+	if (finish_command(&diff_files))
+		die("diff-files did not exit properly");
+	strbuf_release(&index_env);
+	strbuf_release(&buf);
+}
+
+static NORETURN void exit_cleanup(const char *tmpdir, int exit_code)
+{
+	struct strbuf buf = STRBUF_INIT;
+	strbuf_addstr(&buf, tmpdir);
+	remove_dir_recursively(&buf, 0);
+	if (exit_code)
+		warning(_("failed: %d"), exit_code);
+	exit(exit_code);
+}
+
+static int ensure_leading_directories(char *path)
+{
+	switch (safe_create_leading_directories(path)) {
+		case SCLD_OK:
+		case SCLD_EXISTS:
+			return 0;
+		default:
+			return error(_("could not create leading directories "
+				       "of '%s'"), path);
+	}
+}
+
+/*
+ * Unconditional writing of a plain regular file is what
+ * "git difftool --dir-diff" wants to do for symlinks.  We are preparing two
+ * temporary directories to be fed to a Git-unaware tool that knows how to
+ * show a diff of two directories (e.g. "diff -r A B").
+ *
+ * Because the tool is Git-unaware, if a symbolic link appears in either of
+ * these temporary directories, it will try to dereference and show the
+ * difference of the target of the symbolic link, which is not what we want,
+ * as the goal of the dir-diff mode is to produce an output that is logically
+ * equivalent to what "git diff" produces.
+ *
+ * Most importantly, we want to get textual comparison of the result of the
+ * readlink(2).  get_symlink() provides that---it returns the contents of
+ * the symlink that gets written to a regular file to force the external tool
+ * to compare the readlink(2) result as text, even on a filesystem that is
+ * capable of doing a symbolic link.
+ */
+static char *get_symlink(const struct object_id *oid, const char *path)
+{
+	char *data;
+	if (is_null_oid(oid)) {
+		/* The symlink is unknown to Git so read from the filesystem */
+		struct strbuf link = STRBUF_INIT;
+		if (has_symlinks) {
+			if (strbuf_readlink(&link, path, strlen(path)))
+				die(_("could not read symlink %s"), path);
+		} else if (strbuf_read_file(&link, path, 128))
+			die(_("could not read symlink file %s"), path);
+
+		data = strbuf_detach(&link, NULL);
+	} else {
+		enum object_type type;
+		unsigned long size;
+		data = read_object_file(oid, &type, &size);
+		if (!data)
+			die(_("could not read object %s for symlink %s"),
+				oid_to_hex(oid), path);
+	}
+
+	return data;
+}
+
+static int checkout_path(unsigned mode, struct object_id *oid,
+			 const char *path, const struct checkout *state)
+{
+	struct cache_entry *ce;
+	int ret;
+
+	ce = make_transient_cache_entry(mode, oid, path, 0);
+	ret = checkout_entry(ce, state, NULL, NULL);
+
+	discard_cache_entry(ce);
+	return ret;
+}
+
+static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
+			int argc, const char **argv)
+{
+	char tmpdir[PATH_MAX];
+	struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT;
+	struct strbuf rpath = STRBUF_INIT, buf = STRBUF_INIT;
+	struct strbuf ldir = STRBUF_INIT, rdir = STRBUF_INIT;
+	struct strbuf wtdir = STRBUF_INIT;
+	char *lbase_dir, *rbase_dir;
+	size_t ldir_len, rdir_len, wtdir_len;
+	const char *workdir, *tmp;
+	int ret = 0, i;
+	FILE *fp;
+	struct hashmap working_tree_dups, submodules, symlinks2;
+	struct hashmap_iter iter;
+	struct pair_entry *entry;
+	struct index_state wtindex;
+	struct checkout lstate, rstate;
+	int rc, flags = RUN_GIT_CMD, err = 0;
+	struct child_process child = CHILD_PROCESS_INIT;
+	const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL };
+	struct hashmap wt_modified, tmp_modified;
+	int indices_loaded = 0;
+
+	workdir = get_git_work_tree();
+
+	/* Setup temp directories */
+	tmp = getenv("TMPDIR");
+	xsnprintf(tmpdir, sizeof(tmpdir), "%s/git-difftool.XXXXXX", tmp ? tmp : "/tmp");
+	if (!mkdtemp(tmpdir))
+		return error("could not create '%s'", tmpdir);
+	strbuf_addf(&ldir, "%s/left/", tmpdir);
+	strbuf_addf(&rdir, "%s/right/", tmpdir);
+	strbuf_addstr(&wtdir, workdir);
+	if (!wtdir.len || !is_dir_sep(wtdir.buf[wtdir.len - 1]))
+		strbuf_addch(&wtdir, '/');
+	mkdir(ldir.buf, 0700);
+	mkdir(rdir.buf, 0700);
+
+	memset(&wtindex, 0, sizeof(wtindex));
+
+	memset(&lstate, 0, sizeof(lstate));
+	lstate.base_dir = lbase_dir = xstrdup(ldir.buf);
+	lstate.base_dir_len = ldir.len;
+	lstate.force = 1;
+	memset(&rstate, 0, sizeof(rstate));
+	rstate.base_dir = rbase_dir = xstrdup(rdir.buf);
+	rstate.base_dir_len = rdir.len;
+	rstate.force = 1;
+
+	ldir_len = ldir.len;
+	rdir_len = rdir.len;
+	wtdir_len = wtdir.len;
+
+	hashmap_init(&working_tree_dups, working_tree_entry_cmp, NULL, 0);
+	hashmap_init(&submodules, pair_cmp, NULL, 0);
+	hashmap_init(&symlinks2, pair_cmp, NULL, 0);
+
+	child.no_stdin = 1;
+	child.git_cmd = 1;
+	child.use_shell = 0;
+	child.clean_on_exit = 1;
+	child.dir = prefix;
+	child.out = -1;
+	argv_array_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z",
+			 NULL);
+	for (i = 0; i < argc; i++)
+		argv_array_push(&child.args, argv[i]);
+	if (start_command(&child))
+		die("could not obtain raw diff");
+	fp = xfdopen(child.out, "r");
+
+	/* Build index info for left and right sides of the diff */
+	i = 0;
+	while (!strbuf_getline_nul(&info, fp)) {
+		int lmode, rmode;
+		struct object_id loid, roid;
+		char status;
+		const char *src_path, *dst_path;
+
+		if (starts_with(info.buf, "::"))
+			die(N_("combined diff formats('-c' and '--cc') are "
+			       "not supported in\n"
+			       "directory diff mode('-d' and '--dir-diff')."));
+
+		if (parse_index_info(info.buf, &lmode, &rmode, &loid, &roid,
+				     &status))
+			break;
+		if (strbuf_getline_nul(&lpath, fp))
+			break;
+		src_path = lpath.buf;
+
+		i++;
+		if (status != 'C' && status != 'R') {
+			dst_path = src_path;
+		} else {
+			if (strbuf_getline_nul(&rpath, fp))
+				break;
+			dst_path = rpath.buf;
+		}
+
+		if (S_ISGITLINK(lmode) || S_ISGITLINK(rmode)) {
+			strbuf_reset(&buf);
+			strbuf_addf(&buf, "Subproject commit %s",
+				    oid_to_hex(&loid));
+			add_left_or_right(&submodules, src_path, buf.buf, 0);
+			strbuf_reset(&buf);
+			strbuf_addf(&buf, "Subproject commit %s",
+				    oid_to_hex(&roid));
+			if (oideq(&loid, &roid))
+				strbuf_addstr(&buf, "-dirty");
+			add_left_or_right(&submodules, dst_path, buf.buf, 1);
+			continue;
+		}
+
+		if (S_ISLNK(lmode)) {
+			char *content = get_symlink(&loid, src_path);
+			add_left_or_right(&symlinks2, src_path, content, 0);
+			free(content);
+		}
+
+		if (S_ISLNK(rmode)) {
+			char *content = get_symlink(&roid, dst_path);
+			add_left_or_right(&symlinks2, dst_path, content, 1);
+			free(content);
+		}
+
+		if (lmode && status != 'C') {
+			if (checkout_path(lmode, &loid, src_path, &lstate)) {
+				ret = error("could not write '%s'", src_path);
+				goto finish;
+			}
+		}
+
+		if (rmode && !S_ISLNK(rmode)) {
+			struct working_tree_entry *entry;
+
+			/* Avoid duplicate working_tree entries */
+			FLEX_ALLOC_STR(entry, path, dst_path);
+			hashmap_entry_init(entry, strhash(dst_path));
+			if (hashmap_get(&working_tree_dups, entry, NULL)) {
+				free(entry);
+				continue;
+			}
+			hashmap_add(&working_tree_dups, entry);
+
+			if (!use_wt_file(workdir, dst_path, &roid)) {
+				if (checkout_path(rmode, &roid, dst_path,
+						  &rstate)) {
+					ret = error("could not write '%s'",
+						    dst_path);
+					goto finish;
+				}
+			} else if (!is_null_oid(&roid)) {
+				/*
+				 * Changes in the working tree need special
+				 * treatment since they are not part of the
+				 * index.
+				 */
+				struct cache_entry *ce2 =
+					make_cache_entry(&wtindex, rmode, &roid,
+							 dst_path, 0, 0);
+
+				add_index_entry(&wtindex, ce2,
+						ADD_CACHE_JUST_APPEND);
+
+				add_path(&rdir, rdir_len, dst_path);
+				if (ensure_leading_directories(rdir.buf)) {
+					ret = error("could not create "
+						    "directory for '%s'",
+						    dst_path);
+					goto finish;
+				}
+				add_path(&wtdir, wtdir_len, dst_path);
+				if (symlinks) {
+					if (symlink(wtdir.buf, rdir.buf)) {
+						ret = error_errno("could not symlink '%s' to '%s'", wtdir.buf, rdir.buf);
+						goto finish;
+					}
+				} else {
+					struct stat st;
+					if (stat(wtdir.buf, &st))
+						st.st_mode = 0644;
+					if (copy_file(rdir.buf, wtdir.buf,
+						      st.st_mode)) {
+						ret = error("could not copy '%s' to '%s'", wtdir.buf, rdir.buf);
+						goto finish;
+					}
+				}
+			}
+		}
+	}
+
+	fclose(fp);
+	fp = NULL;
+	if (finish_command(&child)) {
+		ret = error("error occurred running diff --raw");
+		goto finish;
+	}
+
+	if (!i)
+		goto finish;
+
+	/*
+	 * Changes to submodules require special treatment.This loop writes a
+	 * temporary file to both the left and right directories to show the
+	 * change in the recorded SHA1 for the submodule.
+	 */
+	hashmap_iter_init(&submodules, &iter);
+	while ((entry = hashmap_iter_next(&iter))) {
+		if (*entry->left) {
+			add_path(&ldir, ldir_len, entry->path);
+			ensure_leading_directories(ldir.buf);
+			write_file(ldir.buf, "%s", entry->left);
+		}
+		if (*entry->right) {
+			add_path(&rdir, rdir_len, entry->path);
+			ensure_leading_directories(rdir.buf);
+			write_file(rdir.buf, "%s", entry->right);
+		}
+	}
+
+	/*
+	 * Symbolic links require special treatment.The standard "git diff"
+	 * shows only the link itself, not the contents of the link target.
+	 * This loop replicates that behavior.
+	 */
+	hashmap_iter_init(&symlinks2, &iter);
+	while ((entry = hashmap_iter_next(&iter))) {
+		if (*entry->left) {
+			add_path(&ldir, ldir_len, entry->path);
+			ensure_leading_directories(ldir.buf);
+			write_file(ldir.buf, "%s", entry->left);
+		}
+		if (*entry->right) {
+			add_path(&rdir, rdir_len, entry->path);
+			ensure_leading_directories(rdir.buf);
+			write_file(rdir.buf, "%s", entry->right);
+		}
+	}
+
+	strbuf_release(&buf);
+
+	strbuf_setlen(&ldir, ldir_len);
+	helper_argv[1] = ldir.buf;
+	strbuf_setlen(&rdir, rdir_len);
+	helper_argv[2] = rdir.buf;
+
+	if (extcmd) {
+		helper_argv[0] = extcmd;
+		flags = 0;
+	} else
+		setenv("GIT_DIFFTOOL_DIRDIFF", "true", 1);
+	rc = run_command_v_opt(helper_argv, flags);
+
+	/*
+	 * If the diff includes working copy files and those
+	 * files were modified during the diff, then the changes
+	 * should be copied back to the working tree.
+	 * Do not copy back files when symlinks are used and the
+	 * external tool did not replace the original link with a file.
+	 *
+	 * These hashes are loaded lazily since they aren't needed
+	 * in the common case of --symlinks and the difftool updating
+	 * files through the symlink.
+	 */
+	hashmap_init(&wt_modified, path_entry_cmp, NULL, wtindex.cache_nr);
+	hashmap_init(&tmp_modified, path_entry_cmp, NULL, wtindex.cache_nr);
+
+	for (i = 0; i < wtindex.cache_nr; i++) {
+		struct hashmap_entry dummy;
+		const char *name = wtindex.cache[i]->name;
+		struct stat st;
+
+		add_path(&rdir, rdir_len, name);
+		if (lstat(rdir.buf, &st))
+			continue;
+
+		if ((symlinks && S_ISLNK(st.st_mode)) || !S_ISREG(st.st_mode))
+			continue;
+
+		if (!indices_loaded) {
+			struct lock_file lock = LOCK_INIT;
+			strbuf_reset(&buf);
+			strbuf_addf(&buf, "%s/wtindex", tmpdir);
+			if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 ||
+			    write_locked_index(&wtindex, &lock, COMMIT_LOCK)) {
+				ret = error("could not write %s", buf.buf);
+				goto finish;
+			}
+			changed_files(&wt_modified, buf.buf, workdir);
+			strbuf_setlen(&rdir, rdir_len);
+			changed_files(&tmp_modified, buf.buf, rdir.buf);
+			add_path(&rdir, rdir_len, name);
+			indices_loaded = 1;
+		}
+
+		hashmap_entry_init(&dummy, strhash(name));
+		if (hashmap_get(&tmp_modified, &dummy, name)) {
+			add_path(&wtdir, wtdir_len, name);
+			if (hashmap_get(&wt_modified, &dummy, name)) {
+				warning(_("both files modified: '%s' and '%s'."),
+					wtdir.buf, rdir.buf);
+				warning(_("working tree file has been left."));
+				warning("%s", "");
+				err = 1;
+			} else if (unlink(wtdir.buf) ||
+				   copy_file(wtdir.buf, rdir.buf, st.st_mode))
+				warning_errno(_("could not copy '%s' to '%s'"),
+					      rdir.buf, wtdir.buf);
+		}
+	}
+
+	if (err) {
+		warning(_("temporary files exist in '%s'."), tmpdir);
+		warning(_("you may want to cleanup or recover these."));
+		exit(1);
+	} else
+		exit_cleanup(tmpdir, rc);
+
+finish:
+	if (fp)
+		fclose(fp);
+
+	free(lbase_dir);
+	free(rbase_dir);
+	strbuf_release(&ldir);
+	strbuf_release(&rdir);
+	strbuf_release(&wtdir);
+	strbuf_release(&buf);
+
+	return ret;
+}
+
+static int run_file_diff(int prompt, const char *prefix,
+			 int argc, const char **argv)
+{
+	struct argv_array args = ARGV_ARRAY_INIT;
+	const char *env[] = {
+		"GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL,
+		NULL
+	};
+	int ret = 0, i;
+
+	if (prompt > 0)
+		env[2] = "GIT_DIFFTOOL_PROMPT=true";
+	else if (!prompt)
+		env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";
+
+
+	argv_array_push(&args, "diff");
+	for (i = 0; i < argc; i++)
+		argv_array_push(&args, argv[i]);
+	ret = run_command_v_opt_cd_env(args.argv, RUN_GIT_CMD, prefix, env);
+	exit(ret);
+}
+
+int cmd_difftool(int argc, const char **argv, const char *prefix)
+{
+	int use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0,
+	    tool_help = 0, no_index = 0;
+	static char *difftool_cmd = NULL, *extcmd = NULL;
+	struct option builtin_difftool_options[] = {
+		OPT_BOOL('g', "gui", &use_gui_tool,
+			 N_("use `diff.guitool` instead of `diff.tool`")),
+		OPT_BOOL('d', "dir-diff", &dir_diff,
+			 N_("perform a full-directory diff")),
+		OPT_SET_INT_F('y', "no-prompt", &prompt,
+			N_("do not prompt before launching a diff tool"),
+			0, PARSE_OPT_NONEG),
+		OPT_SET_INT_F(0, "prompt", &prompt, NULL,
+			1, PARSE_OPT_NONEG | PARSE_OPT_HIDDEN),
+		OPT_BOOL(0, "symlinks", &symlinks,
+			 N_("use symlinks in dir-diff mode")),
+		OPT_STRING('t', "tool", &difftool_cmd, N_("tool"),
+			   N_("use the specified diff tool")),
+		OPT_BOOL(0, "tool-help", &tool_help,
+			 N_("print a list of diff tools that may be used with "
+			    "`--tool`")),
+		OPT_BOOL(0, "trust-exit-code", &trust_exit_code,
+			 N_("make 'git-difftool' exit when an invoked diff "
+			    "tool returns a non - zero exit code")),
+		OPT_STRING('x', "extcmd", &extcmd, N_("command"),
+			   N_("specify a custom command for viewing diffs")),
+		OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")),
+		OPT_END()
+	};
+
+	git_config(difftool_config, NULL);
+	symlinks = has_symlinks;
+
+	argc = parse_options(argc, argv, prefix, builtin_difftool_options,
+			     builtin_difftool_usage, PARSE_OPT_KEEP_UNKNOWN |
+			     PARSE_OPT_KEEP_DASHDASH);
+
+	if (tool_help)
+		return print_tool_help();
+
+	if (!no_index && !startup_info->have_repository)
+		die(_("difftool requires worktree or --no-index"));
+
+	if (!no_index){
+		setup_work_tree();
+		setenv(GIT_DIR_ENVIRONMENT, absolute_path(get_git_dir()), 1);
+		setenv(GIT_WORK_TREE_ENVIRONMENT, absolute_path(get_git_work_tree()), 1);
+	} else if (dir_diff)
+		die(_("--dir-diff is incompatible with --no-index"));
+
+	if (use_gui_tool + !!difftool_cmd + !!extcmd > 1)
+		die(_("--gui, --tool and --extcmd are mutually exclusive"));
+
+	if (use_gui_tool)
+		setenv("GIT_MERGETOOL_GUI", "true", 1);
+	else if (difftool_cmd) {
+		if (*difftool_cmd)
+			setenv("GIT_DIFF_TOOL", difftool_cmd, 1);
+		else
+			die(_("no <tool> given for --tool=<tool>"));
+	}
+
+	if (extcmd) {
+		if (*extcmd)
+			setenv("GIT_DIFFTOOL_EXTCMD", extcmd, 1);
+		else
+			die(_("no <cmd> given for --extcmd=<cmd>"));
+	}
+
+	setenv("GIT_DIFFTOOL_TRUST_EXIT_CODE",
+	       trust_exit_code ? "true" : "false", 1);
+
+	/*
+	 * In directory diff mode, 'git-difftool--helper' is called once
+	 * to compare the a / b directories. In file diff mode, 'git diff'
+	 * will invoke a separate instance of 'git-difftool--helper' for
+	 * each file that changed.
+	 */
+	if (dir_diff)
+		return run_dir_diff(extcmd, symlinks, prefix, argc, argv);
+	return run_file_diff(prompt, prefix, argc, argv);
+}
diff --git a/builtin/env--helper.c b/builtin/env--helper.c
new file mode 100644
index 0000000000..23c214fff6
--- /dev/null
+++ b/builtin/env--helper.c
@@ -0,0 +1,95 @@
+#include "builtin.h"
+#include "config.h"
+#include "parse-options.h"
+
+static char const * const env__helper_usage[] = {
+	N_("git env--helper --type=[bool|ulong] <options> <env-var>"),
+	NULL
+};
+
+static enum {
+	ENV_HELPER_TYPE_BOOL = 1,
+	ENV_HELPER_TYPE_ULONG
+} cmdmode = 0;
+
+static int option_parse_type(const struct option *opt, const char *arg,
+			     int unset)
+{
+	if (!strcmp(arg, "bool"))
+		cmdmode = ENV_HELPER_TYPE_BOOL;
+	else if (!strcmp(arg, "ulong"))
+		cmdmode = ENV_HELPER_TYPE_ULONG;
+	else
+		die(_("unrecognized --type argument, %s"), arg);
+
+	return 0;
+}
+
+int cmd_env__helper(int argc, const char **argv, const char *prefix)
+{
+	int exit_code = 0;
+	const char *env_variable = NULL;
+	const char *env_default = NULL;
+	int ret;
+	int ret_int, default_int;
+	unsigned long ret_ulong, default_ulong;
+	struct option opts[] = {
+		OPT_CALLBACK_F(0, "type", &cmdmode, N_("type"),
+			       N_("value is given this type"), PARSE_OPT_NONEG,
+			       option_parse_type),
+		OPT_STRING(0, "default", &env_default, N_("value"),
+			   N_("default for git_env_*(...) to fall back on")),
+		OPT_BOOL(0, "exit-code", &exit_code,
+			 N_("be quiet only use git_env_*() value as exit code")),
+		OPT_END(),
+	};
+
+	argc = parse_options(argc, argv, prefix, opts, env__helper_usage,
+			     PARSE_OPT_KEEP_UNKNOWN);
+	if (env_default && !*env_default)
+		usage_with_options(env__helper_usage, opts);
+	if (!cmdmode)
+		usage_with_options(env__helper_usage, opts);
+	if (argc != 1)
+		usage_with_options(env__helper_usage, opts);
+	env_variable = argv[0];
+
+	switch (cmdmode) {
+	case ENV_HELPER_TYPE_BOOL:
+		if (env_default) {
+			default_int = git_parse_maybe_bool(env_default);
+			if (default_int == -1) {
+				error(_("option `--default' expects a boolean value with `--type=bool`, not `%s`"),
+				      env_default);
+				usage_with_options(env__helper_usage, opts);
+			}
+		} else {
+			default_int = 0;
+		}
+		ret_int = git_env_bool(env_variable, default_int);
+		if (!exit_code)
+			puts(ret_int ? "true" : "false");
+		ret = ret_int;
+		break;
+	case ENV_HELPER_TYPE_ULONG:
+		if (env_default) {
+			if (!git_parse_ulong(env_default, &default_ulong)) {
+				error(_("option `--default' expects an unsigned long value with `--type=ulong`, not `%s`"),
+				      env_default);
+				usage_with_options(env__helper_usage, opts);
+			}
+		} else {
+			default_ulong = 0;
+		}
+		ret_ulong = git_env_ulong(env_variable, default_ulong);
+		if (!exit_code)
+			printf("%lu\n", ret_ulong);
+		ret = ret_ulong;
+		break;
+	default:
+		BUG("unknown <type> value");
+		break;
+	}
+
+	return !ret;
+}
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
new file mode 100644
index 0000000000..f541f55d33
--- /dev/null
+++ b/builtin/fast-export.c
@@ -0,0 +1,1222 @@
+/*
+ * "git fast-export" builtin command
+ *
+ * Copyright (C) 2007 Johannes E. Schindelin
+ */
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "refs.h"
+#include "refspec.h"
+#include "object-store.h"
+#include "commit.h"
+#include "object.h"
+#include "tag.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "log-tree.h"
+#include "revision.h"
+#include "decorate.h"
+#include "string-list.h"
+#include "utf8.h"
+#include "parse-options.h"
+#include "quote.h"
+#include "remote.h"
+#include "blob.h"
+#include "commit-slab.h"
+
+static const char *fast_export_usage[] = {
+	N_("git fast-export [rev-list-opts]"),
+	NULL
+};
+
+static int progress;
+static enum { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT;
+static enum { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT;
+static enum { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT;
+static int fake_missing_tagger;
+static int use_done_feature;
+static int no_data;
+static int full_tree;
+static int reference_excluded_commits;
+static int show_original_ids;
+static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
+static struct string_list tag_refs = STRING_LIST_INIT_NODUP;
+static struct refspec refspecs = REFSPEC_INIT_FETCH;
+static int anonymize;
+static struct revision_sources revision_sources;
+
+static int parse_opt_signed_tag_mode(const struct option *opt,
+				     const char *arg, int unset)
+{
+	if (unset || !strcmp(arg, "abort"))
+		signed_tag_mode = SIGNED_TAG_ABORT;
+	else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
+		signed_tag_mode = VERBATIM;
+	else if (!strcmp(arg, "warn"))
+		signed_tag_mode = WARN;
+	else if (!strcmp(arg, "warn-strip"))
+		signed_tag_mode = WARN_STRIP;
+	else if (!strcmp(arg, "strip"))
+		signed_tag_mode = STRIP;
+	else
+		return error("Unknown signed-tags mode: %s", arg);
+	return 0;
+}
+
+static int parse_opt_tag_of_filtered_mode(const struct option *opt,
+					  const char *arg, int unset)
+{
+	if (unset || !strcmp(arg, "abort"))
+		tag_of_filtered_mode = TAG_FILTERING_ABORT;
+	else if (!strcmp(arg, "drop"))
+		tag_of_filtered_mode = DROP;
+	else if (!strcmp(arg, "rewrite"))
+		tag_of_filtered_mode = REWRITE;
+	else
+		return error("Unknown tag-of-filtered mode: %s", arg);
+	return 0;
+}
+
+static int parse_opt_reencode_mode(const struct option *opt,
+				   const char *arg, int unset)
+{
+	if (unset) {
+		reencode_mode = REENCODE_ABORT;
+		return 0;
+	}
+
+	switch (git_parse_maybe_bool(arg)) {
+	case 0:
+		reencode_mode = REENCODE_NO;
+		break;
+	case 1:
+		reencode_mode = REENCODE_YES;
+		break;
+	default:
+		if (!strcasecmp(arg, "abort"))
+			reencode_mode = REENCODE_ABORT;
+		else
+			return error("Unknown reencoding mode: %s", arg);
+	}
+
+	return 0;
+}
+
+static struct decoration idnums;
+static uint32_t last_idnum;
+
+static int has_unshown_parent(struct commit *commit)
+{
+	struct commit_list *parent;
+
+	for (parent = commit->parents; parent; parent = parent->next)
+		if (!(parent->item->object.flags & SHOWN) &&
+		    !(parent->item->object.flags & UNINTERESTING))
+			return 1;
+	return 0;
+}
+
+struct anonymized_entry {
+	struct hashmap_entry hash;
+	const char *orig;
+	size_t orig_len;
+	const char *anon;
+	size_t anon_len;
+};
+
+static int anonymized_entry_cmp(const void *unused_cmp_data,
+				const void *va, const void *vb,
+				const void *unused_keydata)
+{
+	const struct anonymized_entry *a = va, *b = vb;
+	return a->orig_len != b->orig_len ||
+		memcmp(a->orig, b->orig, a->orig_len);
+}
+
+/*
+ * Basically keep a cache of X->Y so that we can repeatedly replace
+ * the same anonymized string with another. The actual generation
+ * is farmed out to the generate function.
+ */
+static const void *anonymize_mem(struct hashmap *map,
+				 void *(*generate)(const void *, size_t *),
+				 const void *orig, size_t *len)
+{
+	struct anonymized_entry key, *ret;
+
+	if (!map->cmpfn)
+		hashmap_init(map, anonymized_entry_cmp, NULL, 0);
+
+	hashmap_entry_init(&key, memhash(orig, *len));
+	key.orig = orig;
+	key.orig_len = *len;
+	ret = hashmap_get(map, &key, NULL);
+
+	if (!ret) {
+		ret = xmalloc(sizeof(*ret));
+		hashmap_entry_init(&ret->hash, key.hash.hash);
+		ret->orig = xstrdup(orig);
+		ret->orig_len = *len;
+		ret->anon = generate(orig, len);
+		ret->anon_len = *len;
+		hashmap_put(map, ret);
+	}
+
+	*len = ret->anon_len;
+	return ret->anon;
+}
+
+/*
+ * We anonymize each component of a path individually,
+ * so that paths a/b and a/c will share a common root.
+ * The paths are cached via anonymize_mem so that repeated
+ * lookups for "a" will yield the same value.
+ */
+static void anonymize_path(struct strbuf *out, const char *path,
+			   struct hashmap *map,
+			   void *(*generate)(const void *, size_t *))
+{
+	while (*path) {
+		const char *end_of_component = strchrnul(path, '/');
+		size_t len = end_of_component - path;
+		const char *c = anonymize_mem(map, generate, path, &len);
+		strbuf_add(out, c, len);
+		path = end_of_component;
+		if (*path)
+			strbuf_addch(out, *path++);
+	}
+}
+
+static inline void *mark_to_ptr(uint32_t mark)
+{
+	return (void *)(uintptr_t)mark;
+}
+
+static inline uint32_t ptr_to_mark(void * mark)
+{
+	return (uint32_t)(uintptr_t)mark;
+}
+
+static inline void mark_object(struct object *object, uint32_t mark)
+{
+	add_decoration(&idnums, object, mark_to_ptr(mark));
+}
+
+static inline void mark_next_object(struct object *object)
+{
+	mark_object(object, ++last_idnum);
+}
+
+static int get_object_mark(struct object *object)
+{
+	void *decoration = lookup_decoration(&idnums, object);
+	if (!decoration)
+		return 0;
+	return ptr_to_mark(decoration);
+}
+
+static struct commit *rewrite_commit(struct commit *p)
+{
+	for (;;) {
+		if (p->parents && p->parents->next)
+			break;
+		if (p->object.flags & UNINTERESTING)
+			break;
+		if (!(p->object.flags & TREESAME))
+			break;
+		if (!p->parents)
+			return NULL;
+		p = p->parents->item;
+	}
+	return p;
+}
+
+static void show_progress(void)
+{
+	static int counter = 0;
+	if (!progress)
+		return;
+	if ((++counter % progress) == 0)
+		printf("progress %d objects\n", counter);
+}
+
+/*
+ * Ideally we would want some transformation of the blob data here
+ * that is unreversible, but would still be the same size and have
+ * the same data relationship to other blobs (so that we get the same
+ * delta and packing behavior as the original). But the first and last
+ * requirements there are probably mutually exclusive, so let's take
+ * the easy way out for now, and just generate arbitrary content.
+ *
+ * There's no need to cache this result with anonymize_mem, since
+ * we already handle blob content caching with marks.
+ */
+static char *anonymize_blob(unsigned long *size)
+{
+	static int counter;
+	struct strbuf out = STRBUF_INIT;
+	strbuf_addf(&out, "anonymous blob %d", counter++);
+	*size = out.len;
+	return strbuf_detach(&out, NULL);
+}
+
+static void export_blob(const struct object_id *oid)
+{
+	unsigned long size;
+	enum object_type type;
+	char *buf;
+	struct object *object;
+	int eaten;
+
+	if (no_data)
+		return;
+
+	if (is_null_oid(oid))
+		return;
+
+	object = lookup_object(the_repository, oid);
+	if (object && object->flags & SHOWN)
+		return;
+
+	if (anonymize) {
+		buf = anonymize_blob(&size);
+		object = (struct object *)lookup_blob(the_repository, oid);
+		eaten = 0;
+	} else {
+		buf = read_object_file(oid, &type, &size);
+		if (!buf)
+			die("could not read blob %s", oid_to_hex(oid));
+		if (check_object_signature(oid, buf, size, type_name(type)) < 0)
+			die("oid mismatch in blob %s", oid_to_hex(oid));
+		object = parse_object_buffer(the_repository, oid, type,
+					     size, buf, &eaten);
+	}
+
+	if (!object)
+		die("Could not read blob %s", oid_to_hex(oid));
+
+	mark_next_object(object);
+
+	printf("blob\nmark :%"PRIu32"\n", last_idnum);
+	if (show_original_ids)
+		printf("original-oid %s\n", oid_to_hex(oid));
+	printf("data %"PRIuMAX"\n", (uintmax_t)size);
+	if (size && fwrite(buf, size, 1, stdout) != 1)
+		die_errno("could not write blob '%s'", oid_to_hex(oid));
+	printf("\n");
+
+	show_progress();
+
+	object->flags |= SHOWN;
+	if (!eaten)
+		free(buf);
+}
+
+static int depth_first(const void *a_, const void *b_)
+{
+	const struct diff_filepair *a = *((const struct diff_filepair **)a_);
+	const struct diff_filepair *b = *((const struct diff_filepair **)b_);
+	const char *name_a, *name_b;
+	int len_a, len_b, len;
+	int cmp;
+
+	name_a = a->one ? a->one->path : a->two->path;
+	name_b = b->one ? b->one->path : b->two->path;
+
+	len_a = strlen(name_a);
+	len_b = strlen(name_b);
+	len = (len_a < len_b) ? len_a : len_b;
+
+	/* strcmp will sort 'd' before 'd/e', we want 'd/e' before 'd' */
+	cmp = memcmp(name_a, name_b, len);
+	if (cmp)
+		return cmp;
+	cmp = len_b - len_a;
+	if (cmp)
+		return cmp;
+	/*
+	 * Move 'R'ename entries last so that all references of the file
+	 * appear in the output before it is renamed (e.g., when a file
+	 * was copied and renamed in the same commit).
+	 */
+	return (a->status == 'R') - (b->status == 'R');
+}
+
+static void print_path_1(const char *path)
+{
+	int need_quote = quote_c_style(path, NULL, NULL, 0);
+	if (need_quote)
+		quote_c_style(path, NULL, stdout, 0);
+	else if (strchr(path, ' '))
+		printf("\"%s\"", path);
+	else
+		printf("%s", path);
+}
+
+static void *anonymize_path_component(const void *path, size_t *len)
+{
+	static int counter;
+	struct strbuf out = STRBUF_INIT;
+	strbuf_addf(&out, "path%d", counter++);
+	return strbuf_detach(&out, len);
+}
+
+static void print_path(const char *path)
+{
+	if (!anonymize)
+		print_path_1(path);
+	else {
+		static struct hashmap paths;
+		static struct strbuf anon = STRBUF_INIT;
+
+		anonymize_path(&anon, path, &paths, anonymize_path_component);
+		print_path_1(anon.buf);
+		strbuf_reset(&anon);
+	}
+}
+
+static void *generate_fake_oid(const void *old, size_t *len)
+{
+	static uint32_t counter = 1; /* avoid null oid */
+	const unsigned hashsz = the_hash_algo->rawsz;
+	unsigned char *out = xcalloc(hashsz, 1);
+	put_be32(out + hashsz - 4, counter++);
+	return out;
+}
+
+static const struct object_id *anonymize_oid(const struct object_id *oid)
+{
+	static struct hashmap objs;
+	size_t len = the_hash_algo->rawsz;
+	return anonymize_mem(&objs, generate_fake_oid, oid, &len);
+}
+
+static void show_filemodify(struct diff_queue_struct *q,
+			    struct diff_options *options, void *data)
+{
+	int i;
+	struct string_list *changed = data;
+
+	/*
+	 * Handle files below a directory first, in case they are all deleted
+	 * and the directory changes to a file or symlink.
+	 */
+	QSORT(q->queue, q->nr, depth_first);
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filespec *ospec = q->queue[i]->one;
+		struct diff_filespec *spec = q->queue[i]->two;
+
+		switch (q->queue[i]->status) {
+		case DIFF_STATUS_DELETED:
+			printf("D ");
+			print_path(spec->path);
+			string_list_insert(changed, spec->path);
+			putchar('\n');
+			break;
+
+		case DIFF_STATUS_COPIED:
+		case DIFF_STATUS_RENAMED:
+			/*
+			 * If a change in the file corresponding to ospec->path
+			 * has been observed, we cannot trust its contents
+			 * because the diff is calculated based on the prior
+			 * contents, not the current contents.  So, declare a
+			 * copy or rename only if there was no change observed.
+			 */
+			if (!string_list_has_string(changed, ospec->path)) {
+				printf("%c ", q->queue[i]->status);
+				print_path(ospec->path);
+				putchar(' ');
+				print_path(spec->path);
+				string_list_insert(changed, spec->path);
+				putchar('\n');
+
+				if (oideq(&ospec->oid, &spec->oid) &&
+				    ospec->mode == spec->mode)
+					break;
+			}
+			/* fallthrough */
+
+		case DIFF_STATUS_TYPE_CHANGED:
+		case DIFF_STATUS_MODIFIED:
+		case DIFF_STATUS_ADDED:
+			/*
+			 * Links refer to objects in another repositories;
+			 * output the SHA-1 verbatim.
+			 */
+			if (no_data || S_ISGITLINK(spec->mode))
+				printf("M %06o %s ", spec->mode,
+				       oid_to_hex(anonymize ?
+						  anonymize_oid(&spec->oid) :
+						  &spec->oid));
+			else {
+				struct object *object = lookup_object(the_repository,
+								      &spec->oid);
+				printf("M %06o :%d ", spec->mode,
+				       get_object_mark(object));
+			}
+			print_path(spec->path);
+			string_list_insert(changed, spec->path);
+			putchar('\n');
+			break;
+
+		default:
+			die("Unexpected comparison status '%c' for %s, %s",
+				q->queue[i]->status,
+				ospec->path ? ospec->path : "none",
+				spec->path ? spec->path : "none");
+		}
+	}
+}
+
+static const char *find_encoding(const char *begin, const char *end)
+{
+	const char *needle = "\nencoding ";
+	char *bol, *eol;
+
+	bol = memmem(begin, end ? end - begin : strlen(begin),
+		     needle, strlen(needle));
+	if (!bol)
+		return NULL;
+	bol += strlen(needle);
+	eol = strchrnul(bol, '\n');
+	*eol = '\0';
+	return bol;
+}
+
+static void *anonymize_ref_component(const void *old, size_t *len)
+{
+	static int counter;
+	struct strbuf out = STRBUF_INIT;
+	strbuf_addf(&out, "ref%d", counter++);
+	return strbuf_detach(&out, len);
+}
+
+static const char *anonymize_refname(const char *refname)
+{
+	/*
+	 * If any of these prefixes is found, we will leave it intact
+	 * so that tags remain tags and so forth.
+	 */
+	static const char *prefixes[] = {
+		"refs/heads/",
+		"refs/tags/",
+		"refs/remotes/",
+		"refs/"
+	};
+	static struct hashmap refs;
+	static struct strbuf anon = STRBUF_INIT;
+	int i;
+
+	/*
+	 * We also leave "master" as a special case, since it does not reveal
+	 * anything interesting.
+	 */
+	if (!strcmp(refname, "refs/heads/master"))
+		return refname;
+
+	strbuf_reset(&anon);
+	for (i = 0; i < ARRAY_SIZE(prefixes); i++) {
+		if (skip_prefix(refname, prefixes[i], &refname)) {
+			strbuf_addstr(&anon, prefixes[i]);
+			break;
+		}
+	}
+
+	anonymize_path(&anon, refname, &refs, anonymize_ref_component);
+	return anon.buf;
+}
+
+/*
+ * We do not even bother to cache commit messages, as they are unlikely
+ * to be repeated verbatim, and it is not that interesting when they are.
+ */
+static char *anonymize_commit_message(const char *old)
+{
+	static int counter;
+	return xstrfmt("subject %d\n\nbody\n", counter++);
+}
+
+static struct hashmap idents;
+static void *anonymize_ident(const void *old, size_t *len)
+{
+	static int counter;
+	struct strbuf out = STRBUF_INIT;
+	strbuf_addf(&out, "User %d <user%d@example.com>", counter, counter);
+	counter++;
+	return strbuf_detach(&out, len);
+}
+
+/*
+ * Our strategy here is to anonymize the names and email addresses,
+ * but keep timestamps intact, as they influence things like traversal
+ * order (and by themselves should not be too revealing).
+ */
+static void anonymize_ident_line(const char **beg, const char **end)
+{
+	static struct strbuf buffers[] = { STRBUF_INIT, STRBUF_INIT };
+	static unsigned which_buffer;
+
+	struct strbuf *out;
+	struct ident_split split;
+	const char *end_of_header;
+
+	out = &buffers[which_buffer++];
+	which_buffer %= ARRAY_SIZE(buffers);
+	strbuf_reset(out);
+
+	/* skip "committer", "author", "tagger", etc */
+	end_of_header = strchr(*beg, ' ');
+	if (!end_of_header)
+		BUG("malformed line fed to anonymize_ident_line: %.*s",
+		    (int)(*end - *beg), *beg);
+	end_of_header++;
+	strbuf_add(out, *beg, end_of_header - *beg);
+
+	if (!split_ident_line(&split, end_of_header, *end - end_of_header) &&
+	    split.date_begin) {
+		const char *ident;
+		size_t len;
+
+		len = split.mail_end - split.name_begin;
+		ident = anonymize_mem(&idents, anonymize_ident,
+				      split.name_begin, &len);
+		strbuf_add(out, ident, len);
+		strbuf_addch(out, ' ');
+		strbuf_add(out, split.date_begin, split.tz_end - split.date_begin);
+	} else {
+		strbuf_addstr(out, "Malformed Ident <malformed@example.com> 0 -0000");
+	}
+
+	*beg = out->buf;
+	*end = out->buf + out->len;
+}
+
+static void handle_commit(struct commit *commit, struct rev_info *rev,
+			  struct string_list *paths_of_changed_objects)
+{
+	int saved_output_format = rev->diffopt.output_format;
+	const char *commit_buffer;
+	const char *author, *author_end, *committer, *committer_end;
+	const char *encoding, *message;
+	char *reencoded = NULL;
+	struct commit_list *p;
+	const char *refname;
+	int i;
+
+	rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
+
+	parse_commit_or_die(commit);
+	commit_buffer = get_commit_buffer(commit, NULL);
+	author = strstr(commit_buffer, "\nauthor ");
+	if (!author)
+		die("could not find author in commit %s",
+		    oid_to_hex(&commit->object.oid));
+	author++;
+	author_end = strchrnul(author, '\n');
+	committer = strstr(author_end, "\ncommitter ");
+	if (!committer)
+		die("could not find committer in commit %s",
+		    oid_to_hex(&commit->object.oid));
+	committer++;
+	committer_end = strchrnul(committer, '\n');
+	message = strstr(committer_end, "\n\n");
+	encoding = find_encoding(committer_end, message);
+	if (message)
+		message += 2;
+
+	if (commit->parents &&
+	    (get_object_mark(&commit->parents->item->object) != 0 ||
+	     reference_excluded_commits) &&
+	    !full_tree) {
+		parse_commit_or_die(commit->parents->item);
+		diff_tree_oid(get_commit_tree_oid(commit->parents->item),
+			      get_commit_tree_oid(commit), "", &rev->diffopt);
+	}
+	else
+		diff_root_tree_oid(get_commit_tree_oid(commit),
+				   "", &rev->diffopt);
+
+	/* Export the referenced blobs, and remember the marks. */
+	for (i = 0; i < diff_queued_diff.nr; i++)
+		if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode))
+			export_blob(&diff_queued_diff.queue[i]->two->oid);
+
+	refname = *revision_sources_at(&revision_sources, commit);
+	/*
+	 * FIXME: string_list_remove() below for each ref is overall
+	 * O(N^2).  Compared to a history walk and diffing trees, this is
+	 * just lost in the noise in practice.  However, theoretically a
+	 * repo may have enough refs for this to become slow.
+	 */
+	string_list_remove(&extra_refs, refname, 0);
+	if (anonymize) {
+		refname = anonymize_refname(refname);
+		anonymize_ident_line(&committer, &committer_end);
+		anonymize_ident_line(&author, &author_end);
+	}
+
+	mark_next_object(&commit->object);
+	if (anonymize) {
+		reencoded = anonymize_commit_message(message);
+	} else if (encoding) {
+		switch(reencode_mode) {
+		case REENCODE_YES:
+			reencoded = reencode_string(message, "UTF-8", encoding);
+			break;
+		case REENCODE_NO:
+			break;
+		case REENCODE_ABORT:
+			die("Encountered commit-specific encoding %s in commit "
+			    "%s; use --reencode=[yes|no] to handle it",
+			    encoding, oid_to_hex(&commit->object.oid));
+		}
+	}
+	if (!commit->parents)
+		printf("reset %s\n", refname);
+	printf("commit %s\nmark :%"PRIu32"\n", refname, last_idnum);
+	if (show_original_ids)
+		printf("original-oid %s\n", oid_to_hex(&commit->object.oid));
+	printf("%.*s\n%.*s\n",
+	       (int)(author_end - author), author,
+	       (int)(committer_end - committer), committer);
+	if (!reencoded && encoding)
+		printf("encoding %s\n", encoding);
+	printf("data %u\n%s",
+	       (unsigned)(reencoded
+			  ? strlen(reencoded) : message
+			  ? strlen(message) : 0),
+	       reencoded ? reencoded : message ? message : "");
+	free(reencoded);
+	unuse_commit_buffer(commit, commit_buffer);
+
+	for (i = 0, p = commit->parents; p; p = p->next) {
+		struct object *obj = &p->item->object;
+		int mark = get_object_mark(obj);
+
+		if (!mark && !reference_excluded_commits)
+			continue;
+		if (i == 0)
+			printf("from ");
+		else
+			printf("merge ");
+		if (mark)
+			printf(":%d\n", mark);
+		else
+			printf("%s\n", oid_to_hex(anonymize ?
+						  anonymize_oid(&obj->oid) :
+						  &obj->oid));
+		i++;
+	}
+
+	if (full_tree)
+		printf("deleteall\n");
+	log_tree_diff_flush(rev);
+	string_list_clear(paths_of_changed_objects, 0);
+	rev->diffopt.output_format = saved_output_format;
+
+	printf("\n");
+
+	show_progress();
+}
+
+static void *anonymize_tag(const void *old, size_t *len)
+{
+	static int counter;
+	struct strbuf out = STRBUF_INIT;
+	strbuf_addf(&out, "tag message %d", counter++);
+	return strbuf_detach(&out, len);
+}
+
+static void handle_tail(struct object_array *commits, struct rev_info *revs,
+			struct string_list *paths_of_changed_objects)
+{
+	struct commit *commit;
+	while (commits->nr) {
+		commit = (struct commit *)object_array_pop(commits);
+		if (has_unshown_parent(commit)) {
+			/* Queue again, to be handled later */
+			add_object_array(&commit->object, NULL, commits);
+			return;
+		}
+		handle_commit(commit, revs, paths_of_changed_objects);
+	}
+}
+
+static void handle_tag(const char *name, struct tag *tag)
+{
+	unsigned long size;
+	enum object_type type;
+	char *buf;
+	const char *tagger, *tagger_end, *message;
+	size_t message_size = 0;
+	struct object *tagged;
+	int tagged_mark;
+	struct commit *p;
+
+	/* Trees have no identifier in fast-export output, thus we have no way
+	 * to output tags of trees, tags of tags of trees, etc.  Simply omit
+	 * such tags.
+	 */
+	tagged = tag->tagged;
+	while (tagged->type == OBJ_TAG) {
+		tagged = ((struct tag *)tagged)->tagged;
+	}
+	if (tagged->type == OBJ_TREE) {
+		warning("Omitting tag %s,\nsince tags of trees (or tags of tags of trees, etc.) are not supported.",
+			oid_to_hex(&tag->object.oid));
+		return;
+	}
+
+	buf = read_object_file(&tag->object.oid, &type, &size);
+	if (!buf)
+		die("could not read tag %s", oid_to_hex(&tag->object.oid));
+	message = memmem(buf, size, "\n\n", 2);
+	if (message) {
+		message += 2;
+		message_size = strlen(message);
+	}
+	tagger = memmem(buf, message ? message - buf : size, "\ntagger ", 8);
+	if (!tagger) {
+		if (fake_missing_tagger)
+			tagger = "tagger Unspecified Tagger "
+				"<unspecified-tagger> 0 +0000";
+		else
+			tagger = "";
+		tagger_end = tagger + strlen(tagger);
+	} else {
+		tagger++;
+		tagger_end = strchrnul(tagger, '\n');
+		if (anonymize)
+			anonymize_ident_line(&tagger, &tagger_end);
+	}
+
+	if (anonymize) {
+		name = anonymize_refname(name);
+		if (message) {
+			static struct hashmap tags;
+			message = anonymize_mem(&tags, anonymize_tag,
+						message, &message_size);
+		}
+	}
+
+	/* handle signed tags */
+	if (message) {
+		const char *signature = strstr(message,
+					       "\n-----BEGIN PGP SIGNATURE-----\n");
+		if (signature)
+			switch(signed_tag_mode) {
+			case SIGNED_TAG_ABORT:
+				die("encountered signed tag %s; use "
+				    "--signed-tags=<mode> to handle it",
+				    oid_to_hex(&tag->object.oid));
+			case WARN:
+				warning("exporting signed tag %s",
+					oid_to_hex(&tag->object.oid));
+				/* fallthru */
+			case VERBATIM:
+				break;
+			case WARN_STRIP:
+				warning("stripping signature from tag %s",
+					oid_to_hex(&tag->object.oid));
+				/* fallthru */
+			case STRIP:
+				message_size = signature + 1 - message;
+				break;
+			}
+	}
+
+	/* handle tag->tagged having been filtered out due to paths specified */
+	tagged = tag->tagged;
+	tagged_mark = get_object_mark(tagged);
+	if (!tagged_mark) {
+		switch(tag_of_filtered_mode) {
+		case TAG_FILTERING_ABORT:
+			die("tag %s tags unexported object; use "
+			    "--tag-of-filtered-object=<mode> to handle it",
+			    oid_to_hex(&tag->object.oid));
+		case DROP:
+			/* Ignore this tag altogether */
+			free(buf);
+			return;
+		case REWRITE:
+			if (tagged->type != OBJ_COMMIT) {
+				die("tag %s tags unexported %s!",
+				    oid_to_hex(&tag->object.oid),
+				    type_name(tagged->type));
+			}
+			p = rewrite_commit((struct commit *)tagged);
+			if (!p) {
+				printf("reset %s\nfrom %s\n\n",
+				       name, oid_to_hex(&null_oid));
+				free(buf);
+				return;
+			}
+			tagged_mark = get_object_mark(&p->object);
+		}
+	}
+
+	if (starts_with(name, "refs/tags/"))
+		name += 10;
+	printf("tag %s\nfrom :%d\n", name, tagged_mark);
+	if (show_original_ids)
+		printf("original-oid %s\n", oid_to_hex(&tag->object.oid));
+	printf("%.*s%sdata %d\n%.*s\n",
+	       (int)(tagger_end - tagger), tagger,
+	       tagger == tagger_end ? "" : "\n",
+	       (int)message_size, (int)message_size, message ? message : "");
+	free(buf);
+}
+
+static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name)
+{
+	switch (e->item->type) {
+	case OBJ_COMMIT:
+		return (struct commit *)e->item;
+	case OBJ_TAG: {
+		struct tag *tag = (struct tag *)e->item;
+
+		/* handle nested tags */
+		while (tag && tag->object.type == OBJ_TAG) {
+			parse_object(the_repository, &tag->object.oid);
+			string_list_append(&tag_refs, full_name)->util = tag;
+			tag = (struct tag *)tag->tagged;
+		}
+		if (!tag)
+			die("Tag %s points nowhere?", e->name);
+		return (struct commit *)tag;
+		break;
+	}
+	default:
+		return NULL;
+	}
+}
+
+static void get_tags_and_duplicates(struct rev_cmdline_info *info)
+{
+	int i;
+
+	for (i = 0; i < info->nr; i++) {
+		struct rev_cmdline_entry *e = info->rev + i;
+		struct object_id oid;
+		struct commit *commit;
+		char *full_name;
+
+		if (e->flags & UNINTERESTING)
+			continue;
+
+		if (dwim_ref(e->name, strlen(e->name), &oid, &full_name) != 1)
+			continue;
+
+		if (refspecs.nr) {
+			char *private;
+			private = apply_refspecs(&refspecs, full_name);
+			if (private) {
+				free(full_name);
+				full_name = private;
+			}
+		}
+
+		commit = get_commit(e, full_name);
+		if (!commit) {
+			warning("%s: Unexpected object of type %s, skipping.",
+				e->name,
+				type_name(e->item->type));
+			continue;
+		}
+
+		switch(commit->object.type) {
+		case OBJ_COMMIT:
+			break;
+		case OBJ_BLOB:
+			export_blob(&commit->object.oid);
+			continue;
+		default: /* OBJ_TAG (nested tags) is already handled */
+			warning("Tag points to object of unexpected type %s, skipping.",
+				type_name(commit->object.type));
+			continue;
+		}
+
+		/*
+		 * Make sure this ref gets properly updated eventually, whether
+		 * through a commit or manually at the end.
+		 */
+		if (e->item->type != OBJ_TAG)
+			string_list_append(&extra_refs, full_name)->util = commit;
+
+		if (!*revision_sources_at(&revision_sources, commit))
+			*revision_sources_at(&revision_sources, commit) = full_name;
+	}
+
+	string_list_sort(&extra_refs);
+	string_list_remove_duplicates(&extra_refs, 0);
+}
+
+static void handle_tags_and_duplicates(struct string_list *extras)
+{
+	struct commit *commit;
+	int i;
+
+	for (i = extras->nr - 1; i >= 0; i--) {
+		const char *name = extras->items[i].string;
+		struct object *object = extras->items[i].util;
+		int mark;
+
+		switch (object->type) {
+		case OBJ_TAG:
+			handle_tag(name, (struct tag *)object);
+			break;
+		case OBJ_COMMIT:
+			if (anonymize)
+				name = anonymize_refname(name);
+			/* create refs pointing to already seen commits */
+			commit = rewrite_commit((struct commit *)object);
+			if (!commit) {
+				/*
+				 * Neither this object nor any of its
+				 * ancestors touch any relevant paths, so
+				 * it has been filtered to nothing.  Delete
+				 * it.
+				 */
+				printf("reset %s\nfrom %s\n\n",
+				       name, oid_to_hex(&null_oid));
+				continue;
+			}
+
+			mark = get_object_mark(&commit->object);
+			if (!mark) {
+				/*
+				 * Getting here means we have a commit which
+				 * was excluded by a negative refspec (e.g.
+				 * fast-export ^master master).  If we are
+				 * referencing excluded commits, set the ref
+				 * to the exact commit.  Otherwise, the user
+				 * wants the branch exported but every commit
+				 * in its history to be deleted, which basically
+				 * just means deletion of the ref.
+				 */
+				if (!reference_excluded_commits) {
+					/* delete the ref */
+					printf("reset %s\nfrom %s\n\n",
+					       name, oid_to_hex(&null_oid));
+					continue;
+				}
+				/* set ref to commit using oid, not mark */
+				printf("reset %s\nfrom %s\n\n", name,
+				       oid_to_hex(&commit->object.oid));
+				continue;
+			}
+
+			printf("reset %s\nfrom :%d\n\n", name, mark
+			       );
+			show_progress();
+			break;
+		}
+	}
+}
+
+static void export_marks(char *file)
+{
+	unsigned int i;
+	uint32_t mark;
+	struct decoration_entry *deco = idnums.entries;
+	FILE *f;
+	int e = 0;
+
+	f = fopen_for_writing(file);
+	if (!f)
+		die_errno("Unable to open marks file %s for writing.", file);
+
+	for (i = 0; i < idnums.size; i++) {
+		if (deco->base && deco->base->type == 1) {
+			mark = ptr_to_mark(deco->decoration);
+			if (fprintf(f, ":%"PRIu32" %s\n", mark,
+				oid_to_hex(&deco->base->oid)) < 0) {
+			    e = 1;
+			    break;
+			}
+		}
+		deco++;
+	}
+
+	e |= ferror(f);
+	e |= fclose(f);
+	if (e)
+		error("Unable to write marks file %s.", file);
+}
+
+static void import_marks(char *input_file)
+{
+	char line[512];
+	FILE *f = xfopen(input_file, "r");
+
+	while (fgets(line, sizeof(line), f)) {
+		uint32_t mark;
+		char *line_end, *mark_end;
+		struct object_id oid;
+		struct object *object;
+		struct commit *commit;
+		enum object_type type;
+
+		line_end = strchr(line, '\n');
+		if (line[0] != ':' || !line_end)
+			die("corrupt mark line: %s", line);
+		*line_end = '\0';
+
+		mark = strtoumax(line + 1, &mark_end, 10);
+		if (!mark || mark_end == line + 1
+			|| *mark_end != ' ' || get_oid_hex(mark_end + 1, &oid))
+			die("corrupt mark line: %s", line);
+
+		if (last_idnum < mark)
+			last_idnum = mark;
+
+		type = oid_object_info(the_repository, &oid, NULL);
+		if (type < 0)
+			die("object not found: %s", oid_to_hex(&oid));
+
+		if (type != OBJ_COMMIT)
+			/* only commits */
+			continue;
+
+		commit = lookup_commit(the_repository, &oid);
+		if (!commit)
+			die("not a commit? can't happen: %s", oid_to_hex(&oid));
+
+		object = &commit->object;
+
+		if (object->flags & SHOWN)
+			error("Object %s already has a mark", oid_to_hex(&oid));
+
+		mark_object(object, mark);
+
+		object->flags |= SHOWN;
+	}
+	fclose(f);
+}
+
+static void handle_deletes(void)
+{
+	int i;
+	for (i = 0; i < refspecs.nr; i++) {
+		struct refspec_item *refspec = &refspecs.items[i];
+		if (*refspec->src)
+			continue;
+
+		printf("reset %s\nfrom %s\n\n",
+				refspec->dst, oid_to_hex(&null_oid));
+	}
+}
+
+int cmd_fast_export(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info revs;
+	struct object_array commits = OBJECT_ARRAY_INIT;
+	struct commit *commit;
+	char *export_filename = NULL, *import_filename = NULL;
+	uint32_t lastimportid;
+	struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
+	struct string_list paths_of_changed_objects = STRING_LIST_INIT_DUP;
+	struct option options[] = {
+		OPT_INTEGER(0, "progress", &progress,
+			    N_("show progress after <n> objects")),
+		OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, N_("mode"),
+			     N_("select handling of signed tags"),
+			     parse_opt_signed_tag_mode),
+		OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, N_("mode"),
+			     N_("select handling of tags that tag filtered objects"),
+			     parse_opt_tag_of_filtered_mode),
+		OPT_CALLBACK(0, "reencode", &reencode_mode, N_("mode"),
+			     N_("select handling of commit messages in an alternate encoding"),
+			     parse_opt_reencode_mode),
+		OPT_STRING(0, "export-marks", &export_filename, N_("file"),
+			     N_("Dump marks to this file")),
+		OPT_STRING(0, "import-marks", &import_filename, N_("file"),
+			     N_("Import marks from this file")),
+		OPT_BOOL(0, "fake-missing-tagger", &fake_missing_tagger,
+			 N_("Fake a tagger when tags lack one")),
+		OPT_BOOL(0, "full-tree", &full_tree,
+			 N_("Output full tree for each commit")),
+		OPT_BOOL(0, "use-done-feature", &use_done_feature,
+			     N_("Use the done feature to terminate the stream")),
+		OPT_BOOL(0, "no-data", &no_data, N_("Skip output of blob data")),
+		OPT_STRING_LIST(0, "refspec", &refspecs_list, N_("refspec"),
+			     N_("Apply refspec to exported refs")),
+		OPT_BOOL(0, "anonymize", &anonymize, N_("anonymize output")),
+		OPT_BOOL(0, "reference-excluded-parents",
+			 &reference_excluded_commits, N_("Reference parents which are not in fast-export stream by object id")),
+		OPT_BOOL(0, "show-original-ids", &show_original_ids,
+			    N_("Show original object ids of blobs/commits")),
+
+		OPT_END()
+	};
+
+	if (argc == 1)
+		usage_with_options (fast_export_usage, options);
+
+	/* we handle encodings */
+	git_config(git_default_config, NULL);
+
+	repo_init_revisions(the_repository, &revs, prefix);
+	init_revision_sources(&revision_sources);
+	revs.topo_order = 1;
+	revs.sources = &revision_sources;
+	revs.rewrite_parents = 1;
+	argc = parse_options(argc, argv, prefix, options, fast_export_usage,
+			PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN);
+	argc = setup_revisions(argc, argv, &revs, NULL);
+	if (argc > 1)
+		usage_with_options (fast_export_usage, options);
+
+	if (refspecs_list.nr) {
+		int i;
+
+		for (i = 0; i < refspecs_list.nr; i++)
+			refspec_append(&refspecs, refspecs_list.items[i].string);
+
+		string_list_clear(&refspecs_list, 1);
+	}
+
+	if (use_done_feature)
+		printf("feature done\n");
+
+	if (import_filename)
+		import_marks(import_filename);
+	lastimportid = last_idnum;
+
+	if (import_filename && revs.prune_data.nr)
+		full_tree = 1;
+
+	get_tags_and_duplicates(&revs.cmdline);
+
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+	revs.diffopt.format_callback = show_filemodify;
+	revs.diffopt.format_callback_data = &paths_of_changed_objects;
+	revs.diffopt.flags.recursive = 1;
+	while ((commit = get_revision(&revs))) {
+		if (has_unshown_parent(commit)) {
+			add_object_array(&commit->object, NULL, &commits);
+		}
+		else {
+			handle_commit(commit, &revs, &paths_of_changed_objects);
+			handle_tail(&commits, &revs, &paths_of_changed_objects);
+		}
+	}
+
+	handle_tags_and_duplicates(&extra_refs);
+	handle_tags_and_duplicates(&tag_refs);
+	handle_deletes();
+
+	if (export_filename && lastimportid != last_idnum)
+		export_marks(export_filename);
+
+	if (use_done_feature)
+		printf("done\n");
+
+	refspec_clear(&refspecs);
+
+	return 0;
+}
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
new file mode 100644
index 0000000000..dc1485c8aa
--- /dev/null
+++ b/builtin/fetch-pack.c
@@ -0,0 +1,270 @@
+#include "builtin.h"
+#include "pkt-line.h"
+#include "fetch-pack.h"
+#include "remote.h"
+#include "connect.h"
+#include "sha1-array.h"
+#include "protocol.h"
+
+static const char fetch_pack_usage[] =
+"git fetch-pack [--all] [--stdin] [--quiet | -q] [--keep | -k] [--thin] "
+"[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
+"[--no-progress] [--diag-url] [-v] [<host>:]<directory> [<refs>...]";
+
+static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
+			     const char *name)
+{
+	struct ref *ref;
+	struct object_id oid;
+	const char *p;
+
+	if (!parse_oid_hex(name, &oid, &p)) {
+		if (*p == ' ') {
+			/* <oid> <ref>, find refname */
+			name = p + 1;
+		} else if (*p == '\0') {
+			; /* <oid>, leave oid as name */
+		} else {
+			/* <ref>, clear cruft from oid */
+			oidclr(&oid);
+		}
+	} else {
+		/* <ref>, clear cruft from get_oid_hex */
+		oidclr(&oid);
+	}
+
+	ref = alloc_ref(name);
+	oidcpy(&ref->old_oid, &oid);
+	(*nr)++;
+	ALLOC_GROW(*sought, *nr, *alloc);
+	(*sought)[*nr - 1] = ref;
+}
+
+int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
+{
+	int i, ret;
+	struct ref *ref = NULL;
+	const char *dest = NULL;
+	struct ref **sought = NULL;
+	int nr_sought = 0, alloc_sought = 0;
+	int fd[2];
+	char *pack_lockfile = NULL;
+	char **pack_lockfile_ptr = NULL;
+	struct child_process *conn;
+	struct fetch_pack_args args;
+	struct oid_array shallow = OID_ARRAY_INIT;
+	struct string_list deepen_not = STRING_LIST_INIT_DUP;
+	struct packet_reader reader;
+	enum protocol_version version;
+
+	fetch_if_missing = 0;
+
+	packet_trace_identity("fetch-pack");
+
+	memset(&args, 0, sizeof(args));
+	args.uploadpack = "git-upload-pack";
+
+	for (i = 1; i < argc && *argv[i] == '-'; i++) {
+		const char *arg = argv[i];
+
+		if (skip_prefix(arg, "--upload-pack=", &arg)) {
+			args.uploadpack = arg;
+			continue;
+		}
+		if (skip_prefix(arg, "--exec=", &arg)) {
+			args.uploadpack = arg;
+			continue;
+		}
+		if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
+			args.quiet = 1;
+			continue;
+		}
+		if (!strcmp("--keep", arg) || !strcmp("-k", arg)) {
+			args.lock_pack = args.keep_pack;
+			args.keep_pack = 1;
+			continue;
+		}
+		if (!strcmp("--thin", arg)) {
+			args.use_thin_pack = 1;
+			continue;
+		}
+		if (!strcmp("--include-tag", arg)) {
+			args.include_tag = 1;
+			continue;
+		}
+		if (!strcmp("--all", arg)) {
+			args.fetch_all = 1;
+			continue;
+		}
+		if (!strcmp("--stdin", arg)) {
+			args.stdin_refs = 1;
+			continue;
+		}
+		if (!strcmp("--diag-url", arg)) {
+			args.diag_url = 1;
+			continue;
+		}
+		if (!strcmp("-v", arg)) {
+			args.verbose = 1;
+			continue;
+		}
+		if (skip_prefix(arg, "--depth=", &arg)) {
+			args.depth = strtol(arg, NULL, 0);
+			continue;
+		}
+		if (skip_prefix(arg, "--shallow-since=", &arg)) {
+			args.deepen_since = xstrdup(arg);
+			continue;
+		}
+		if (skip_prefix(arg, "--shallow-exclude=", &arg)) {
+			string_list_append(&deepen_not, arg);
+			continue;
+		}
+		if (!strcmp(arg, "--deepen-relative")) {
+			args.deepen_relative = 1;
+			continue;
+		}
+		if (!strcmp("--no-progress", arg)) {
+			args.no_progress = 1;
+			continue;
+		}
+		if (!strcmp("--stateless-rpc", arg)) {
+			args.stateless_rpc = 1;
+			continue;
+		}
+		if (!strcmp("--lock-pack", arg)) {
+			args.lock_pack = 1;
+			pack_lockfile_ptr = &pack_lockfile;
+			continue;
+		}
+		if (!strcmp("--check-self-contained-and-connected", arg)) {
+			args.check_self_contained_and_connected = 1;
+			continue;
+		}
+		if (!strcmp("--cloning", arg)) {
+			args.cloning = 1;
+			continue;
+		}
+		if (!strcmp("--update-shallow", arg)) {
+			args.update_shallow = 1;
+			continue;
+		}
+		if (!strcmp("--from-promisor", arg)) {
+			args.from_promisor = 1;
+			continue;
+		}
+		if (!strcmp("--no-dependents", arg)) {
+			args.no_dependents = 1;
+			continue;
+		}
+		if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) {
+			parse_list_objects_filter(&args.filter_options, arg);
+			continue;
+		}
+		if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) {
+			list_objects_filter_set_no_filter(&args.filter_options);
+			continue;
+		}
+		usage(fetch_pack_usage);
+	}
+	if (deepen_not.nr)
+		args.deepen_not = &deepen_not;
+
+	if (i < argc)
+		dest = argv[i++];
+	else
+		usage(fetch_pack_usage);
+
+	/*
+	 * Copy refs from cmdline to growable list, then append any
+	 * refs from the standard input:
+	 */
+	for (; i < argc; i++)
+		add_sought_entry(&sought, &nr_sought, &alloc_sought, argv[i]);
+	if (args.stdin_refs) {
+		if (args.stateless_rpc) {
+			/* in stateless RPC mode we use pkt-line to read
+			 * from stdin, until we get a flush packet
+			 */
+			for (;;) {
+				char *line = packet_read_line(0, NULL);
+				if (!line)
+					break;
+				add_sought_entry(&sought, &nr_sought,  &alloc_sought, line);
+			}
+		}
+		else {
+			/* read from stdin one ref per line, until EOF */
+			struct strbuf line = STRBUF_INIT;
+			while (strbuf_getline_lf(&line, stdin) != EOF)
+				add_sought_entry(&sought, &nr_sought, &alloc_sought, line.buf);
+			strbuf_release(&line);
+		}
+	}
+
+	if (args.stateless_rpc) {
+		conn = NULL;
+		fd[0] = 0;
+		fd[1] = 1;
+	} else {
+		int flags = args.verbose ? CONNECT_VERBOSE : 0;
+		if (args.diag_url)
+			flags |= CONNECT_DIAG_URL;
+		conn = git_connect(fd, dest, args.uploadpack,
+				   flags);
+		if (!conn)
+			return args.diag_url ? 0 : 1;
+	}
+
+	packet_reader_init(&reader, fd[0], NULL, 0,
+			   PACKET_READ_CHOMP_NEWLINE |
+			   PACKET_READ_GENTLE_ON_EOF |
+			   PACKET_READ_DIE_ON_ERR_PACKET);
+
+	version = discover_version(&reader);
+	switch (version) {
+	case protocol_v2:
+		get_remote_refs(fd[1], &reader, &ref, 0, NULL, NULL);
+		break;
+	case protocol_v1:
+	case protocol_v0:
+		get_remote_heads(&reader, &ref, 0, NULL, &shallow);
+		break;
+	case protocol_unknown_version:
+		BUG("unknown protocol version");
+	}
+
+	ref = fetch_pack(&args, fd, ref, sought, nr_sought,
+			 &shallow, pack_lockfile_ptr, version);
+	if (pack_lockfile) {
+		printf("lock %s\n", pack_lockfile);
+		fflush(stdout);
+	}
+	if (args.check_self_contained_and_connected &&
+	    args.self_contained_and_connected) {
+		printf("connectivity-ok\n");
+		fflush(stdout);
+	}
+	close(fd[0]);
+	close(fd[1]);
+	if (finish_connect(conn))
+		return 1;
+
+	ret = !ref;
+
+	/*
+	 * If the heads to pull were given, we should have consumed
+	 * all of them by matching the remote.  Otherwise, 'git fetch
+	 * remote no-such-ref' would silently succeed without issuing
+	 * an error.
+	 */
+	ret |= report_unmatched_refs(sought, nr_sought);
+
+	while (ref) {
+		printf("%s %s\n",
+		       oid_to_hex(&ref->old_oid), ref->name);
+		ref = ref->next;
+	}
+
+	return ret;
+}
diff --git a/builtin/fetch.c b/builtin/fetch.c
new file mode 100644
index 0000000000..717dd14e89
--- /dev/null
+++ b/builtin/fetch.c
@@ -0,0 +1,1736 @@
+/*
+ * "git fetch"
+ */
+#include "cache.h"
+#include "config.h"
+#include "repository.h"
+#include "refs.h"
+#include "refspec.h"
+#include "object-store.h"
+#include "commit.h"
+#include "builtin.h"
+#include "string-list.h"
+#include "remote.h"
+#include "transport.h"
+#include "run-command.h"
+#include "parse-options.h"
+#include "sigchain.h"
+#include "submodule-config.h"
+#include "submodule.h"
+#include "connected.h"
+#include "argv-array.h"
+#include "utf8.h"
+#include "packfile.h"
+#include "list-objects-filter-options.h"
+#include "commit-reach.h"
+
+#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
+
+static const char * const builtin_fetch_usage[] = {
+	N_("git fetch [<options>] [<repository> [<refspec>...]]"),
+	N_("git fetch [<options>] <group>"),
+	N_("git fetch --multiple [<options>] [(<repository> | <group>)...]"),
+	N_("git fetch --all [<options>]"),
+	NULL
+};
+
+enum {
+	TAGS_UNSET = 0,
+	TAGS_DEFAULT = 1,
+	TAGS_SET = 2
+};
+
+static int fetch_prune_config = -1; /* unspecified */
+static int fetch_show_forced_updates = 1;
+static uint64_t forced_updates_ms = 0;
+static int prune = -1; /* unspecified */
+#define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
+
+static int fetch_prune_tags_config = -1; /* unspecified */
+static int prune_tags = -1; /* unspecified */
+#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
+
+static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
+static int progress = -1;
+static int enable_auto_gc = 1;
+static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
+static int max_children = 1;
+static enum transport_family family;
+static const char *depth;
+static const char *deepen_since;
+static const char *upload_pack;
+static struct string_list deepen_not = STRING_LIST_INIT_NODUP;
+static struct strbuf default_rla = STRBUF_INIT;
+static struct transport *gtransport;
+static struct transport *gsecondary;
+static const char *submodule_prefix = "";
+static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
+static int shown_url = 0;
+static struct refspec refmap = REFSPEC_INIT_FETCH;
+static struct list_objects_filter_options filter_options;
+static struct string_list server_options = STRING_LIST_INIT_DUP;
+static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
+
+static int git_fetch_config(const char *k, const char *v, void *cb)
+{
+	if (!strcmp(k, "fetch.prune")) {
+		fetch_prune_config = git_config_bool(k, v);
+		return 0;
+	}
+
+	if (!strcmp(k, "fetch.prunetags")) {
+		fetch_prune_tags_config = git_config_bool(k, v);
+		return 0;
+	}
+
+	if (!strcmp(k, "fetch.showforcedupdates")) {
+		fetch_show_forced_updates = git_config_bool(k, v);
+		return 0;
+	}
+
+	if (!strcmp(k, "submodule.recurse")) {
+		int r = git_config_bool(k, v) ?
+			RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
+		recurse_submodules = r;
+	}
+
+	if (!strcmp(k, "submodule.fetchjobs")) {
+		max_children = parse_submodule_fetchjobs(k, v);
+		return 0;
+	} else if (!strcmp(k, "fetch.recursesubmodules")) {
+		recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
+		return 0;
+	}
+
+	return git_default_config(k, v, cb);
+}
+
+static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+
+	/*
+	 * "git fetch --refmap='' origin foo"
+	 * can be used to tell the command not to store anywhere
+	 */
+	refspec_append(&refmap, arg);
+
+	return 0;
+}
+
+static struct option builtin_fetch_options[] = {
+	OPT__VERBOSITY(&verbosity),
+	OPT_BOOL(0, "all", &all,
+		 N_("fetch from all remotes")),
+	OPT_BOOL('a', "append", &append,
+		 N_("append to .git/FETCH_HEAD instead of overwriting")),
+	OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
+		   N_("path to upload pack on remote end")),
+	OPT__FORCE(&force, N_("force overwrite of local reference"), 0),
+	OPT_BOOL('m', "multiple", &multiple,
+		 N_("fetch from multiple remotes")),
+	OPT_SET_INT('t', "tags", &tags,
+		    N_("fetch all tags and associated objects"), TAGS_SET),
+	OPT_SET_INT('n', NULL, &tags,
+		    N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
+	OPT_INTEGER('j', "jobs", &max_children,
+		    N_("number of submodules fetched in parallel")),
+	OPT_BOOL('p', "prune", &prune,
+		 N_("prune remote-tracking branches no longer on remote")),
+	OPT_BOOL('P', "prune-tags", &prune_tags,
+		 N_("prune local tags no longer on remote and clobber changed tags")),
+	{ OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, N_("on-demand"),
+		    N_("control recursive fetching of submodules"),
+		    PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },
+	OPT_BOOL(0, "dry-run", &dry_run,
+		 N_("dry run")),
+	OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
+	OPT_BOOL('u', "update-head-ok", &update_head_ok,
+		    N_("allow updating of HEAD ref")),
+	OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
+	OPT_STRING(0, "depth", &depth, N_("depth"),
+		   N_("deepen history of shallow clone")),
+	OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
+		   N_("deepen history of shallow repository based on time")),
+	OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
+			N_("deepen history of shallow clone, excluding rev")),
+	OPT_INTEGER(0, "deepen", &deepen_relative,
+		    N_("deepen history of shallow clone")),
+	OPT_SET_INT_F(0, "unshallow", &unshallow,
+		      N_("convert to a complete repository"),
+		      1, PARSE_OPT_NONEG),
+	{ OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
+		   N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
+	{ OPTION_CALLBACK, 0, "recurse-submodules-default",
+		   &recurse_submodules_default, N_("on-demand"),
+		   N_("default for recursive fetching of submodules "
+		      "(lower priority than config files)"),
+		   PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules },
+	OPT_BOOL(0, "update-shallow", &update_shallow,
+		 N_("accept refs that update .git/shallow")),
+	{ OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
+	  N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg },
+	OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
+	OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
+			TRANSPORT_FAMILY_IPV4),
+	OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
+			TRANSPORT_FAMILY_IPV6),
+	OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
+			N_("report that we have only objects reachable from this object")),
+	OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+	OPT_BOOL(0, "auto-gc", &enable_auto_gc,
+		 N_("run 'gc --auto' after fetching")),
+	OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
+		 N_("check for forced-updates on all updated branches")),
+	OPT_END()
+};
+
+static void unlock_pack(void)
+{
+	if (gtransport)
+		transport_unlock_pack(gtransport);
+	if (gsecondary)
+		transport_unlock_pack(gsecondary);
+}
+
+static void unlock_pack_on_signal(int signo)
+{
+	unlock_pack();
+	sigchain_pop(signo);
+	raise(signo);
+}
+
+static void add_merge_config(struct ref **head,
+			   const struct ref *remote_refs,
+		           struct branch *branch,
+		           struct ref ***tail)
+{
+	int i;
+
+	for (i = 0; i < branch->merge_nr; i++) {
+		struct ref *rm, **old_tail = *tail;
+		struct refspec_item refspec;
+
+		for (rm = *head; rm; rm = rm->next) {
+			if (branch_merge_matches(branch, i, rm->name)) {
+				rm->fetch_head_status = FETCH_HEAD_MERGE;
+				break;
+			}
+		}
+		if (rm)
+			continue;
+
+		/*
+		 * Not fetched to a remote-tracking branch?  We need to fetch
+		 * it anyway to allow this branch's "branch.$name.merge"
+		 * to be honored by 'git pull', but we do not have to
+		 * fail if branch.$name.merge is misconfigured to point
+		 * at a nonexisting branch.  If we were indeed called by
+		 * 'git pull', it will notice the misconfiguration because
+		 * there is no entry in the resulting FETCH_HEAD marked
+		 * for merging.
+		 */
+		memset(&refspec, 0, sizeof(refspec));
+		refspec.src = branch->merge[i]->src;
+		get_fetch_map(remote_refs, &refspec, tail, 1);
+		for (rm = *old_tail; rm; rm = rm->next)
+			rm->fetch_head_status = FETCH_HEAD_MERGE;
+	}
+}
+
+static int will_fetch(struct ref **head, const unsigned char *sha1)
+{
+	struct ref *rm = *head;
+	while (rm) {
+		if (hasheq(rm->old_oid.hash, sha1))
+			return 1;
+		rm = rm->next;
+	}
+	return 0;
+}
+
+struct refname_hash_entry {
+	struct hashmap_entry ent; /* must be the first member */
+	struct object_id oid;
+	int ignore;
+	char refname[FLEX_ARRAY];
+};
+
+static int refname_hash_entry_cmp(const void *hashmap_cmp_fn_data,
+				  const void *e1_,
+				  const void *e2_,
+				  const void *keydata)
+{
+	const struct refname_hash_entry *e1 = e1_;
+	const struct refname_hash_entry *e2 = e2_;
+
+	return strcmp(e1->refname, keydata ? keydata : e2->refname);
+}
+
+static struct refname_hash_entry *refname_hash_add(struct hashmap *map,
+						   const char *refname,
+						   const struct object_id *oid)
+{
+	struct refname_hash_entry *ent;
+	size_t len = strlen(refname);
+
+	FLEX_ALLOC_MEM(ent, refname, refname, len);
+	hashmap_entry_init(ent, strhash(refname));
+	oidcpy(&ent->oid, oid);
+	hashmap_add(map, ent);
+	return ent;
+}
+
+static int add_one_refname(const char *refname,
+			   const struct object_id *oid,
+			   int flag, void *cbdata)
+{
+	struct hashmap *refname_map = cbdata;
+
+	(void) refname_hash_add(refname_map, refname, oid);
+	return 0;
+}
+
+static void refname_hash_init(struct hashmap *map)
+{
+	hashmap_init(map, refname_hash_entry_cmp, NULL, 0);
+}
+
+static int refname_hash_exists(struct hashmap *map, const char *refname)
+{
+	return !!hashmap_get_from_hash(map, strhash(refname), refname);
+}
+
+static void clear_item(struct refname_hash_entry *item)
+{
+	item->ignore = 1;
+}
+
+static void find_non_local_tags(const struct ref *refs,
+				struct ref **head,
+				struct ref ***tail)
+{
+	struct hashmap existing_refs;
+	struct hashmap remote_refs;
+	struct string_list remote_refs_list = STRING_LIST_INIT_NODUP;
+	struct string_list_item *remote_ref_item;
+	const struct ref *ref;
+	struct refname_hash_entry *item = NULL;
+
+	refname_hash_init(&existing_refs);
+	refname_hash_init(&remote_refs);
+
+	for_each_ref(add_one_refname, &existing_refs);
+	for (ref = refs; ref; ref = ref->next) {
+		if (!starts_with(ref->name, "refs/tags/"))
+			continue;
+
+		/*
+		 * The peeled ref always follows the matching base
+		 * ref, so if we see a peeled ref that we don't want
+		 * to fetch then we can mark the ref entry in the list
+		 * as one to ignore by setting util to NULL.
+		 */
+		if (ends_with(ref->name, "^{}")) {
+			if (item &&
+			    !has_object_file_with_flags(&ref->old_oid,
+							OBJECT_INFO_QUICK) &&
+			    !will_fetch(head, ref->old_oid.hash) &&
+			    !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
+			    !will_fetch(head, item->oid.hash))
+				clear_item(item);
+			item = NULL;
+			continue;
+		}
+
+		/*
+		 * If item is non-NULL here, then we previously saw a
+		 * ref not followed by a peeled reference, so we need
+		 * to check if it is a lightweight tag that we want to
+		 * fetch.
+		 */
+		if (item &&
+		    !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
+		    !will_fetch(head, item->oid.hash))
+			clear_item(item);
+
+		item = NULL;
+
+		/* skip duplicates and refs that we already have */
+		if (refname_hash_exists(&remote_refs, ref->name) ||
+		    refname_hash_exists(&existing_refs, ref->name))
+			continue;
+
+		item = refname_hash_add(&remote_refs, ref->name, &ref->old_oid);
+		string_list_insert(&remote_refs_list, ref->name);
+	}
+	hashmap_free(&existing_refs, 1);
+
+	/*
+	 * We may have a final lightweight tag that needs to be
+	 * checked to see if it needs fetching.
+	 */
+	if (item &&
+	    !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
+	    !will_fetch(head, item->oid.hash))
+		clear_item(item);
+
+	/*
+	 * For all the tags in the remote_refs_list,
+	 * add them to the list of refs to be fetched
+	 */
+	for_each_string_list_item(remote_ref_item, &remote_refs_list) {
+		const char *refname = remote_ref_item->string;
+		struct ref *rm;
+
+		item = hashmap_get_from_hash(&remote_refs, strhash(refname), refname);
+		if (!item)
+			BUG("unseen remote ref?");
+
+		/* Unless we have already decided to ignore this item... */
+		if (item->ignore)
+			continue;
+
+		rm = alloc_ref(item->refname);
+		rm->peer_ref = alloc_ref(item->refname);
+		oidcpy(&rm->old_oid, &item->oid);
+		**tail = rm;
+		*tail = &rm->next;
+	}
+	hashmap_free(&remote_refs, 1);
+	string_list_clear(&remote_refs_list, 0);
+}
+
+static struct ref *get_ref_map(struct remote *remote,
+			       const struct ref *remote_refs,
+			       struct refspec *rs,
+			       int tags, int *autotags)
+{
+	int i;
+	struct ref *rm;
+	struct ref *ref_map = NULL;
+	struct ref **tail = &ref_map;
+
+	/* opportunistically-updated references: */
+	struct ref *orefs = NULL, **oref_tail = &orefs;
+
+	struct hashmap existing_refs;
+
+	if (rs->nr) {
+		struct refspec *fetch_refspec;
+
+		for (i = 0; i < rs->nr; i++) {
+			get_fetch_map(remote_refs, &rs->items[i], &tail, 0);
+			if (rs->items[i].dst && rs->items[i].dst[0])
+				*autotags = 1;
+		}
+		/* Merge everything on the command line (but not --tags) */
+		for (rm = ref_map; rm; rm = rm->next)
+			rm->fetch_head_status = FETCH_HEAD_MERGE;
+
+		/*
+		 * For any refs that we happen to be fetching via
+		 * command-line arguments, the destination ref might
+		 * have been missing or have been different than the
+		 * remote-tracking ref that would be derived from the
+		 * configured refspec.  In these cases, we want to
+		 * take the opportunity to update their configured
+		 * remote-tracking reference.  However, we do not want
+		 * to mention these entries in FETCH_HEAD at all, as
+		 * they would simply be duplicates of existing
+		 * entries, so we set them FETCH_HEAD_IGNORE below.
+		 *
+		 * We compute these entries now, based only on the
+		 * refspecs specified on the command line.  But we add
+		 * them to the list following the refspecs resulting
+		 * from the tags option so that one of the latter,
+		 * which has FETCH_HEAD_NOT_FOR_MERGE, is not removed
+		 * by ref_remove_duplicates() in favor of one of these
+		 * opportunistic entries with FETCH_HEAD_IGNORE.
+		 */
+		if (refmap.nr)
+			fetch_refspec = &refmap;
+		else
+			fetch_refspec = &remote->fetch;
+
+		for (i = 0; i < fetch_refspec->nr; i++)
+			get_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1);
+	} else if (refmap.nr) {
+		die("--refmap option is only meaningful with command-line refspec(s).");
+	} else {
+		/* Use the defaults */
+		struct branch *branch = branch_get(NULL);
+		int has_merge = branch_has_merge_config(branch);
+		if (remote &&
+		    (remote->fetch.nr ||
+		     /* Note: has_merge implies non-NULL branch->remote_name */
+		     (has_merge && !strcmp(branch->remote_name, remote->name)))) {
+			for (i = 0; i < remote->fetch.nr; i++) {
+				get_fetch_map(remote_refs, &remote->fetch.items[i], &tail, 0);
+				if (remote->fetch.items[i].dst &&
+				    remote->fetch.items[i].dst[0])
+					*autotags = 1;
+				if (!i && !has_merge && ref_map &&
+				    !remote->fetch.items[0].pattern)
+					ref_map->fetch_head_status = FETCH_HEAD_MERGE;
+			}
+			/*
+			 * if the remote we're fetching from is the same
+			 * as given in branch.<name>.remote, we add the
+			 * ref given in branch.<name>.merge, too.
+			 *
+			 * Note: has_merge implies non-NULL branch->remote_name
+			 */
+			if (has_merge &&
+			    !strcmp(branch->remote_name, remote->name))
+				add_merge_config(&ref_map, remote_refs, branch, &tail);
+		} else {
+			ref_map = get_remote_ref(remote_refs, "HEAD");
+			if (!ref_map)
+				die(_("Couldn't find remote ref HEAD"));
+			ref_map->fetch_head_status = FETCH_HEAD_MERGE;
+			tail = &ref_map->next;
+		}
+	}
+
+	if (tags == TAGS_SET)
+		/* also fetch all tags */
+		get_fetch_map(remote_refs, tag_refspec, &tail, 0);
+	else if (tags == TAGS_DEFAULT && *autotags)
+		find_non_local_tags(remote_refs, &ref_map, &tail);
+
+	/* Now append any refs to be updated opportunistically: */
+	*tail = orefs;
+	for (rm = orefs; rm; rm = rm->next) {
+		rm->fetch_head_status = FETCH_HEAD_IGNORE;
+		tail = &rm->next;
+	}
+
+	ref_map = ref_remove_duplicates(ref_map);
+
+	refname_hash_init(&existing_refs);
+	for_each_ref(add_one_refname, &existing_refs);
+
+	for (rm = ref_map; rm; rm = rm->next) {
+		if (rm->peer_ref) {
+			const char *refname = rm->peer_ref->name;
+			struct refname_hash_entry *peer_item;
+
+			peer_item = hashmap_get_from_hash(&existing_refs,
+							  strhash(refname),
+							  refname);
+			if (peer_item) {
+				struct object_id *old_oid = &peer_item->oid;
+				oidcpy(&rm->peer_ref->old_oid, old_oid);
+			}
+		}
+	}
+	hashmap_free(&existing_refs, 1);
+
+	return ref_map;
+}
+
+#define STORE_REF_ERROR_OTHER 1
+#define STORE_REF_ERROR_DF_CONFLICT 2
+
+static int s_update_ref(const char *action,
+			struct ref *ref,
+			int check_old)
+{
+	char *msg;
+	char *rla = getenv("GIT_REFLOG_ACTION");
+	struct ref_transaction *transaction;
+	struct strbuf err = STRBUF_INIT;
+	int ret, df_conflict = 0;
+
+	if (dry_run)
+		return 0;
+	if (!rla)
+		rla = default_rla.buf;
+	msg = xstrfmt("%s: %s", rla, action);
+
+	transaction = ref_transaction_begin(&err);
+	if (!transaction ||
+	    ref_transaction_update(transaction, ref->name,
+				   &ref->new_oid,
+				   check_old ? &ref->old_oid : NULL,
+				   0, msg, &err))
+		goto fail;
+
+	ret = ref_transaction_commit(transaction, &err);
+	if (ret) {
+		df_conflict = (ret == TRANSACTION_NAME_CONFLICT);
+		goto fail;
+	}
+
+	ref_transaction_free(transaction);
+	strbuf_release(&err);
+	free(msg);
+	return 0;
+fail:
+	ref_transaction_free(transaction);
+	error("%s", err.buf);
+	strbuf_release(&err);
+	free(msg);
+	return df_conflict ? STORE_REF_ERROR_DF_CONFLICT
+			   : STORE_REF_ERROR_OTHER;
+}
+
+static int refcol_width = 10;
+static int compact_format;
+
+static void adjust_refcol_width(const struct ref *ref)
+{
+	int max, rlen, llen, len;
+
+	/* uptodate lines are only shown on high verbosity level */
+	if (!verbosity && oideq(&ref->peer_ref->old_oid, &ref->old_oid))
+		return;
+
+	max    = term_columns();
+	rlen   = utf8_strwidth(prettify_refname(ref->name));
+
+	llen   = utf8_strwidth(prettify_refname(ref->peer_ref->name));
+
+	/*
+	 * rough estimation to see if the output line is too long and
+	 * should not be counted (we can't do precise calculation
+	 * anyway because we don't know if the error explanation part
+	 * will be printed in update_local_ref)
+	 */
+	if (compact_format) {
+		llen = 0;
+		max = max * 2 / 3;
+	}
+	len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
+	if (len >= max)
+		return;
+
+	/*
+	 * Not precise calculation for compact mode because '*' can
+	 * appear on the left hand side of '->' and shrink the column
+	 * back.
+	 */
+	if (refcol_width < rlen)
+		refcol_width = rlen;
+}
+
+static void prepare_format_display(struct ref *ref_map)
+{
+	struct ref *rm;
+	const char *format = "full";
+
+	git_config_get_string_const("fetch.output", &format);
+	if (!strcasecmp(format, "full"))
+		compact_format = 0;
+	else if (!strcasecmp(format, "compact"))
+		compact_format = 1;
+	else
+		die(_("configuration fetch.output contains invalid value %s"),
+		    format);
+
+	for (rm = ref_map; rm; rm = rm->next) {
+		if (rm->status == REF_STATUS_REJECT_SHALLOW ||
+		    !rm->peer_ref ||
+		    !strcmp(rm->name, "HEAD"))
+			continue;
+
+		adjust_refcol_width(rm);
+	}
+}
+
+static void print_remote_to_local(struct strbuf *display,
+				  const char *remote, const char *local)
+{
+	strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local);
+}
+
+static int find_and_replace(struct strbuf *haystack,
+			    const char *needle,
+			    const char *placeholder)
+{
+	const char *p = NULL;
+	int plen, nlen;
+
+	nlen = strlen(needle);
+	if (ends_with(haystack->buf, needle))
+		p = haystack->buf + haystack->len - nlen;
+	else
+		p = strstr(haystack->buf, needle);
+	if (!p)
+		return 0;
+
+	if (p > haystack->buf && p[-1] != '/')
+		return 0;
+
+	plen = strlen(p);
+	if (plen > nlen && p[nlen] != '/')
+		return 0;
+
+	strbuf_splice(haystack, p - haystack->buf, nlen,
+		      placeholder, strlen(placeholder));
+	return 1;
+}
+
+static void print_compact(struct strbuf *display,
+			  const char *remote, const char *local)
+{
+	struct strbuf r = STRBUF_INIT;
+	struct strbuf l = STRBUF_INIT;
+
+	if (!strcmp(remote, local)) {
+		strbuf_addf(display, "%-*s -> *", refcol_width, remote);
+		return;
+	}
+
+	strbuf_addstr(&r, remote);
+	strbuf_addstr(&l, local);
+
+	if (!find_and_replace(&r, local, "*"))
+		find_and_replace(&l, remote, "*");
+	print_remote_to_local(display, r.buf, l.buf);
+
+	strbuf_release(&r);
+	strbuf_release(&l);
+}
+
+static void format_display(struct strbuf *display, char code,
+			   const char *summary, const char *error,
+			   const char *remote, const char *local,
+			   int summary_width)
+{
+	int width = (summary_width + strlen(summary) - gettext_width(summary));
+
+	strbuf_addf(display, "%c %-*s ", code, width, summary);
+	if (!compact_format)
+		print_remote_to_local(display, remote, local);
+	else
+		print_compact(display, remote, local);
+	if (error)
+		strbuf_addf(display, "  (%s)", error);
+}
+
+static int update_local_ref(struct ref *ref,
+			    const char *remote,
+			    const struct ref *remote_ref,
+			    struct strbuf *display,
+			    int summary_width)
+{
+	struct commit *current = NULL, *updated;
+	enum object_type type;
+	struct branch *current_branch = branch_get(NULL);
+	const char *pretty_ref = prettify_refname(ref->name);
+	int fast_forward = 0;
+
+	type = oid_object_info(the_repository, &ref->new_oid, NULL);
+	if (type < 0)
+		die(_("object %s not found"), oid_to_hex(&ref->new_oid));
+
+	if (oideq(&ref->old_oid, &ref->new_oid)) {
+		if (verbosity > 0)
+			format_display(display, '=', _("[up to date]"), NULL,
+				       remote, pretty_ref, summary_width);
+		return 0;
+	}
+
+	if (current_branch &&
+	    !strcmp(ref->name, current_branch->name) &&
+	    !(update_head_ok || is_bare_repository()) &&
+	    !is_null_oid(&ref->old_oid)) {
+		/*
+		 * If this is the head, and it's not okay to update
+		 * the head, and the old value of the head isn't empty...
+		 */
+		format_display(display, '!', _("[rejected]"),
+			       _("can't fetch in current branch"),
+			       remote, pretty_ref, summary_width);
+		return 1;
+	}
+
+	if (!is_null_oid(&ref->old_oid) &&
+	    starts_with(ref->name, "refs/tags/")) {
+		if (force || ref->force) {
+			int r;
+			r = s_update_ref("updating tag", ref, 0);
+			format_display(display, r ? '!' : 't', _("[tag update]"),
+				       r ? _("unable to update local ref") : NULL,
+				       remote, pretty_ref, summary_width);
+			return r;
+		} else {
+			format_display(display, '!', _("[rejected]"), _("would clobber existing tag"),
+				       remote, pretty_ref, summary_width);
+			return 1;
+		}
+	}
+
+	current = lookup_commit_reference_gently(the_repository,
+						 &ref->old_oid, 1);
+	updated = lookup_commit_reference_gently(the_repository,
+						 &ref->new_oid, 1);
+	if (!current || !updated) {
+		const char *msg;
+		const char *what;
+		int r;
+		/*
+		 * Nicely describe the new ref we're fetching.
+		 * Base this on the remote's ref name, as it's
+		 * more likely to follow a standard layout.
+		 */
+		const char *name = remote_ref ? remote_ref->name : "";
+		if (starts_with(name, "refs/tags/")) {
+			msg = "storing tag";
+			what = _("[new tag]");
+		} else if (starts_with(name, "refs/heads/")) {
+			msg = "storing head";
+			what = _("[new branch]");
+		} else {
+			msg = "storing ref";
+			what = _("[new ref]");
+		}
+
+		r = s_update_ref(msg, ref, 0);
+		format_display(display, r ? '!' : '*', what,
+			       r ? _("unable to update local ref") : NULL,
+			       remote, pretty_ref, summary_width);
+		return r;
+	}
+
+	if (fetch_show_forced_updates) {
+		uint64_t t_before = getnanotime();
+		fast_forward = in_merge_bases(current, updated);
+		forced_updates_ms += (getnanotime() - t_before) / 1000000;
+	} else {
+		fast_forward = 1;
+	}
+
+	if (fast_forward) {
+		struct strbuf quickref = STRBUF_INIT;
+		int r;
+
+		strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
+		strbuf_addstr(&quickref, "..");
+		strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
+		r = s_update_ref("fast-forward", ref, 1);
+		format_display(display, r ? '!' : ' ', quickref.buf,
+			       r ? _("unable to update local ref") : NULL,
+			       remote, pretty_ref, summary_width);
+		strbuf_release(&quickref);
+		return r;
+	} else if (force || ref->force) {
+		struct strbuf quickref = STRBUF_INIT;
+		int r;
+		strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
+		strbuf_addstr(&quickref, "...");
+		strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
+		r = s_update_ref("forced-update", ref, 1);
+		format_display(display, r ? '!' : '+', quickref.buf,
+			       r ? _("unable to update local ref") : _("forced update"),
+			       remote, pretty_ref, summary_width);
+		strbuf_release(&quickref);
+		return r;
+	} else {
+		format_display(display, '!', _("[rejected]"), _("non-fast-forward"),
+			       remote, pretty_ref, summary_width);
+		return 1;
+	}
+}
+
+static int iterate_ref_map(void *cb_data, struct object_id *oid)
+{
+	struct ref **rm = cb_data;
+	struct ref *ref = *rm;
+
+	while (ref && ref->status == REF_STATUS_REJECT_SHALLOW)
+		ref = ref->next;
+	if (!ref)
+		return -1; /* end of the list */
+	*rm = ref->next;
+	oidcpy(oid, &ref->old_oid);
+	return 0;
+}
+
+static const char warn_show_forced_updates[] =
+N_("Fetch normally indicates which branches had a forced update,\n"
+   "but that check has been disabled. To re-enable, use '--show-forced-updates'\n"
+   "flag or run 'git config fetch.showForcedUpdates true'.");
+static const char warn_time_show_forced_updates[] =
+N_("It took %.2f seconds to check forced updates. You can use\n"
+   "'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates false'\n"
+   " to avoid this check.\n");
+
+static int store_updated_refs(const char *raw_url, const char *remote_name,
+			      int connectivity_checked, struct ref *ref_map)
+{
+	FILE *fp;
+	struct commit *commit;
+	int url_len, i, rc = 0;
+	struct strbuf note = STRBUF_INIT;
+	const char *what, *kind;
+	struct ref *rm;
+	char *url;
+	const char *filename = dry_run ? "/dev/null" : git_path_fetch_head(the_repository);
+	int want_status;
+	int summary_width = transport_summary_width(ref_map);
+
+	fp = fopen(filename, "a");
+	if (!fp)
+		return error_errno(_("cannot open %s"), filename);
+
+	if (raw_url)
+		url = transport_anonymize_url(raw_url);
+	else
+		url = xstrdup("foreign");
+
+	if (!connectivity_checked) {
+		rm = ref_map;
+		if (check_connected(iterate_ref_map, &rm, NULL)) {
+			rc = error(_("%s did not send all necessary objects\n"), url);
+			goto abort;
+		}
+	}
+
+	prepare_format_display(ref_map);
+
+	/*
+	 * We do a pass for each fetch_head_status type in their enum order, so
+	 * merged entries are written before not-for-merge. That lets readers
+	 * use FETCH_HEAD as a refname to refer to the ref to be merged.
+	 */
+	for (want_status = FETCH_HEAD_MERGE;
+	     want_status <= FETCH_HEAD_IGNORE;
+	     want_status++) {
+		for (rm = ref_map; rm; rm = rm->next) {
+			struct ref *ref = NULL;
+			const char *merge_status_marker = "";
+
+			if (rm->status == REF_STATUS_REJECT_SHALLOW) {
+				if (want_status == FETCH_HEAD_MERGE)
+					warning(_("reject %s because shallow roots are not allowed to be updated"),
+						rm->peer_ref ? rm->peer_ref->name : rm->name);
+				continue;
+			}
+
+			commit = lookup_commit_reference_gently(the_repository,
+								&rm->old_oid,
+								1);
+			if (!commit)
+				rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
+
+			if (rm->fetch_head_status != want_status)
+				continue;
+
+			if (rm->peer_ref) {
+				ref = alloc_ref(rm->peer_ref->name);
+				oidcpy(&ref->old_oid, &rm->peer_ref->old_oid);
+				oidcpy(&ref->new_oid, &rm->old_oid);
+				ref->force = rm->peer_ref->force;
+			}
+
+			if (recurse_submodules != RECURSE_SUBMODULES_OFF)
+				check_for_new_submodule_commits(&rm->old_oid);
+
+			if (!strcmp(rm->name, "HEAD")) {
+				kind = "";
+				what = "";
+			}
+			else if (starts_with(rm->name, "refs/heads/")) {
+				kind = "branch";
+				what = rm->name + 11;
+			}
+			else if (starts_with(rm->name, "refs/tags/")) {
+				kind = "tag";
+				what = rm->name + 10;
+			}
+			else if (starts_with(rm->name, "refs/remotes/")) {
+				kind = "remote-tracking branch";
+				what = rm->name + 13;
+			}
+			else {
+				kind = "";
+				what = rm->name;
+			}
+
+			url_len = strlen(url);
+			for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
+				;
+			url_len = i + 1;
+			if (4 < i && !strncmp(".git", url + i - 3, 4))
+				url_len = i - 3;
+
+			strbuf_reset(&note);
+			if (*what) {
+				if (*kind)
+					strbuf_addf(&note, "%s ", kind);
+				strbuf_addf(&note, "'%s' of ", what);
+			}
+			switch (rm->fetch_head_status) {
+			case FETCH_HEAD_NOT_FOR_MERGE:
+				merge_status_marker = "not-for-merge";
+				/* fall-through */
+			case FETCH_HEAD_MERGE:
+				fprintf(fp, "%s\t%s\t%s",
+					oid_to_hex(&rm->old_oid),
+					merge_status_marker,
+					note.buf);
+				for (i = 0; i < url_len; ++i)
+					if ('\n' == url[i])
+						fputs("\\n", fp);
+					else
+						fputc(url[i], fp);
+				fputc('\n', fp);
+				break;
+			default:
+				/* do not write anything to FETCH_HEAD */
+				break;
+			}
+
+			strbuf_reset(&note);
+			if (ref) {
+				rc |= update_local_ref(ref, what, rm, &note,
+						       summary_width);
+				free(ref);
+			} else
+				format_display(&note, '*',
+					       *kind ? kind : "branch", NULL,
+					       *what ? what : "HEAD",
+					       "FETCH_HEAD", summary_width);
+			if (note.len) {
+				if (verbosity >= 0 && !shown_url) {
+					fprintf(stderr, _("From %.*s\n"),
+							url_len, url);
+					shown_url = 1;
+				}
+				if (verbosity >= 0)
+					fprintf(stderr, " %s\n", note.buf);
+			}
+		}
+	}
+
+	if (rc & STORE_REF_ERROR_DF_CONFLICT)
+		error(_("some local refs could not be updated; try running\n"
+		      " 'git remote prune %s' to remove any old, conflicting "
+		      "branches"), remote_name);
+
+	if (advice_fetch_show_forced_updates) {
+		if (!fetch_show_forced_updates) {
+			warning(_(warn_show_forced_updates));
+		} else if (forced_updates_ms > FORCED_UPDATES_DELAY_WARNING_IN_MS) {
+			warning(_(warn_time_show_forced_updates),
+				forced_updates_ms / 1000.0);
+		}
+	}
+
+ abort:
+	strbuf_release(&note);
+	free(url);
+	fclose(fp);
+	return rc;
+}
+
+/*
+ * We would want to bypass the object transfer altogether if
+ * everything we are going to fetch already exists and is connected
+ * locally.
+ */
+static int check_exist_and_connected(struct ref *ref_map)
+{
+	struct ref *rm = ref_map;
+	struct check_connected_options opt = CHECK_CONNECTED_INIT;
+	struct ref *r;
+
+	/*
+	 * If we are deepening a shallow clone we already have these
+	 * objects reachable.  Running rev-list here will return with
+	 * a good (0) exit status and we'll bypass the fetch that we
+	 * really need to perform.  Claiming failure now will ensure
+	 * we perform the network exchange to deepen our history.
+	 */
+	if (deepen)
+		return -1;
+
+	/*
+	 * check_connected() allows objects to merely be promised, but
+	 * we need all direct targets to exist.
+	 */
+	for (r = rm; r; r = r->next) {
+		if (!has_object_file(&r->old_oid))
+			return -1;
+	}
+
+	opt.quiet = 1;
+	return check_connected(iterate_ref_map, &rm, &opt);
+}
+
+static int fetch_refs(struct transport *transport, struct ref *ref_map)
+{
+	int ret = check_exist_and_connected(ref_map);
+	if (ret)
+		ret = transport_fetch_refs(transport, ref_map);
+	if (!ret)
+		/*
+		 * Keep the new pack's ".keep" file around to allow the caller
+		 * time to update refs to reference the new objects.
+		 */
+		return 0;
+	transport_unlock_pack(transport);
+	return ret;
+}
+
+/* Update local refs based on the ref values fetched from a remote */
+static int consume_refs(struct transport *transport, struct ref *ref_map)
+{
+	int connectivity_checked = transport->smart_options
+		? transport->smart_options->connectivity_checked : 0;
+	int ret = store_updated_refs(transport->url,
+				     transport->remote->name,
+				     connectivity_checked,
+				     ref_map);
+	transport_unlock_pack(transport);
+	return ret;
+}
+
+static int prune_refs(struct refspec *rs, struct ref *ref_map,
+		      const char *raw_url)
+{
+	int url_len, i, result = 0;
+	struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map);
+	char *url;
+	int summary_width = transport_summary_width(stale_refs);
+	const char *dangling_msg = dry_run
+		? _("   (%s will become dangling)")
+		: _("   (%s has become dangling)");
+
+	if (raw_url)
+		url = transport_anonymize_url(raw_url);
+	else
+		url = xstrdup("foreign");
+
+	url_len = strlen(url);
+	for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
+		;
+
+	url_len = i + 1;
+	if (4 < i && !strncmp(".git", url + i - 3, 4))
+		url_len = i - 3;
+
+	if (!dry_run) {
+		struct string_list refnames = STRING_LIST_INIT_NODUP;
+
+		for (ref = stale_refs; ref; ref = ref->next)
+			string_list_append(&refnames, ref->name);
+
+		result = delete_refs("fetch: prune", &refnames, 0);
+		string_list_clear(&refnames, 0);
+	}
+
+	if (verbosity >= 0) {
+		for (ref = stale_refs; ref; ref = ref->next) {
+			struct strbuf sb = STRBUF_INIT;
+			if (!shown_url) {
+				fprintf(stderr, _("From %.*s\n"), url_len, url);
+				shown_url = 1;
+			}
+			format_display(&sb, '-', _("[deleted]"), NULL,
+				       _("(none)"), prettify_refname(ref->name),
+				       summary_width);
+			fprintf(stderr, " %s\n",sb.buf);
+			strbuf_release(&sb);
+			warn_dangling_symref(stderr, dangling_msg, ref->name);
+		}
+	}
+
+	free(url);
+	free_refs(stale_refs);
+	return result;
+}
+
+static void check_not_current_branch(struct ref *ref_map)
+{
+	struct branch *current_branch = branch_get(NULL);
+
+	if (is_bare_repository() || !current_branch)
+		return;
+
+	for (; ref_map; ref_map = ref_map->next)
+		if (ref_map->peer_ref && !strcmp(current_branch->refname,
+					ref_map->peer_ref->name))
+			die(_("Refusing to fetch into current branch %s "
+			    "of non-bare repository"), current_branch->refname);
+}
+
+static int truncate_fetch_head(void)
+{
+	const char *filename = git_path_fetch_head(the_repository);
+	FILE *fp = fopen_for_writing(filename);
+
+	if (!fp)
+		return error_errno(_("cannot open %s"), filename);
+	fclose(fp);
+	return 0;
+}
+
+static void set_option(struct transport *transport, const char *name, const char *value)
+{
+	int r = transport_set_option(transport, name, value);
+	if (r < 0)
+		die(_("Option \"%s\" value \"%s\" is not valid for %s"),
+		    name, value, transport->url);
+	if (r > 0)
+		warning(_("Option \"%s\" is ignored for %s\n"),
+			name, transport->url);
+}
+
+
+static int add_oid(const char *refname, const struct object_id *oid, int flags,
+		   void *cb_data)
+{
+	struct oid_array *oids = cb_data;
+
+	oid_array_append(oids, oid);
+	return 0;
+}
+
+static void add_negotiation_tips(struct git_transport_options *smart_options)
+{
+	struct oid_array *oids = xcalloc(1, sizeof(*oids));
+	int i;
+
+	for (i = 0; i < negotiation_tip.nr; i++) {
+		const char *s = negotiation_tip.items[i].string;
+		int old_nr;
+		if (!has_glob_specials(s)) {
+			struct object_id oid;
+			if (get_oid(s, &oid))
+				die("%s is not a valid object", s);
+			oid_array_append(oids, &oid);
+			continue;
+		}
+		old_nr = oids->nr;
+		for_each_glob_ref(add_oid, s, oids);
+		if (old_nr == oids->nr)
+			warning("Ignoring --negotiation-tip=%s because it does not match any refs",
+				s);
+	}
+	smart_options->negotiation_tips = oids;
+}
+
+static struct transport *prepare_transport(struct remote *remote, int deepen)
+{
+	struct transport *transport;
+
+	transport = transport_get(remote, NULL);
+	transport_set_verbosity(transport, verbosity, progress);
+	transport->family = family;
+	if (upload_pack)
+		set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack);
+	if (keep)
+		set_option(transport, TRANS_OPT_KEEP, "yes");
+	if (depth)
+		set_option(transport, TRANS_OPT_DEPTH, depth);
+	if (deepen && deepen_since)
+		set_option(transport, TRANS_OPT_DEEPEN_SINCE, deepen_since);
+	if (deepen && deepen_not.nr)
+		set_option(transport, TRANS_OPT_DEEPEN_NOT,
+			   (const char *)&deepen_not);
+	if (deepen_relative)
+		set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes");
+	if (update_shallow)
+		set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
+	if (filter_options.choice) {
+		struct strbuf expanded_filter_spec = STRBUF_INIT;
+		expand_list_objects_filter_spec(&filter_options,
+						&expanded_filter_spec);
+		set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
+			   expanded_filter_spec.buf);
+		set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+		strbuf_release(&expanded_filter_spec);
+	}
+	if (negotiation_tip.nr) {
+		if (transport->smart_options)
+			add_negotiation_tips(transport->smart_options);
+		else
+			warning("Ignoring --negotiation-tip because the protocol does not support it.");
+	}
+	return transport;
+}
+
+static void backfill_tags(struct transport *transport, struct ref *ref_map)
+{
+	int cannot_reuse;
+
+	/*
+	 * Once we have set TRANS_OPT_DEEPEN_SINCE, we can't unset it
+	 * when remote helper is used (setting it to an empty string
+	 * is not unsetting). We could extend the remote helper
+	 * protocol for that, but for now, just force a new connection
+	 * without deepen-since. Similar story for deepen-not.
+	 */
+	cannot_reuse = transport->cannot_reuse ||
+		deepen_since || deepen_not.nr;
+	if (cannot_reuse) {
+		gsecondary = prepare_transport(transport->remote, 0);
+		transport = gsecondary;
+	}
+
+	transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
+	transport_set_option(transport, TRANS_OPT_DEPTH, "0");
+	transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
+	if (!fetch_refs(transport, ref_map))
+		consume_refs(transport, ref_map);
+
+	if (gsecondary) {
+		transport_disconnect(gsecondary);
+		gsecondary = NULL;
+	}
+}
+
+static int do_fetch(struct transport *transport,
+		    struct refspec *rs)
+{
+	struct ref *ref_map;
+	int autotags = (transport->remote->fetch_tags == 1);
+	int retcode = 0;
+	const struct ref *remote_refs;
+	struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
+	int must_list_refs = 1;
+
+	if (tags == TAGS_DEFAULT) {
+		if (transport->remote->fetch_tags == 2)
+			tags = TAGS_SET;
+		if (transport->remote->fetch_tags == -1)
+			tags = TAGS_UNSET;
+	}
+
+	/* if not appending, truncate FETCH_HEAD */
+	if (!append && !dry_run) {
+		retcode = truncate_fetch_head();
+		if (retcode)
+			goto cleanup;
+	}
+
+	if (rs->nr) {
+		int i;
+
+		refspec_ref_prefixes(rs, &ref_prefixes);
+
+		/*
+		 * We can avoid listing refs if all of them are exact
+		 * OIDs
+		 */
+		must_list_refs = 0;
+		for (i = 0; i < rs->nr; i++) {
+			if (!rs->items[i].exact_sha1) {
+				must_list_refs = 1;
+				break;
+			}
+		}
+	} else if (transport->remote && transport->remote->fetch.nr)
+		refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes);
+
+	if (tags == TAGS_SET || tags == TAGS_DEFAULT) {
+		must_list_refs = 1;
+		if (ref_prefixes.argc)
+			argv_array_push(&ref_prefixes, "refs/tags/");
+	}
+
+	if (must_list_refs)
+		remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
+	else
+		remote_refs = NULL;
+
+	argv_array_clear(&ref_prefixes);
+
+	ref_map = get_ref_map(transport->remote, remote_refs, rs,
+			      tags, &autotags);
+	if (!update_head_ok)
+		check_not_current_branch(ref_map);
+
+	if (tags == TAGS_DEFAULT && autotags)
+		transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
+	if (prune) {
+		/*
+		 * We only prune based on refspecs specified
+		 * explicitly (via command line or configuration); we
+		 * don't care whether --tags was specified.
+		 */
+		if (rs->nr) {
+			prune_refs(rs, ref_map, transport->url);
+		} else {
+			prune_refs(&transport->remote->fetch,
+				   ref_map,
+				   transport->url);
+		}
+	}
+	if (fetch_refs(transport, ref_map) || consume_refs(transport, ref_map)) {
+		free_refs(ref_map);
+		retcode = 1;
+		goto cleanup;
+	}
+	free_refs(ref_map);
+
+	/* if neither --no-tags nor --tags was specified, do automated tag
+	 * following ... */
+	if (tags == TAGS_DEFAULT && autotags) {
+		struct ref **tail = &ref_map;
+		ref_map = NULL;
+		find_non_local_tags(remote_refs, &ref_map, &tail);
+		if (ref_map)
+			backfill_tags(transport, ref_map);
+		free_refs(ref_map);
+	}
+
+ cleanup:
+	return retcode;
+}
+
+static int get_one_remote_for_fetch(struct remote *remote, void *priv)
+{
+	struct string_list *list = priv;
+	if (!remote->skip_default_update)
+		string_list_append(list, remote->name);
+	return 0;
+}
+
+struct remote_group_data {
+	const char *name;
+	struct string_list *list;
+};
+
+static int get_remote_group(const char *key, const char *value, void *priv)
+{
+	struct remote_group_data *g = priv;
+
+	if (skip_prefix(key, "remotes.", &key) && !strcmp(key, g->name)) {
+		/* split list by white space */
+		while (*value) {
+			size_t wordlen = strcspn(value, " \t\n");
+
+			if (wordlen >= 1)
+				string_list_append_nodup(g->list,
+						   xstrndup(value, wordlen));
+			value += wordlen + (value[wordlen] != '\0');
+		}
+	}
+
+	return 0;
+}
+
+static int add_remote_or_group(const char *name, struct string_list *list)
+{
+	int prev_nr = list->nr;
+	struct remote_group_data g;
+	g.name = name; g.list = list;
+
+	git_config(get_remote_group, &g);
+	if (list->nr == prev_nr) {
+		struct remote *remote = remote_get(name);
+		if (!remote_is_configured(remote, 0))
+			return 0;
+		string_list_append(list, remote->name);
+	}
+	return 1;
+}
+
+static void add_options_to_argv(struct argv_array *argv)
+{
+	if (dry_run)
+		argv_array_push(argv, "--dry-run");
+	if (prune != -1)
+		argv_array_push(argv, prune ? "--prune" : "--no-prune");
+	if (prune_tags != -1)
+		argv_array_push(argv, prune_tags ? "--prune-tags" : "--no-prune-tags");
+	if (update_head_ok)
+		argv_array_push(argv, "--update-head-ok");
+	if (force)
+		argv_array_push(argv, "--force");
+	if (keep)
+		argv_array_push(argv, "--keep");
+	if (recurse_submodules == RECURSE_SUBMODULES_ON)
+		argv_array_push(argv, "--recurse-submodules");
+	else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
+		argv_array_push(argv, "--recurse-submodules=on-demand");
+	if (tags == TAGS_SET)
+		argv_array_push(argv, "--tags");
+	else if (tags == TAGS_UNSET)
+		argv_array_push(argv, "--no-tags");
+	if (verbosity >= 2)
+		argv_array_push(argv, "-v");
+	if (verbosity >= 1)
+		argv_array_push(argv, "-v");
+	else if (verbosity < 0)
+		argv_array_push(argv, "-q");
+
+}
+
+static int fetch_multiple(struct string_list *list)
+{
+	int i, result = 0;
+	struct argv_array argv = ARGV_ARRAY_INIT;
+
+	if (!append && !dry_run) {
+		int errcode = truncate_fetch_head();
+		if (errcode)
+			return errcode;
+	}
+
+	argv_array_pushl(&argv, "fetch", "--append", "--no-auto-gc", NULL);
+	add_options_to_argv(&argv);
+
+	for (i = 0; i < list->nr; i++) {
+		const char *name = list->items[i].string;
+		argv_array_push(&argv, name);
+		if (verbosity >= 0)
+			printf(_("Fetching %s\n"), name);
+		if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+			error(_("Could not fetch %s"), name);
+			result = 1;
+		}
+		argv_array_pop(&argv);
+	}
+
+	argv_array_clear(&argv);
+	return result;
+}
+
+/*
+ * Fetching from the promisor remote should use the given filter-spec
+ * or inherit the default filter-spec from the config.
+ */
+static inline void fetch_one_setup_partial(struct remote *remote)
+{
+	/*
+	 * Explicit --no-filter argument overrides everything, regardless
+	 * of any prior partial clones and fetches.
+	 */
+	if (filter_options.no_filter)
+		return;
+
+	/*
+	 * If no prior partial clone/fetch and the current fetch DID NOT
+	 * request a partial-fetch, do a normal fetch.
+	 */
+	if (!repository_format_partial_clone && !filter_options.choice)
+		return;
+
+	/*
+	 * If this is the FIRST partial-fetch request, we enable partial
+	 * on this repo and remember the given filter-spec as the default
+	 * for subsequent fetches to this remote.
+	 */
+	if (!repository_format_partial_clone && filter_options.choice) {
+		partial_clone_register(remote->name, &filter_options);
+		return;
+	}
+
+	/*
+	 * We are currently limited to only ONE promisor remote and only
+	 * allow partial-fetches from the promisor remote.
+	 */
+	if (strcmp(remote->name, repository_format_partial_clone)) {
+		if (filter_options.choice)
+			die(_("--filter can only be used with the remote "
+			      "configured in extensions.partialClone"));
+		return;
+	}
+
+	/*
+	 * Do a partial-fetch from the promisor remote using either the
+	 * explicitly given filter-spec or inherit the filter-spec from
+	 * the config.
+	 */
+	if (!filter_options.choice)
+		partial_clone_get_default_filter_spec(&filter_options);
+	return;
+}
+
+static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok)
+{
+	struct refspec rs = REFSPEC_INIT_FETCH;
+	int i;
+	int exit_code;
+	int maybe_prune_tags;
+	int remote_via_config = remote_is_configured(remote, 0);
+
+	if (!remote)
+		die(_("No remote repository specified.  Please, specify either a URL or a\n"
+		    "remote name from which new revisions should be fetched."));
+
+	gtransport = prepare_transport(remote, 1);
+
+	if (prune < 0) {
+		/* no command line request */
+		if (0 <= remote->prune)
+			prune = remote->prune;
+		else if (0 <= fetch_prune_config)
+			prune = fetch_prune_config;
+		else
+			prune = PRUNE_BY_DEFAULT;
+	}
+
+	if (prune_tags < 0) {
+		/* no command line request */
+		if (0 <= remote->prune_tags)
+			prune_tags = remote->prune_tags;
+		else if (0 <= fetch_prune_tags_config)
+			prune_tags = fetch_prune_tags_config;
+		else
+			prune_tags = PRUNE_TAGS_BY_DEFAULT;
+	}
+
+	maybe_prune_tags = prune_tags_ok && prune_tags;
+	if (maybe_prune_tags && remote_via_config)
+		refspec_append(&remote->fetch, TAG_REFSPEC);
+
+	if (maybe_prune_tags && (argc || !remote_via_config))
+		refspec_append(&rs, TAG_REFSPEC);
+
+	for (i = 0; i < argc; i++) {
+		if (!strcmp(argv[i], "tag")) {
+			char *tag;
+			i++;
+			if (i >= argc)
+				die(_("You need to specify a tag name."));
+
+			tag = xstrfmt("refs/tags/%s:refs/tags/%s",
+				      argv[i], argv[i]);
+			refspec_append(&rs, tag);
+			free(tag);
+		} else {
+			refspec_append(&rs, argv[i]);
+		}
+	}
+
+	if (server_options.nr)
+		gtransport->server_options = &server_options;
+
+	sigchain_push_common(unlock_pack_on_signal);
+	atexit(unlock_pack);
+	sigchain_push(SIGPIPE, SIG_IGN);
+	exit_code = do_fetch(gtransport, &rs);
+	sigchain_pop(SIGPIPE);
+	refspec_clear(&rs);
+	transport_disconnect(gtransport);
+	gtransport = NULL;
+	return exit_code;
+}
+
+int cmd_fetch(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct string_list list = STRING_LIST_INIT_DUP;
+	struct remote *remote = NULL;
+	int result = 0;
+	int prune_tags_ok = 1;
+	struct argv_array argv_gc_auto = ARGV_ARRAY_INIT;
+
+	packet_trace_identity("fetch");
+
+	fetch_if_missing = 0;
+
+	/* Record the command line for the reflog */
+	strbuf_addstr(&default_rla, "fetch");
+	for (i = 1; i < argc; i++)
+		strbuf_addf(&default_rla, " %s", argv[i]);
+
+	fetch_config_from_gitmodules(&max_children, &recurse_submodules);
+	git_config(git_fetch_config, NULL);
+
+	argc = parse_options(argc, argv, prefix,
+			     builtin_fetch_options, builtin_fetch_usage, 0);
+
+	if (deepen_relative) {
+		if (deepen_relative < 0)
+			die(_("Negative depth in --deepen is not supported"));
+		if (depth)
+			die(_("--deepen and --depth are mutually exclusive"));
+		depth = xstrfmt("%d", deepen_relative);
+	}
+	if (unshallow) {
+		if (depth)
+			die(_("--depth and --unshallow cannot be used together"));
+		else if (!is_repository_shallow(the_repository))
+			die(_("--unshallow on a complete repository does not make sense"));
+		else
+			depth = xstrfmt("%d", INFINITE_DEPTH);
+	}
+
+	/* no need to be strict, transport_set_option() will validate it again */
+	if (depth && atoi(depth) < 1)
+		die(_("depth %s is not a positive number"), depth);
+	if (depth || deepen_since || deepen_not.nr)
+		deepen = 1;
+
+	if (filter_options.choice && !repository_format_partial_clone)
+		die("--filter can only be used when extensions.partialClone is set");
+
+	if (all) {
+		if (argc == 1)
+			die(_("fetch --all does not take a repository argument"));
+		else if (argc > 1)
+			die(_("fetch --all does not make sense with refspecs"));
+		(void) for_each_remote(get_one_remote_for_fetch, &list);
+	} else if (argc == 0) {
+		/* No arguments -- use default remote */
+		remote = remote_get(NULL);
+	} else if (multiple) {
+		/* All arguments are assumed to be remotes or groups */
+		for (i = 0; i < argc; i++)
+			if (!add_remote_or_group(argv[i], &list))
+				die(_("No such remote or remote group: %s"), argv[i]);
+	} else {
+		/* Single remote or group */
+		(void) add_remote_or_group(argv[0], &list);
+		if (list.nr > 1) {
+			/* More than one remote */
+			if (argc > 1)
+				die(_("Fetching a group and specifying refspecs does not make sense"));
+		} else {
+			/* Zero or one remotes */
+			remote = remote_get(argv[0]);
+			prune_tags_ok = (argc == 1);
+			argc--;
+			argv++;
+		}
+	}
+
+	if (remote) {
+		if (filter_options.choice || repository_format_partial_clone)
+			fetch_one_setup_partial(remote);
+		result = fetch_one(remote, argc, argv, prune_tags_ok);
+	} else {
+		if (filter_options.choice)
+			die(_("--filter can only be used with the remote "
+			      "configured in extensions.partialclone"));
+		/* TODO should this also die if we have a previous partial-clone? */
+		result = fetch_multiple(&list);
+	}
+
+	if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
+		struct argv_array options = ARGV_ARRAY_INIT;
+
+		add_options_to_argv(&options);
+		result = fetch_populated_submodules(the_repository,
+						    &options,
+						    submodule_prefix,
+						    recurse_submodules,
+						    recurse_submodules_default,
+						    verbosity < 0,
+						    max_children);
+		argv_array_clear(&options);
+	}
+
+	string_list_clear(&list, 0);
+
+	close_object_store(the_repository->objects);
+
+	if (enable_auto_gc) {
+		argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
+		if (verbosity < 0)
+			argv_array_push(&argv_gc_auto, "--quiet");
+		run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD);
+		argv_array_clear(&argv_gc_auto);
+	}
+
+	return result;
+}
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
new file mode 100644
index 0000000000..a4615587fd
--- /dev/null
+++ b/builtin/fmt-merge-msg.c
@@ -0,0 +1,719 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "refs.h"
+#include "object-store.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "tag.h"
+#include "string-list.h"
+#include "branch.h"
+#include "fmt-merge-msg.h"
+#include "gpg-interface.h"
+#include "repository.h"
+#include "commit-reach.h"
+
+static const char * const fmt_merge_msg_usage[] = {
+	N_("git fmt-merge-msg [-m <message>] [--log[=<n>] | --no-log] [--file <file>]"),
+	NULL
+};
+
+static int use_branch_desc;
+
+int fmt_merge_msg_config(const char *key, const char *value, void *cb)
+{
+	if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
+		int is_bool;
+		merge_log_config = git_config_bool_or_int(key, value, &is_bool);
+		if (!is_bool && merge_log_config < 0)
+			return error("%s: negative length %s", key, value);
+		if (is_bool && merge_log_config)
+			merge_log_config = DEFAULT_MERGE_LOG_LEN;
+	} else if (!strcmp(key, "merge.branchdesc")) {
+		use_branch_desc = git_config_bool(key, value);
+	} else {
+		return git_default_config(key, value, cb);
+	}
+	return 0;
+}
+
+/* merge data per repository where the merged tips came from */
+struct src_data {
+	struct string_list branch, tag, r_branch, generic;
+	int head_status;
+};
+
+struct origin_data {
+	struct object_id oid;
+	unsigned is_local_branch:1;
+};
+
+static void init_src_data(struct src_data *data)
+{
+	data->branch.strdup_strings = 1;
+	data->tag.strdup_strings = 1;
+	data->r_branch.strdup_strings = 1;
+	data->generic.strdup_strings = 1;
+}
+
+static struct string_list srcs = STRING_LIST_INIT_DUP;
+static struct string_list origins = STRING_LIST_INIT_DUP;
+
+struct merge_parents {
+	int alloc, nr;
+	struct merge_parent {
+		struct object_id given;
+		struct object_id commit;
+		unsigned char used;
+	} *item;
+};
+
+/*
+ * I know, I know, this is inefficient, but you won't be pulling and merging
+ * hundreds of heads at a time anyway.
+ */
+static struct merge_parent *find_merge_parent(struct merge_parents *table,
+					      struct object_id *given,
+					      struct object_id *commit)
+{
+	int i;
+	for (i = 0; i < table->nr; i++) {
+		if (given && !oideq(&table->item[i].given, given))
+			continue;
+		if (commit && !oideq(&table->item[i].commit, commit))
+			continue;
+		return &table->item[i];
+	}
+	return NULL;
+}
+
+static void add_merge_parent(struct merge_parents *table,
+			     struct object_id *given,
+			     struct object_id *commit)
+{
+	if (table->nr && find_merge_parent(table, given, commit))
+		return;
+	ALLOC_GROW(table->item, table->nr + 1, table->alloc);
+	oidcpy(&table->item[table->nr].given, given);
+	oidcpy(&table->item[table->nr].commit, commit);
+	table->item[table->nr].used = 0;
+	table->nr++;
+}
+
+static int handle_line(char *line, struct merge_parents *merge_parents)
+{
+	int i, len = strlen(line);
+	struct origin_data *origin_data;
+	char *src;
+	const char *origin;
+	struct src_data *src_data;
+	struct string_list_item *item;
+	int pulling_head = 0;
+	struct object_id oid;
+	const unsigned hexsz = the_hash_algo->hexsz;
+
+	if (len < hexsz + 3 || line[hexsz] != '\t')
+		return 1;
+
+	if (starts_with(line + hexsz + 1, "not-for-merge"))
+		return 0;
+
+	if (line[hexsz + 1] != '\t')
+		return 2;
+
+	i = get_oid_hex(line, &oid);
+	if (i)
+		return 3;
+
+	if (!find_merge_parent(merge_parents, &oid, NULL))
+		return 0; /* subsumed by other parents */
+
+	origin_data = xcalloc(1, sizeof(struct origin_data));
+	oidcpy(&origin_data->oid, &oid);
+
+	if (line[len - 1] == '\n')
+		line[len - 1] = 0;
+	line += hexsz + 2;
+
+	/*
+	 * At this point, line points at the beginning of comment e.g.
+	 * "branch 'frotz' of git://that/repository.git".
+	 * Find the repository name and point it with src.
+	 */
+	src = strstr(line, " of ");
+	if (src) {
+		*src = 0;
+		src += 4;
+		pulling_head = 0;
+	} else {
+		src = line;
+		pulling_head = 1;
+	}
+
+	item = unsorted_string_list_lookup(&srcs, src);
+	if (!item) {
+		item = string_list_append(&srcs, src);
+		item->util = xcalloc(1, sizeof(struct src_data));
+		init_src_data(item->util);
+	}
+	src_data = item->util;
+
+	if (pulling_head) {
+		origin = src;
+		src_data->head_status |= 1;
+	} else if (starts_with(line, "branch ")) {
+		origin_data->is_local_branch = 1;
+		origin = line + 7;
+		string_list_append(&src_data->branch, origin);
+		src_data->head_status |= 2;
+	} else if (starts_with(line, "tag ")) {
+		origin = line;
+		string_list_append(&src_data->tag, origin + 4);
+		src_data->head_status |= 2;
+	} else if (skip_prefix(line, "remote-tracking branch ", &origin)) {
+		string_list_append(&src_data->r_branch, origin);
+		src_data->head_status |= 2;
+	} else {
+		origin = src;
+		string_list_append(&src_data->generic, line);
+		src_data->head_status |= 2;
+	}
+
+	if (!strcmp(".", src) || !strcmp(src, origin)) {
+		int len = strlen(origin);
+		if (origin[0] == '\'' && origin[len - 1] == '\'')
+			origin = xmemdupz(origin + 1, len - 2);
+	} else
+		origin = xstrfmt("%s of %s", origin, src);
+	if (strcmp(".", src))
+		origin_data->is_local_branch = 0;
+	string_list_append(&origins, origin)->util = origin_data;
+	return 0;
+}
+
+static void print_joined(const char *singular, const char *plural,
+		struct string_list *list, struct strbuf *out)
+{
+	if (list->nr == 0)
+		return;
+	if (list->nr == 1) {
+		strbuf_addf(out, "%s%s", singular, list->items[0].string);
+	} else {
+		int i;
+		strbuf_addstr(out, plural);
+		for (i = 0; i < list->nr - 1; i++)
+			strbuf_addf(out, "%s%s", i > 0 ? ", " : "",
+				    list->items[i].string);
+		strbuf_addf(out, " and %s", list->items[list->nr - 1].string);
+	}
+}
+
+static void add_branch_desc(struct strbuf *out, const char *name)
+{
+	struct strbuf desc = STRBUF_INIT;
+
+	if (!read_branch_desc(&desc, name)) {
+		const char *bp = desc.buf;
+		while (*bp) {
+			const char *ep = strchrnul(bp, '\n');
+			if (*ep)
+				ep++;
+			strbuf_addf(out, "  : %.*s", (int)(ep - bp), bp);
+			bp = ep;
+		}
+		strbuf_complete_line(out);
+	}
+	strbuf_release(&desc);
+}
+
+#define util_as_integral(elem) ((intptr_t)((elem)->util))
+
+static void record_person_from_buf(int which, struct string_list *people,
+				   const char *buffer)
+{
+	char *name_buf, *name, *name_end;
+	struct string_list_item *elem;
+	const char *field;
+
+	field = (which == 'a') ? "\nauthor " : "\ncommitter ";
+	name = strstr(buffer, field);
+	if (!name)
+		return;
+	name += strlen(field);
+	name_end = strchrnul(name, '<');
+	if (*name_end)
+		name_end--;
+	while (isspace(*name_end) && name <= name_end)
+		name_end--;
+	if (name_end < name)
+		return;
+	name_buf = xmemdupz(name, name_end - name + 1);
+
+	elem = string_list_lookup(people, name_buf);
+	if (!elem) {
+		elem = string_list_insert(people, name_buf);
+		elem->util = (void *)0;
+	}
+	elem->util = (void*)(util_as_integral(elem) + 1);
+	free(name_buf);
+}
+
+
+static void record_person(int which, struct string_list *people,
+			  struct commit *commit)
+{
+	const char *buffer = get_commit_buffer(commit, NULL);
+	record_person_from_buf(which, people, buffer);
+	unuse_commit_buffer(commit, buffer);
+}
+
+static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
+{
+	const struct string_list_item *a = a_, *b = b_;
+	return util_as_integral(b) - util_as_integral(a);
+}
+
+static void add_people_count(struct strbuf *out, struct string_list *people)
+{
+	if (people->nr == 1)
+		strbuf_addstr(out, people->items[0].string);
+	else if (people->nr == 2)
+		strbuf_addf(out, "%s (%d) and %s (%d)",
+			    people->items[0].string,
+			    (int)util_as_integral(&people->items[0]),
+			    people->items[1].string,
+			    (int)util_as_integral(&people->items[1]));
+	else if (people->nr)
+		strbuf_addf(out, "%s (%d) and others",
+			    people->items[0].string,
+			    (int)util_as_integral(&people->items[0]));
+}
+
+static void credit_people(struct strbuf *out,
+			  struct string_list *them,
+			  int kind)
+{
+	const char *label;
+	const char *me;
+
+	if (kind == 'a') {
+		label = "By";
+		me = git_author_info(IDENT_NO_DATE);
+	} else {
+		label = "Via";
+		me = git_committer_info(IDENT_NO_DATE);
+	}
+
+	if (!them->nr ||
+	    (them->nr == 1 &&
+	     me &&
+	     skip_prefix(me, them->items->string, &me) &&
+	     starts_with(me, " <")))
+		return;
+	strbuf_addf(out, "\n%c %s ", comment_line_char, label);
+	add_people_count(out, them);
+}
+
+static void add_people_info(struct strbuf *out,
+			    struct string_list *authors,
+			    struct string_list *committers)
+{
+	QSORT(authors->items, authors->nr,
+	      cmp_string_list_util_as_integral);
+	QSORT(committers->items, committers->nr,
+	      cmp_string_list_util_as_integral);
+
+	credit_people(out, authors, 'a');
+	credit_people(out, committers, 'c');
+}
+
+static void shortlog(const char *name,
+		     struct origin_data *origin_data,
+		     struct commit *head,
+		     struct rev_info *rev,
+		     struct fmt_merge_msg_opts *opts,
+		     struct strbuf *out)
+{
+	int i, count = 0;
+	struct commit *commit;
+	struct object *branch;
+	struct string_list subjects = STRING_LIST_INIT_DUP;
+	struct string_list authors = STRING_LIST_INIT_DUP;
+	struct string_list committers = STRING_LIST_INIT_DUP;
+	int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
+	struct strbuf sb = STRBUF_INIT;
+	const struct object_id *oid = &origin_data->oid;
+	int limit = opts->shortlog_len;
+
+	branch = deref_tag(the_repository, parse_object(the_repository, oid),
+			   oid_to_hex(oid),
+			   the_hash_algo->hexsz);
+	if (!branch || branch->type != OBJ_COMMIT)
+		return;
+
+	setup_revisions(0, NULL, rev, NULL);
+	add_pending_object(rev, branch, name);
+	add_pending_object(rev, &head->object, "^HEAD");
+	head->object.flags |= UNINTERESTING;
+	if (prepare_revision_walk(rev))
+		die("revision walk setup failed");
+	while ((commit = get_revision(rev)) != NULL) {
+		struct pretty_print_context ctx = {0};
+
+		if (commit->parents && commit->parents->next) {
+			/* do not list a merge but count committer */
+			if (opts->credit_people)
+				record_person('c', &committers, commit);
+			continue;
+		}
+		if (!count && opts->credit_people)
+			/* the 'tip' committer */
+			record_person('c', &committers, commit);
+		if (opts->credit_people)
+			record_person('a', &authors, commit);
+		count++;
+		if (subjects.nr > limit)
+			continue;
+
+		format_commit_message(commit, "%s", &sb, &ctx);
+		strbuf_ltrim(&sb);
+
+		if (!sb.len)
+			string_list_append(&subjects,
+					   oid_to_hex(&commit->object.oid));
+		else
+			string_list_append_nodup(&subjects,
+						 strbuf_detach(&sb, NULL));
+	}
+
+	if (opts->credit_people)
+		add_people_info(out, &authors, &committers);
+	if (count > limit)
+		strbuf_addf(out, "\n* %s: (%d commits)\n", name, count);
+	else
+		strbuf_addf(out, "\n* %s:\n", name);
+
+	if (origin_data->is_local_branch && use_branch_desc)
+		add_branch_desc(out, name);
+
+	for (i = 0; i < subjects.nr; i++)
+		if (i >= limit)
+			strbuf_addstr(out, "  ...\n");
+		else
+			strbuf_addf(out, "  %s\n", subjects.items[i].string);
+
+	clear_commit_marks((struct commit *)branch, flags);
+	clear_commit_marks(head, flags);
+	free_commit_list(rev->commits);
+	rev->commits = NULL;
+	rev->pending.nr = 0;
+
+	string_list_clear(&authors, 0);
+	string_list_clear(&committers, 0);
+	string_list_clear(&subjects, 0);
+}
+
+static void fmt_merge_msg_title(struct strbuf *out,
+				const char *current_branch)
+{
+	int i = 0;
+	char *sep = "";
+
+	strbuf_addstr(out, "Merge ");
+	for (i = 0; i < srcs.nr; i++) {
+		struct src_data *src_data = srcs.items[i].util;
+		const char *subsep = "";
+
+		strbuf_addstr(out, sep);
+		sep = "; ";
+
+		if (src_data->head_status == 1) {
+			strbuf_addstr(out, srcs.items[i].string);
+			continue;
+		}
+		if (src_data->head_status == 3) {
+			subsep = ", ";
+			strbuf_addstr(out, "HEAD");
+		}
+		if (src_data->branch.nr) {
+			strbuf_addstr(out, subsep);
+			subsep = ", ";
+			print_joined("branch ", "branches ", &src_data->branch,
+					out);
+		}
+		if (src_data->r_branch.nr) {
+			strbuf_addstr(out, subsep);
+			subsep = ", ";
+			print_joined("remote-tracking branch ", "remote-tracking branches ",
+					&src_data->r_branch, out);
+		}
+		if (src_data->tag.nr) {
+			strbuf_addstr(out, subsep);
+			subsep = ", ";
+			print_joined("tag ", "tags ", &src_data->tag, out);
+		}
+		if (src_data->generic.nr) {
+			strbuf_addstr(out, subsep);
+			print_joined("commit ", "commits ", &src_data->generic,
+					out);
+		}
+		if (strcmp(".", srcs.items[i].string))
+			strbuf_addf(out, " of %s", srcs.items[i].string);
+	}
+
+	if (!strcmp("master", current_branch))
+		strbuf_addch(out, '\n');
+	else
+		strbuf_addf(out, " into %s\n", current_branch);
+}
+
+static void fmt_tag_signature(struct strbuf *tagbuf,
+			      struct strbuf *sig,
+			      const char *buf,
+			      unsigned long len)
+{
+	const char *tag_body = strstr(buf, "\n\n");
+	if (tag_body) {
+		tag_body += 2;
+		strbuf_add(tagbuf, tag_body, buf + len - tag_body);
+	}
+	strbuf_complete_line(tagbuf);
+	if (sig->len) {
+		strbuf_addch(tagbuf, '\n');
+		strbuf_add_commented_lines(tagbuf, sig->buf, sig->len);
+	}
+}
+
+static void fmt_merge_msg_sigs(struct strbuf *out)
+{
+	int i, tag_number = 0, first_tag = 0;
+	struct strbuf tagbuf = STRBUF_INIT;
+
+	for (i = 0; i < origins.nr; i++) {
+		struct object_id *oid = origins.items[i].util;
+		enum object_type type;
+		unsigned long size, len;
+		char *buf = read_object_file(oid, &type, &size);
+		struct strbuf sig = STRBUF_INIT;
+
+		if (!buf || type != OBJ_TAG)
+			goto next;
+		len = parse_signature(buf, size);
+
+		if (size == len)
+			; /* merely annotated */
+		else if (verify_signed_buffer(buf, len, buf + len, size - len, &sig, NULL)) {
+			if (!sig.len)
+				strbuf_addstr(&sig, "gpg verification failed.\n");
+		}
+
+		if (!tag_number++) {
+			fmt_tag_signature(&tagbuf, &sig, buf, len);
+			first_tag = i;
+		} else {
+			if (tag_number == 2) {
+				struct strbuf tagline = STRBUF_INIT;
+				strbuf_addch(&tagline, '\n');
+				strbuf_add_commented_lines(&tagline,
+						origins.items[first_tag].string,
+						strlen(origins.items[first_tag].string));
+				strbuf_insert(&tagbuf, 0, tagline.buf,
+					      tagline.len);
+				strbuf_release(&tagline);
+			}
+			strbuf_addch(&tagbuf, '\n');
+			strbuf_add_commented_lines(&tagbuf,
+					origins.items[i].string,
+					strlen(origins.items[i].string));
+			fmt_tag_signature(&tagbuf, &sig, buf, len);
+		}
+		strbuf_release(&sig);
+	next:
+		free(buf);
+	}
+	if (tagbuf.len) {
+		strbuf_addch(out, '\n');
+		strbuf_addbuf(out, &tagbuf);
+	}
+	strbuf_release(&tagbuf);
+}
+
+static void find_merge_parents(struct merge_parents *result,
+			       struct strbuf *in, struct object_id *head)
+{
+	struct commit_list *parents;
+	struct commit *head_commit;
+	int pos = 0, i, j;
+
+	parents = NULL;
+	while (pos < in->len) {
+		int len;
+		char *p = in->buf + pos;
+		char *newline = strchr(p, '\n');
+		const char *q;
+		struct object_id oid;
+		struct commit *parent;
+		struct object *obj;
+
+		len = newline ? newline - p : strlen(p);
+		pos += len + !!newline;
+
+		if (parse_oid_hex(p, &oid, &q) ||
+		    q[0] != '\t' ||
+		    q[1] != '\t')
+			continue; /* skip not-for-merge */
+		/*
+		 * Do not use get_merge_parent() here; we do not have
+		 * "name" here and we do not want to contaminate its
+		 * util field yet.
+		 */
+		obj = parse_object(the_repository, &oid);
+		parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT);
+		if (!parent)
+			continue;
+		commit_list_insert(parent, &parents);
+		add_merge_parent(result, &obj->oid, &parent->object.oid);
+	}
+	head_commit = lookup_commit(the_repository, head);
+	if (head_commit)
+		commit_list_insert(head_commit, &parents);
+	reduce_heads_replace(&parents);
+
+	while (parents) {
+		struct commit *cmit = pop_commit(&parents);
+		for (i = 0; i < result->nr; i++)
+			if (oideq(&result->item[i].commit, &cmit->object.oid))
+				result->item[i].used = 1;
+	}
+
+	for (i = j = 0; i < result->nr; i++) {
+		if (result->item[i].used) {
+			if (i != j)
+				result->item[j] = result->item[i];
+			j++;
+		}
+	}
+	result->nr = j;
+}
+
+int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
+		  struct fmt_merge_msg_opts *opts)
+{
+	int i = 0, pos = 0;
+	struct object_id head_oid;
+	const char *current_branch;
+	void *current_branch_to_free;
+	struct merge_parents merge_parents;
+
+	memset(&merge_parents, 0, sizeof(merge_parents));
+
+	/* get current branch */
+	current_branch = current_branch_to_free =
+		resolve_refdup("HEAD", RESOLVE_REF_READING, &head_oid, NULL);
+	if (!current_branch)
+		die("No current branch");
+	if (starts_with(current_branch, "refs/heads/"))
+		current_branch += 11;
+
+	find_merge_parents(&merge_parents, in, &head_oid);
+
+	/* get a line */
+	while (pos < in->len) {
+		int len;
+		char *newline, *p = in->buf + pos;
+
+		newline = strchr(p, '\n');
+		len = newline ? newline - p : strlen(p);
+		pos += len + !!newline;
+		i++;
+		p[len] = 0;
+		if (handle_line(p, &merge_parents))
+			die("error in line %d: %.*s", i, len, p);
+	}
+
+	if (opts->add_title && srcs.nr)
+		fmt_merge_msg_title(out, current_branch);
+
+	if (origins.nr)
+		fmt_merge_msg_sigs(out);
+
+	if (opts->shortlog_len) {
+		struct commit *head;
+		struct rev_info rev;
+
+		head = lookup_commit_or_die(&head_oid, "HEAD");
+		repo_init_revisions(the_repository, &rev, NULL);
+		rev.commit_format = CMIT_FMT_ONELINE;
+		rev.ignore_merges = 1;
+		rev.limited = 1;
+
+		strbuf_complete_line(out);
+
+		for (i = 0; i < origins.nr; i++)
+			shortlog(origins.items[i].string,
+				 origins.items[i].util,
+				 head, &rev, opts, out);
+	}
+
+	strbuf_complete_line(out);
+	free(current_branch_to_free);
+	free(merge_parents.item);
+	return 0;
+}
+
+int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
+{
+	const char *inpath = NULL;
+	const char *message = NULL;
+	int shortlog_len = -1;
+	struct option options[] = {
+		{ OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"),
+		  N_("populate log with at most <n> entries from shortlog"),
+		  PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
+		{ OPTION_INTEGER, 0, "summary", &shortlog_len, N_("n"),
+		  N_("alias for --log (deprecated)"),
+		  PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, NULL,
+		  DEFAULT_MERGE_LOG_LEN },
+		OPT_STRING('m', "message", &message, N_("text"),
+			N_("use <text> as start of message")),
+		OPT_FILENAME('F', "file", &inpath, N_("file to read from")),
+		OPT_END()
+	};
+
+	FILE *in = stdin;
+	struct strbuf input = STRBUF_INIT, output = STRBUF_INIT;
+	int ret;
+	struct fmt_merge_msg_opts opts;
+
+	git_config(fmt_merge_msg_config, NULL);
+	argc = parse_options(argc, argv, prefix, options, fmt_merge_msg_usage,
+			     0);
+	if (argc > 0)
+		usage_with_options(fmt_merge_msg_usage, options);
+	if (shortlog_len < 0)
+		shortlog_len = (merge_log_config > 0) ? merge_log_config : 0;
+
+	if (inpath && strcmp(inpath, "-")) {
+		in = fopen(inpath, "r");
+		if (!in)
+			die_errno("cannot open '%s'", inpath);
+	}
+
+	if (strbuf_read(&input, fileno(in), 0) < 0)
+		die_errno("could not read input file");
+
+	if (message)
+		strbuf_addstr(&output, message);
+
+	memset(&opts, 0, sizeof(opts));
+	opts.add_title = !message;
+	opts.credit_people = 1;
+	opts.shortlog_len = shortlog_len;
+
+	ret = fmt_merge_msg(&input, &output, &opts);
+	if (ret)
+		return ret;
+	write_in_full(STDOUT_FILENO, output.buf, output.len);
+	return 0;
+}
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
new file mode 100644
index 0000000000..465153e853
--- /dev/null
+++ b/builtin/for-each-ref.c
@@ -0,0 +1,87 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "refs.h"
+#include "object.h"
+#include "parse-options.h"
+#include "ref-filter.h"
+
+static char const * const for_each_ref_usage[] = {
+	N_("git for-each-ref [<options>] [<pattern>]"),
+	N_("git for-each-ref [--points-at <object>]"),
+	N_("git for-each-ref [(--merged | --no-merged) [<commit>]]"),
+	N_("git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"),
+	NULL
+};
+
+int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+	int maxcount = 0, icase = 0;
+	struct ref_array array;
+	struct ref_filter filter;
+	struct ref_format format = REF_FORMAT_INIT;
+
+	struct option opts[] = {
+		OPT_BIT('s', "shell", &format.quote_style,
+			N_("quote placeholders suitably for shells"), QUOTE_SHELL),
+		OPT_BIT('p', "perl",  &format.quote_style,
+			N_("quote placeholders suitably for perl"), QUOTE_PERL),
+		OPT_BIT(0 , "python", &format.quote_style,
+			N_("quote placeholders suitably for python"), QUOTE_PYTHON),
+		OPT_BIT(0 , "tcl",  &format.quote_style,
+			N_("quote placeholders suitably for Tcl"), QUOTE_TCL),
+
+		OPT_GROUP(""),
+		OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
+		OPT_STRING(  0 , "format", &format.format, N_("format"), N_("format to use for the output")),
+		OPT__COLOR(&format.use_color, N_("respect format colors")),
+		OPT_REF_SORT(sorting_tail),
+		OPT_CALLBACK(0, "points-at", &filter.points_at,
+			     N_("object"), N_("print only refs which points at the given object"),
+			     parse_opt_object_name),
+		OPT_MERGED(&filter, N_("print only refs that are merged")),
+		OPT_NO_MERGED(&filter, N_("print only refs that are not merged")),
+		OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
+		OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
+		OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
+		OPT_END(),
+	};
+
+	memset(&array, 0, sizeof(array));
+	memset(&filter, 0, sizeof(filter));
+
+	format.format = "%(objectname) %(objecttype)\t%(refname)";
+
+	git_config(git_default_config, NULL);
+
+	parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
+	if (maxcount < 0) {
+		error("invalid --count argument: `%d'", maxcount);
+		usage_with_options(for_each_ref_usage, opts);
+	}
+	if (HAS_MULTI_BITS(format.quote_style)) {
+		error("more than one quoting style?");
+		usage_with_options(for_each_ref_usage, opts);
+	}
+	if (verify_ref_format(&format))
+		usage_with_options(for_each_ref_usage, opts);
+
+	if (!sorting)
+		sorting = ref_default_sorting();
+	sorting->ignore_case = icase;
+	filter.ignore_case = icase;
+
+	filter.name_patterns = argv;
+	filter.match_as_path = 1;
+	filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);
+	ref_array_sort(sorting, &array);
+
+	if (!maxcount || array.nr < maxcount)
+		maxcount = array.nr;
+	for (i = 0; i < maxcount; i++)
+		show_ref_array_item(array.items[i], &format);
+	ref_array_clear(&array);
+	return 0;
+}
diff --git a/builtin/fsck.c b/builtin/fsck.c
new file mode 100644
index 0000000000..18403a94fa
--- /dev/null
+++ b/builtin/fsck.c
@@ -0,0 +1,976 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "cache.h"
+#include "repository.h"
+#include "config.h"
+#include "commit.h"
+#include "tree.h"
+#include "blob.h"
+#include "tag.h"
+#include "refs.h"
+#include "pack.h"
+#include "cache-tree.h"
+#include "tree-walk.h"
+#include "fsck.h"
+#include "parse-options.h"
+#include "dir.h"
+#include "progress.h"
+#include "streaming.h"
+#include "decorate.h"
+#include "packfile.h"
+#include "object-store.h"
+#include "run-command.h"
+#include "worktree.h"
+
+#define REACHABLE 0x0001
+#define SEEN      0x0002
+#define HAS_OBJ   0x0004
+/* This flag is set if something points to this object. */
+#define USED      0x0008
+
+static int show_root;
+static int show_tags;
+static int show_unreachable;
+static int include_reflogs = 1;
+static int check_full = 1;
+static int connectivity_only;
+static int check_strict;
+static int keep_cache_objects;
+static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;
+static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;
+static int errors_found;
+static int write_lost_and_found;
+static int verbose;
+static int show_progress = -1;
+static int show_dangling = 1;
+static int name_objects;
+#define ERROR_OBJECT 01
+#define ERROR_REACHABLE 02
+#define ERROR_PACK 04
+#define ERROR_REFS 010
+#define ERROR_COMMIT_GRAPH 020
+
+static const char *describe_object(struct object *obj)
+{
+	static struct strbuf bufs[] = {
+		STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
+	};
+	static int b = 0;
+	struct strbuf *buf;
+	char *name = NULL;
+
+	if (name_objects)
+		name = lookup_decoration(fsck_walk_options.object_names, obj);
+
+	buf = bufs + b;
+	b = (b + 1) % ARRAY_SIZE(bufs);
+	strbuf_reset(buf);
+	strbuf_addstr(buf, oid_to_hex(&obj->oid));
+	if (name)
+		strbuf_addf(buf, " (%s)", name);
+
+	return buf->buf;
+}
+
+static const char *printable_type(struct object *obj)
+{
+	const char *ret;
+
+	if (obj->type == OBJ_NONE) {
+		enum object_type type = oid_object_info(the_repository,
+							&obj->oid, NULL);
+		if (type > 0)
+			object_as_type(the_repository, obj, type, 0);
+	}
+
+	ret = type_name(obj->type);
+	if (!ret)
+		ret = _("unknown");
+
+	return ret;
+}
+
+static int fsck_config(const char *var, const char *value, void *cb)
+{
+	if (strcmp(var, "fsck.skiplist") == 0) {
+		const char *path;
+		struct strbuf sb = STRBUF_INIT;
+
+		if (git_config_pathname(&path, var, value))
+			return 1;
+		strbuf_addf(&sb, "skiplist=%s", path);
+		free((char *)path);
+		fsck_set_msg_types(&fsck_obj_options, sb.buf);
+		strbuf_release(&sb);
+		return 0;
+	}
+
+	if (skip_prefix(var, "fsck.", &var)) {
+		fsck_set_msg_type(&fsck_obj_options, var, value);
+		return 0;
+	}
+
+	return git_default_config(var, value, cb);
+}
+
+static int objerror(struct object *obj, const char *err)
+{
+	errors_found |= ERROR_OBJECT;
+	/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
+	fprintf_ln(stderr, _("error in %s %s: %s"),
+		   printable_type(obj), describe_object(obj), err);
+	return -1;
+}
+
+static int fsck_error_func(struct fsck_options *o,
+	struct object *obj, int type, const char *message)
+{
+	switch (type) {
+	case FSCK_WARN:
+		/* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */
+		fprintf_ln(stderr, _("warning in %s %s: %s"),
+			   printable_type(obj), describe_object(obj), message);
+		return 0;
+	case FSCK_ERROR:
+		/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
+		fprintf_ln(stderr, _("error in %s %s: %s"),
+			   printable_type(obj), describe_object(obj), message);
+		return 1;
+	default:
+		BUG("%d (FSCK_IGNORE?) should never trigger this callback", type);
+	}
+}
+
+static struct object_array pending;
+
+static int mark_object(struct object *obj, int type, void *data, struct fsck_options *options)
+{
+	struct object *parent = data;
+
+	/*
+	 * The only case data is NULL or type is OBJ_ANY is when
+	 * mark_object_reachable() calls us.  All the callers of
+	 * that function has non-NULL obj hence ...
+	 */
+	if (!obj) {
+		/* ... these references to parent->fld are safe here */
+		printf_ln(_("broken link from %7s %s"),
+			  printable_type(parent), describe_object(parent));
+		printf_ln(_("broken link from %7s %s"),
+			  (type == OBJ_ANY ? _("unknown") : type_name(type)),
+			  _("unknown"));
+		errors_found |= ERROR_REACHABLE;
+		return 1;
+	}
+
+	if (type != OBJ_ANY && obj->type != type)
+		/* ... and the reference to parent is safe here */
+		objerror(parent, _("wrong object type in link"));
+
+	if (obj->flags & REACHABLE)
+		return 0;
+	obj->flags |= REACHABLE;
+
+	if (is_promisor_object(&obj->oid))
+		/*
+		 * Further recursion does not need to be performed on this
+		 * object since it is a promisor object (so it does not need to
+		 * be added to "pending").
+		 */
+		return 0;
+
+	if (!(obj->flags & HAS_OBJ)) {
+		if (parent && !has_object_file(&obj->oid)) {
+			printf_ln(_("broken link from %7s %s\n"
+				    "              to %7s %s"),
+				  printable_type(parent),
+				  describe_object(parent),
+				  printable_type(obj),
+				  describe_object(obj));
+			errors_found |= ERROR_REACHABLE;
+		}
+		return 1;
+	}
+
+	add_object_array(obj, NULL, &pending);
+	return 0;
+}
+
+static void mark_object_reachable(struct object *obj)
+{
+	mark_object(obj, OBJ_ANY, NULL, NULL);
+}
+
+static int traverse_one_object(struct object *obj)
+{
+	int result = fsck_walk(obj, obj, &fsck_walk_options);
+
+	if (obj->type == OBJ_TREE) {
+		struct tree *tree = (struct tree *)obj;
+		free_tree_buffer(tree);
+	}
+	return result;
+}
+
+static int traverse_reachable(void)
+{
+	struct progress *progress = NULL;
+	unsigned int nr = 0;
+	int result = 0;
+	if (show_progress)
+		progress = start_delayed_progress(_("Checking connectivity"), 0);
+	while (pending.nr) {
+		result |= traverse_one_object(object_array_pop(&pending));
+		display_progress(progress, ++nr);
+	}
+	stop_progress(&progress);
+	return !!result;
+}
+
+static int mark_used(struct object *obj, int type, void *data, struct fsck_options *options)
+{
+	if (!obj)
+		return 1;
+	obj->flags |= USED;
+	return 0;
+}
+
+static void mark_unreachable_referents(const struct object_id *oid)
+{
+	struct fsck_options options = FSCK_OPTIONS_DEFAULT;
+	struct object *obj = lookup_object(the_repository, oid);
+
+	if (!obj || !(obj->flags & HAS_OBJ))
+		return; /* not part of our original set */
+	if (obj->flags & REACHABLE)
+		return; /* reachable objects already traversed */
+
+	/*
+	 * Avoid passing OBJ_NONE to fsck_walk, which will parse the object
+	 * (and we want to avoid parsing blobs).
+	 */
+	if (obj->type == OBJ_NONE) {
+		enum object_type type = oid_object_info(the_repository,
+							&obj->oid, NULL);
+		if (type > 0)
+			object_as_type(the_repository, obj, type, 0);
+	}
+
+	options.walk = mark_used;
+	fsck_walk(obj, NULL, &options);
+}
+
+static int mark_loose_unreachable_referents(const struct object_id *oid,
+					    const char *path,
+					    void *data)
+{
+	mark_unreachable_referents(oid);
+	return 0;
+}
+
+static int mark_packed_unreachable_referents(const struct object_id *oid,
+					     struct packed_git *pack,
+					     uint32_t pos,
+					     void *data)
+{
+	mark_unreachable_referents(oid);
+	return 0;
+}
+
+/*
+ * Check a single reachable object
+ */
+static void check_reachable_object(struct object *obj)
+{
+	/*
+	 * We obviously want the object to be parsed,
+	 * except if it was in a pack-file and we didn't
+	 * do a full fsck
+	 */
+	if (!(obj->flags & HAS_OBJ)) {
+		if (is_promisor_object(&obj->oid))
+			return;
+		if (has_object_pack(&obj->oid))
+			return; /* it is in pack - forget about it */
+		printf_ln(_("missing %s %s"), printable_type(obj),
+			  describe_object(obj));
+		errors_found |= ERROR_REACHABLE;
+		return;
+	}
+}
+
+/*
+ * Check a single unreachable object
+ */
+static void check_unreachable_object(struct object *obj)
+{
+	/*
+	 * Missing unreachable object? Ignore it. It's not like
+	 * we miss it (since it can't be reached), nor do we want
+	 * to complain about it being unreachable (since it does
+	 * not exist).
+	 */
+	if (!(obj->flags & HAS_OBJ))
+		return;
+
+	/*
+	 * Unreachable object that exists? Show it if asked to,
+	 * since this is something that is prunable.
+	 */
+	if (show_unreachable) {
+		printf_ln(_("unreachable %s %s"), printable_type(obj),
+			  describe_object(obj));
+		return;
+	}
+
+	/*
+	 * "!USED" means that nothing at all points to it, including
+	 * other unreachable objects. In other words, it's the "tip"
+	 * of some set of unreachable objects, usually a commit that
+	 * got dropped.
+	 *
+	 * Such starting points are more interesting than some random
+	 * set of unreachable objects, so we show them even if the user
+	 * hasn't asked for _all_ unreachable objects. If you have
+	 * deleted a branch by mistake, this is a prime candidate to
+	 * start looking at, for example.
+	 */
+	if (!(obj->flags & USED)) {
+		if (show_dangling)
+			printf_ln(_("dangling %s %s"), printable_type(obj),
+				  describe_object(obj));
+		if (write_lost_and_found) {
+			char *filename = git_pathdup("lost-found/%s/%s",
+				obj->type == OBJ_COMMIT ? "commit" : "other",
+				describe_object(obj));
+			FILE *f;
+
+			if (safe_create_leading_directories_const(filename)) {
+				error(_("could not create lost-found"));
+				free(filename);
+				return;
+			}
+			f = xfopen(filename, "w");
+			if (obj->type == OBJ_BLOB) {
+				if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1))
+					die_errno(_("could not write '%s'"), filename);
+			} else
+				fprintf(f, "%s\n", describe_object(obj));
+			if (fclose(f))
+				die_errno(_("could not finish '%s'"),
+					  filename);
+			free(filename);
+		}
+		return;
+	}
+
+	/*
+	 * Otherwise? It's there, it's unreachable, and some other unreachable
+	 * object points to it. Ignore it - it's not interesting, and we showed
+	 * all the interesting cases above.
+	 */
+}
+
+static void check_object(struct object *obj)
+{
+	if (verbose)
+		fprintf_ln(stderr, _("Checking %s"), describe_object(obj));
+
+	if (obj->flags & REACHABLE)
+		check_reachable_object(obj);
+	else
+		check_unreachable_object(obj);
+}
+
+static void check_connectivity(void)
+{
+	int i, max;
+
+	/* Traverse the pending reachable objects */
+	traverse_reachable();
+
+	/*
+	 * With --connectivity-only, we won't have actually opened and marked
+	 * unreachable objects with USED. Do that now to make --dangling, etc
+	 * accurate.
+	 */
+	if (connectivity_only && (show_dangling || write_lost_and_found)) {
+		/*
+		 * Even though we already have a "struct object" for each of
+		 * these in memory, we must not iterate over the internal
+		 * object hash as we do below. Our loop would potentially
+		 * resize the hash, making our iteration invalid.
+		 *
+		 * Instead, we'll just go back to the source list of objects,
+		 * and ignore any that weren't present in our earlier
+		 * traversal.
+		 */
+		for_each_loose_object(mark_loose_unreachable_referents, NULL, 0);
+		for_each_packed_object(mark_packed_unreachable_referents, NULL, 0);
+	}
+
+	/* Look up all the requirements, warn about missing objects.. */
+	max = get_max_object_index();
+	if (verbose)
+		fprintf_ln(stderr, _("Checking connectivity (%d objects)"), max);
+
+	for (i = 0; i < max; i++) {
+		struct object *obj = get_indexed_object(i);
+
+		if (obj)
+			check_object(obj);
+	}
+}
+
+static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
+{
+	int err;
+
+	if (obj->flags & SEEN)
+		return 0;
+	obj->flags |= SEEN;
+
+	if (verbose)
+		fprintf_ln(stderr, _("Checking %s %s"),
+			   printable_type(obj), describe_object(obj));
+
+	if (fsck_walk(obj, NULL, &fsck_obj_options))
+		objerror(obj, _("broken links"));
+	err = fsck_object(obj, buffer, size, &fsck_obj_options);
+	if (err)
+		goto out;
+
+	if (obj->type == OBJ_COMMIT) {
+		struct commit *commit = (struct commit *) obj;
+
+		if (!commit->parents && show_root)
+			printf_ln(_("root %s"),
+				  describe_object(&commit->object));
+	}
+
+	if (obj->type == OBJ_TAG) {
+		struct tag *tag = (struct tag *) obj;
+
+		if (show_tags && tag->tagged) {
+			printf_ln(_("tagged %s %s (%s) in %s"),
+				  printable_type(tag->tagged),
+				  describe_object(tag->tagged),
+				  tag->tag,
+				  describe_object(&tag->object));
+		}
+	}
+
+out:
+	if (obj->type == OBJ_TREE)
+		free_tree_buffer((struct tree *)obj);
+	if (obj->type == OBJ_COMMIT)
+		free_commit_buffer(the_repository->parsed_objects,
+				   (struct commit *)obj);
+	return err;
+}
+
+static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
+			   unsigned long size, void *buffer, int *eaten)
+{
+	/*
+	 * Note, buffer may be NULL if type is OBJ_BLOB. See
+	 * verify_packfile(), data_valid variable for details.
+	 */
+	struct object *obj;
+	obj = parse_object_buffer(the_repository, oid, type, size, buffer,
+				  eaten);
+	if (!obj) {
+		errors_found |= ERROR_OBJECT;
+		return error(_("%s: object corrupt or missing"),
+			     oid_to_hex(oid));
+	}
+	obj->flags &= ~(REACHABLE | SEEN);
+	obj->flags |= HAS_OBJ;
+	return fsck_obj(obj, buffer, size);
+}
+
+static int default_refs;
+
+static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
+	timestamp_t timestamp)
+{
+	struct object *obj;
+
+	if (!is_null_oid(oid)) {
+		obj = lookup_object(the_repository, oid);
+		if (obj && (obj->flags & HAS_OBJ)) {
+			if (timestamp && name_objects)
+				add_decoration(fsck_walk_options.object_names,
+					obj,
+					xstrfmt("%s@{%"PRItime"}", refname, timestamp));
+			obj->flags |= USED;
+			mark_object_reachable(obj);
+		} else if (!is_promisor_object(oid)) {
+			error(_("%s: invalid reflog entry %s"),
+			      refname, oid_to_hex(oid));
+			errors_found |= ERROR_REACHABLE;
+		}
+	}
+}
+
+static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
+		const char *email, timestamp_t timestamp, int tz,
+		const char *message, void *cb_data)
+{
+	const char *refname = cb_data;
+
+	if (verbose)
+		fprintf_ln(stderr, _("Checking reflog %s->%s"),
+			   oid_to_hex(ooid), oid_to_hex(noid));
+
+	fsck_handle_reflog_oid(refname, ooid, 0);
+	fsck_handle_reflog_oid(refname, noid, timestamp);
+	return 0;
+}
+
+static int fsck_handle_reflog(const char *logname, const struct object_id *oid,
+			      int flag, void *cb_data)
+{
+	struct strbuf refname = STRBUF_INIT;
+
+	strbuf_worktree_ref(cb_data, &refname, logname);
+	for_each_reflog_ent(refname.buf, fsck_handle_reflog_ent, refname.buf);
+	strbuf_release(&refname);
+	return 0;
+}
+
+static int fsck_handle_ref(const char *refname, const struct object_id *oid,
+			   int flag, void *cb_data)
+{
+	struct object *obj;
+
+	obj = parse_object(the_repository, oid);
+	if (!obj) {
+		if (is_promisor_object(oid)) {
+			/*
+			 * Increment default_refs anyway, because this is a
+			 * valid ref.
+			 */
+			 default_refs++;
+			 return 0;
+		}
+		error(_("%s: invalid sha1 pointer %s"),
+		      refname, oid_to_hex(oid));
+		errors_found |= ERROR_REACHABLE;
+		/* We'll continue with the rest despite the error.. */
+		return 0;
+	}
+	if (obj->type != OBJ_COMMIT && is_branch(refname)) {
+		error(_("%s: not a commit"), refname);
+		errors_found |= ERROR_REFS;
+	}
+	default_refs++;
+	obj->flags |= USED;
+	if (name_objects)
+		add_decoration(fsck_walk_options.object_names,
+			obj, xstrdup(refname));
+	mark_object_reachable(obj);
+
+	return 0;
+}
+
+static int fsck_head_link(const char *head_ref_name,
+			  const char **head_points_at,
+			  struct object_id *head_oid);
+
+static void get_default_heads(void)
+{
+	struct worktree **worktrees, **p;
+	const char *head_points_at;
+	struct object_id head_oid;
+
+	for_each_rawref(fsck_handle_ref, NULL);
+
+	worktrees = get_worktrees(0);
+	for (p = worktrees; *p; p++) {
+		struct worktree *wt = *p;
+		struct strbuf ref = STRBUF_INIT;
+
+		strbuf_worktree_ref(wt, &ref, "HEAD");
+		fsck_head_link(ref.buf, &head_points_at, &head_oid);
+		if (head_points_at && !is_null_oid(&head_oid))
+			fsck_handle_ref(ref.buf, &head_oid, 0, NULL);
+		strbuf_release(&ref);
+
+		if (include_reflogs)
+			refs_for_each_reflog(get_worktree_ref_store(wt),
+					     fsck_handle_reflog, wt);
+	}
+	free_worktrees(worktrees);
+
+	/*
+	 * Not having any default heads isn't really fatal, but
+	 * it does mean that "--unreachable" no longer makes any
+	 * sense (since in this case everything will obviously
+	 * be unreachable by definition.
+	 *
+	 * Showing dangling objects is valid, though (as those
+	 * dangling objects are likely lost heads).
+	 *
+	 * So we just print a warning about it, and clear the
+	 * "show_unreachable" flag.
+	 */
+	if (!default_refs) {
+		fprintf_ln(stderr, _("notice: No default references"));
+		show_unreachable = 0;
+	}
+}
+
+static int fsck_loose(const struct object_id *oid, const char *path, void *data)
+{
+	struct object *obj;
+	enum object_type type;
+	unsigned long size;
+	void *contents;
+	int eaten;
+
+	if (read_loose_object(path, oid, &type, &size, &contents) < 0) {
+		errors_found |= ERROR_OBJECT;
+		error(_("%s: object corrupt or missing: %s"),
+		      oid_to_hex(oid), path);
+		return 0; /* keep checking other objects */
+	}
+
+	if (!contents && type != OBJ_BLOB)
+		BUG("read_loose_object streamed a non-blob");
+
+	obj = parse_object_buffer(the_repository, oid, type, size,
+				  contents, &eaten);
+
+	if (!obj) {
+		errors_found |= ERROR_OBJECT;
+		error(_("%s: object could not be parsed: %s"),
+		      oid_to_hex(oid), path);
+		if (!eaten)
+			free(contents);
+		return 0; /* keep checking other objects */
+	}
+
+	obj->flags &= ~(REACHABLE | SEEN);
+	obj->flags |= HAS_OBJ;
+	if (fsck_obj(obj, contents, size))
+		errors_found |= ERROR_OBJECT;
+
+	if (!eaten)
+		free(contents);
+	return 0; /* keep checking other objects, even if we saw an error */
+}
+
+static int fsck_cruft(const char *basename, const char *path, void *data)
+{
+	if (!starts_with(basename, "tmp_obj_"))
+		fprintf_ln(stderr, _("bad sha1 file: %s"), path);
+	return 0;
+}
+
+static int fsck_subdir(unsigned int nr, const char *path, void *progress)
+{
+	display_progress(progress, nr + 1);
+	return 0;
+}
+
+static void fsck_object_dir(const char *path)
+{
+	struct progress *progress = NULL;
+
+	if (verbose)
+		fprintf_ln(stderr, _("Checking object directory"));
+
+	if (show_progress)
+		progress = start_progress(_("Checking object directories"), 256);
+
+	for_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir,
+				      progress);
+	display_progress(progress, 256);
+	stop_progress(&progress);
+}
+
+static int fsck_head_link(const char *head_ref_name,
+			  const char **head_points_at,
+			  struct object_id *head_oid)
+{
+	int null_is_error = 0;
+
+	if (verbose)
+		fprintf_ln(stderr, _("Checking %s link"), head_ref_name);
+
+	*head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL);
+	if (!*head_points_at) {
+		errors_found |= ERROR_REFS;
+		return error(_("invalid %s"), head_ref_name);
+	}
+	if (!strcmp(*head_points_at, head_ref_name))
+		/* detached HEAD */
+		null_is_error = 1;
+	else if (!starts_with(*head_points_at, "refs/heads/")) {
+		errors_found |= ERROR_REFS;
+		return error(_("%s points to something strange (%s)"),
+			     head_ref_name, *head_points_at);
+	}
+	if (is_null_oid(head_oid)) {
+		if (null_is_error) {
+			errors_found |= ERROR_REFS;
+			return error(_("%s: detached HEAD points at nothing"),
+				     head_ref_name);
+		}
+		fprintf_ln(stderr,
+			   _("notice: %s points to an unborn branch (%s)"),
+			   head_ref_name, *head_points_at + 11);
+	}
+	return 0;
+}
+
+static int fsck_cache_tree(struct cache_tree *it)
+{
+	int i;
+	int err = 0;
+
+	if (verbose)
+		fprintf_ln(stderr, _("Checking cache tree"));
+
+	if (0 <= it->entry_count) {
+		struct object *obj = parse_object(the_repository, &it->oid);
+		if (!obj) {
+			error(_("%s: invalid sha1 pointer in cache-tree"),
+			      oid_to_hex(&it->oid));
+			errors_found |= ERROR_REFS;
+			return 1;
+		}
+		obj->flags |= USED;
+		if (name_objects)
+			add_decoration(fsck_walk_options.object_names,
+				obj, xstrdup(":"));
+		mark_object_reachable(obj);
+		if (obj->type != OBJ_TREE)
+			err |= objerror(obj, _("non-tree in cache-tree"));
+	}
+	for (i = 0; i < it->subtree_nr; i++)
+		err |= fsck_cache_tree(it->down[i]->cache_tree);
+	return err;
+}
+
+static void mark_object_for_connectivity(const struct object_id *oid)
+{
+	struct object *obj = lookup_unknown_object(oid);
+	obj->flags |= HAS_OBJ;
+}
+
+static int mark_loose_for_connectivity(const struct object_id *oid,
+				       const char *path,
+				       void *data)
+{
+	mark_object_for_connectivity(oid);
+	return 0;
+}
+
+static int mark_packed_for_connectivity(const struct object_id *oid,
+					struct packed_git *pack,
+					uint32_t pos,
+					void *data)
+{
+	mark_object_for_connectivity(oid);
+	return 0;
+}
+
+static char const * const fsck_usage[] = {
+	N_("git fsck [<options>] [<object>...]"),
+	NULL
+};
+
+static struct option fsck_opts[] = {
+	OPT__VERBOSE(&verbose, N_("be verbose")),
+	OPT_BOOL(0, "unreachable", &show_unreachable, N_("show unreachable objects")),
+	OPT_BOOL(0, "dangling", &show_dangling, N_("show dangling objects")),
+	OPT_BOOL(0, "tags", &show_tags, N_("report tags")),
+	OPT_BOOL(0, "root", &show_root, N_("report root nodes")),
+	OPT_BOOL(0, "cache", &keep_cache_objects, N_("make index objects head nodes")),
+	OPT_BOOL(0, "reflogs", &include_reflogs, N_("make reflogs head nodes (default)")),
+	OPT_BOOL(0, "full", &check_full, N_("also consider packs and alternate objects")),
+	OPT_BOOL(0, "connectivity-only", &connectivity_only, N_("check only connectivity")),
+	OPT_BOOL(0, "strict", &check_strict, N_("enable more strict checking")),
+	OPT_BOOL(0, "lost-found", &write_lost_and_found,
+				N_("write dangling objects in .git/lost-found")),
+	OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
+	OPT_BOOL(0, "name-objects", &name_objects, N_("show verbose names for reachable objects")),
+	OPT_END(),
+};
+
+int cmd_fsck(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct object_directory *odb;
+
+	/* fsck knows how to handle missing promisor objects */
+	fetch_if_missing = 0;
+
+	errors_found = 0;
+	read_replace_refs = 0;
+
+	argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
+
+	fsck_walk_options.walk = mark_object;
+	fsck_obj_options.walk = mark_used;
+	fsck_obj_options.error_func = fsck_error_func;
+	if (check_strict)
+		fsck_obj_options.strict = 1;
+
+	if (show_progress == -1)
+		show_progress = isatty(2);
+	if (verbose)
+		show_progress = 0;
+
+	if (write_lost_and_found) {
+		check_full = 1;
+		include_reflogs = 0;
+	}
+
+	if (name_objects)
+		fsck_walk_options.object_names =
+			xcalloc(1, sizeof(struct decoration));
+
+	git_config(fsck_config, NULL);
+
+	if (connectivity_only) {
+		for_each_loose_object(mark_loose_for_connectivity, NULL, 0);
+		for_each_packed_object(mark_packed_for_connectivity, NULL, 0);
+	} else {
+		prepare_alt_odb(the_repository);
+		for (odb = the_repository->objects->odb; odb; odb = odb->next)
+			fsck_object_dir(odb->path);
+
+		if (check_full) {
+			struct packed_git *p;
+			uint32_t total = 0, count = 0;
+			struct progress *progress = NULL;
+
+			if (show_progress) {
+				for (p = get_all_packs(the_repository); p;
+				     p = p->next) {
+					if (open_pack_index(p))
+						continue;
+					total += p->num_objects;
+				}
+
+				progress = start_progress(_("Checking objects"), total);
+			}
+			for (p = get_all_packs(the_repository); p;
+			     p = p->next) {
+				/* verify gives error messages itself */
+				if (verify_pack(the_repository,
+						p, fsck_obj_buffer,
+						progress, count))
+					errors_found |= ERROR_PACK;
+				count += p->num_objects;
+			}
+			stop_progress(&progress);
+		}
+
+		if (fsck_finish(&fsck_obj_options))
+			errors_found |= ERROR_OBJECT;
+	}
+
+	for (i = 0; i < argc; i++) {
+		const char *arg = argv[i];
+		struct object_id oid;
+		if (!get_oid(arg, &oid)) {
+			struct object *obj = lookup_object(the_repository,
+							   &oid);
+
+			if (!obj || !(obj->flags & HAS_OBJ)) {
+				if (is_promisor_object(&oid))
+					continue;
+				error(_("%s: object missing"), oid_to_hex(&oid));
+				errors_found |= ERROR_OBJECT;
+				continue;
+			}
+
+			obj->flags |= USED;
+			if (name_objects)
+				add_decoration(fsck_walk_options.object_names,
+					obj, xstrdup(arg));
+			mark_object_reachable(obj);
+			continue;
+		}
+		error(_("invalid parameter: expected sha1, got '%s'"), arg);
+		errors_found |= ERROR_OBJECT;
+	}
+
+	/*
+	 * If we've not been given any explicit head information, do the
+	 * default ones from .git/refs. We also consider the index file
+	 * in this case (ie this implies --cache).
+	 */
+	if (!argc) {
+		get_default_heads();
+		keep_cache_objects = 1;
+	}
+
+	if (keep_cache_objects) {
+		verify_index_checksum = 1;
+		verify_ce_order = 1;
+		read_cache();
+		for (i = 0; i < active_nr; i++) {
+			unsigned int mode;
+			struct blob *blob;
+			struct object *obj;
+
+			mode = active_cache[i]->ce_mode;
+			if (S_ISGITLINK(mode))
+				continue;
+			blob = lookup_blob(the_repository,
+					   &active_cache[i]->oid);
+			if (!blob)
+				continue;
+			obj = &blob->object;
+			obj->flags |= USED;
+			if (name_objects)
+				add_decoration(fsck_walk_options.object_names,
+					obj,
+					xstrfmt(":%s", active_cache[i]->name));
+			mark_object_reachable(obj);
+		}
+		if (active_cache_tree)
+			fsck_cache_tree(active_cache_tree);
+	}
+
+	check_connectivity();
+
+	if (!git_config_get_bool("core.commitgraph", &i) && i) {
+		struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
+		const char *verify_argv[] = { "commit-graph", "verify", NULL, NULL, NULL };
+
+		prepare_alt_odb(the_repository);
+		for (odb = the_repository->objects->odb; odb; odb = odb->next) {
+			child_process_init(&commit_graph_verify);
+			commit_graph_verify.argv = verify_argv;
+			commit_graph_verify.git_cmd = 1;
+			verify_argv[2] = "--object-dir";
+			verify_argv[3] = odb->path;
+			if (run_command(&commit_graph_verify))
+				errors_found |= ERROR_COMMIT_GRAPH;
+		}
+	}
+
+	if (!git_config_get_bool("core.multipackindex", &i) && i) {
+		struct child_process midx_verify = CHILD_PROCESS_INIT;
+		const char *midx_argv[] = { "multi-pack-index", "verify", NULL, NULL, NULL };
+
+		prepare_alt_odb(the_repository);
+		for (odb = the_repository->objects->odb; odb; odb = odb->next) {
+			child_process_init(&midx_verify);
+			midx_verify.argv = midx_argv;
+			midx_verify.git_cmd = 1;
+			midx_argv[2] = "--object-dir";
+			midx_argv[3] = odb->path;
+			if (run_command(&midx_verify))
+				errors_found |= ERROR_COMMIT_GRAPH;
+		}
+	}
+
+	return errors_found;
+}
diff --git a/builtin/gc.c b/builtin/gc.c
new file mode 100644
index 0000000000..c18efadda5
--- /dev/null
+++ b/builtin/gc.c
@@ -0,0 +1,702 @@
+/*
+ * git gc builtin command
+ *
+ * Cleanup unreachable files and optimize the repository.
+ *
+ * Copyright (c) 2007 James Bowes
+ *
+ * Based on git-gc.sh, which is
+ *
+ * Copyright (c) 2006 Shawn O. Pearce
+ */
+
+#include "builtin.h"
+#include "repository.h"
+#include "config.h"
+#include "tempfile.h"
+#include "lockfile.h"
+#include "parse-options.h"
+#include "run-command.h"
+#include "sigchain.h"
+#include "argv-array.h"
+#include "commit.h"
+#include "commit-graph.h"
+#include "packfile.h"
+#include "object-store.h"
+#include "pack.h"
+#include "pack-objects.h"
+#include "blob.h"
+#include "tree.h"
+
+#define FAILED_RUN "failed to run %s"
+
+static const char * const builtin_gc_usage[] = {
+	N_("git gc [<options>]"),
+	NULL
+};
+
+static int pack_refs = 1;
+static int prune_reflogs = 1;
+static int aggressive_depth = 50;
+static int aggressive_window = 250;
+static int gc_auto_threshold = 6700;
+static int gc_auto_pack_limit = 50;
+static int gc_write_commit_graph;
+static int detach_auto = 1;
+static timestamp_t gc_log_expire_time;
+static const char *gc_log_expire = "1.day.ago";
+static const char *prune_expire = "2.weeks.ago";
+static const char *prune_worktrees_expire = "3.months.ago";
+static unsigned long big_pack_threshold;
+static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE;
+
+static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT;
+static struct argv_array reflog = ARGV_ARRAY_INIT;
+static struct argv_array repack = ARGV_ARRAY_INIT;
+static struct argv_array prune = ARGV_ARRAY_INIT;
+static struct argv_array prune_worktrees = ARGV_ARRAY_INIT;
+static struct argv_array rerere = ARGV_ARRAY_INIT;
+
+static struct tempfile *pidfile;
+static struct lock_file log_lock;
+
+static struct string_list pack_garbage = STRING_LIST_INIT_DUP;
+
+static void clean_pack_garbage(void)
+{
+	int i;
+	for (i = 0; i < pack_garbage.nr; i++)
+		unlink_or_warn(pack_garbage.items[i].string);
+	string_list_clear(&pack_garbage, 0);
+}
+
+static void report_pack_garbage(unsigned seen_bits, const char *path)
+{
+	if (seen_bits == PACKDIR_FILE_IDX)
+		string_list_append(&pack_garbage, path);
+}
+
+static void process_log_file(void)
+{
+	struct stat st;
+	if (fstat(get_lock_file_fd(&log_lock), &st)) {
+		/*
+		 * Perhaps there was an i/o error or another
+		 * unlikely situation.  Try to make a note of
+		 * this in gc.log along with any existing
+		 * messages.
+		 */
+		int saved_errno = errno;
+		fprintf(stderr, _("Failed to fstat %s: %s"),
+			get_tempfile_path(log_lock.tempfile),
+			strerror(saved_errno));
+		fflush(stderr);
+		commit_lock_file(&log_lock);
+		errno = saved_errno;
+	} else if (st.st_size) {
+		/* There was some error recorded in the lock file */
+		commit_lock_file(&log_lock);
+	} else {
+		/* No error, clean up any old gc.log */
+		unlink(git_path("gc.log"));
+		rollback_lock_file(&log_lock);
+	}
+}
+
+static void process_log_file_at_exit(void)
+{
+	fflush(stderr);
+	process_log_file();
+}
+
+static void process_log_file_on_signal(int signo)
+{
+	process_log_file();
+	sigchain_pop(signo);
+	raise(signo);
+}
+
+static int gc_config_is_timestamp_never(const char *var)
+{
+	const char *value;
+	timestamp_t expire;
+
+	if (!git_config_get_value(var, &value) && value) {
+		if (parse_expiry_date(value, &expire))
+			die(_("failed to parse '%s' value '%s'"), var, value);
+		return expire == 0;
+	}
+	return 0;
+}
+
+static void gc_config(void)
+{
+	const char *value;
+
+	if (!git_config_get_value("gc.packrefs", &value)) {
+		if (value && !strcmp(value, "notbare"))
+			pack_refs = -1;
+		else
+			pack_refs = git_config_bool("gc.packrefs", value);
+	}
+
+	if (gc_config_is_timestamp_never("gc.reflogexpire") &&
+	    gc_config_is_timestamp_never("gc.reflogexpireunreachable"))
+		prune_reflogs = 0;
+
+	git_config_get_int("gc.aggressivewindow", &aggressive_window);
+	git_config_get_int("gc.aggressivedepth", &aggressive_depth);
+	git_config_get_int("gc.auto", &gc_auto_threshold);
+	git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
+	git_config_get_bool("gc.writecommitgraph", &gc_write_commit_graph);
+	git_config_get_bool("gc.autodetach", &detach_auto);
+	git_config_get_expiry("gc.pruneexpire", &prune_expire);
+	git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
+	git_config_get_expiry("gc.logexpiry", &gc_log_expire);
+
+	git_config_get_ulong("gc.bigpackthreshold", &big_pack_threshold);
+	git_config_get_ulong("pack.deltacachesize", &max_delta_cache_size);
+
+	git_config(git_default_config, NULL);
+}
+
+static int too_many_loose_objects(void)
+{
+	/*
+	 * Quickly check if a "gc" is needed, by estimating how
+	 * many loose objects there are.  Because SHA-1 is evenly
+	 * distributed, we can check only one and get a reasonable
+	 * estimate.
+	 */
+	DIR *dir;
+	struct dirent *ent;
+	int auto_threshold;
+	int num_loose = 0;
+	int needed = 0;
+	const unsigned hexsz_loose = the_hash_algo->hexsz - 2;
+
+	dir = opendir(git_path("objects/17"));
+	if (!dir)
+		return 0;
+
+	auto_threshold = DIV_ROUND_UP(gc_auto_threshold, 256);
+	while ((ent = readdir(dir)) != NULL) {
+		if (strspn(ent->d_name, "0123456789abcdef") != hexsz_loose ||
+		    ent->d_name[hexsz_loose] != '\0')
+			continue;
+		if (++num_loose > auto_threshold) {
+			needed = 1;
+			break;
+		}
+	}
+	closedir(dir);
+	return needed;
+}
+
+static struct packed_git *find_base_packs(struct string_list *packs,
+					  unsigned long limit)
+{
+	struct packed_git *p, *base = NULL;
+
+	for (p = get_all_packs(the_repository); p; p = p->next) {
+		if (!p->pack_local)
+			continue;
+		if (limit) {
+			if (p->pack_size >= limit)
+				string_list_append(packs, p->pack_name);
+		} else if (!base || base->pack_size < p->pack_size) {
+			base = p;
+		}
+	}
+
+	if (base)
+		string_list_append(packs, base->pack_name);
+
+	return base;
+}
+
+static int too_many_packs(void)
+{
+	struct packed_git *p;
+	int cnt;
+
+	if (gc_auto_pack_limit <= 0)
+		return 0;
+
+	for (cnt = 0, p = get_all_packs(the_repository); p; p = p->next) {
+		if (!p->pack_local)
+			continue;
+		if (p->pack_keep)
+			continue;
+		/*
+		 * Perhaps check the size of the pack and count only
+		 * very small ones here?
+		 */
+		cnt++;
+	}
+	return gc_auto_pack_limit < cnt;
+}
+
+static uint64_t total_ram(void)
+{
+#if defined(HAVE_SYSINFO)
+	struct sysinfo si;
+
+	if (!sysinfo(&si))
+		return si.totalram;
+#elif defined(HAVE_BSD_SYSCTL) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM))
+	int64_t physical_memory;
+	int mib[2];
+	size_t length;
+
+	mib[0] = CTL_HW;
+# if defined(HW_MEMSIZE)
+	mib[1] = HW_MEMSIZE;
+# else
+	mib[1] = HW_PHYSMEM;
+# endif
+	length = sizeof(int64_t);
+	if (!sysctl(mib, 2, &physical_memory, &length, NULL, 0))
+		return physical_memory;
+#elif defined(GIT_WINDOWS_NATIVE)
+	MEMORYSTATUSEX memInfo;
+
+	memInfo.dwLength = sizeof(MEMORYSTATUSEX);
+	if (GlobalMemoryStatusEx(&memInfo))
+		return memInfo.ullTotalPhys;
+#endif
+	return 0;
+}
+
+static uint64_t estimate_repack_memory(struct packed_git *pack)
+{
+	unsigned long nr_objects = approximate_object_count();
+	size_t os_cache, heap;
+
+	if (!pack || !nr_objects)
+		return 0;
+
+	/*
+	 * First we have to scan through at least one pack.
+	 * Assume enough room in OS file cache to keep the entire pack
+	 * or we may accidentally evict data of other processes from
+	 * the cache.
+	 */
+	os_cache = pack->pack_size + pack->index_size;
+	/* then pack-objects needs lots more for book keeping */
+	heap = sizeof(struct object_entry) * nr_objects;
+	/*
+	 * internal rev-list --all --objects takes up some memory too,
+	 * let's say half of it is for blobs
+	 */
+	heap += sizeof(struct blob) * nr_objects / 2;
+	/*
+	 * and the other half is for trees (commits and tags are
+	 * usually insignificant)
+	 */
+	heap += sizeof(struct tree) * nr_objects / 2;
+	/* and then obj_hash[], underestimated in fact */
+	heap += sizeof(struct object *) * nr_objects;
+	/* revindex is used also */
+	heap += sizeof(struct revindex_entry) * nr_objects;
+	/*
+	 * read_sha1_file() (either at delta calculation phase, or
+	 * writing phase) also fills up the delta base cache
+	 */
+	heap += delta_base_cache_limit;
+	/* and of course pack-objects has its own delta cache */
+	heap += max_delta_cache_size;
+
+	return os_cache + heap;
+}
+
+static int keep_one_pack(struct string_list_item *item, void *data)
+{
+	argv_array_pushf(&repack, "--keep-pack=%s", basename(item->string));
+	return 0;
+}
+
+static void add_repack_all_option(struct string_list *keep_pack)
+{
+	if (prune_expire && !strcmp(prune_expire, "now"))
+		argv_array_push(&repack, "-a");
+	else {
+		argv_array_push(&repack, "-A");
+		if (prune_expire)
+			argv_array_pushf(&repack, "--unpack-unreachable=%s", prune_expire);
+	}
+
+	if (keep_pack)
+		for_each_string_list(keep_pack, keep_one_pack, NULL);
+}
+
+static void add_repack_incremental_option(void)
+{
+	argv_array_push(&repack, "--no-write-bitmap-index");
+}
+
+static int need_to_gc(void)
+{
+	/*
+	 * Setting gc.auto to 0 or negative can disable the
+	 * automatic gc.
+	 */
+	if (gc_auto_threshold <= 0)
+		return 0;
+
+	/*
+	 * If there are too many loose objects, but not too many
+	 * packs, we run "repack -d -l".  If there are too many packs,
+	 * we run "repack -A -d -l".  Otherwise we tell the caller
+	 * there is no need.
+	 */
+	if (too_many_packs()) {
+		struct string_list keep_pack = STRING_LIST_INIT_NODUP;
+
+		if (big_pack_threshold) {
+			find_base_packs(&keep_pack, big_pack_threshold);
+			if (keep_pack.nr >= gc_auto_pack_limit) {
+				big_pack_threshold = 0;
+				string_list_clear(&keep_pack, 0);
+				find_base_packs(&keep_pack, 0);
+			}
+		} else {
+			struct packed_git *p = find_base_packs(&keep_pack, 0);
+			uint64_t mem_have, mem_want;
+
+			mem_have = total_ram();
+			mem_want = estimate_repack_memory(p);
+
+			/*
+			 * Only allow 1/2 of memory for pack-objects, leave
+			 * the rest for the OS and other processes in the
+			 * system.
+			 */
+			if (!mem_have || mem_want < mem_have / 2)
+				string_list_clear(&keep_pack, 0);
+		}
+
+		add_repack_all_option(&keep_pack);
+		string_list_clear(&keep_pack, 0);
+	} else if (too_many_loose_objects())
+		add_repack_incremental_option();
+	else
+		return 0;
+
+	if (run_hook_le(NULL, "pre-auto-gc", NULL))
+		return 0;
+	return 1;
+}
+
+/* return NULL on success, else hostname running the gc */
+static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
+{
+	struct lock_file lock = LOCK_INIT;
+	char my_host[HOST_NAME_MAX + 1];
+	struct strbuf sb = STRBUF_INIT;
+	struct stat st;
+	uintmax_t pid;
+	FILE *fp;
+	int fd;
+	char *pidfile_path;
+
+	if (is_tempfile_active(pidfile))
+		/* already locked */
+		return NULL;
+
+	if (xgethostname(my_host, sizeof(my_host)))
+		xsnprintf(my_host, sizeof(my_host), "unknown");
+
+	pidfile_path = git_pathdup("gc.pid");
+	fd = hold_lock_file_for_update(&lock, pidfile_path,
+				       LOCK_DIE_ON_ERROR);
+	if (!force) {
+		static char locking_host[HOST_NAME_MAX + 1];
+		static char *scan_fmt;
+		int should_exit;
+
+		if (!scan_fmt)
+			scan_fmt = xstrfmt("%s %%%ds", "%"SCNuMAX, HOST_NAME_MAX);
+		fp = fopen(pidfile_path, "r");
+		memset(locking_host, 0, sizeof(locking_host));
+		should_exit =
+			fp != NULL &&
+			!fstat(fileno(fp), &st) &&
+			/*
+			 * 12 hour limit is very generous as gc should
+			 * never take that long. On the other hand we
+			 * don't really need a strict limit here,
+			 * running gc --auto one day late is not a big
+			 * problem. --force can be used in manual gc
+			 * after the user verifies that no gc is
+			 * running.
+			 */
+			time(NULL) - st.st_mtime <= 12 * 3600 &&
+			fscanf(fp, scan_fmt, &pid, locking_host) == 2 &&
+			/* be gentle to concurrent "gc" on remote hosts */
+			(strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM);
+		if (fp != NULL)
+			fclose(fp);
+		if (should_exit) {
+			if (fd >= 0)
+				rollback_lock_file(&lock);
+			*ret_pid = pid;
+			free(pidfile_path);
+			return locking_host;
+		}
+	}
+
+	strbuf_addf(&sb, "%"PRIuMAX" %s",
+		    (uintmax_t) getpid(), my_host);
+	write_in_full(fd, sb.buf, sb.len);
+	strbuf_release(&sb);
+	commit_lock_file(&lock);
+	pidfile = register_tempfile(pidfile_path);
+	free(pidfile_path);
+	return NULL;
+}
+
+/*
+ * Returns 0 if there was no previous error and gc can proceed, 1 if
+ * gc should not proceed due to an error in the last run. Prints a
+ * message and returns -1 if an error occured while reading gc.log
+ */
+static int report_last_gc_error(void)
+{
+	struct strbuf sb = STRBUF_INIT;
+	int ret = 0;
+	ssize_t len;
+	struct stat st;
+	char *gc_log_path = git_pathdup("gc.log");
+
+	if (stat(gc_log_path, &st)) {
+		if (errno == ENOENT)
+			goto done;
+
+		ret = error_errno(_("cannot stat '%s'"), gc_log_path);
+		goto done;
+	}
+
+	if (st.st_mtime < gc_log_expire_time)
+		goto done;
+
+	len = strbuf_read_file(&sb, gc_log_path, 0);
+	if (len < 0)
+		ret = error_errno(_("cannot read '%s'"), gc_log_path);
+	else if (len > 0) {
+		/*
+		 * A previous gc failed.  Report the error, and don't
+		 * bother with an automatic gc run since it is likely
+		 * to fail in the same way.
+		 */
+		warning(_("The last gc run reported the following. "
+			       "Please correct the root cause\n"
+			       "and remove %s.\n"
+			       "Automatic cleanup will not be performed "
+			       "until the file is removed.\n\n"
+			       "%s"),
+			    gc_log_path, sb.buf);
+		ret = 1;
+	}
+	strbuf_release(&sb);
+done:
+	free(gc_log_path);
+	return ret;
+}
+
+static void gc_before_repack(void)
+{
+	/*
+	 * We may be called twice, as both the pre- and
+	 * post-daemonized phases will call us, but running these
+	 * commands more than once is pointless and wasteful.
+	 */
+	static int done = 0;
+	if (done++)
+		return;
+
+	if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
+		die(FAILED_RUN, pack_refs_cmd.argv[0]);
+
+	if (prune_reflogs && run_command_v_opt(reflog.argv, RUN_GIT_CMD))
+		die(FAILED_RUN, reflog.argv[0]);
+}
+
+int cmd_gc(int argc, const char **argv, const char *prefix)
+{
+	int aggressive = 0;
+	int auto_gc = 0;
+	int quiet = 0;
+	int force = 0;
+	const char *name;
+	pid_t pid;
+	int daemonized = 0;
+	int keep_base_pack = -1;
+	timestamp_t dummy;
+
+	struct option builtin_gc_options[] = {
+		OPT__QUIET(&quiet, N_("suppress progress reporting")),
+		{ OPTION_STRING, 0, "prune", &prune_expire, N_("date"),
+			N_("prune unreferenced objects"),
+			PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
+		OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
+		OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL_F(0, "force", &force,
+			   N_("force running gc even if there may be another gc running"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL(0, "keep-largest-pack", &keep_base_pack,
+			 N_("repack all other packs except the largest pack")),
+		OPT_END()
+	};
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(builtin_gc_usage, builtin_gc_options);
+
+	argv_array_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
+	argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
+	argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
+	argv_array_pushl(&prune, "prune", "--expire", NULL);
+	argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
+	argv_array_pushl(&rerere, "rerere", "gc", NULL);
+
+	/* default expiry time, overwritten in gc_config */
+	gc_config();
+	if (parse_expiry_date(gc_log_expire, &gc_log_expire_time))
+		die(_("failed to parse gc.logexpiry value %s"), gc_log_expire);
+
+	if (pack_refs < 0)
+		pack_refs = !is_bare_repository();
+
+	argc = parse_options(argc, argv, prefix, builtin_gc_options,
+			     builtin_gc_usage, 0);
+	if (argc > 0)
+		usage_with_options(builtin_gc_usage, builtin_gc_options);
+
+	if (prune_expire && parse_expiry_date(prune_expire, &dummy))
+		die(_("failed to parse prune expiry value %s"), prune_expire);
+
+	if (aggressive) {
+		argv_array_push(&repack, "-f");
+		if (aggressive_depth > 0)
+			argv_array_pushf(&repack, "--depth=%d", aggressive_depth);
+		if (aggressive_window > 0)
+			argv_array_pushf(&repack, "--window=%d", aggressive_window);
+	}
+	if (quiet)
+		argv_array_push(&repack, "-q");
+
+	if (auto_gc) {
+		/*
+		 * Auto-gc should be least intrusive as possible.
+		 */
+		if (!need_to_gc())
+			return 0;
+		if (!quiet) {
+			if (detach_auto)
+				fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n"));
+			else
+				fprintf(stderr, _("Auto packing the repository for optimum performance.\n"));
+			fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
+		}
+		if (detach_auto) {
+			int ret = report_last_gc_error();
+			if (ret < 0)
+				/* an I/O error occured, already reported */
+				exit(128);
+			if (ret == 1)
+				/* Last gc --auto failed. Skip this one. */
+				return 0;
+
+			if (lock_repo_for_gc(force, &pid))
+				return 0;
+			gc_before_repack(); /* dies on failure */
+			delete_tempfile(&pidfile);
+
+			/*
+			 * failure to daemonize is ok, we'll continue
+			 * in foreground
+			 */
+			daemonized = !daemonize();
+		}
+	} else {
+		struct string_list keep_pack = STRING_LIST_INIT_NODUP;
+
+		if (keep_base_pack != -1) {
+			if (keep_base_pack)
+				find_base_packs(&keep_pack, 0);
+		} else if (big_pack_threshold) {
+			find_base_packs(&keep_pack, big_pack_threshold);
+		}
+
+		add_repack_all_option(&keep_pack);
+		string_list_clear(&keep_pack, 0);
+	}
+
+	name = lock_repo_for_gc(force, &pid);
+	if (name) {
+		if (auto_gc)
+			return 0; /* be quiet on --auto */
+		die(_("gc is already running on machine '%s' pid %"PRIuMAX" (use --force if not)"),
+		    name, (uintmax_t)pid);
+	}
+
+	if (daemonized) {
+		hold_lock_file_for_update(&log_lock,
+					  git_path("gc.log"),
+					  LOCK_DIE_ON_ERROR);
+		dup2(get_lock_file_fd(&log_lock), 2);
+		sigchain_push_common(process_log_file_on_signal);
+		atexit(process_log_file_at_exit);
+	}
+
+	gc_before_repack();
+
+	if (!repository_format_precious_objects) {
+		close_object_store(the_repository->objects);
+		if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
+			die(FAILED_RUN, repack.argv[0]);
+
+		if (prune_expire) {
+			argv_array_push(&prune, prune_expire);
+			if (quiet)
+				argv_array_push(&prune, "--no-progress");
+			if (repository_format_partial_clone)
+				argv_array_push(&prune,
+						"--exclude-promisor-objects");
+			if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
+				die(FAILED_RUN, prune.argv[0]);
+		}
+	}
+
+	if (prune_worktrees_expire) {
+		argv_array_push(&prune_worktrees, prune_worktrees_expire);
+		if (run_command_v_opt(prune_worktrees.argv, RUN_GIT_CMD))
+			die(FAILED_RUN, prune_worktrees.argv[0]);
+	}
+
+	if (run_command_v_opt(rerere.argv, RUN_GIT_CMD))
+		die(FAILED_RUN, rerere.argv[0]);
+
+	report_garbage = report_pack_garbage;
+	reprepare_packed_git(the_repository);
+	if (pack_garbage.nr > 0) {
+		close_object_store(the_repository->objects);
+		clean_pack_garbage();
+	}
+
+	if (gc_write_commit_graph &&
+	    write_commit_graph_reachable(get_object_directory(),
+					 !quiet && !daemonized ? COMMIT_GRAPH_PROGRESS : 0,
+					 NULL))
+		return 1;
+
+	if (auto_gc && too_many_loose_objects())
+		warning(_("There are too many unreachable loose objects; "
+			"run 'git prune' to remove them."));
+
+	if (!daemonized)
+		unlink(git_path("gc.log"));
+
+	return 0;
+}
diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c
new file mode 100644
index 0000000000..491af9202d
--- /dev/null
+++ b/builtin/get-tar-commit-id.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2005, 2006 Rene Scharfe
+ */
+#include "cache.h"
+#include "commit.h"
+#include "tar.h"
+#include "builtin.h"
+#include "quote.h"
+
+static const char builtin_get_tar_commit_id_usage[] =
+"git get-tar-commit-id";
+
+/* ustar header + extended global header content */
+#define RECORDSIZE	(512)
+#define HEADERSIZE (2 * RECORDSIZE)
+
+int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
+{
+	char buffer[HEADERSIZE];
+	struct ustar_header *header = (struct ustar_header *)buffer;
+	char *content = buffer + RECORDSIZE;
+	const char *comment;
+	ssize_t n;
+	long len;
+	char *end;
+
+	if (argc != 1)
+		usage(builtin_get_tar_commit_id_usage);
+
+	n = read_in_full(0, buffer, HEADERSIZE);
+	if (n < 0)
+		die_errno("git get-tar-commit-id: read error");
+	if (n != HEADERSIZE)
+		die_errno("git get-tar-commit-id: EOF before reading tar header");
+	if (header->typeflag[0] != 'g')
+		return 1;
+
+	len = strtol(content, &end, 10);
+	if (errno == ERANGE || end == content || len < 0)
+		return 1;
+	if (!skip_prefix(end, " comment=", &comment))
+		return 1;
+	len -= comment - content;
+	if (len < 1 || !(len % 2) ||
+	    hash_algo_by_length((len - 1) / 2) == GIT_HASH_UNKNOWN)
+		return 1;
+
+	if (write_in_full(1, comment, len) < 0)
+		die_errno("git get-tar-commit-id: write error");
+
+	return 0;
+}
diff --git a/builtin/grep.c b/builtin/grep.c
new file mode 100644
index 0000000000..560051784e
--- /dev/null
+++ b/builtin/grep.c
@@ -0,0 +1,1149 @@
+/*
+ * Builtin "git grep"
+ *
+ * Copyright (c) 2006 Junio C Hamano
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "repository.h"
+#include "config.h"
+#include "blob.h"
+#include "tree.h"
+#include "commit.h"
+#include "tag.h"
+#include "tree-walk.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "run-command.h"
+#include "userdiff.h"
+#include "grep.h"
+#include "quote.h"
+#include "dir.h"
+#include "pathspec.h"
+#include "submodule.h"
+#include "submodule-config.h"
+#include "object-store.h"
+
+static char const * const grep_usage[] = {
+	N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"),
+	NULL
+};
+
+static int recurse_submodules;
+
+#define GREP_NUM_THREADS_DEFAULT 8
+static int num_threads;
+
+static pthread_t *threads;
+
+/* We use one producer thread and THREADS consumer
+ * threads. The producer adds struct work_items to 'todo' and the
+ * consumers pick work items from the same array.
+ */
+struct work_item {
+	struct grep_source source;
+	char done;
+	struct strbuf out;
+};
+
+/* In the range [todo_done, todo_start) in 'todo' we have work_items
+ * that have been or are processed by a consumer thread. We haven't
+ * written the result for these to stdout yet.
+ *
+ * The work_items in [todo_start, todo_end) are waiting to be picked
+ * up by a consumer thread.
+ *
+ * The ranges are modulo TODO_SIZE.
+ */
+#define TODO_SIZE 128
+static struct work_item todo[TODO_SIZE];
+static int todo_start;
+static int todo_end;
+static int todo_done;
+
+/* Has all work items been added? */
+static int all_work_added;
+
+/* This lock protects all the variables above. */
+static pthread_mutex_t grep_mutex;
+
+static inline void grep_lock(void)
+{
+	pthread_mutex_lock(&grep_mutex);
+}
+
+static inline void grep_unlock(void)
+{
+	pthread_mutex_unlock(&grep_mutex);
+}
+
+/* Signalled when a new work_item is added to todo. */
+static pthread_cond_t cond_add;
+
+/* Signalled when the result from one work_item is written to
+ * stdout.
+ */
+static pthread_cond_t cond_write;
+
+/* Signalled when we are finished with everything. */
+static pthread_cond_t cond_result;
+
+static int skip_first_line;
+
+static void add_work(struct grep_opt *opt, const struct grep_source *gs)
+{
+	grep_lock();
+
+	while ((todo_end+1) % ARRAY_SIZE(todo) == todo_done) {
+		pthread_cond_wait(&cond_write, &grep_mutex);
+	}
+
+	todo[todo_end].source = *gs;
+	if (opt->binary != GREP_BINARY_TEXT)
+		grep_source_load_driver(&todo[todo_end].source,
+					opt->repo->index);
+	todo[todo_end].done = 0;
+	strbuf_reset(&todo[todo_end].out);
+	todo_end = (todo_end + 1) % ARRAY_SIZE(todo);
+
+	pthread_cond_signal(&cond_add);
+	grep_unlock();
+}
+
+static struct work_item *get_work(void)
+{
+	struct work_item *ret;
+
+	grep_lock();
+	while (todo_start == todo_end && !all_work_added) {
+		pthread_cond_wait(&cond_add, &grep_mutex);
+	}
+
+	if (todo_start == todo_end && all_work_added) {
+		ret = NULL;
+	} else {
+		ret = &todo[todo_start];
+		todo_start = (todo_start + 1) % ARRAY_SIZE(todo);
+	}
+	grep_unlock();
+	return ret;
+}
+
+static void work_done(struct work_item *w)
+{
+	int old_done;
+
+	grep_lock();
+	w->done = 1;
+	old_done = todo_done;
+	for(; todo[todo_done].done && todo_done != todo_start;
+	    todo_done = (todo_done+1) % ARRAY_SIZE(todo)) {
+		w = &todo[todo_done];
+		if (w->out.len) {
+			const char *p = w->out.buf;
+			size_t len = w->out.len;
+
+			/* Skip the leading hunk mark of the first file. */
+			if (skip_first_line) {
+				while (len) {
+					len--;
+					if (*p++ == '\n')
+						break;
+				}
+				skip_first_line = 0;
+			}
+
+			write_or_die(1, p, len);
+		}
+		grep_source_clear(&w->source);
+	}
+
+	if (old_done != todo_done)
+		pthread_cond_signal(&cond_write);
+
+	if (all_work_added && todo_done == todo_end)
+		pthread_cond_signal(&cond_result);
+
+	grep_unlock();
+}
+
+static void *run(void *arg)
+{
+	int hit = 0;
+	struct grep_opt *opt = arg;
+
+	while (1) {
+		struct work_item *w = get_work();
+		if (!w)
+			break;
+
+		opt->output_priv = w;
+		hit |= grep_source(opt, &w->source);
+		grep_source_clear_data(&w->source);
+		work_done(w);
+	}
+	free_grep_patterns(arg);
+	free(arg);
+
+	return (void*) (intptr_t) hit;
+}
+
+static void strbuf_out(struct grep_opt *opt, const void *buf, size_t size)
+{
+	struct work_item *w = opt->output_priv;
+	strbuf_add(&w->out, buf, size);
+}
+
+static void start_threads(struct grep_opt *opt)
+{
+	int i;
+
+	pthread_mutex_init(&grep_mutex, NULL);
+	pthread_mutex_init(&grep_read_mutex, NULL);
+	pthread_mutex_init(&grep_attr_mutex, NULL);
+	pthread_cond_init(&cond_add, NULL);
+	pthread_cond_init(&cond_write, NULL);
+	pthread_cond_init(&cond_result, NULL);
+	grep_use_locks = 1;
+
+	for (i = 0; i < ARRAY_SIZE(todo); i++) {
+		strbuf_init(&todo[i].out, 0);
+	}
+
+	threads = xcalloc(num_threads, sizeof(*threads));
+	for (i = 0; i < num_threads; i++) {
+		int err;
+		struct grep_opt *o = grep_opt_dup(opt);
+		o->output = strbuf_out;
+		if (i)
+			o->debug = 0;
+		compile_grep_patterns(o);
+		err = pthread_create(&threads[i], NULL, run, o);
+
+		if (err)
+			die(_("grep: failed to create thread: %s"),
+			    strerror(err));
+	}
+}
+
+static int wait_all(void)
+{
+	int hit = 0;
+	int i;
+
+	if (!HAVE_THREADS)
+		BUG("Never call this function unless you have started threads");
+
+	grep_lock();
+	all_work_added = 1;
+
+	/* Wait until all work is done. */
+	while (todo_done != todo_end)
+		pthread_cond_wait(&cond_result, &grep_mutex);
+
+	/* Wake up all the consumer threads so they can see that there
+	 * is no more work to do.
+	 */
+	pthread_cond_broadcast(&cond_add);
+	grep_unlock();
+
+	for (i = 0; i < num_threads; i++) {
+		void *h;
+		pthread_join(threads[i], &h);
+		hit |= (int) (intptr_t) h;
+	}
+
+	free(threads);
+
+	pthread_mutex_destroy(&grep_mutex);
+	pthread_mutex_destroy(&grep_read_mutex);
+	pthread_mutex_destroy(&grep_attr_mutex);
+	pthread_cond_destroy(&cond_add);
+	pthread_cond_destroy(&cond_write);
+	pthread_cond_destroy(&cond_result);
+	grep_use_locks = 0;
+
+	return hit;
+}
+
+static int grep_cmd_config(const char *var, const char *value, void *cb)
+{
+	int st = grep_config(var, value, cb);
+	if (git_color_default_config(var, value, cb) < 0)
+		st = -1;
+
+	if (!strcmp(var, "grep.threads")) {
+		num_threads = git_config_int(var, value);
+		if (num_threads < 0)
+			die(_("invalid number of threads specified (%d) for %s"),
+			    num_threads, var);
+		else if (!HAVE_THREADS && num_threads > 1) {
+			/*
+			 * TRANSLATORS: %s is the configuration
+			 * variable for tweaking threads, currently
+			 * grep.threads
+			 */
+			warning(_("no threads support, ignoring %s"), var);
+			num_threads = 1;
+		}
+	}
+
+	if (!strcmp(var, "submodule.recurse"))
+		recurse_submodules = git_config_bool(var, value);
+
+	return st;
+}
+
+static void *lock_and_read_oid_file(const struct object_id *oid, enum object_type *type, unsigned long *size)
+{
+	void *data;
+
+	grep_read_lock();
+	data = read_object_file(oid, type, size);
+	grep_read_unlock();
+	return data;
+}
+
+static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
+		     const char *filename, int tree_name_len,
+		     const char *path)
+{
+	struct strbuf pathbuf = STRBUF_INIT;
+	struct grep_source gs;
+
+	if (opt->relative && opt->prefix_length) {
+		quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
+		strbuf_insert(&pathbuf, 0, filename, tree_name_len);
+	} else {
+		strbuf_addstr(&pathbuf, filename);
+	}
+
+	grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid);
+	strbuf_release(&pathbuf);
+
+	if (num_threads > 1) {
+		/*
+		 * add_work() copies gs and thus assumes ownership of
+		 * its fields, so do not call grep_source_clear()
+		 */
+		add_work(opt, &gs);
+		return 0;
+	} else {
+		int hit;
+
+		hit = grep_source(opt, &gs);
+
+		grep_source_clear(&gs);
+		return hit;
+	}
+}
+
+static int grep_file(struct grep_opt *opt, const char *filename)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct grep_source gs;
+
+	if (opt->relative && opt->prefix_length)
+		quote_path_relative(filename, opt->prefix, &buf);
+	else
+		strbuf_addstr(&buf, filename);
+
+	grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename);
+	strbuf_release(&buf);
+
+	if (num_threads > 1) {
+		/*
+		 * add_work() copies gs and thus assumes ownership of
+		 * its fields, so do not call grep_source_clear()
+		 */
+		add_work(opt, &gs);
+		return 0;
+	} else {
+		int hit;
+
+		hit = grep_source(opt, &gs);
+
+		grep_source_clear(&gs);
+		return hit;
+	}
+}
+
+static void append_path(struct grep_opt *opt, const void *data, size_t len)
+{
+	struct string_list *path_list = opt->output_priv;
+
+	if (len == 1 && *(const char *)data == '\0')
+		return;
+	string_list_append(path_list, xstrndup(data, len));
+}
+
+static void run_pager(struct grep_opt *opt, const char *prefix)
+{
+	struct string_list *path_list = opt->output_priv;
+	struct child_process child = CHILD_PROCESS_INIT;
+	int i, status;
+
+	for (i = 0; i < path_list->nr; i++)
+		argv_array_push(&child.args, path_list->items[i].string);
+	child.dir = prefix;
+	child.use_shell = 1;
+
+	status = run_command(&child);
+	if (status)
+		exit(status);
+}
+
+static int grep_cache(struct grep_opt *opt,
+		      const struct pathspec *pathspec, int cached);
+static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
+		     struct tree_desc *tree, struct strbuf *base, int tn_len,
+		     int check_attr);
+
+static int grep_submodule(struct grep_opt *opt,
+			  const struct pathspec *pathspec,
+			  const struct object_id *oid,
+			  const char *filename, const char *path)
+{
+	struct repository subrepo;
+	struct repository *superproject = opt->repo;
+	const struct submodule *sub = submodule_from_path(superproject,
+							  &null_oid, path);
+	struct grep_opt subopt;
+	int hit;
+
+	/*
+	 * NEEDSWORK: submodules functions need to be protected because they
+	 * access the object store via config_from_gitmodules(): the latter
+	 * uses get_oid() which, for now, relies on the global the_repository
+	 * object.
+	 */
+	grep_read_lock();
+
+	if (!is_submodule_active(superproject, path)) {
+		grep_read_unlock();
+		return 0;
+	}
+
+	if (repo_submodule_init(&subrepo, superproject, sub)) {
+		grep_read_unlock();
+		return 0;
+	}
+
+	repo_read_gitmodules(&subrepo);
+
+	/*
+	 * NEEDSWORK: This adds the submodule's object directory to the list of
+	 * alternates for the single in-memory object store.  This has some bad
+	 * consequences for memory (processed objects will never be freed) and
+	 * performance (this increases the number of pack files git has to pay
+	 * attention to, to the sum of the number of pack files in all the
+	 * repositories processed so far).  This can be removed once the object
+	 * store is no longer global and instead is a member of the repository
+	 * object.
+	 */
+	add_to_alternates_memory(subrepo.objects->odb->path);
+	grep_read_unlock();
+
+	memcpy(&subopt, opt, sizeof(subopt));
+	subopt.repo = &subrepo;
+
+	if (oid) {
+		struct object *object;
+		struct tree_desc tree;
+		void *data;
+		unsigned long size;
+		struct strbuf base = STRBUF_INIT;
+
+		object = parse_object_or_die(oid, oid_to_hex(oid));
+
+		grep_read_lock();
+		data = read_object_with_reference(&subrepo,
+						  &object->oid, tree_type,
+						  &size, NULL);
+		grep_read_unlock();
+
+		if (!data)
+			die(_("unable to read tree (%s)"), oid_to_hex(&object->oid));
+
+		strbuf_addstr(&base, filename);
+		strbuf_addch(&base, '/');
+
+		init_tree_desc(&tree, data, size);
+		hit = grep_tree(&subopt, pathspec, &tree, &base, base.len,
+				object->type == OBJ_COMMIT);
+		strbuf_release(&base);
+		free(data);
+	} else {
+		hit = grep_cache(&subopt, pathspec, 1);
+	}
+
+	repo_clear(&subrepo);
+	return hit;
+}
+
+static int grep_cache(struct grep_opt *opt,
+		      const struct pathspec *pathspec, int cached)
+{
+	struct repository *repo = opt->repo;
+	int hit = 0;
+	int nr;
+	struct strbuf name = STRBUF_INIT;
+	int name_base_len = 0;
+	if (repo->submodule_prefix) {
+		name_base_len = strlen(repo->submodule_prefix);
+		strbuf_addstr(&name, repo->submodule_prefix);
+	}
+
+	if (repo_read_index(repo) < 0)
+		die(_("index file corrupt"));
+
+	for (nr = 0; nr < repo->index->cache_nr; nr++) {
+		const struct cache_entry *ce = repo->index->cache[nr];
+		strbuf_setlen(&name, name_base_len);
+		strbuf_addstr(&name, ce->name);
+
+		if (S_ISREG(ce->ce_mode) &&
+		    match_pathspec(repo->index, pathspec, name.buf, name.len, 0, NULL,
+				   S_ISDIR(ce->ce_mode) ||
+				   S_ISGITLINK(ce->ce_mode))) {
+			/*
+			 * If CE_VALID is on, we assume worktree file and its
+			 * cache entry are identical, even if worktree file has
+			 * been modified, so use cache version instead
+			 */
+			if (cached || (ce->ce_flags & CE_VALID) ||
+			    ce_skip_worktree(ce)) {
+				if (ce_stage(ce) || ce_intent_to_add(ce))
+					continue;
+				hit |= grep_oid(opt, &ce->oid, name.buf,
+						 0, name.buf);
+			} else {
+				hit |= grep_file(opt, name.buf);
+			}
+		} else if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
+			   submodule_path_match(repo->index, pathspec, name.buf, NULL)) {
+			hit |= grep_submodule(opt, pathspec, NULL, ce->name, ce->name);
+		} else {
+			continue;
+		}
+
+		if (ce_stage(ce)) {
+			do {
+				nr++;
+			} while (nr < repo->index->cache_nr &&
+				 !strcmp(ce->name, repo->index->cache[nr]->name));
+			nr--; /* compensate for loop control */
+		}
+		if (hit && opt->status_only)
+			break;
+	}
+
+	strbuf_release(&name);
+	return hit;
+}
+
+static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
+		     struct tree_desc *tree, struct strbuf *base, int tn_len,
+		     int check_attr)
+{
+	struct repository *repo = opt->repo;
+	int hit = 0;
+	enum interesting match = entry_not_interesting;
+	struct name_entry entry;
+	int old_baselen = base->len;
+	struct strbuf name = STRBUF_INIT;
+	int name_base_len = 0;
+	if (repo->submodule_prefix) {
+		strbuf_addstr(&name, repo->submodule_prefix);
+		name_base_len = name.len;
+	}
+
+	while (tree_entry(tree, &entry)) {
+		int te_len = tree_entry_len(&entry);
+
+		if (match != all_entries_interesting) {
+			strbuf_addstr(&name, base->buf + tn_len);
+			match = tree_entry_interesting(repo->index,
+						       &entry, &name,
+						       0, pathspec);
+			strbuf_setlen(&name, name_base_len);
+
+			if (match == all_entries_not_interesting)
+				break;
+			if (match == entry_not_interesting)
+				continue;
+		}
+
+		strbuf_add(base, entry.path, te_len);
+
+		if (S_ISREG(entry.mode)) {
+			hit |= grep_oid(opt, &entry.oid, base->buf, tn_len,
+					 check_attr ? base->buf + tn_len : NULL);
+		} else if (S_ISDIR(entry.mode)) {
+			enum object_type type;
+			struct tree_desc sub;
+			void *data;
+			unsigned long size;
+
+			data = lock_and_read_oid_file(&entry.oid, &type, &size);
+			if (!data)
+				die(_("unable to read tree (%s)"),
+				    oid_to_hex(&entry.oid));
+
+			strbuf_addch(base, '/');
+			init_tree_desc(&sub, data, size);
+			hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
+					 check_attr);
+			free(data);
+		} else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
+			hit |= grep_submodule(opt, pathspec, &entry.oid,
+					      base->buf, base->buf + tn_len);
+		}
+
+		strbuf_setlen(base, old_baselen);
+
+		if (hit && opt->status_only)
+			break;
+	}
+
+	strbuf_release(&name);
+	return hit;
+}
+
+static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
+		       struct object *obj, const char *name, const char *path)
+{
+	if (obj->type == OBJ_BLOB)
+		return grep_oid(opt, &obj->oid, name, 0, path);
+	if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
+		struct tree_desc tree;
+		void *data;
+		unsigned long size;
+		struct strbuf base;
+		int hit, len;
+
+		grep_read_lock();
+		data = read_object_with_reference(opt->repo,
+						  &obj->oid, tree_type,
+						  &size, NULL);
+		grep_read_unlock();
+
+		if (!data)
+			die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid));
+
+		len = name ? strlen(name) : 0;
+		strbuf_init(&base, PATH_MAX + len + 1);
+		if (len) {
+			strbuf_add(&base, name, len);
+			strbuf_addch(&base, ':');
+		}
+		init_tree_desc(&tree, data, size);
+		hit = grep_tree(opt, pathspec, &tree, &base, base.len,
+				obj->type == OBJ_COMMIT);
+		strbuf_release(&base);
+		free(data);
+		return hit;
+	}
+	die(_("unable to grep from object of type %s"), type_name(obj->type));
+}
+
+static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
+			const struct object_array *list)
+{
+	unsigned int i;
+	int hit = 0;
+	const unsigned int nr = list->nr;
+
+	for (i = 0; i < nr; i++) {
+		struct object *real_obj;
+		real_obj = deref_tag(opt->repo, list->objects[i].item,
+				     NULL, 0);
+
+		/* load the gitmodules file for this rev */
+		if (recurse_submodules) {
+			submodule_free(opt->repo);
+			gitmodules_config_oid(&real_obj->oid);
+		}
+		if (grep_object(opt, pathspec, real_obj, list->objects[i].name,
+				list->objects[i].path)) {
+			hit = 1;
+			if (opt->status_only)
+				break;
+		}
+	}
+	return hit;
+}
+
+static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
+			  int exc_std, int use_index)
+{
+	struct dir_struct dir;
+	int i, hit = 0;
+
+	memset(&dir, 0, sizeof(dir));
+	if (!use_index)
+		dir.flags |= DIR_NO_GITLINKS;
+	if (exc_std)
+		setup_standard_excludes(&dir);
+
+	fill_directory(&dir, opt->repo->index, pathspec);
+	for (i = 0; i < dir.nr; i++) {
+		if (!dir_path_match(opt->repo->index, dir.entries[i], pathspec, 0, NULL))
+			continue;
+		hit |= grep_file(opt, dir.entries[i]->name);
+		if (hit && opt->status_only)
+			break;
+	}
+	return hit;
+}
+
+static int context_callback(const struct option *opt, const char *arg,
+			    int unset)
+{
+	struct grep_opt *grep_opt = opt->value;
+	int value;
+	const char *endp;
+
+	if (unset) {
+		grep_opt->pre_context = grep_opt->post_context = 0;
+		return 0;
+	}
+	value = strtol(arg, (char **)&endp, 10);
+	if (*endp) {
+		return error(_("switch `%c' expects a numerical value"),
+			     opt->short_name);
+	}
+	grep_opt->pre_context = grep_opt->post_context = value;
+	return 0;
+}
+
+static int file_callback(const struct option *opt, const char *arg, int unset)
+{
+	struct grep_opt *grep_opt = opt->value;
+	int from_stdin;
+	FILE *patterns;
+	int lno = 0;
+	struct strbuf sb = STRBUF_INIT;
+
+	BUG_ON_OPT_NEG(unset);
+
+	from_stdin = !strcmp(arg, "-");
+	patterns = from_stdin ? stdin : fopen(arg, "r");
+	if (!patterns)
+		die_errno(_("cannot open '%s'"), arg);
+	while (strbuf_getline(&sb, patterns) == 0) {
+		/* ignore empty line like grep does */
+		if (sb.len == 0)
+			continue;
+
+		append_grep_pat(grep_opt, sb.buf, sb.len, arg, ++lno,
+				GREP_PATTERN);
+	}
+	if (!from_stdin)
+		fclose(patterns);
+	strbuf_release(&sb);
+	return 0;
+}
+
+static int not_callback(const struct option *opt, const char *arg, int unset)
+{
+	struct grep_opt *grep_opt = opt->value;
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+	append_grep_pattern(grep_opt, "--not", "command line", 0, GREP_NOT);
+	return 0;
+}
+
+static int and_callback(const struct option *opt, const char *arg, int unset)
+{
+	struct grep_opt *grep_opt = opt->value;
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+	append_grep_pattern(grep_opt, "--and", "command line", 0, GREP_AND);
+	return 0;
+}
+
+static int open_callback(const struct option *opt, const char *arg, int unset)
+{
+	struct grep_opt *grep_opt = opt->value;
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+	append_grep_pattern(grep_opt, "(", "command line", 0, GREP_OPEN_PAREN);
+	return 0;
+}
+
+static int close_callback(const struct option *opt, const char *arg, int unset)
+{
+	struct grep_opt *grep_opt = opt->value;
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+	append_grep_pattern(grep_opt, ")", "command line", 0, GREP_CLOSE_PAREN);
+	return 0;
+}
+
+static int pattern_callback(const struct option *opt, const char *arg,
+			    int unset)
+{
+	struct grep_opt *grep_opt = opt->value;
+	BUG_ON_OPT_NEG(unset);
+	append_grep_pattern(grep_opt, arg, "-e option", 0, GREP_PATTERN);
+	return 0;
+}
+
+int cmd_grep(int argc, const char **argv, const char *prefix)
+{
+	int hit = 0;
+	int cached = 0, untracked = 0, opt_exclude = -1;
+	int seen_dashdash = 0;
+	int external_grep_allowed__ignored;
+	const char *show_in_pager = NULL, *default_pager = "dummy";
+	struct grep_opt opt;
+	struct object_array list = OBJECT_ARRAY_INIT;
+	struct pathspec pathspec;
+	struct string_list path_list = STRING_LIST_INIT_NODUP;
+	int i;
+	int dummy;
+	int use_index = 1;
+	int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED;
+	int allow_revs;
+
+	struct option options[] = {
+		OPT_BOOL(0, "cached", &cached,
+			N_("search in index instead of in the work tree")),
+		OPT_NEGBIT(0, "no-index", &use_index,
+			 N_("find in contents not managed by git"), 1),
+		OPT_BOOL(0, "untracked", &untracked,
+			N_("search in both tracked and untracked files")),
+		OPT_SET_INT(0, "exclude-standard", &opt_exclude,
+			    N_("ignore files specified via '.gitignore'"), 1),
+		OPT_BOOL(0, "recurse-submodules", &recurse_submodules,
+			 N_("recursively search in each submodule")),
+		OPT_GROUP(""),
+		OPT_BOOL('v', "invert-match", &opt.invert,
+			N_("show non-matching lines")),
+		OPT_BOOL('i', "ignore-case", &opt.ignore_case,
+			N_("case insensitive matching")),
+		OPT_BOOL('w', "word-regexp", &opt.word_regexp,
+			N_("match patterns only at word boundaries")),
+		OPT_SET_INT('a', "text", &opt.binary,
+			N_("process binary files as text"), GREP_BINARY_TEXT),
+		OPT_SET_INT('I', NULL, &opt.binary,
+			N_("don't match patterns in binary files"),
+			GREP_BINARY_NOMATCH),
+		OPT_BOOL(0, "textconv", &opt.allow_textconv,
+			 N_("process binary files with textconv filters")),
+		OPT_SET_INT('r', "recursive", &opt.max_depth,
+			    N_("search in subdirectories (default)"), -1),
+		{ OPTION_INTEGER, 0, "max-depth", &opt.max_depth, N_("depth"),
+			N_("descend at most <depth> levels"), PARSE_OPT_NONEG,
+			NULL, 1 },
+		OPT_GROUP(""),
+		OPT_SET_INT('E', "extended-regexp", &pattern_type_arg,
+			    N_("use extended POSIX regular expressions"),
+			    GREP_PATTERN_TYPE_ERE),
+		OPT_SET_INT('G', "basic-regexp", &pattern_type_arg,
+			    N_("use basic POSIX regular expressions (default)"),
+			    GREP_PATTERN_TYPE_BRE),
+		OPT_SET_INT('F', "fixed-strings", &pattern_type_arg,
+			    N_("interpret patterns as fixed strings"),
+			    GREP_PATTERN_TYPE_FIXED),
+		OPT_SET_INT('P', "perl-regexp", &pattern_type_arg,
+			    N_("use Perl-compatible regular expressions"),
+			    GREP_PATTERN_TYPE_PCRE),
+		OPT_GROUP(""),
+		OPT_BOOL('n', "line-number", &opt.linenum, N_("show line numbers")),
+		OPT_BOOL(0, "column", &opt.columnnum, N_("show column number of first match")),
+		OPT_NEGBIT('h', NULL, &opt.pathname, N_("don't show filenames"), 1),
+		OPT_BIT('H', NULL, &opt.pathname, N_("show filenames"), 1),
+		OPT_NEGBIT(0, "full-name", &opt.relative,
+			N_("show filenames relative to top directory"), 1),
+		OPT_BOOL('l', "files-with-matches", &opt.name_only,
+			N_("show only filenames instead of matching lines")),
+		OPT_BOOL(0, "name-only", &opt.name_only,
+			N_("synonym for --files-with-matches")),
+		OPT_BOOL('L', "files-without-match",
+			&opt.unmatch_name_only,
+			N_("show only the names of files without match")),
+		OPT_BOOL_F('z', "null", &opt.null_following_name,
+			   N_("print NUL after filenames"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL('o', "only-matching", &opt.only_matching,
+			N_("show only matching parts of a line")),
+		OPT_BOOL('c', "count", &opt.count,
+			N_("show the number of matches instead of matching lines")),
+		OPT__COLOR(&opt.color, N_("highlight matches")),
+		OPT_BOOL(0, "break", &opt.file_break,
+			N_("print empty line between matches from different files")),
+		OPT_BOOL(0, "heading", &opt.heading,
+			N_("show filename only once above matches from same file")),
+		OPT_GROUP(""),
+		OPT_CALLBACK('C', "context", &opt, N_("n"),
+			N_("show <n> context lines before and after matches"),
+			context_callback),
+		OPT_INTEGER('B', "before-context", &opt.pre_context,
+			N_("show <n> context lines before matches")),
+		OPT_INTEGER('A', "after-context", &opt.post_context,
+			N_("show <n> context lines after matches")),
+		OPT_INTEGER(0, "threads", &num_threads,
+			N_("use <n> worker threads")),
+		OPT_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"),
+			context_callback),
+		OPT_BOOL('p', "show-function", &opt.funcname,
+			N_("show a line with the function name before matches")),
+		OPT_BOOL('W', "function-context", &opt.funcbody,
+			N_("show the surrounding function")),
+		OPT_GROUP(""),
+		OPT_CALLBACK('f', NULL, &opt, N_("file"),
+			N_("read patterns from file"), file_callback),
+		{ OPTION_CALLBACK, 'e', NULL, &opt, N_("pattern"),
+			N_("match <pattern>"), PARSE_OPT_NONEG, pattern_callback },
+		{ OPTION_CALLBACK, 0, "and", &opt, NULL,
+		  N_("combine patterns specified with -e"),
+		  PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback },
+		OPT_BOOL(0, "or", &dummy, ""),
+		{ OPTION_CALLBACK, 0, "not", &opt, NULL, "",
+		  PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback },
+		{ OPTION_CALLBACK, '(', NULL, &opt, NULL, "",
+		  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
+		  open_callback },
+		{ OPTION_CALLBACK, ')', NULL, &opt, NULL, "",
+		  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
+		  close_callback },
+		OPT__QUIET(&opt.status_only,
+			   N_("indicate hit with exit status without output")),
+		OPT_BOOL(0, "all-match", &opt.all_match,
+			N_("show only matches from files that match all patterns")),
+		OPT_SET_INT_F(0, "debug", &opt.debug,
+			      N_("show parse tree for grep expression"),
+			      1, PARSE_OPT_HIDDEN),
+		OPT_GROUP(""),
+		{ OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
+			N_("pager"), N_("show matching files in the pager"),
+			PARSE_OPT_OPTARG | PARSE_OPT_NOCOMPLETE,
+			NULL, (intptr_t)default_pager },
+		OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored,
+			   N_("allow calling of grep(1) (ignored by this build)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_END()
+	};
+
+	init_grep_defaults(the_repository);
+	git_config(grep_cmd_config, NULL);
+	grep_init(&opt, the_repository, prefix);
+
+	/*
+	 * If there is no -- then the paths must exist in the working
+	 * tree.  If there is no explicit pattern specified with -e or
+	 * -f, we take the first unrecognized non option to be the
+	 * pattern, but then what follows it must be zero or more
+	 * valid refs up to the -- (if exists), and then existing
+	 * paths.  If there is an explicit pattern, then the first
+	 * unrecognized non option is the beginning of the refs list
+	 * that continues up to the -- (if exists), and then paths.
+	 */
+	argc = parse_options(argc, argv, prefix, options, grep_usage,
+			     PARSE_OPT_KEEP_DASHDASH |
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+	grep_commit_pattern_type(pattern_type_arg, &opt);
+
+	if (use_index && !startup_info->have_repository) {
+		int fallback = 0;
+		git_config_get_bool("grep.fallbacktonoindex", &fallback);
+		if (fallback)
+			use_index = 0;
+		else
+			/* die the same way as if we did it at the beginning */
+			setup_git_directory();
+	}
+
+	/*
+	 * skip a -- separator; we know it cannot be
+	 * separating revisions from pathnames if
+	 * we haven't even had any patterns yet
+	 */
+	if (argc > 0 && !opt.pattern_list && !strcmp(argv[0], "--")) {
+		argv++;
+		argc--;
+	}
+
+	/* First unrecognized non-option token */
+	if (argc > 0 && !opt.pattern_list) {
+		append_grep_pattern(&opt, argv[0], "command line", 0,
+				    GREP_PATTERN);
+		argv++;
+		argc--;
+	}
+
+	if (show_in_pager == default_pager)
+		show_in_pager = git_pager(1);
+	if (show_in_pager) {
+		opt.color = 0;
+		opt.name_only = 1;
+		opt.null_following_name = 1;
+		opt.output_priv = &path_list;
+		opt.output = append_path;
+		string_list_append(&path_list, show_in_pager);
+	}
+
+	if (!opt.pattern_list)
+		die(_("no pattern given"));
+
+	/* --only-matching has no effect with --invert. */
+	if (opt.invert)
+		opt.only_matching = 0;
+
+	/*
+	 * We have to find "--" in a separate pass, because its presence
+	 * influences how we will parse arguments that come before it.
+	 */
+	for (i = 0; i < argc; i++) {
+		if (!strcmp(argv[i], "--")) {
+			seen_dashdash = 1;
+			break;
+		}
+	}
+
+	/*
+	 * Resolve any rev arguments. If we have a dashdash, then everything up
+	 * to it must resolve as a rev. If not, then we stop at the first
+	 * non-rev and assume everything else is a path.
+	 */
+	allow_revs = use_index && !untracked;
+	for (i = 0; i < argc; i++) {
+		const char *arg = argv[i];
+		struct object_id oid;
+		struct object_context oc;
+		struct object *object;
+
+		if (!strcmp(arg, "--")) {
+			i++;
+			break;
+		}
+
+		if (!allow_revs) {
+			if (seen_dashdash)
+				die(_("--no-index or --untracked cannot be used with revs"));
+			break;
+		}
+
+		if (get_oid_with_context(the_repository, arg,
+					 GET_OID_RECORD_PATH,
+					 &oid, &oc)) {
+			if (seen_dashdash)
+				die(_("unable to resolve revision: %s"), arg);
+			break;
+		}
+
+		object = parse_object_or_die(&oid, arg);
+		if (!seen_dashdash)
+			verify_non_filename(prefix, arg);
+		add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
+		free(oc.path);
+	}
+
+	/*
+	 * Anything left over is presumed to be a path. But in the non-dashdash
+	 * "do what I mean" case, we verify and complain when that isn't true.
+	 */
+	if (!seen_dashdash) {
+		int j;
+		for (j = i; j < argc; j++)
+			verify_filename(prefix, argv[j], j == i && allow_revs);
+	}
+
+	parse_pathspec(&pathspec, 0,
+		       PATHSPEC_PREFER_CWD |
+		       (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0),
+		       prefix, argv + i);
+	pathspec.max_depth = opt.max_depth;
+	pathspec.recursive = 1;
+	pathspec.recurse_submodules = !!recurse_submodules;
+
+	if (list.nr || cached || show_in_pager) {
+		if (num_threads > 1)
+			warning(_("invalid option combination, ignoring --threads"));
+		num_threads = 1;
+	} else if (!HAVE_THREADS && num_threads > 1) {
+		warning(_("no threads support, ignoring --threads"));
+		num_threads = 1;
+	} else if (num_threads < 0)
+		die(_("invalid number of threads specified (%d)"), num_threads);
+	else if (num_threads == 0)
+		num_threads = HAVE_THREADS ? GREP_NUM_THREADS_DEFAULT : 1;
+
+	if (num_threads > 1) {
+		if (!HAVE_THREADS)
+			BUG("Somebody got num_threads calculation wrong!");
+		if (!(opt.name_only || opt.unmatch_name_only || opt.count)
+		    && (opt.pre_context || opt.post_context ||
+			opt.file_break || opt.funcbody))
+			skip_first_line = 1;
+		start_threads(&opt);
+	} else {
+		/*
+		 * The compiled patterns on the main path are only
+		 * used when not using threading. Otherwise
+		 * start_threads() above calls compile_grep_patterns()
+		 * for each thread.
+		 */
+		compile_grep_patterns(&opt);
+	}
+
+	if (show_in_pager && (cached || list.nr))
+		die(_("--open-files-in-pager only works on the worktree"));
+
+	if (show_in_pager && opt.pattern_list && !opt.pattern_list->next) {
+		const char *pager = path_list.items[0].string;
+		int len = strlen(pager);
+
+		if (len > 4 && is_dir_sep(pager[len - 5]))
+			pager += len - 4;
+
+		if (opt.ignore_case && !strcmp("less", pager))
+			string_list_append(&path_list, "-I");
+
+		if (!strcmp("less", pager) || !strcmp("vi", pager)) {
+			struct strbuf buf = STRBUF_INIT;
+			strbuf_addf(&buf, "+/%s%s",
+					strcmp("less", pager) ? "" : "*",
+					opt.pattern_list->pattern);
+			string_list_append(&path_list, buf.buf);
+			strbuf_detach(&buf, NULL);
+		}
+	}
+
+	if (recurse_submodules && (!use_index || untracked))
+		die(_("option not supported with --recurse-submodules"));
+
+	if (!show_in_pager && !opt.status_only)
+		setup_pager();
+
+	if (!use_index && (untracked || cached))
+		die(_("--cached or --untracked cannot be used with --no-index"));
+
+	if (!use_index || untracked) {
+		int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
+		hit = grep_directory(&opt, &pathspec, use_exclude, use_index);
+	} else if (0 <= opt_exclude) {
+		die(_("--[no-]exclude-standard cannot be used for tracked contents"));
+	} else if (!list.nr) {
+		if (!cached)
+			setup_work_tree();
+
+		hit = grep_cache(&opt, &pathspec, cached);
+	} else {
+		if (cached)
+			die(_("both --cached and trees are given"));
+
+		hit = grep_objects(&opt, &pathspec, &list);
+	}
+
+	if (num_threads > 1)
+		hit |= wait_all();
+	if (hit && show_in_pager)
+		run_pager(&opt, prefix);
+	clear_pathspec(&pathspec);
+	free_grep_patterns(&opt);
+	return !hit;
+}
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
new file mode 100644
index 0000000000..640ef4ded5
--- /dev/null
+++ b/builtin/hash-object.c
@@ -0,0 +1,162 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ * Copyright (C) Junio C Hamano, 2005
+ */
+#include "builtin.h"
+#include "config.h"
+#include "object-store.h"
+#include "blob.h"
+#include "quote.h"
+#include "parse-options.h"
+#include "exec-cmd.h"
+
+/*
+ * This is to create corrupt objects for debugging and as such it
+ * needs to bypass the data conversion performed by, and the type
+ * limitation imposed by, index_fd() and its callees.
+ */
+static int hash_literally(struct object_id *oid, int fd, const char *type, unsigned flags)
+{
+	struct strbuf buf = STRBUF_INIT;
+	int ret;
+
+	if (strbuf_read(&buf, fd, 4096) < 0)
+		ret = -1;
+	else
+		ret = hash_object_file_literally(buf.buf, buf.len, type, oid,
+						 flags);
+	strbuf_release(&buf);
+	return ret;
+}
+
+static void hash_fd(int fd, const char *type, const char *path, unsigned flags,
+		    int literally)
+{
+	struct stat st;
+	struct object_id oid;
+
+	if (fstat(fd, &st) < 0 ||
+	    (literally
+	     ? hash_literally(&oid, fd, type, flags)
+	     : index_fd(the_repository->index, &oid, fd, &st,
+			type_from_string(type), path, flags)))
+		die((flags & HASH_WRITE_OBJECT)
+		    ? "Unable to add %s to database"
+		    : "Unable to hash %s", path);
+	printf("%s\n", oid_to_hex(&oid));
+	maybe_flush_or_die(stdout, "hash to stdout");
+}
+
+static void hash_object(const char *path, const char *type, const char *vpath,
+			unsigned flags, int literally)
+{
+	int fd;
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		die_errno("Cannot open '%s'", path);
+	hash_fd(fd, type, vpath, flags, literally);
+}
+
+static void hash_stdin_paths(const char *type, int no_filters, unsigned flags,
+			     int literally)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf unquoted = STRBUF_INIT;
+
+	while (strbuf_getline(&buf, stdin) != EOF) {
+		if (buf.buf[0] == '"') {
+			strbuf_reset(&unquoted);
+			if (unquote_c_style(&unquoted, buf.buf, NULL))
+				die("line is badly quoted");
+			strbuf_swap(&buf, &unquoted);
+		}
+		hash_object(buf.buf, type, no_filters ? NULL : buf.buf, flags,
+			    literally);
+	}
+	strbuf_release(&buf);
+	strbuf_release(&unquoted);
+}
+
+int cmd_hash_object(int argc, const char **argv, const char *prefix)
+{
+	static const char * const hash_object_usage[] = {
+		N_("git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] [--] <file>..."),
+		N_("git hash-object  --stdin-paths"),
+		NULL
+	};
+	const char *type = blob_type;
+	int hashstdin = 0;
+	int stdin_paths = 0;
+	int no_filters = 0;
+	int literally = 0;
+	int nongit = 0;
+	unsigned flags = HASH_FORMAT_CHECK;
+	const char *vpath = NULL;
+	const struct option hash_object_options[] = {
+		OPT_STRING('t', NULL, &type, N_("type"), N_("object type")),
+		OPT_BIT('w', NULL, &flags, N_("write the object into the object database"),
+			HASH_WRITE_OBJECT),
+		OPT_COUNTUP( 0 , "stdin", &hashstdin, N_("read the object from stdin")),
+		OPT_BOOL( 0 , "stdin-paths", &stdin_paths, N_("read file names from stdin")),
+		OPT_BOOL( 0 , "no-filters", &no_filters, N_("store file as is without filters")),
+		OPT_BOOL( 0, "literally", &literally, N_("just hash any random garbage to create corrupt objects for debugging Git")),
+		OPT_STRING( 0 , "path", &vpath, N_("file"), N_("process file as it were from this path")),
+		OPT_END()
+	};
+	int i;
+	const char *errstr = NULL;
+
+	argc = parse_options(argc, argv, prefix, hash_object_options,
+			     hash_object_usage, 0);
+
+	if (flags & HASH_WRITE_OBJECT)
+		prefix = setup_git_directory();
+	else
+		prefix = setup_git_directory_gently(&nongit);
+
+	if (vpath && prefix)
+		vpath = xstrdup(prefix_filename(prefix, vpath));
+
+	git_config(git_default_config, NULL);
+
+	if (stdin_paths) {
+		if (hashstdin)
+			errstr = "Can't use --stdin-paths with --stdin";
+		else if (argc)
+			errstr = "Can't specify files with --stdin-paths";
+		else if (vpath)
+			errstr = "Can't use --stdin-paths with --path";
+	}
+	else {
+		if (hashstdin > 1)
+			errstr = "Multiple --stdin arguments are not supported";
+		if (vpath && no_filters)
+			errstr = "Can't use --path with --no-filters";
+	}
+
+	if (errstr) {
+		error("%s", errstr);
+		usage_with_options(hash_object_usage, hash_object_options);
+	}
+
+	if (hashstdin)
+		hash_fd(0, type, vpath, flags, literally);
+
+	for (i = 0 ; i < argc; i++) {
+		const char *arg = argv[i];
+		char *to_free = NULL;
+
+		if (prefix)
+			arg = to_free = prefix_filename(prefix, arg);
+		hash_object(arg, type, no_filters ? NULL : vpath ? vpath : arg,
+			    flags, literally);
+		free(to_free);
+	}
+
+	if (stdin_paths)
+		hash_stdin_paths(type, no_filters, flags, literally);
+
+	return 0;
+}
diff --git a/builtin/help.c b/builtin/help.c
new file mode 100644
index 0000000000..e5590d7787
--- /dev/null
+++ b/builtin/help.c
@@ -0,0 +1,537 @@
+/*
+ * Builtin help command
+ */
+#include "cache.h"
+#include "config.h"
+#include "builtin.h"
+#include "exec-cmd.h"
+#include "parse-options.h"
+#include "run-command.h"
+#include "column.h"
+#include "help.h"
+#include "alias.h"
+
+#ifndef DEFAULT_HELP_FORMAT
+#define DEFAULT_HELP_FORMAT "man"
+#endif
+
+static struct man_viewer_list {
+	struct man_viewer_list *next;
+	char name[FLEX_ARRAY];
+} *man_viewer_list;
+
+static struct man_viewer_info_list {
+	struct man_viewer_info_list *next;
+	const char *info;
+	char name[FLEX_ARRAY];
+} *man_viewer_info_list;
+
+enum help_format {
+	HELP_FORMAT_NONE,
+	HELP_FORMAT_MAN,
+	HELP_FORMAT_INFO,
+	HELP_FORMAT_WEB
+};
+
+static const char *html_path;
+
+static int show_all = 0;
+static int show_guides = 0;
+static int show_config;
+static int verbose = 1;
+static unsigned int colopts;
+static enum help_format help_format = HELP_FORMAT_NONE;
+static int exclude_guides;
+static struct option builtin_help_options[] = {
+	OPT_BOOL('a', "all", &show_all, N_("print all available commands")),
+	OPT_HIDDEN_BOOL(0, "exclude-guides", &exclude_guides, N_("exclude guides")),
+	OPT_BOOL('g', "guides", &show_guides, N_("print list of useful guides")),
+	OPT_BOOL('c', "config", &show_config, N_("print all configuration variable names")),
+	OPT_SET_INT_F(0, "config-for-completion", &show_config, "", 2, PARSE_OPT_HIDDEN),
+	OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN),
+	OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"),
+			HELP_FORMAT_WEB),
+	OPT_SET_INT('i', "info", &help_format, N_("show info page"),
+			HELP_FORMAT_INFO),
+	OPT__VERBOSE(&verbose, N_("print command description")),
+	OPT_END(),
+};
+
+static const char * const builtin_help_usage[] = {
+	N_("git help [--all] [--guides] [--man | --web | --info] [<command>]"),
+	NULL
+};
+
+static enum help_format parse_help_format(const char *format)
+{
+	if (!strcmp(format, "man"))
+		return HELP_FORMAT_MAN;
+	if (!strcmp(format, "info"))
+		return HELP_FORMAT_INFO;
+	if (!strcmp(format, "web") || !strcmp(format, "html"))
+		return HELP_FORMAT_WEB;
+	/*
+	 * Please update _git_config() in git-completion.bash when you
+	 * add new help formats.
+	 */
+	die(_("unrecognized help format '%s'"), format);
+}
+
+static const char *get_man_viewer_info(const char *name)
+{
+	struct man_viewer_info_list *viewer;
+
+	for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)
+	{
+		if (!strcasecmp(name, viewer->name))
+			return viewer->info;
+	}
+	return NULL;
+}
+
+static int check_emacsclient_version(void)
+{
+	struct strbuf buffer = STRBUF_INIT;
+	struct child_process ec_process = CHILD_PROCESS_INIT;
+	const char *argv_ec[] = { "emacsclient", "--version", NULL };
+	int version;
+
+	/* emacsclient prints its version number on stderr */
+	ec_process.argv = argv_ec;
+	ec_process.err = -1;
+	ec_process.stdout_to_stderr = 1;
+	if (start_command(&ec_process))
+		return error(_("Failed to start emacsclient."));
+
+	strbuf_read(&buffer, ec_process.err, 20);
+	close(ec_process.err);
+
+	/*
+	 * Don't bother checking return value, because "emacsclient --version"
+	 * seems to always exits with code 1.
+	 */
+	finish_command(&ec_process);
+
+	if (!starts_with(buffer.buf, "emacsclient")) {
+		strbuf_release(&buffer);
+		return error(_("Failed to parse emacsclient version."));
+	}
+
+	strbuf_remove(&buffer, 0, strlen("emacsclient"));
+	version = atoi(buffer.buf);
+
+	if (version < 22) {
+		strbuf_release(&buffer);
+		return error(_("emacsclient version '%d' too old (< 22)."),
+			version);
+	}
+
+	strbuf_release(&buffer);
+	return 0;
+}
+
+static void exec_woman_emacs(const char *path, const char *page)
+{
+	if (!check_emacsclient_version()) {
+		/* This works only with emacsclient version >= 22. */
+		struct strbuf man_page = STRBUF_INIT;
+
+		if (!path)
+			path = "emacsclient";
+		strbuf_addf(&man_page, "(woman \"%s\")", page);
+		execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL);
+		warning_errno(_("failed to exec '%s'"), path);
+		strbuf_release(&man_page);
+	}
+}
+
+static void exec_man_konqueror(const char *path, const char *page)
+{
+	const char *display = getenv("DISPLAY");
+	if (display && *display) {
+		struct strbuf man_page = STRBUF_INIT;
+		const char *filename = "kfmclient";
+
+		/* It's simpler to launch konqueror using kfmclient. */
+		if (path) {
+			size_t len;
+			if (strip_suffix(path, "/konqueror", &len))
+				path = xstrfmt("%.*s/kfmclient", (int)len, path);
+			filename = basename((char *)path);
+		} else
+			path = "kfmclient";
+		strbuf_addf(&man_page, "man:%s(1)", page);
+		execlp(path, filename, "newTab", man_page.buf, (char *)NULL);
+		warning_errno(_("failed to exec '%s'"), path);
+		strbuf_release(&man_page);
+	}
+}
+
+static void exec_man_man(const char *path, const char *page)
+{
+	if (!path)
+		path = "man";
+	execlp(path, "man", page, (char *)NULL);
+	warning_errno(_("failed to exec '%s'"), path);
+}
+
+static void exec_man_cmd(const char *cmd, const char *page)
+{
+	struct strbuf shell_cmd = STRBUF_INIT;
+	strbuf_addf(&shell_cmd, "%s %s", cmd, page);
+	execl(SHELL_PATH, SHELL_PATH, "-c", shell_cmd.buf, (char *)NULL);
+	warning(_("failed to exec '%s'"), cmd);
+	strbuf_release(&shell_cmd);
+}
+
+static void add_man_viewer(const char *name)
+{
+	struct man_viewer_list **p = &man_viewer_list;
+
+	while (*p)
+		p = &((*p)->next);
+	FLEX_ALLOC_STR(*p, name, name);
+}
+
+static int supported_man_viewer(const char *name, size_t len)
+{
+	return (!strncasecmp("man", name, len) ||
+		!strncasecmp("woman", name, len) ||
+		!strncasecmp("konqueror", name, len));
+}
+
+static void do_add_man_viewer_info(const char *name,
+				   size_t len,
+				   const char *value)
+{
+	struct man_viewer_info_list *new_man_viewer;
+	FLEX_ALLOC_MEM(new_man_viewer, name, name, len);
+	new_man_viewer->info = xstrdup(value);
+	new_man_viewer->next = man_viewer_info_list;
+	man_viewer_info_list = new_man_viewer;
+}
+
+static int add_man_viewer_path(const char *name,
+			       size_t len,
+			       const char *value)
+{
+	if (supported_man_viewer(name, len))
+		do_add_man_viewer_info(name, len, value);
+	else
+		warning(_("'%s': path for unsupported man viewer.\n"
+			  "Please consider using 'man.<tool>.cmd' instead."),
+			name);
+
+	return 0;
+}
+
+static int add_man_viewer_cmd(const char *name,
+			      size_t len,
+			      const char *value)
+{
+	if (supported_man_viewer(name, len))
+		warning(_("'%s': cmd for supported man viewer.\n"
+			  "Please consider using 'man.<tool>.path' instead."),
+			name);
+	else
+		do_add_man_viewer_info(name, len, value);
+
+	return 0;
+}
+
+static int add_man_viewer_info(const char *var, const char *value)
+{
+	const char *name, *subkey;
+	int namelen;
+
+	if (parse_config_key(var, "man", &name, &namelen, &subkey) < 0 || !name)
+		return 0;
+
+	if (!strcmp(subkey, "path")) {
+		if (!value)
+			return config_error_nonbool(var);
+		return add_man_viewer_path(name, namelen, value);
+	}
+	if (!strcmp(subkey, "cmd")) {
+		if (!value)
+			return config_error_nonbool(var);
+		return add_man_viewer_cmd(name, namelen, value);
+	}
+
+	return 0;
+}
+
+static int git_help_config(const char *var, const char *value, void *cb)
+{
+	if (starts_with(var, "column."))
+		return git_column_config(var, value, "help", &colopts);
+	if (!strcmp(var, "help.format")) {
+		if (!value)
+			return config_error_nonbool(var);
+		help_format = parse_help_format(value);
+		return 0;
+	}
+	if (!strcmp(var, "help.htmlpath")) {
+		if (!value)
+			return config_error_nonbool(var);
+		html_path = xstrdup(value);
+		return 0;
+	}
+	if (!strcmp(var, "man.viewer")) {
+		if (!value)
+			return config_error_nonbool(var);
+		add_man_viewer(value);
+		return 0;
+	}
+	if (starts_with(var, "man."))
+		return add_man_viewer_info(var, value);
+
+	return git_default_config(var, value, cb);
+}
+
+static struct cmdnames main_cmds, other_cmds;
+
+static int is_git_command(const char *s)
+{
+	if (is_builtin(s))
+		return 1;
+
+	load_command_list("git-", &main_cmds, &other_cmds);
+	return is_in_cmdlist(&main_cmds, s) ||
+		is_in_cmdlist(&other_cmds, s);
+}
+
+static const char *cmd_to_page(const char *git_cmd)
+{
+	if (!git_cmd)
+		return "git";
+	else if (starts_with(git_cmd, "git"))
+		return git_cmd;
+	else if (is_git_command(git_cmd))
+		return xstrfmt("git-%s", git_cmd);
+	else
+		return xstrfmt("git%s", git_cmd);
+}
+
+static void setup_man_path(void)
+{
+	struct strbuf new_path = STRBUF_INIT;
+	const char *old_path = getenv("MANPATH");
+	char *git_man_path = system_path(GIT_MAN_PATH);
+
+	/* We should always put ':' after our path. If there is no
+	 * old_path, the ':' at the end will let 'man' to try
+	 * system-wide paths after ours to find the manual page. If
+	 * there is old_path, we need ':' as delimiter. */
+	strbuf_addstr(&new_path, git_man_path);
+	strbuf_addch(&new_path, ':');
+	if (old_path)
+		strbuf_addstr(&new_path, old_path);
+
+	free(git_man_path);
+	setenv("MANPATH", new_path.buf, 1);
+
+	strbuf_release(&new_path);
+}
+
+static void exec_viewer(const char *name, const char *page)
+{
+	const char *info = get_man_viewer_info(name);
+
+	if (!strcasecmp(name, "man"))
+		exec_man_man(info, page);
+	else if (!strcasecmp(name, "woman"))
+		exec_woman_emacs(info, page);
+	else if (!strcasecmp(name, "konqueror"))
+		exec_man_konqueror(info, page);
+	else if (info)
+		exec_man_cmd(info, page);
+	else
+		warning(_("'%s': unknown man viewer."), name);
+}
+
+static void show_man_page(const char *git_cmd)
+{
+	struct man_viewer_list *viewer;
+	const char *page = cmd_to_page(git_cmd);
+	const char *fallback = getenv("GIT_MAN_VIEWER");
+
+	setup_man_path();
+	for (viewer = man_viewer_list; viewer; viewer = viewer->next)
+	{
+		exec_viewer(viewer->name, page); /* will return when unable */
+	}
+	if (fallback)
+		exec_viewer(fallback, page);
+	exec_viewer("man", page);
+	die(_("no man viewer handled the request"));
+}
+
+static void show_info_page(const char *git_cmd)
+{
+	const char *page = cmd_to_page(git_cmd);
+	setenv("INFOPATH", system_path(GIT_INFO_PATH), 1);
+	execlp("info", "info", "gitman", page, (char *)NULL);
+	die(_("no info viewer handled the request"));
+}
+
+static void get_html_page_path(struct strbuf *page_path, const char *page)
+{
+	struct stat st;
+	char *to_free = NULL;
+
+	if (!html_path)
+		html_path = to_free = system_path(GIT_HTML_PATH);
+
+	/* Check that we have a git documentation directory. */
+	if (!strstr(html_path, "://")) {
+		if (stat(mkpath("%s/git.html", html_path), &st)
+		    || !S_ISREG(st.st_mode))
+			die("'%s': not a documentation directory.", html_path);
+	}
+
+	strbuf_init(page_path, 0);
+	strbuf_addf(page_path, "%s/%s.html", html_path, page);
+	free(to_free);
+}
+
+static void open_html(const char *path)
+{
+	execl_git_cmd("web--browse", "-c", "help.browser", path, (char *)NULL);
+}
+
+static void show_html_page(const char *git_cmd)
+{
+	const char *page = cmd_to_page(git_cmd);
+	struct strbuf page_path; /* it leaks but we exec bellow */
+
+	get_html_page_path(&page_path, page);
+
+	open_html(page_path.buf);
+}
+
+static const char *check_git_cmd(const char* cmd)
+{
+	char *alias;
+
+	if (is_git_command(cmd))
+		return cmd;
+
+	alias = alias_lookup(cmd);
+	if (alias) {
+		const char **argv;
+		int count;
+
+		/*
+		 * handle_builtin() in git.c rewrites "git cmd --help"
+		 * to "git help --exclude-guides cmd", so we can use
+		 * exclude_guides to distinguish "git cmd --help" from
+		 * "git help cmd". In the latter case, or if cmd is an
+		 * alias for a shell command, just print the alias
+		 * definition.
+		 */
+		if (!exclude_guides || alias[0] == '!') {
+			printf_ln(_("'%s' is aliased to '%s'"), cmd, alias);
+			free(alias);
+			exit(0);
+		}
+		/*
+		 * Otherwise, we pretend that the command was "git
+		 * word0 --help". We use split_cmdline() to get the
+		 * first word of the alias, to ensure that we use the
+		 * same rules as when the alias is actually
+		 * used. split_cmdline() modifies alias in-place.
+		 */
+		fprintf_ln(stderr, _("'%s' is aliased to '%s'"), cmd, alias);
+		count = split_cmdline(alias, &argv);
+		if (count < 0)
+			die(_("bad alias.%s string: %s"), cmd,
+			    split_cmdline_strerror(count));
+		free(argv);
+		UNLEAK(alias);
+		return alias;
+	}
+
+	if (exclude_guides)
+		return help_unknown_cmd(cmd);
+
+	return cmd;
+}
+
+int cmd_help(int argc, const char **argv, const char *prefix)
+{
+	int nongit;
+	enum help_format parsed_help_format;
+
+	argc = parse_options(argc, argv, prefix, builtin_help_options,
+			builtin_help_usage, 0);
+	parsed_help_format = help_format;
+
+	if (show_all) {
+		git_config(git_help_config, NULL);
+		if (verbose) {
+			setup_pager();
+			list_all_cmds_help();
+			return 0;
+		}
+		printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
+		load_command_list("git-", &main_cmds, &other_cmds);
+		list_commands(colopts, &main_cmds, &other_cmds);
+	}
+
+	if (show_config) {
+		int for_human = show_config == 1;
+
+		if (!for_human) {
+			list_config_help(for_human);
+			return 0;
+		}
+		setup_pager();
+		list_config_help(for_human);
+		printf("\n%s\n", _("'git help config' for more information"));
+		return 0;
+	}
+
+	if (show_guides)
+		list_common_guides_help();
+
+	if (show_all || show_guides) {
+		printf("%s\n", _(git_more_info_string));
+		/*
+		* We're done. Ignore any remaining args
+		*/
+		return 0;
+	}
+
+	if (!argv[0]) {
+		printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
+		list_common_cmds_help();
+		printf("\n%s\n", _(git_more_info_string));
+		return 0;
+	}
+
+	setup_git_directory_gently(&nongit);
+	git_config(git_help_config, NULL);
+
+	if (parsed_help_format != HELP_FORMAT_NONE)
+		help_format = parsed_help_format;
+	if (help_format == HELP_FORMAT_NONE)
+		help_format = parse_help_format(DEFAULT_HELP_FORMAT);
+
+	argv[0] = check_git_cmd(argv[0]);
+
+	switch (help_format) {
+	case HELP_FORMAT_NONE:
+	case HELP_FORMAT_MAN:
+		show_man_page(argv[0]);
+		break;
+	case HELP_FORMAT_INFO:
+		show_info_page(argv[0]);
+		break;
+	case HELP_FORMAT_WEB:
+		show_html_page(argv[0]);
+		break;
+	}
+
+	return 0;
+}
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
new file mode 100644
index 0000000000..0d55f73b0b
--- /dev/null
+++ b/builtin/index-pack.c
@@ -0,0 +1,1849 @@
+#include "builtin.h"
+#include "config.h"
+#include "delta.h"
+#include "pack.h"
+#include "csum-file.h"
+#include "blob.h"
+#include "commit.h"
+#include "tag.h"
+#include "tree.h"
+#include "progress.h"
+#include "fsck.h"
+#include "exec-cmd.h"
+#include "streaming.h"
+#include "thread-utils.h"
+#include "packfile.h"
+#include "object-store.h"
+#include "fetch-object.h"
+
+static const char index_pack_usage[] =
+"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
+
+struct object_entry {
+	struct pack_idx_entry idx;
+	unsigned long size;
+	unsigned char hdr_size;
+	signed char type;
+	signed char real_type;
+};
+
+struct object_stat {
+	unsigned delta_depth;
+	int base_object_no;
+};
+
+struct base_data {
+	struct base_data *base;
+	struct base_data *child;
+	struct object_entry *obj;
+	void *data;
+	unsigned long size;
+	int ref_first, ref_last;
+	int ofs_first, ofs_last;
+};
+
+struct thread_local {
+	pthread_t thread;
+	struct base_data *base_cache;
+	size_t base_cache_used;
+	int pack_fd;
+};
+
+/* Remember to update object flag allocation in object.h */
+#define FLAG_LINK (1u<<20)
+#define FLAG_CHECKED (1u<<21)
+
+struct ofs_delta_entry {
+	off_t offset;
+	int obj_no;
+};
+
+struct ref_delta_entry {
+	struct object_id oid;
+	int obj_no;
+};
+
+static struct object_entry *objects;
+static struct object_stat *obj_stat;
+static struct ofs_delta_entry *ofs_deltas;
+static struct ref_delta_entry *ref_deltas;
+static struct thread_local nothread_data;
+static int nr_objects;
+static int nr_ofs_deltas;
+static int nr_ref_deltas;
+static int ref_deltas_alloc;
+static int nr_resolved_deltas;
+static int nr_threads;
+
+static int from_stdin;
+static int strict;
+static int do_fsck_object;
+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
+static int verbose;
+static int show_resolving_progress;
+static int show_stat;
+static int check_self_contained_and_connected;
+
+static struct progress *progress;
+
+/* We always read in 4kB chunks. */
+static unsigned char input_buffer[4096];
+static unsigned int input_offset, input_len;
+static off_t consumed_bytes;
+static off_t max_input_size;
+static unsigned deepest_delta;
+static git_hash_ctx input_ctx;
+static uint32_t input_crc32;
+static int input_fd, output_fd;
+static const char *curr_pack;
+
+static struct thread_local *thread_data;
+static int nr_dispatched;
+static int threads_active;
+
+static pthread_mutex_t read_mutex;
+#define read_lock()		lock_mutex(&read_mutex)
+#define read_unlock()		unlock_mutex(&read_mutex)
+
+static pthread_mutex_t counter_mutex;
+#define counter_lock()		lock_mutex(&counter_mutex)
+#define counter_unlock()	unlock_mutex(&counter_mutex)
+
+static pthread_mutex_t work_mutex;
+#define work_lock()		lock_mutex(&work_mutex)
+#define work_unlock()		unlock_mutex(&work_mutex)
+
+static pthread_mutex_t deepest_delta_mutex;
+#define deepest_delta_lock()	lock_mutex(&deepest_delta_mutex)
+#define deepest_delta_unlock()	unlock_mutex(&deepest_delta_mutex)
+
+static pthread_mutex_t type_cas_mutex;
+#define type_cas_lock()		lock_mutex(&type_cas_mutex)
+#define type_cas_unlock()	unlock_mutex(&type_cas_mutex)
+
+static pthread_key_t key;
+
+static inline void lock_mutex(pthread_mutex_t *mutex)
+{
+	if (threads_active)
+		pthread_mutex_lock(mutex);
+}
+
+static inline void unlock_mutex(pthread_mutex_t *mutex)
+{
+	if (threads_active)
+		pthread_mutex_unlock(mutex);
+}
+
+/*
+ * Mutex and conditional variable can't be statically-initialized on Windows.
+ */
+static void init_thread(void)
+{
+	int i;
+	init_recursive_mutex(&read_mutex);
+	pthread_mutex_init(&counter_mutex, NULL);
+	pthread_mutex_init(&work_mutex, NULL);
+	pthread_mutex_init(&type_cas_mutex, NULL);
+	if (show_stat)
+		pthread_mutex_init(&deepest_delta_mutex, NULL);
+	pthread_key_create(&key, NULL);
+	thread_data = xcalloc(nr_threads, sizeof(*thread_data));
+	for (i = 0; i < nr_threads; i++) {
+		thread_data[i].pack_fd = open(curr_pack, O_RDONLY);
+		if (thread_data[i].pack_fd == -1)
+			die_errno(_("unable to open %s"), curr_pack);
+	}
+
+	threads_active = 1;
+}
+
+static void cleanup_thread(void)
+{
+	int i;
+	if (!threads_active)
+		return;
+	threads_active = 0;
+	pthread_mutex_destroy(&read_mutex);
+	pthread_mutex_destroy(&counter_mutex);
+	pthread_mutex_destroy(&work_mutex);
+	pthread_mutex_destroy(&type_cas_mutex);
+	if (show_stat)
+		pthread_mutex_destroy(&deepest_delta_mutex);
+	for (i = 0; i < nr_threads; i++)
+		close(thread_data[i].pack_fd);
+	pthread_key_delete(key);
+	free(thread_data);
+}
+
+static int mark_link(struct object *obj, int type, void *data, struct fsck_options *options)
+{
+	if (!obj)
+		return -1;
+
+	if (type != OBJ_ANY && obj->type != type)
+		die(_("object type mismatch at %s"), oid_to_hex(&obj->oid));
+
+	obj->flags |= FLAG_LINK;
+	return 0;
+}
+
+/* The content of each linked object must have been checked
+   or it must be already present in the object database */
+static unsigned check_object(struct object *obj)
+{
+	if (!obj)
+		return 0;
+
+	if (!(obj->flags & FLAG_LINK))
+		return 0;
+
+	if (!(obj->flags & FLAG_CHECKED)) {
+		unsigned long size;
+		int type = oid_object_info(the_repository, &obj->oid, &size);
+		if (type <= 0)
+			die(_("did not receive expected object %s"),
+			      oid_to_hex(&obj->oid));
+		if (type != obj->type)
+			die(_("object %s: expected type %s, found %s"),
+			    oid_to_hex(&obj->oid),
+			    type_name(obj->type), type_name(type));
+		obj->flags |= FLAG_CHECKED;
+		return 1;
+	}
+
+	return 0;
+}
+
+static unsigned check_objects(void)
+{
+	unsigned i, max, foreign_nr = 0;
+
+	max = get_max_object_index();
+
+	if (verbose)
+		progress = start_delayed_progress(_("Checking objects"), max);
+
+	for (i = 0; i < max; i++) {
+		foreign_nr += check_object(get_indexed_object(i));
+		display_progress(progress, i + 1);
+	}
+
+	stop_progress(&progress);
+	return foreign_nr;
+}
+
+
+/* Discard current buffer used content. */
+static void flush(void)
+{
+	if (input_offset) {
+		if (output_fd >= 0)
+			write_or_die(output_fd, input_buffer, input_offset);
+		the_hash_algo->update_fn(&input_ctx, input_buffer, input_offset);
+		memmove(input_buffer, input_buffer + input_offset, input_len);
+		input_offset = 0;
+	}
+}
+
+/*
+ * Make sure at least "min" bytes are available in the buffer, and
+ * return the pointer to the buffer.
+ */
+static void *fill(int min)
+{
+	if (min <= input_len)
+		return input_buffer + input_offset;
+	if (min > sizeof(input_buffer))
+		die(Q_("cannot fill %d byte",
+		       "cannot fill %d bytes",
+		       min),
+		    min);
+	flush();
+	do {
+		ssize_t ret = xread(input_fd, input_buffer + input_len,
+				sizeof(input_buffer) - input_len);
+		if (ret <= 0) {
+			if (!ret)
+				die(_("early EOF"));
+			die_errno(_("read error on input"));
+		}
+		input_len += ret;
+		if (from_stdin)
+			display_throughput(progress, consumed_bytes + input_len);
+	} while (input_len < min);
+	return input_buffer;
+}
+
+static void use(int bytes)
+{
+	if (bytes > input_len)
+		die(_("used more bytes than were available"));
+	input_crc32 = crc32(input_crc32, input_buffer + input_offset, bytes);
+	input_len -= bytes;
+	input_offset += bytes;
+
+	/* make sure off_t is sufficiently large not to wrap */
+	if (signed_add_overflows(consumed_bytes, bytes))
+		die(_("pack too large for current definition of off_t"));
+	consumed_bytes += bytes;
+	if (max_input_size && consumed_bytes > max_input_size)
+		die(_("pack exceeds maximum allowed size"));
+}
+
+static const char *open_pack_file(const char *pack_name)
+{
+	if (from_stdin) {
+		input_fd = 0;
+		if (!pack_name) {
+			struct strbuf tmp_file = STRBUF_INIT;
+			output_fd = odb_mkstemp(&tmp_file,
+						"pack/tmp_pack_XXXXXX");
+			pack_name = strbuf_detach(&tmp_file, NULL);
+		} else {
+			output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
+			if (output_fd < 0)
+				die_errno(_("unable to create '%s'"), pack_name);
+		}
+		nothread_data.pack_fd = output_fd;
+	} else {
+		input_fd = open(pack_name, O_RDONLY);
+		if (input_fd < 0)
+			die_errno(_("cannot open packfile '%s'"), pack_name);
+		output_fd = -1;
+		nothread_data.pack_fd = input_fd;
+	}
+	the_hash_algo->init_fn(&input_ctx);
+	return pack_name;
+}
+
+static void parse_pack_header(void)
+{
+	struct pack_header *hdr = fill(sizeof(struct pack_header));
+
+	/* Header consistency check */
+	if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
+		die(_("pack signature mismatch"));
+	if (!pack_version_ok(hdr->hdr_version))
+		die(_("pack version %"PRIu32" unsupported"),
+			ntohl(hdr->hdr_version));
+
+	nr_objects = ntohl(hdr->hdr_entries);
+	use(sizeof(struct pack_header));
+}
+
+static NORETURN void bad_object(off_t offset, const char *format,
+		       ...) __attribute__((format (printf, 2, 3)));
+
+static NORETURN void bad_object(off_t offset, const char *format, ...)
+{
+	va_list params;
+	char buf[1024];
+
+	va_start(params, format);
+	vsnprintf(buf, sizeof(buf), format, params);
+	va_end(params);
+	die(_("pack has bad object at offset %"PRIuMAX": %s"),
+	    (uintmax_t)offset, buf);
+}
+
+static inline struct thread_local *get_thread_data(void)
+{
+	if (HAVE_THREADS) {
+		if (threads_active)
+			return pthread_getspecific(key);
+		assert(!threads_active &&
+		       "This should only be reached when all threads are gone");
+	}
+	return &nothread_data;
+}
+
+static void set_thread_data(struct thread_local *data)
+{
+	if (threads_active)
+		pthread_setspecific(key, data);
+}
+
+static struct base_data *alloc_base_data(void)
+{
+	struct base_data *base = xcalloc(1, sizeof(struct base_data));
+	base->ref_last = -1;
+	base->ofs_last = -1;
+	return base;
+}
+
+static void free_base_data(struct base_data *c)
+{
+	if (c->data) {
+		FREE_AND_NULL(c->data);
+		get_thread_data()->base_cache_used -= c->size;
+	}
+}
+
+static void prune_base_data(struct base_data *retain)
+{
+	struct base_data *b;
+	struct thread_local *data = get_thread_data();
+	for (b = data->base_cache;
+	     data->base_cache_used > delta_base_cache_limit && b;
+	     b = b->child) {
+		if (b->data && b != retain)
+			free_base_data(b);
+	}
+}
+
+static void link_base_data(struct base_data *base, struct base_data *c)
+{
+	if (base)
+		base->child = c;
+	else
+		get_thread_data()->base_cache = c;
+
+	c->base = base;
+	c->child = NULL;
+	if (c->data)
+		get_thread_data()->base_cache_used += c->size;
+	prune_base_data(c);
+}
+
+static void unlink_base_data(struct base_data *c)
+{
+	struct base_data *base = c->base;
+	if (base)
+		base->child = NULL;
+	else
+		get_thread_data()->base_cache = NULL;
+	free_base_data(c);
+}
+
+static int is_delta_type(enum object_type type)
+{
+	return (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA);
+}
+
+static void *unpack_entry_data(off_t offset, unsigned long size,
+			       enum object_type type, struct object_id *oid)
+{
+	static char fixed_buf[8192];
+	int status;
+	git_zstream stream;
+	void *buf;
+	git_hash_ctx c;
+	char hdr[32];
+	int hdrlen;
+
+	if (!is_delta_type(type)) {
+		hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX,
+				   type_name(type),(uintmax_t)size) + 1;
+		the_hash_algo->init_fn(&c);
+		the_hash_algo->update_fn(&c, hdr, hdrlen);
+	} else
+		oid = NULL;
+	if (type == OBJ_BLOB && size > big_file_threshold)
+		buf = fixed_buf;
+	else
+		buf = xmallocz(size);
+
+	memset(&stream, 0, sizeof(stream));
+	git_inflate_init(&stream);
+	stream.next_out = buf;
+	stream.avail_out = buf == fixed_buf ? sizeof(fixed_buf) : size;
+
+	do {
+		unsigned char *last_out = stream.next_out;
+		stream.next_in = fill(1);
+		stream.avail_in = input_len;
+		status = git_inflate(&stream, 0);
+		use(input_len - stream.avail_in);
+		if (oid)
+			the_hash_algo->update_fn(&c, last_out, stream.next_out - last_out);
+		if (buf == fixed_buf) {
+			stream.next_out = buf;
+			stream.avail_out = sizeof(fixed_buf);
+		}
+	} while (status == Z_OK);
+	if (stream.total_out != size || status != Z_STREAM_END)
+		bad_object(offset, _("inflate returned %d"), status);
+	git_inflate_end(&stream);
+	if (oid)
+		the_hash_algo->final_fn(oid->hash, &c);
+	return buf == fixed_buf ? NULL : buf;
+}
+
+static void *unpack_raw_entry(struct object_entry *obj,
+			      off_t *ofs_offset,
+			      struct object_id *ref_oid,
+			      struct object_id *oid)
+{
+	unsigned char *p;
+	unsigned long size, c;
+	off_t base_offset;
+	unsigned shift;
+	void *data;
+
+	obj->idx.offset = consumed_bytes;
+	input_crc32 = crc32(0, NULL, 0);
+
+	p = fill(1);
+	c = *p;
+	use(1);
+	obj->type = (c >> 4) & 7;
+	size = (c & 15);
+	shift = 4;
+	while (c & 0x80) {
+		p = fill(1);
+		c = *p;
+		use(1);
+		size += (c & 0x7f) << shift;
+		shift += 7;
+	}
+	obj->size = size;
+
+	switch (obj->type) {
+	case OBJ_REF_DELTA:
+		hashcpy(ref_oid->hash, fill(the_hash_algo->rawsz));
+		use(the_hash_algo->rawsz);
+		break;
+	case OBJ_OFS_DELTA:
+		p = fill(1);
+		c = *p;
+		use(1);
+		base_offset = c & 127;
+		while (c & 128) {
+			base_offset += 1;
+			if (!base_offset || MSB(base_offset, 7))
+				bad_object(obj->idx.offset, _("offset value overflow for delta base object"));
+			p = fill(1);
+			c = *p;
+			use(1);
+			base_offset = (base_offset << 7) + (c & 127);
+		}
+		*ofs_offset = obj->idx.offset - base_offset;
+		if (*ofs_offset <= 0 || *ofs_offset >= obj->idx.offset)
+			bad_object(obj->idx.offset, _("delta base offset is out of bound"));
+		break;
+	case OBJ_COMMIT:
+	case OBJ_TREE:
+	case OBJ_BLOB:
+	case OBJ_TAG:
+		break;
+	default:
+		bad_object(obj->idx.offset, _("unknown object type %d"), obj->type);
+	}
+	obj->hdr_size = consumed_bytes - obj->idx.offset;
+
+	data = unpack_entry_data(obj->idx.offset, obj->size, obj->type, oid);
+	obj->idx.crc32 = input_crc32;
+	return data;
+}
+
+static void *unpack_data(struct object_entry *obj,
+			 int (*consume)(const unsigned char *, unsigned long, void *),
+			 void *cb_data)
+{
+	off_t from = obj[0].idx.offset + obj[0].hdr_size;
+	off_t len = obj[1].idx.offset - from;
+	unsigned char *data, *inbuf;
+	git_zstream stream;
+	int status;
+
+	data = xmallocz(consume ? 64*1024 : obj->size);
+	inbuf = xmalloc((len < 64*1024) ? (int)len : 64*1024);
+
+	memset(&stream, 0, sizeof(stream));
+	git_inflate_init(&stream);
+	stream.next_out = data;
+	stream.avail_out = consume ? 64*1024 : obj->size;
+
+	do {
+		ssize_t n = (len < 64*1024) ? (ssize_t)len : 64*1024;
+		n = xpread(get_thread_data()->pack_fd, inbuf, n, from);
+		if (n < 0)
+			die_errno(_("cannot pread pack file"));
+		if (!n)
+			die(Q_("premature end of pack file, %"PRIuMAX" byte missing",
+			       "premature end of pack file, %"PRIuMAX" bytes missing",
+			       (unsigned int)len),
+			    (uintmax_t)len);
+		from += n;
+		len -= n;
+		stream.next_in = inbuf;
+		stream.avail_in = n;
+		if (!consume)
+			status = git_inflate(&stream, 0);
+		else {
+			do {
+				status = git_inflate(&stream, 0);
+				if (consume(data, stream.next_out - data, cb_data)) {
+					free(inbuf);
+					free(data);
+					return NULL;
+				}
+				stream.next_out = data;
+				stream.avail_out = 64*1024;
+			} while (status == Z_OK && stream.avail_in);
+		}
+	} while (len && status == Z_OK && !stream.avail_in);
+
+	/* This has been inflated OK when first encountered, so... */
+	if (status != Z_STREAM_END || stream.total_out != obj->size)
+		die(_("serious inflate inconsistency"));
+
+	git_inflate_end(&stream);
+	free(inbuf);
+	if (consume) {
+		FREE_AND_NULL(data);
+	}
+	return data;
+}
+
+static void *get_data_from_pack(struct object_entry *obj)
+{
+	return unpack_data(obj, NULL, NULL);
+}
+
+static int compare_ofs_delta_bases(off_t offset1, off_t offset2,
+				   enum object_type type1,
+				   enum object_type type2)
+{
+	int cmp = type1 - type2;
+	if (cmp)
+		return cmp;
+	return offset1 < offset2 ? -1 :
+	       offset1 > offset2 ?  1 :
+	       0;
+}
+
+static int find_ofs_delta(const off_t offset, enum object_type type)
+{
+	int first = 0, last = nr_ofs_deltas;
+
+	while (first < last) {
+		int next = first + (last - first) / 2;
+		struct ofs_delta_entry *delta = &ofs_deltas[next];
+		int cmp;
+
+		cmp = compare_ofs_delta_bases(offset, delta->offset,
+					      type, objects[delta->obj_no].type);
+		if (!cmp)
+			return next;
+		if (cmp < 0) {
+			last = next;
+			continue;
+		}
+		first = next+1;
+	}
+	return -first-1;
+}
+
+static void find_ofs_delta_children(off_t offset,
+				    int *first_index, int *last_index,
+				    enum object_type type)
+{
+	int first = find_ofs_delta(offset, type);
+	int last = first;
+	int end = nr_ofs_deltas - 1;
+
+	if (first < 0) {
+		*first_index = 0;
+		*last_index = -1;
+		return;
+	}
+	while (first > 0 && ofs_deltas[first - 1].offset == offset)
+		--first;
+	while (last < end && ofs_deltas[last + 1].offset == offset)
+		++last;
+	*first_index = first;
+	*last_index = last;
+}
+
+static int compare_ref_delta_bases(const struct object_id *oid1,
+				   const struct object_id *oid2,
+				   enum object_type type1,
+				   enum object_type type2)
+{
+	int cmp = type1 - type2;
+	if (cmp)
+		return cmp;
+	return oidcmp(oid1, oid2);
+}
+
+static int find_ref_delta(const struct object_id *oid, enum object_type type)
+{
+	int first = 0, last = nr_ref_deltas;
+
+	while (first < last) {
+		int next = first + (last - first) / 2;
+		struct ref_delta_entry *delta = &ref_deltas[next];
+		int cmp;
+
+		cmp = compare_ref_delta_bases(oid, &delta->oid,
+					      type, objects[delta->obj_no].type);
+		if (!cmp)
+			return next;
+		if (cmp < 0) {
+			last = next;
+			continue;
+		}
+		first = next+1;
+	}
+	return -first-1;
+}
+
+static void find_ref_delta_children(const struct object_id *oid,
+				    int *first_index, int *last_index,
+				    enum object_type type)
+{
+	int first = find_ref_delta(oid, type);
+	int last = first;
+	int end = nr_ref_deltas - 1;
+
+	if (first < 0) {
+		*first_index = 0;
+		*last_index = -1;
+		return;
+	}
+	while (first > 0 && oideq(&ref_deltas[first - 1].oid, oid))
+		--first;
+	while (last < end && oideq(&ref_deltas[last + 1].oid, oid))
+		++last;
+	*first_index = first;
+	*last_index = last;
+}
+
+struct compare_data {
+	struct object_entry *entry;
+	struct git_istream *st;
+	unsigned char *buf;
+	unsigned long buf_size;
+};
+
+static int compare_objects(const unsigned char *buf, unsigned long size,
+			   void *cb_data)
+{
+	struct compare_data *data = cb_data;
+
+	if (data->buf_size < size) {
+		free(data->buf);
+		data->buf = xmalloc(size);
+		data->buf_size = size;
+	}
+
+	while (size) {
+		ssize_t len = read_istream(data->st, data->buf, size);
+		if (len == 0)
+			die(_("SHA1 COLLISION FOUND WITH %s !"),
+			    oid_to_hex(&data->entry->idx.oid));
+		if (len < 0)
+			die(_("unable to read %s"),
+			    oid_to_hex(&data->entry->idx.oid));
+		if (memcmp(buf, data->buf, len))
+			die(_("SHA1 COLLISION FOUND WITH %s !"),
+			    oid_to_hex(&data->entry->idx.oid));
+		size -= len;
+		buf += len;
+	}
+	return 0;
+}
+
+static int check_collison(struct object_entry *entry)
+{
+	struct compare_data data;
+	enum object_type type;
+	unsigned long size;
+
+	if (entry->size <= big_file_threshold || entry->type != OBJ_BLOB)
+		return -1;
+
+	memset(&data, 0, sizeof(data));
+	data.entry = entry;
+	data.st = open_istream(&entry->idx.oid, &type, &size, NULL);
+	if (!data.st)
+		return -1;
+	if (size != entry->size || type != entry->type)
+		die(_("SHA1 COLLISION FOUND WITH %s !"),
+		    oid_to_hex(&entry->idx.oid));
+	unpack_data(entry, compare_objects, &data);
+	close_istream(data.st);
+	free(data.buf);
+	return 0;
+}
+
+static void sha1_object(const void *data, struct object_entry *obj_entry,
+			unsigned long size, enum object_type type,
+			const struct object_id *oid)
+{
+	void *new_data = NULL;
+	int collision_test_needed = 0;
+
+	assert(data || obj_entry);
+
+	if (startup_info->have_repository) {
+		read_lock();
+		collision_test_needed =
+			has_object_file_with_flags(oid, OBJECT_INFO_QUICK);
+		read_unlock();
+	}
+
+	if (collision_test_needed && !data) {
+		read_lock();
+		if (!check_collison(obj_entry))
+			collision_test_needed = 0;
+		read_unlock();
+	}
+	if (collision_test_needed) {
+		void *has_data;
+		enum object_type has_type;
+		unsigned long has_size;
+		read_lock();
+		has_type = oid_object_info(the_repository, oid, &has_size);
+		if (has_type < 0)
+			die(_("cannot read existing object info %s"), oid_to_hex(oid));
+		if (has_type != type || has_size != size)
+			die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid));
+		has_data = read_object_file(oid, &has_type, &has_size);
+		read_unlock();
+		if (!data)
+			data = new_data = get_data_from_pack(obj_entry);
+		if (!has_data)
+			die(_("cannot read existing object %s"), oid_to_hex(oid));
+		if (size != has_size || type != has_type ||
+		    memcmp(data, has_data, size) != 0)
+			die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid));
+		free(has_data);
+	}
+
+	if (strict || do_fsck_object) {
+		read_lock();
+		if (type == OBJ_BLOB) {
+			struct blob *blob = lookup_blob(the_repository, oid);
+			if (blob)
+				blob->object.flags |= FLAG_CHECKED;
+			else
+				die(_("invalid blob object %s"), oid_to_hex(oid));
+			if (do_fsck_object &&
+			    fsck_object(&blob->object, (void *)data, size, &fsck_options))
+				die(_("fsck error in packed object"));
+		} else {
+			struct object *obj;
+			int eaten;
+			void *buf = (void *) data;
+
+			assert(data && "data can only be NULL for large _blobs_");
+
+			/*
+			 * we do not need to free the memory here, as the
+			 * buf is deleted by the caller.
+			 */
+			obj = parse_object_buffer(the_repository, oid, type,
+						  size, buf,
+						  &eaten);
+			if (!obj)
+				die(_("invalid %s"), type_name(type));
+			if (do_fsck_object &&
+			    fsck_object(obj, buf, size, &fsck_options))
+				die(_("fsck error in packed object"));
+			if (strict && fsck_walk(obj, NULL, &fsck_options))
+				die(_("Not all child objects of %s are reachable"), oid_to_hex(&obj->oid));
+
+			if (obj->type == OBJ_TREE) {
+				struct tree *item = (struct tree *) obj;
+				item->buffer = NULL;
+				obj->parsed = 0;
+			}
+			if (obj->type == OBJ_COMMIT) {
+				struct commit *commit = (struct commit *) obj;
+				if (detach_commit_buffer(commit, NULL) != data)
+					BUG("parse_object_buffer transmogrified our buffer");
+			}
+			obj->flags |= FLAG_CHECKED;
+		}
+		read_unlock();
+	}
+
+	free(new_data);
+}
+
+/*
+ * This function is part of find_unresolved_deltas(). There are two
+ * walkers going in the opposite ways.
+ *
+ * The first one in find_unresolved_deltas() traverses down from
+ * parent node to children, deflating nodes along the way. However,
+ * memory for deflated nodes is limited by delta_base_cache_limit, so
+ * at some point parent node's deflated content may be freed.
+ *
+ * The second walker is this function, which goes from current node up
+ * to top parent if necessary to deflate the node. In normal
+ * situation, its parent node would be already deflated, so it just
+ * needs to apply delta.
+ *
+ * In the worst case scenario, parent node is no longer deflated because
+ * we're running out of delta_base_cache_limit; we need to re-deflate
+ * parents, possibly up to the top base.
+ *
+ * All deflated objects here are subject to be freed if we exceed
+ * delta_base_cache_limit, just like in find_unresolved_deltas(), we
+ * just need to make sure the last node is not freed.
+ */
+static void *get_base_data(struct base_data *c)
+{
+	if (!c->data) {
+		struct object_entry *obj = c->obj;
+		struct base_data **delta = NULL;
+		int delta_nr = 0, delta_alloc = 0;
+
+		while (is_delta_type(c->obj->type) && !c->data) {
+			ALLOC_GROW(delta, delta_nr + 1, delta_alloc);
+			delta[delta_nr++] = c;
+			c = c->base;
+		}
+		if (!delta_nr) {
+			c->data = get_data_from_pack(obj);
+			c->size = obj->size;
+			get_thread_data()->base_cache_used += c->size;
+			prune_base_data(c);
+		}
+		for (; delta_nr > 0; delta_nr--) {
+			void *base, *raw;
+			c = delta[delta_nr - 1];
+			obj = c->obj;
+			base = get_base_data(c->base);
+			raw = get_data_from_pack(obj);
+			c->data = patch_delta(
+				base, c->base->size,
+				raw, obj->size,
+				&c->size);
+			free(raw);
+			if (!c->data)
+				bad_object(obj->idx.offset, _("failed to apply delta"));
+			get_thread_data()->base_cache_used += c->size;
+			prune_base_data(c);
+		}
+		free(delta);
+	}
+	return c->data;
+}
+
+static void resolve_delta(struct object_entry *delta_obj,
+			  struct base_data *base, struct base_data *result)
+{
+	void *base_data, *delta_data;
+
+	if (show_stat) {
+		int i = delta_obj - objects;
+		int j = base->obj - objects;
+		obj_stat[i].delta_depth = obj_stat[j].delta_depth + 1;
+		deepest_delta_lock();
+		if (deepest_delta < obj_stat[i].delta_depth)
+			deepest_delta = obj_stat[i].delta_depth;
+		deepest_delta_unlock();
+		obj_stat[i].base_object_no = j;
+	}
+	delta_data = get_data_from_pack(delta_obj);
+	base_data = get_base_data(base);
+	result->obj = delta_obj;
+	result->data = patch_delta(base_data, base->size,
+				   delta_data, delta_obj->size, &result->size);
+	free(delta_data);
+	if (!result->data)
+		bad_object(delta_obj->idx.offset, _("failed to apply delta"));
+	hash_object_file(result->data, result->size,
+			 type_name(delta_obj->real_type), &delta_obj->idx.oid);
+	sha1_object(result->data, NULL, result->size, delta_obj->real_type,
+		    &delta_obj->idx.oid);
+	counter_lock();
+	nr_resolved_deltas++;
+	counter_unlock();
+}
+
+/*
+ * Standard boolean compare-and-swap: atomically check whether "*type" is
+ * "want"; if so, swap in "set" and return true. Otherwise, leave it untouched
+ * and return false.
+ */
+static int compare_and_swap_type(signed char *type,
+				 enum object_type want,
+				 enum object_type set)
+{
+	enum object_type old;
+
+	type_cas_lock();
+	old = *type;
+	if (old == want)
+		*type = set;
+	type_cas_unlock();
+
+	return old == want;
+}
+
+static struct base_data *find_unresolved_deltas_1(struct base_data *base,
+						  struct base_data *prev_base)
+{
+	if (base->ref_last == -1 && base->ofs_last == -1) {
+		find_ref_delta_children(&base->obj->idx.oid,
+					&base->ref_first, &base->ref_last,
+					OBJ_REF_DELTA);
+
+		find_ofs_delta_children(base->obj->idx.offset,
+					&base->ofs_first, &base->ofs_last,
+					OBJ_OFS_DELTA);
+
+		if (base->ref_last == -1 && base->ofs_last == -1) {
+			free(base->data);
+			return NULL;
+		}
+
+		link_base_data(prev_base, base);
+	}
+
+	if (base->ref_first <= base->ref_last) {
+		struct object_entry *child = objects + ref_deltas[base->ref_first].obj_no;
+		struct base_data *result = alloc_base_data();
+
+		if (!compare_and_swap_type(&child->real_type, OBJ_REF_DELTA,
+					   base->obj->real_type))
+			BUG("child->real_type != OBJ_REF_DELTA");
+
+		resolve_delta(child, base, result);
+		if (base->ref_first == base->ref_last && base->ofs_last == -1)
+			free_base_data(base);
+
+		base->ref_first++;
+		return result;
+	}
+
+	if (base->ofs_first <= base->ofs_last) {
+		struct object_entry *child = objects + ofs_deltas[base->ofs_first].obj_no;
+		struct base_data *result = alloc_base_data();
+
+		assert(child->real_type == OBJ_OFS_DELTA);
+		child->real_type = base->obj->real_type;
+		resolve_delta(child, base, result);
+		if (base->ofs_first == base->ofs_last)
+			free_base_data(base);
+
+		base->ofs_first++;
+		return result;
+	}
+
+	unlink_base_data(base);
+	return NULL;
+}
+
+static void find_unresolved_deltas(struct base_data *base)
+{
+	struct base_data *new_base, *prev_base = NULL;
+	for (;;) {
+		new_base = find_unresolved_deltas_1(base, prev_base);
+
+		if (new_base) {
+			prev_base = base;
+			base = new_base;
+		} else {
+			free(base);
+			base = prev_base;
+			if (!base)
+				return;
+			prev_base = base->base;
+		}
+	}
+}
+
+static int compare_ofs_delta_entry(const void *a, const void *b)
+{
+	const struct ofs_delta_entry *delta_a = a;
+	const struct ofs_delta_entry *delta_b = b;
+
+	return delta_a->offset < delta_b->offset ? -1 :
+	       delta_a->offset > delta_b->offset ?  1 :
+	       0;
+}
+
+static int compare_ref_delta_entry(const void *a, const void *b)
+{
+	const struct ref_delta_entry *delta_a = a;
+	const struct ref_delta_entry *delta_b = b;
+
+	return oidcmp(&delta_a->oid, &delta_b->oid);
+}
+
+static void resolve_base(struct object_entry *obj)
+{
+	struct base_data *base_obj = alloc_base_data();
+	base_obj->obj = obj;
+	base_obj->data = NULL;
+	find_unresolved_deltas(base_obj);
+}
+
+static void *threaded_second_pass(void *data)
+{
+	set_thread_data(data);
+	for (;;) {
+		int i;
+		counter_lock();
+		display_progress(progress, nr_resolved_deltas);
+		counter_unlock();
+		work_lock();
+		while (nr_dispatched < nr_objects &&
+		       is_delta_type(objects[nr_dispatched].type))
+			nr_dispatched++;
+		if (nr_dispatched >= nr_objects) {
+			work_unlock();
+			break;
+		}
+		i = nr_dispatched++;
+		work_unlock();
+
+		resolve_base(&objects[i]);
+	}
+	return NULL;
+}
+
+/*
+ * First pass:
+ * - find locations of all objects;
+ * - calculate SHA1 of all non-delta objects;
+ * - remember base (SHA1 or offset) for all deltas.
+ */
+static void parse_pack_objects(unsigned char *hash)
+{
+	int i, nr_delays = 0;
+	struct ofs_delta_entry *ofs_delta = ofs_deltas;
+	struct object_id ref_delta_oid;
+	struct stat st;
+
+	if (verbose)
+		progress = start_progress(
+				from_stdin ? _("Receiving objects") : _("Indexing objects"),
+				nr_objects);
+	for (i = 0; i < nr_objects; i++) {
+		struct object_entry *obj = &objects[i];
+		void *data = unpack_raw_entry(obj, &ofs_delta->offset,
+					      &ref_delta_oid,
+					      &obj->idx.oid);
+		obj->real_type = obj->type;
+		if (obj->type == OBJ_OFS_DELTA) {
+			nr_ofs_deltas++;
+			ofs_delta->obj_no = i;
+			ofs_delta++;
+		} else if (obj->type == OBJ_REF_DELTA) {
+			ALLOC_GROW(ref_deltas, nr_ref_deltas + 1, ref_deltas_alloc);
+			oidcpy(&ref_deltas[nr_ref_deltas].oid, &ref_delta_oid);
+			ref_deltas[nr_ref_deltas].obj_no = i;
+			nr_ref_deltas++;
+		} else if (!data) {
+			/* large blobs, check later */
+			obj->real_type = OBJ_BAD;
+			nr_delays++;
+		} else
+			sha1_object(data, NULL, obj->size, obj->type,
+				    &obj->idx.oid);
+		free(data);
+		display_progress(progress, i+1);
+	}
+	objects[i].idx.offset = consumed_bytes;
+	stop_progress(&progress);
+
+	/* Check pack integrity */
+	flush();
+	the_hash_algo->final_fn(hash, &input_ctx);
+	if (!hasheq(fill(the_hash_algo->rawsz), hash))
+		die(_("pack is corrupted (SHA1 mismatch)"));
+	use(the_hash_algo->rawsz);
+
+	/* If input_fd is a file, we should have reached its end now. */
+	if (fstat(input_fd, &st))
+		die_errno(_("cannot fstat packfile"));
+	if (S_ISREG(st.st_mode) &&
+			lseek(input_fd, 0, SEEK_CUR) - input_len != st.st_size)
+		die(_("pack has junk at the end"));
+
+	for (i = 0; i < nr_objects; i++) {
+		struct object_entry *obj = &objects[i];
+		if (obj->real_type != OBJ_BAD)
+			continue;
+		obj->real_type = obj->type;
+		sha1_object(NULL, obj, obj->size, obj->type,
+			    &obj->idx.oid);
+		nr_delays--;
+	}
+	if (nr_delays)
+		die(_("confusion beyond insanity in parse_pack_objects()"));
+}
+
+/*
+ * Second pass:
+ * - for all non-delta objects, look if it is used as a base for
+ *   deltas;
+ * - if used as a base, uncompress the object and apply all deltas,
+ *   recursively checking if the resulting object is used as a base
+ *   for some more deltas.
+ */
+static void resolve_deltas(void)
+{
+	int i;
+
+	if (!nr_ofs_deltas && !nr_ref_deltas)
+		return;
+
+	/* Sort deltas by base SHA1/offset for fast searching */
+	QSORT(ofs_deltas, nr_ofs_deltas, compare_ofs_delta_entry);
+	QSORT(ref_deltas, nr_ref_deltas, compare_ref_delta_entry);
+
+	if (verbose || show_resolving_progress)
+		progress = start_progress(_("Resolving deltas"),
+					  nr_ref_deltas + nr_ofs_deltas);
+
+	nr_dispatched = 0;
+	if (nr_threads > 1 || getenv("GIT_FORCE_THREADS")) {
+		init_thread();
+		for (i = 0; i < nr_threads; i++) {
+			int ret = pthread_create(&thread_data[i].thread, NULL,
+						 threaded_second_pass, thread_data + i);
+			if (ret)
+				die(_("unable to create thread: %s"),
+				    strerror(ret));
+		}
+		for (i = 0; i < nr_threads; i++)
+			pthread_join(thread_data[i].thread, NULL);
+		cleanup_thread();
+		return;
+	}
+
+	for (i = 0; i < nr_objects; i++) {
+		struct object_entry *obj = &objects[i];
+
+		if (is_delta_type(obj->type))
+			continue;
+		resolve_base(obj);
+		display_progress(progress, nr_resolved_deltas);
+	}
+}
+
+/*
+ * Third pass:
+ * - append objects to convert thin pack to full pack if required
+ * - write the final pack hash
+ */
+static void fix_unresolved_deltas(struct hashfile *f);
+static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned char *pack_hash)
+{
+	if (nr_ref_deltas + nr_ofs_deltas == nr_resolved_deltas) {
+		stop_progress(&progress);
+		/* Flush remaining pack final hash. */
+		flush();
+		return;
+	}
+
+	if (fix_thin_pack) {
+		struct hashfile *f;
+		unsigned char read_hash[GIT_MAX_RAWSZ], tail_hash[GIT_MAX_RAWSZ];
+		struct strbuf msg = STRBUF_INIT;
+		int nr_unresolved = nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas;
+		int nr_objects_initial = nr_objects;
+		if (nr_unresolved <= 0)
+			die(_("confusion beyond insanity"));
+		REALLOC_ARRAY(objects, nr_objects + nr_unresolved + 1);
+		memset(objects + nr_objects + 1, 0,
+		       nr_unresolved * sizeof(*objects));
+		f = hashfd(output_fd, curr_pack);
+		fix_unresolved_deltas(f);
+		strbuf_addf(&msg, Q_("completed with %d local object",
+				     "completed with %d local objects",
+				     nr_objects - nr_objects_initial),
+			    nr_objects - nr_objects_initial);
+		stop_progress_msg(&progress, msg.buf);
+		strbuf_release(&msg);
+		finalize_hashfile(f, tail_hash, 0);
+		hashcpy(read_hash, pack_hash);
+		fixup_pack_header_footer(output_fd, pack_hash,
+					 curr_pack, nr_objects,
+					 read_hash, consumed_bytes-the_hash_algo->rawsz);
+		if (!hasheq(read_hash, tail_hash))
+			die(_("Unexpected tail checksum for %s "
+			      "(disk corruption?)"), curr_pack);
+	}
+	if (nr_ofs_deltas + nr_ref_deltas != nr_resolved_deltas)
+		die(Q_("pack has %d unresolved delta",
+		       "pack has %d unresolved deltas",
+		       nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas),
+		    nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas);
+}
+
+static int write_compressed(struct hashfile *f, void *in, unsigned int size)
+{
+	git_zstream stream;
+	int status;
+	unsigned char outbuf[4096];
+
+	git_deflate_init(&stream, zlib_compression_level);
+	stream.next_in = in;
+	stream.avail_in = size;
+
+	do {
+		stream.next_out = outbuf;
+		stream.avail_out = sizeof(outbuf);
+		status = git_deflate(&stream, Z_FINISH);
+		hashwrite(f, outbuf, sizeof(outbuf) - stream.avail_out);
+	} while (status == Z_OK);
+
+	if (status != Z_STREAM_END)
+		die(_("unable to deflate appended object (%d)"), status);
+	size = stream.total_out;
+	git_deflate_end(&stream);
+	return size;
+}
+
+static struct object_entry *append_obj_to_pack(struct hashfile *f,
+			       const unsigned char *sha1, void *buf,
+			       unsigned long size, enum object_type type)
+{
+	struct object_entry *obj = &objects[nr_objects++];
+	unsigned char header[10];
+	unsigned long s = size;
+	int n = 0;
+	unsigned char c = (type << 4) | (s & 15);
+	s >>= 4;
+	while (s) {
+		header[n++] = c | 0x80;
+		c = s & 0x7f;
+		s >>= 7;
+	}
+	header[n++] = c;
+	crc32_begin(f);
+	hashwrite(f, header, n);
+	obj[0].size = size;
+	obj[0].hdr_size = n;
+	obj[0].type = type;
+	obj[0].real_type = type;
+	obj[1].idx.offset = obj[0].idx.offset + n;
+	obj[1].idx.offset += write_compressed(f, buf, size);
+	obj[0].idx.crc32 = crc32_end(f);
+	hashflush(f);
+	hashcpy(obj->idx.oid.hash, sha1);
+	return obj;
+}
+
+static int delta_pos_compare(const void *_a, const void *_b)
+{
+	struct ref_delta_entry *a = *(struct ref_delta_entry **)_a;
+	struct ref_delta_entry *b = *(struct ref_delta_entry **)_b;
+	return a->obj_no - b->obj_no;
+}
+
+static void fix_unresolved_deltas(struct hashfile *f)
+{
+	struct ref_delta_entry **sorted_by_pos;
+	int i;
+
+	/*
+	 * Since many unresolved deltas may well be themselves base objects
+	 * for more unresolved deltas, we really want to include the
+	 * smallest number of base objects that would cover as much delta
+	 * as possible by picking the
+	 * trunc deltas first, allowing for other deltas to resolve without
+	 * additional base objects.  Since most base objects are to be found
+	 * before deltas depending on them, a good heuristic is to start
+	 * resolving deltas in the same order as their position in the pack.
+	 */
+	ALLOC_ARRAY(sorted_by_pos, nr_ref_deltas);
+	for (i = 0; i < nr_ref_deltas; i++)
+		sorted_by_pos[i] = &ref_deltas[i];
+	QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
+
+	if (repository_format_partial_clone) {
+		/*
+		 * Prefetch the delta bases.
+		 */
+		struct oid_array to_fetch = OID_ARRAY_INIT;
+		for (i = 0; i < nr_ref_deltas; i++) {
+			struct ref_delta_entry *d = sorted_by_pos[i];
+			if (!oid_object_info_extended(the_repository, &d->oid,
+						      NULL,
+						      OBJECT_INFO_FOR_PREFETCH))
+				continue;
+			oid_array_append(&to_fetch, &d->oid);
+		}
+		if (to_fetch.nr)
+			fetch_objects(repository_format_partial_clone,
+				      to_fetch.oid, to_fetch.nr);
+		oid_array_clear(&to_fetch);
+	}
+
+	for (i = 0; i < nr_ref_deltas; i++) {
+		struct ref_delta_entry *d = sorted_by_pos[i];
+		enum object_type type;
+		struct base_data *base_obj = alloc_base_data();
+
+		if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
+			continue;
+		base_obj->data = read_object_file(&d->oid, &type,
+						  &base_obj->size);
+		if (!base_obj->data)
+			continue;
+
+		if (check_object_signature(&d->oid, base_obj->data,
+				base_obj->size, type_name(type)))
+			die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
+		base_obj->obj = append_obj_to_pack(f, d->oid.hash,
+					base_obj->data, base_obj->size, type);
+		find_unresolved_deltas(base_obj);
+		display_progress(progress, nr_resolved_deltas);
+	}
+	free(sorted_by_pos);
+}
+
+static const char *derive_filename(const char *pack_name, const char *suffix,
+				   struct strbuf *buf)
+{
+	size_t len;
+	if (!strip_suffix(pack_name, ".pack", &len))
+		die(_("packfile name '%s' does not end with '.pack'"),
+		    pack_name);
+	strbuf_add(buf, pack_name, len);
+	strbuf_addch(buf, '.');
+	strbuf_addstr(buf, suffix);
+	return buf->buf;
+}
+
+static void write_special_file(const char *suffix, const char *msg,
+			       const char *pack_name, const unsigned char *hash,
+			       const char **report)
+{
+	struct strbuf name_buf = STRBUF_INIT;
+	const char *filename;
+	int fd;
+	int msg_len = strlen(msg);
+
+	if (pack_name)
+		filename = derive_filename(pack_name, suffix, &name_buf);
+	else
+		filename = odb_pack_name(&name_buf, hash, suffix);
+
+	fd = odb_pack_keep(filename);
+	if (fd < 0) {
+		if (errno != EEXIST)
+			die_errno(_("cannot write %s file '%s'"),
+				  suffix, filename);
+	} else {
+		if (msg_len > 0) {
+			write_or_die(fd, msg, msg_len);
+			write_or_die(fd, "\n", 1);
+		}
+		if (close(fd) != 0)
+			die_errno(_("cannot close written %s file '%s'"),
+				  suffix, filename);
+		if (report)
+			*report = suffix;
+	}
+	strbuf_release(&name_buf);
+}
+
+static void final(const char *final_pack_name, const char *curr_pack_name,
+		  const char *final_index_name, const char *curr_index_name,
+		  const char *keep_msg, const char *promisor_msg,
+		  unsigned char *hash)
+{
+	const char *report = "pack";
+	struct strbuf pack_name = STRBUF_INIT;
+	struct strbuf index_name = STRBUF_INIT;
+	int err;
+
+	if (!from_stdin) {
+		close(input_fd);
+	} else {
+		fsync_or_die(output_fd, curr_pack_name);
+		err = close(output_fd);
+		if (err)
+			die_errno(_("error while closing pack file"));
+	}
+
+	if (keep_msg)
+		write_special_file("keep", keep_msg, final_pack_name, hash,
+				   &report);
+	if (promisor_msg)
+		write_special_file("promisor", promisor_msg, final_pack_name,
+				   hash, NULL);
+
+	if (final_pack_name != curr_pack_name) {
+		if (!final_pack_name)
+			final_pack_name = odb_pack_name(&pack_name, hash, "pack");
+		if (finalize_object_file(curr_pack_name, final_pack_name))
+			die(_("cannot store pack file"));
+	} else if (from_stdin)
+		chmod(final_pack_name, 0444);
+
+	if (final_index_name != curr_index_name) {
+		if (!final_index_name)
+			final_index_name = odb_pack_name(&index_name, hash, "idx");
+		if (finalize_object_file(curr_index_name, final_index_name))
+			die(_("cannot store index file"));
+	} else
+		chmod(final_index_name, 0444);
+
+	if (do_fsck_object) {
+		struct packed_git *p;
+		p = add_packed_git(final_index_name, strlen(final_index_name), 0);
+		if (p)
+			install_packed_git(the_repository, p);
+	}
+
+	if (!from_stdin) {
+		printf("%s\n", sha1_to_hex(hash));
+	} else {
+		struct strbuf buf = STRBUF_INIT;
+
+		strbuf_addf(&buf, "%s\t%s\n", report, sha1_to_hex(hash));
+		write_or_die(1, buf.buf, buf.len);
+		strbuf_release(&buf);
+
+		/*
+		 * Let's just mimic git-unpack-objects here and write
+		 * the last part of the input buffer to stdout.
+		 */
+		while (input_len) {
+			err = xwrite(1, input_buffer + input_offset, input_len);
+			if (err <= 0)
+				break;
+			input_len -= err;
+			input_offset += err;
+		}
+	}
+
+	strbuf_release(&index_name);
+	strbuf_release(&pack_name);
+}
+
+static int git_index_pack_config(const char *k, const char *v, void *cb)
+{
+	struct pack_idx_option *opts = cb;
+
+	if (!strcmp(k, "pack.indexversion")) {
+		opts->version = git_config_int(k, v);
+		if (opts->version > 2)
+			die(_("bad pack.indexversion=%"PRIu32), opts->version);
+		return 0;
+	}
+	if (!strcmp(k, "pack.threads")) {
+		nr_threads = git_config_int(k, v);
+		if (nr_threads < 0)
+			die(_("invalid number of threads specified (%d)"),
+			    nr_threads);
+		if (!HAVE_THREADS && nr_threads != 1) {
+			warning(_("no threads support, ignoring %s"), k);
+			nr_threads = 1;
+		}
+		return 0;
+	}
+	return git_default_config(k, v, cb);
+}
+
+static int cmp_uint32(const void *a_, const void *b_)
+{
+	uint32_t a = *((uint32_t *)a_);
+	uint32_t b = *((uint32_t *)b_);
+
+	return (a < b) ? -1 : (a != b);
+}
+
+static void read_v2_anomalous_offsets(struct packed_git *p,
+				      struct pack_idx_option *opts)
+{
+	const uint32_t *idx1, *idx2;
+	uint32_t i;
+	const uint32_t hashwords = the_hash_algo->rawsz / sizeof(uint32_t);
+
+	/* The address of the 4-byte offset table */
+	idx1 = (((const uint32_t *)p->index_data)
+		+ 2 /* 8-byte header */
+		+ 256 /* fan out */
+		+ hashwords * p->num_objects /* object ID table */
+		+ p->num_objects /* CRC32 table */
+		);
+
+	/* The address of the 8-byte offset table */
+	idx2 = idx1 + p->num_objects;
+
+	for (i = 0; i < p->num_objects; i++) {
+		uint32_t off = ntohl(idx1[i]);
+		if (!(off & 0x80000000))
+			continue;
+		off = off & 0x7fffffff;
+		check_pack_index_ptr(p, &idx2[off * 2]);
+		if (idx2[off * 2])
+			continue;
+		/*
+		 * The real offset is ntohl(idx2[off * 2]) in high 4
+		 * octets, and ntohl(idx2[off * 2 + 1]) in low 4
+		 * octets.  But idx2[off * 2] is Zero!!!
+		 */
+		ALLOC_GROW(opts->anomaly, opts->anomaly_nr + 1, opts->anomaly_alloc);
+		opts->anomaly[opts->anomaly_nr++] = ntohl(idx2[off * 2 + 1]);
+	}
+
+	QSORT(opts->anomaly, opts->anomaly_nr, cmp_uint32);
+}
+
+static void read_idx_option(struct pack_idx_option *opts, const char *pack_name)
+{
+	struct packed_git *p = add_packed_git(pack_name, strlen(pack_name), 1);
+
+	if (!p)
+		die(_("Cannot open existing pack file '%s'"), pack_name);
+	if (open_pack_index(p))
+		die(_("Cannot open existing pack idx file for '%s'"), pack_name);
+
+	/* Read the attributes from the existing idx file */
+	opts->version = p->index_version;
+
+	if (opts->version == 2)
+		read_v2_anomalous_offsets(p, opts);
+
+	/*
+	 * Get rid of the idx file as we do not need it anymore.
+	 * NEEDSWORK: extract this bit from free_pack_by_name() in
+	 * sha1-file.c, perhaps?  It shouldn't matter very much as we
+	 * know we haven't installed this pack (hence we never have
+	 * read anything from it).
+	 */
+	close_pack_index(p);
+	free(p);
+}
+
+static void show_pack_info(int stat_only)
+{
+	int i, baseobjects = nr_objects - nr_ref_deltas - nr_ofs_deltas;
+	unsigned long *chain_histogram = NULL;
+
+	if (deepest_delta)
+		chain_histogram = xcalloc(deepest_delta, sizeof(unsigned long));
+
+	for (i = 0; i < nr_objects; i++) {
+		struct object_entry *obj = &objects[i];
+
+		if (is_delta_type(obj->type))
+			chain_histogram[obj_stat[i].delta_depth - 1]++;
+		if (stat_only)
+			continue;
+		printf("%s %-6s %"PRIuMAX" %"PRIuMAX" %"PRIuMAX,
+		       oid_to_hex(&obj->idx.oid),
+		       type_name(obj->real_type), (uintmax_t)obj->size,
+		       (uintmax_t)(obj[1].idx.offset - obj->idx.offset),
+		       (uintmax_t)obj->idx.offset);
+		if (is_delta_type(obj->type)) {
+			struct object_entry *bobj = &objects[obj_stat[i].base_object_no];
+			printf(" %u %s", obj_stat[i].delta_depth,
+			       oid_to_hex(&bobj->idx.oid));
+		}
+		putchar('\n');
+	}
+
+	if (baseobjects)
+		printf_ln(Q_("non delta: %d object",
+			     "non delta: %d objects",
+			     baseobjects),
+			  baseobjects);
+	for (i = 0; i < deepest_delta; i++) {
+		if (!chain_histogram[i])
+			continue;
+		printf_ln(Q_("chain length = %d: %lu object",
+			     "chain length = %d: %lu objects",
+			     chain_histogram[i]),
+			  i + 1,
+			  chain_histogram[i]);
+	}
+}
+
+int cmd_index_pack(int argc, const char **argv, const char *prefix)
+{
+	int i, fix_thin_pack = 0, verify = 0, stat_only = 0;
+	const char *curr_index;
+	const char *index_name = NULL, *pack_name = NULL;
+	const char *keep_msg = NULL;
+	const char *promisor_msg = NULL;
+	struct strbuf index_name_buf = STRBUF_INIT;
+	struct pack_idx_entry **idx_objects;
+	struct pack_idx_option opts;
+	unsigned char pack_hash[GIT_MAX_RAWSZ];
+	unsigned foreign_nr = 1;	/* zero is a "good" value, assume bad */
+	int report_end_of_input = 0;
+
+	/*
+	 * index-pack never needs to fetch missing objects except when
+	 * REF_DELTA bases are missing (which are explicitly handled). It only
+	 * accesses the repo to do hash collision checks and to check which
+	 * REF_DELTA bases need to be fetched.
+	 */
+	fetch_if_missing = 0;
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(index_pack_usage);
+
+	read_replace_refs = 0;
+	fsck_options.walk = mark_link;
+
+	reset_pack_idx_option(&opts);
+	git_config(git_index_pack_config, &opts);
+	if (prefix && chdir(prefix))
+		die(_("Cannot come back to cwd"));
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (*arg == '-') {
+			if (!strcmp(arg, "--stdin")) {
+				from_stdin = 1;
+			} else if (!strcmp(arg, "--fix-thin")) {
+				fix_thin_pack = 1;
+			} else if (skip_to_optional_arg(arg, "--strict", &arg)) {
+				strict = 1;
+				do_fsck_object = 1;
+				fsck_set_msg_types(&fsck_options, arg);
+			} else if (!strcmp(arg, "--check-self-contained-and-connected")) {
+				strict = 1;
+				check_self_contained_and_connected = 1;
+			} else if (!strcmp(arg, "--fsck-objects")) {
+				do_fsck_object = 1;
+			} else if (!strcmp(arg, "--verify")) {
+				verify = 1;
+			} else if (!strcmp(arg, "--verify-stat")) {
+				verify = 1;
+				show_stat = 1;
+			} else if (!strcmp(arg, "--verify-stat-only")) {
+				verify = 1;
+				show_stat = 1;
+				stat_only = 1;
+			} else if (skip_to_optional_arg(arg, "--keep", &keep_msg)) {
+				; /* nothing to do */
+			} else if (skip_to_optional_arg(arg, "--promisor", &promisor_msg)) {
+				; /* already parsed */
+			} else if (starts_with(arg, "--threads=")) {
+				char *end;
+				nr_threads = strtoul(arg+10, &end, 0);
+				if (!arg[10] || *end || nr_threads < 0)
+					usage(index_pack_usage);
+				if (!HAVE_THREADS && nr_threads != 1) {
+					warning(_("no threads support, ignoring %s"), arg);
+					nr_threads = 1;
+				}
+			} else if (starts_with(arg, "--pack_header=")) {
+				struct pack_header *hdr;
+				char *c;
+
+				hdr = (struct pack_header *)input_buffer;
+				hdr->hdr_signature = htonl(PACK_SIGNATURE);
+				hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10));
+				if (*c != ',')
+					die(_("bad %s"), arg);
+				hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10));
+				if (*c)
+					die(_("bad %s"), arg);
+				input_len = sizeof(*hdr);
+			} else if (!strcmp(arg, "-v")) {
+				verbose = 1;
+			} else if (!strcmp(arg, "--show-resolving-progress")) {
+				show_resolving_progress = 1;
+			} else if (!strcmp(arg, "--report-end-of-input")) {
+				report_end_of_input = 1;
+			} else if (!strcmp(arg, "-o")) {
+				if (index_name || (i+1) >= argc)
+					usage(index_pack_usage);
+				index_name = argv[++i];
+			} else if (starts_with(arg, "--index-version=")) {
+				char *c;
+				opts.version = strtoul(arg + 16, &c, 10);
+				if (opts.version > 2)
+					die(_("bad %s"), arg);
+				if (*c == ',')
+					opts.off32_limit = strtoul(c+1, &c, 0);
+				if (*c || opts.off32_limit & 0x80000000)
+					die(_("bad %s"), arg);
+			} else if (skip_prefix(arg, "--max-input-size=", &arg)) {
+				max_input_size = strtoumax(arg, NULL, 10);
+			} else
+				usage(index_pack_usage);
+			continue;
+		}
+
+		if (pack_name)
+			usage(index_pack_usage);
+		pack_name = arg;
+	}
+
+	if (!pack_name && !from_stdin)
+		usage(index_pack_usage);
+	if (fix_thin_pack && !from_stdin)
+		die(_("--fix-thin cannot be used without --stdin"));
+	if (from_stdin && !startup_info->have_repository)
+		die(_("--stdin requires a git repository"));
+	if (!index_name && pack_name)
+		index_name = derive_filename(pack_name, "idx", &index_name_buf);
+
+	if (verify) {
+		if (!index_name)
+			die(_("--verify with no packfile name given"));
+		read_idx_option(&opts, index_name);
+		opts.flags |= WRITE_IDX_VERIFY | WRITE_IDX_STRICT;
+	}
+	if (strict)
+		opts.flags |= WRITE_IDX_STRICT;
+
+	if (HAVE_THREADS && !nr_threads) {
+		nr_threads = online_cpus();
+		/* An experiment showed that more threads does not mean faster */
+		if (nr_threads > 3)
+			nr_threads = 3;
+	}
+
+	curr_pack = open_pack_file(pack_name);
+	parse_pack_header();
+	objects = xcalloc(st_add(nr_objects, 1), sizeof(struct object_entry));
+	if (show_stat)
+		obj_stat = xcalloc(st_add(nr_objects, 1), sizeof(struct object_stat));
+	ofs_deltas = xcalloc(nr_objects, sizeof(struct ofs_delta_entry));
+	parse_pack_objects(pack_hash);
+	if (report_end_of_input)
+		write_in_full(2, "\0", 1);
+	resolve_deltas();
+	conclude_pack(fix_thin_pack, curr_pack, pack_hash);
+	free(ofs_deltas);
+	free(ref_deltas);
+	if (strict)
+		foreign_nr = check_objects();
+
+	if (show_stat)
+		show_pack_info(stat_only);
+
+	ALLOC_ARRAY(idx_objects, nr_objects);
+	for (i = 0; i < nr_objects; i++)
+		idx_objects[i] = &objects[i].idx;
+	curr_index = write_idx_file(index_name, idx_objects, nr_objects, &opts, pack_hash);
+	free(idx_objects);
+
+	if (!verify)
+		final(pack_name, curr_pack,
+		      index_name, curr_index,
+		      keep_msg, promisor_msg,
+		      pack_hash);
+	else
+		close(input_fd);
+
+	if (do_fsck_object && fsck_finish(&fsck_options))
+		die(_("fsck error in pack objects"));
+
+	free(objects);
+	strbuf_release(&index_name_buf);
+	if (pack_name == NULL)
+		free((void *) curr_pack);
+	if (index_name == NULL)
+		free((void *) curr_index);
+
+	/*
+	 * Let the caller know this pack is not self contained
+	 */
+	if (check_self_contained_and_connected && foreign_nr)
+		return 1;
+
+	return 0;
+}
diff --git a/builtin/init-db.c b/builtin/init-db.c
new file mode 100644
index 0000000000..944ec77fe1
--- /dev/null
+++ b/builtin/init-db.c
@@ -0,0 +1,601 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+#include "config.h"
+#include "refs.h"
+#include "builtin.h"
+#include "exec-cmd.h"
+#include "parse-options.h"
+
+#ifndef DEFAULT_GIT_TEMPLATE_DIR
+#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
+#endif
+
+#ifdef NO_TRUSTABLE_FILEMODE
+#define TEST_FILEMODE 0
+#else
+#define TEST_FILEMODE 1
+#endif
+
+static int init_is_bare_repository = 0;
+static int init_shared_repository = -1;
+static const char *init_db_template_dir;
+
+static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
+			     DIR *dir)
+{
+	size_t path_baselen = path->len;
+	size_t template_baselen = template_path->len;
+	struct dirent *de;
+
+	/* Note: if ".git/hooks" file exists in the repository being
+	 * re-initialized, /etc/core-git/templates/hooks/update would
+	 * cause "git init" to fail here.  I think this is sane but
+	 * it means that the set of templates we ship by default, along
+	 * with the way the namespace under .git/ is organized, should
+	 * be really carefully chosen.
+	 */
+	safe_create_dir(path->buf, 1);
+	while ((de = readdir(dir)) != NULL) {
+		struct stat st_git, st_template;
+		int exists = 0;
+
+		strbuf_setlen(path, path_baselen);
+		strbuf_setlen(template_path, template_baselen);
+
+		if (de->d_name[0] == '.')
+			continue;
+		strbuf_addstr(path, de->d_name);
+		strbuf_addstr(template_path, de->d_name);
+		if (lstat(path->buf, &st_git)) {
+			if (errno != ENOENT)
+				die_errno(_("cannot stat '%s'"), path->buf);
+		}
+		else
+			exists = 1;
+
+		if (lstat(template_path->buf, &st_template))
+			die_errno(_("cannot stat template '%s'"), template_path->buf);
+
+		if (S_ISDIR(st_template.st_mode)) {
+			DIR *subdir = opendir(template_path->buf);
+			if (!subdir)
+				die_errno(_("cannot opendir '%s'"), template_path->buf);
+			strbuf_addch(path, '/');
+			strbuf_addch(template_path, '/');
+			copy_templates_1(path, template_path, subdir);
+			closedir(subdir);
+		}
+		else if (exists)
+			continue;
+		else if (S_ISLNK(st_template.st_mode)) {
+			struct strbuf lnk = STRBUF_INIT;
+			if (strbuf_readlink(&lnk, template_path->buf,
+					    st_template.st_size) < 0)
+				die_errno(_("cannot readlink '%s'"), template_path->buf);
+			if (symlink(lnk.buf, path->buf))
+				die_errno(_("cannot symlink '%s' '%s'"),
+					  lnk.buf, path->buf);
+			strbuf_release(&lnk);
+		}
+		else if (S_ISREG(st_template.st_mode)) {
+			if (copy_file(path->buf, template_path->buf, st_template.st_mode))
+				die_errno(_("cannot copy '%s' to '%s'"),
+					  template_path->buf, path->buf);
+		}
+		else
+			error(_("ignoring template %s"), template_path->buf);
+	}
+}
+
+static void copy_templates(const char *template_dir)
+{
+	struct strbuf path = STRBUF_INIT;
+	struct strbuf template_path = STRBUF_INIT;
+	size_t template_len;
+	struct repository_format template_format = REPOSITORY_FORMAT_INIT;
+	struct strbuf err = STRBUF_INIT;
+	DIR *dir;
+	char *to_free = NULL;
+
+	if (!template_dir)
+		template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
+	if (!template_dir)
+		template_dir = init_db_template_dir;
+	if (!template_dir)
+		template_dir = to_free = system_path(DEFAULT_GIT_TEMPLATE_DIR);
+	if (!template_dir[0]) {
+		free(to_free);
+		return;
+	}
+
+	strbuf_addstr(&template_path, template_dir);
+	strbuf_complete(&template_path, '/');
+	template_len = template_path.len;
+
+	dir = opendir(template_path.buf);
+	if (!dir) {
+		warning(_("templates not found in %s"), template_dir);
+		goto free_return;
+	}
+
+	/* Make sure that template is from the correct vintage */
+	strbuf_addstr(&template_path, "config");
+	read_repository_format(&template_format, template_path.buf);
+	strbuf_setlen(&template_path, template_len);
+
+	/*
+	 * No mention of version at all is OK, but anything else should be
+	 * verified.
+	 */
+	if (template_format.version >= 0 &&
+	    verify_repository_format(&template_format, &err) < 0) {
+		warning(_("not copying templates from '%s': %s"),
+			  template_dir, err.buf);
+		strbuf_release(&err);
+		goto close_free_return;
+	}
+
+	strbuf_addstr(&path, get_git_common_dir());
+	strbuf_complete(&path, '/');
+	copy_templates_1(&path, &template_path, dir);
+close_free_return:
+	closedir(dir);
+free_return:
+	free(to_free);
+	strbuf_release(&path);
+	strbuf_release(&template_path);
+	clear_repository_format(&template_format);
+}
+
+static int git_init_db_config(const char *k, const char *v, void *cb)
+{
+	if (!strcmp(k, "init.templatedir"))
+		return git_config_pathname(&init_db_template_dir, k, v);
+
+	if (starts_with(k, "core."))
+		return platform_core_config(k, v, cb);
+
+	return 0;
+}
+
+/*
+ * If the git_dir is not directly inside the working tree, then git will not
+ * find it by default, and we need to set the worktree explicitly.
+ */
+static int needs_work_tree_config(const char *git_dir, const char *work_tree)
+{
+	if (!strcmp(work_tree, "/") && !strcmp(git_dir, "/.git"))
+		return 0;
+	if (skip_prefix(git_dir, work_tree, &git_dir) &&
+	    !strcmp(git_dir, "/.git"))
+		return 0;
+	return 1;
+}
+
+static int create_default_files(const char *template_path,
+				const char *original_git_dir)
+{
+	struct stat st1;
+	struct strbuf buf = STRBUF_INIT;
+	char *path;
+	char repo_version_string[10];
+	char junk[2];
+	int reinit;
+	int filemode;
+	struct strbuf err = STRBUF_INIT;
+
+	/* Just look for `init.templatedir` */
+	init_db_template_dir = NULL; /* re-set in case it was set before */
+	git_config(git_init_db_config, NULL);
+
+	/*
+	 * First copy the templates -- we might have the default
+	 * config file there, in which case we would want to read
+	 * from it after installing.
+	 *
+	 * Before reading that config, we also need to clear out any cached
+	 * values (since we've just potentially changed what's available on
+	 * disk).
+	 */
+	copy_templates(template_path);
+	git_config_clear();
+	reset_shared_repository();
+	git_config(git_default_config, NULL);
+
+	/*
+	 * We must make sure command-line options continue to override any
+	 * values we might have just re-read from the config.
+	 */
+	is_bare_repository_cfg = init_is_bare_repository;
+	if (init_shared_repository != -1)
+		set_shared_repository(init_shared_repository);
+
+	/*
+	 * We would have created the above under user's umask -- under
+	 * shared-repository settings, we would need to fix them up.
+	 */
+	if (get_shared_repository()) {
+		adjust_shared_perm(get_git_dir());
+	}
+
+	/*
+	 * We need to create a "refs" dir in any case so that older
+	 * versions of git can tell that this is a repository.
+	 */
+	safe_create_dir(git_path("refs"), 1);
+	adjust_shared_perm(git_path("refs"));
+
+	if (refs_init_db(&err))
+		die("failed to set up refs db: %s", err.buf);
+
+	/*
+	 * Create the default symlink from ".git/HEAD" to the "master"
+	 * branch, if it does not exist yet.
+	 */
+	path = git_path_buf(&buf, "HEAD");
+	reinit = (!access(path, R_OK)
+		  || readlink(path, junk, sizeof(junk)-1) != -1);
+	if (!reinit) {
+		if (create_symref("HEAD", "refs/heads/master", NULL) < 0)
+			exit(1);
+	}
+
+	/* This forces creation of new config file */
+	xsnprintf(repo_version_string, sizeof(repo_version_string),
+		  "%d", GIT_REPO_VERSION);
+	git_config_set("core.repositoryformatversion", repo_version_string);
+
+	/* Check filemode trustability */
+	path = git_path_buf(&buf, "config");
+	filemode = TEST_FILEMODE;
+	if (TEST_FILEMODE && !lstat(path, &st1)) {
+		struct stat st2;
+		filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
+				!lstat(path, &st2) &&
+				st1.st_mode != st2.st_mode &&
+				!chmod(path, st1.st_mode));
+		if (filemode && !reinit && (st1.st_mode & S_IXUSR))
+			filemode = 0;
+	}
+	git_config_set("core.filemode", filemode ? "true" : "false");
+
+	if (is_bare_repository())
+		git_config_set("core.bare", "true");
+	else {
+		const char *work_tree = get_git_work_tree();
+		git_config_set("core.bare", "false");
+		/* allow template config file to override the default */
+		if (log_all_ref_updates == LOG_REFS_UNSET)
+			git_config_set("core.logallrefupdates", "true");
+		if (needs_work_tree_config(original_git_dir, work_tree))
+			git_config_set("core.worktree", work_tree);
+	}
+
+	if (!reinit) {
+		/* Check if symlink is supported in the work tree */
+		path = git_path_buf(&buf, "tXXXXXX");
+		if (!close(xmkstemp(path)) &&
+		    !unlink(path) &&
+		    !symlink("testing", path) &&
+		    !lstat(path, &st1) &&
+		    S_ISLNK(st1.st_mode))
+			unlink(path); /* good */
+		else
+			git_config_set("core.symlinks", "false");
+
+		/* Check if the filesystem is case-insensitive */
+		path = git_path_buf(&buf, "CoNfIg");
+		if (!access(path, F_OK))
+			git_config_set("core.ignorecase", "true");
+		probe_utf8_pathname_composition();
+	}
+
+	strbuf_release(&buf);
+	return reinit;
+}
+
+static void create_object_directory(void)
+{
+	struct strbuf path = STRBUF_INIT;
+	size_t baselen;
+
+	strbuf_addstr(&path, get_object_directory());
+	baselen = path.len;
+
+	safe_create_dir(path.buf, 1);
+
+	strbuf_setlen(&path, baselen);
+	strbuf_addstr(&path, "/pack");
+	safe_create_dir(path.buf, 1);
+
+	strbuf_setlen(&path, baselen);
+	strbuf_addstr(&path, "/info");
+	safe_create_dir(path.buf, 1);
+
+	strbuf_release(&path);
+}
+
+static void separate_git_dir(const char *git_dir, const char *git_link)
+{
+	struct stat st;
+
+	if (!stat(git_link, &st)) {
+		const char *src;
+
+		if (S_ISREG(st.st_mode))
+			src = read_gitfile(git_link);
+		else if (S_ISDIR(st.st_mode))
+			src = git_link;
+		else
+			die(_("unable to handle file type %d"), (int)st.st_mode);
+
+		if (rename(src, git_dir))
+			die_errno(_("unable to move %s to %s"), src, git_dir);
+	}
+
+	write_file(git_link, "gitdir: %s", git_dir);
+}
+
+int init_db(const char *git_dir, const char *real_git_dir,
+	    const char *template_dir, unsigned int flags)
+{
+	int reinit;
+	int exist_ok = flags & INIT_DB_EXIST_OK;
+	char *original_git_dir = real_pathdup(git_dir, 1);
+
+	if (real_git_dir) {
+		struct stat st;
+
+		if (!exist_ok && !stat(git_dir, &st))
+			die(_("%s already exists"), git_dir);
+
+		if (!exist_ok && !stat(real_git_dir, &st))
+			die(_("%s already exists"), real_git_dir);
+
+		set_git_dir(real_path(real_git_dir));
+		git_dir = get_git_dir();
+		separate_git_dir(git_dir, original_git_dir);
+	}
+	else {
+		set_git_dir(real_path(git_dir));
+		git_dir = get_git_dir();
+	}
+	startup_info->have_repository = 1;
+
+	/* Just look for `core.hidedotfiles` */
+	git_config(git_init_db_config, NULL);
+
+	safe_create_dir(git_dir, 0);
+
+	init_is_bare_repository = is_bare_repository();
+
+	/* Check to see if the repository version is right.
+	 * Note that a newly created repository does not have
+	 * config file, so this will not fail.  What we are catching
+	 * is an attempt to reinitialize new repository with an old tool.
+	 */
+	check_repository_format();
+
+	reinit = create_default_files(template_dir, original_git_dir);
+
+	create_object_directory();
+
+	if (get_shared_repository()) {
+		char buf[10];
+		/* We do not spell "group" and such, so that
+		 * the configuration can be read by older version
+		 * of git. Note, we use octal numbers for new share modes,
+		 * and compatibility values for PERM_GROUP and
+		 * PERM_EVERYBODY.
+		 */
+		if (get_shared_repository() < 0)
+			/* force to the mode value */
+			xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository());
+		else if (get_shared_repository() == PERM_GROUP)
+			xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
+		else if (get_shared_repository() == PERM_EVERYBODY)
+			xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
+		else
+			BUG("invalid value for shared_repository");
+		git_config_set("core.sharedrepository", buf);
+		git_config_set("receive.denyNonFastforwards", "true");
+	}
+
+	if (!(flags & INIT_DB_QUIET)) {
+		int len = strlen(git_dir);
+
+		if (reinit)
+			printf(get_shared_repository()
+			       ? _("Reinitialized existing shared Git repository in %s%s\n")
+			       : _("Reinitialized existing Git repository in %s%s\n"),
+			       git_dir, len && git_dir[len-1] != '/' ? "/" : "");
+		else
+			printf(get_shared_repository()
+			       ? _("Initialized empty shared Git repository in %s%s\n")
+			       : _("Initialized empty Git repository in %s%s\n"),
+			       git_dir, len && git_dir[len-1] != '/' ? "/" : "");
+	}
+
+	free(original_git_dir);
+	return 0;
+}
+
+static int guess_repository_type(const char *git_dir)
+{
+	const char *slash;
+	char *cwd;
+	int cwd_is_git_dir;
+
+	/*
+	 * "GIT_DIR=. git init" is always bare.
+	 * "GIT_DIR=`pwd` git init" too.
+	 */
+	if (!strcmp(".", git_dir))
+		return 1;
+	cwd = xgetcwd();
+	cwd_is_git_dir = !strcmp(git_dir, cwd);
+	free(cwd);
+	if (cwd_is_git_dir)
+		return 1;
+	/*
+	 * "GIT_DIR=.git or GIT_DIR=something/.git is usually not.
+	 */
+	if (!strcmp(git_dir, ".git"))
+		return 0;
+	slash = strrchr(git_dir, '/');
+	if (slash && !strcmp(slash, "/.git"))
+		return 0;
+
+	/*
+	 * Otherwise it is often bare.  At this point
+	 * we are just guessing.
+	 */
+	return 1;
+}
+
+static int shared_callback(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+	*((int *) opt->value) = (arg) ? git_config_perm("arg", arg) : PERM_GROUP;
+	return 0;
+}
+
+static const char *const init_db_usage[] = {
+	N_("git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [<directory>]"),
+	NULL
+};
+
+/*
+ * If you want to, you can share the DB area with any number of branches.
+ * That has advantages: you can save space by sharing all the SHA1 objects.
+ * On the other hand, it might just make lookup slower and messier. You
+ * be the judge.  The default case is to have one DB per managed directory.
+ */
+int cmd_init_db(int argc, const char **argv, const char *prefix)
+{
+	const char *git_dir;
+	const char *real_git_dir = NULL;
+	const char *work_tree;
+	const char *template_dir = NULL;
+	unsigned int flags = 0;
+	const struct option init_db_options[] = {
+		OPT_STRING(0, "template", &template_dir, N_("template-directory"),
+				N_("directory from which templates will be used")),
+		OPT_SET_INT(0, "bare", &is_bare_repository_cfg,
+				N_("create a bare repository"), 1),
+		{ OPTION_CALLBACK, 0, "shared", &init_shared_repository,
+			N_("permissions"),
+			N_("specify that the git repository is to be shared amongst several users"),
+			PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
+		OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET),
+		OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
+			   N_("separate git dir from working tree")),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
+
+	if (real_git_dir && !is_absolute_path(real_git_dir))
+		real_git_dir = real_pathdup(real_git_dir, 1);
+
+	if (template_dir && *template_dir && !is_absolute_path(template_dir))
+		template_dir = absolute_pathdup(template_dir);
+
+	if (argc == 1) {
+		int mkdir_tried = 0;
+	retry:
+		if (chdir(argv[0]) < 0) {
+			if (!mkdir_tried) {
+				int saved;
+				/*
+				 * At this point we haven't read any configuration,
+				 * and we know shared_repository should always be 0;
+				 * but just in case we play safe.
+				 */
+				saved = get_shared_repository();
+				set_shared_repository(0);
+				switch (safe_create_leading_directories_const(argv[0])) {
+				case SCLD_OK:
+				case SCLD_PERMS:
+					break;
+				case SCLD_EXISTS:
+					errno = EEXIST;
+					/* fallthru */
+				default:
+					die_errno(_("cannot mkdir %s"), argv[0]);
+					break;
+				}
+				set_shared_repository(saved);
+				if (mkdir(argv[0], 0777) < 0)
+					die_errno(_("cannot mkdir %s"), argv[0]);
+				mkdir_tried = 1;
+				goto retry;
+			}
+			die_errno(_("cannot chdir to %s"), argv[0]);
+		}
+	} else if (0 < argc) {
+		usage(init_db_usage[0]);
+	}
+	if (is_bare_repository_cfg == 1) {
+		char *cwd = xgetcwd();
+		setenv(GIT_DIR_ENVIRONMENT, cwd, argc > 0);
+		free(cwd);
+	}
+
+	if (init_shared_repository != -1)
+		set_shared_repository(init_shared_repository);
+
+	/*
+	 * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
+	 * without --bare.  Catch the error early.
+	 */
+	git_dir = xstrdup_or_null(getenv(GIT_DIR_ENVIRONMENT));
+	work_tree = xstrdup_or_null(getenv(GIT_WORK_TREE_ENVIRONMENT));
+	if ((!git_dir || is_bare_repository_cfg == 1) && work_tree)
+		die(_("%s (or --work-tree=<directory>) not allowed without "
+			  "specifying %s (or --git-dir=<directory>)"),
+		    GIT_WORK_TREE_ENVIRONMENT,
+		    GIT_DIR_ENVIRONMENT);
+
+	/*
+	 * Set up the default .git directory contents
+	 */
+	if (!git_dir)
+		git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
+
+	if (is_bare_repository_cfg < 0)
+		is_bare_repository_cfg = guess_repository_type(git_dir);
+
+	if (!is_bare_repository_cfg) {
+		const char *git_dir_parent = strrchr(git_dir, '/');
+		if (git_dir_parent) {
+			char *rel = xstrndup(git_dir, git_dir_parent - git_dir);
+			git_work_tree_cfg = real_pathdup(rel, 1);
+			free(rel);
+		}
+		if (!git_work_tree_cfg)
+			git_work_tree_cfg = xgetcwd();
+		if (work_tree)
+			set_git_work_tree(work_tree);
+		else
+			set_git_work_tree(git_work_tree_cfg);
+		if (access(get_git_work_tree(), X_OK))
+			die_errno (_("Cannot access work tree '%s'"),
+				   get_git_work_tree());
+	}
+	else {
+		if (work_tree)
+			set_git_work_tree(work_tree);
+	}
+
+	UNLEAK(real_git_dir);
+	UNLEAK(git_dir);
+	UNLEAK(work_tree);
+
+	flags |= INIT_DB_EXIST_OK;
+	return init_db(git_dir, real_git_dir, template_dir, flags);
+}
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
new file mode 100644
index 0000000000..f101d092b8
--- /dev/null
+++ b/builtin/interpret-trailers.c
@@ -0,0 +1,140 @@
+/*
+ * Builtin "git interpret-trailers"
+ *
+ * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
+ *
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "trailer.h"
+#include "config.h"
+
+static const char * const git_interpret_trailers_usage[] = {
+	N_("git interpret-trailers [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
+	NULL
+};
+
+static enum trailer_where where;
+static enum trailer_if_exists if_exists;
+static enum trailer_if_missing if_missing;
+
+static int option_parse_where(const struct option *opt,
+			      const char *arg, int unset)
+{
+	return trailer_set_where(&where, arg);
+}
+
+static int option_parse_if_exists(const struct option *opt,
+				  const char *arg, int unset)
+{
+	return trailer_set_if_exists(&if_exists, arg);
+}
+
+static int option_parse_if_missing(const struct option *opt,
+				   const char *arg, int unset)
+{
+	return trailer_set_if_missing(&if_missing, arg);
+}
+
+static void new_trailers_clear(struct list_head *trailers)
+{
+	struct list_head *pos, *tmp;
+	struct new_trailer_item *item;
+
+	list_for_each_safe(pos, tmp, trailers) {
+		item = list_entry(pos, struct new_trailer_item, list);
+		list_del(pos);
+		free(item);
+	}
+}
+
+static int option_parse_trailer(const struct option *opt,
+				   const char *arg, int unset)
+{
+	struct list_head *trailers = opt->value;
+	struct new_trailer_item *item;
+
+	if (unset) {
+		new_trailers_clear(trailers);
+		return 0;
+	}
+
+	if (!arg)
+		return -1;
+
+	item = xmalloc(sizeof(*item));
+	item->text = arg;
+	item->where = where;
+	item->if_exists = if_exists;
+	item->if_missing = if_missing;
+	list_add_tail(&item->list, trailers);
+	return 0;
+}
+
+static int parse_opt_parse(const struct option *opt, const char *arg,
+			   int unset)
+{
+	struct process_trailer_options *v = opt->value;
+	v->only_trailers = 1;
+	v->only_input = 1;
+	v->unfold = 1;
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+	return 0;
+}
+
+int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
+{
+	struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
+	LIST_HEAD(trailers);
+
+	struct option options[] = {
+		OPT_BOOL(0, "in-place", &opts.in_place, N_("edit files in place")),
+		OPT_BOOL(0, "trim-empty", &opts.trim_empty, N_("trim empty trailers")),
+
+		OPT_CALLBACK(0, "where", NULL, N_("action"),
+			     N_("where to place the new trailer"), option_parse_where),
+		OPT_CALLBACK(0, "if-exists", NULL, N_("action"),
+			     N_("action if trailer already exists"), option_parse_if_exists),
+		OPT_CALLBACK(0, "if-missing", NULL, N_("action"),
+			     N_("action if trailer is missing"), option_parse_if_missing),
+
+		OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")),
+		OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply config rules")),
+		OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")),
+		{ OPTION_CALLBACK, 0, "parse", &opts, NULL, N_("set parsing options"),
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse },
+		OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat --- specially")),
+		OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"),
+				N_("trailer(s) to add"), option_parse_trailer),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_interpret_trailers_usage, 0);
+
+	if (opts.only_input && !list_empty(&trailers))
+		usage_msg_opt(
+			_("--trailer with --only-input does not make sense"),
+			git_interpret_trailers_usage,
+			options);
+
+	if (argc) {
+		int i;
+		for (i = 0; i < argc; i++)
+			process_trailers(argv[i], &opts, &trailers);
+	} else {
+		if (opts.in_place)
+			die(_("no input file given for in-place editing"));
+		process_trailers(NULL, &opts, &trailers);
+	}
+
+	new_trailers_clear(&trailers);
+
+	return 0;
+}
diff --git a/builtin/log.c b/builtin/log.c
new file mode 100644
index 0000000000..44b10b3415
--- /dev/null
+++ b/builtin/log.c
@@ -0,0 +1,2134 @@
+/*
+ * Builtin "git log" and related commands (show, whatchanged)
+ *
+ * (C) Copyright 2006 Linus Torvalds
+ *		 2006 Junio Hamano
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "refs.h"
+#include "object-store.h"
+#include "color.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "log-tree.h"
+#include "builtin.h"
+#include "tag.h"
+#include "reflog-walk.h"
+#include "patch-ids.h"
+#include "run-command.h"
+#include "shortlog.h"
+#include "remote.h"
+#include "string-list.h"
+#include "parse-options.h"
+#include "line-log.h"
+#include "branch.h"
+#include "streaming.h"
+#include "version.h"
+#include "mailmap.h"
+#include "gpg-interface.h"
+#include "progress.h"
+#include "commit-slab.h"
+#include "repository.h"
+#include "commit-reach.h"
+#include "interdiff.h"
+#include "range-diff.h"
+
+#define MAIL_DEFAULT_WRAP 72
+
+/* Set a default date-time format for git log ("log.date" config variable) */
+static const char *default_date_mode = NULL;
+
+static int default_abbrev_commit;
+static int default_show_root = 1;
+static int default_follow;
+static int default_show_signature;
+static int decoration_style;
+static int decoration_given;
+static int use_mailmap_config = 1;
+static const char *fmt_patch_subject_prefix = "PATCH";
+static const char *fmt_pretty;
+
+static const char * const builtin_log_usage[] = {
+	N_("git log [<options>] [<revision-range>] [[--] <path>...]"),
+	N_("git show [<options>] <object>..."),
+	NULL
+};
+
+struct line_opt_callback_data {
+	struct rev_info *rev;
+	const char *prefix;
+	struct string_list args;
+};
+
+static int session_is_interactive(void)
+{
+	return isatty(1) || pager_in_use();
+}
+
+static int auto_decoration_style(void)
+{
+	return session_is_interactive() ? DECORATE_SHORT_REFS : 0;
+}
+
+static int parse_decoration_style(const char *value)
+{
+	switch (git_parse_maybe_bool(value)) {
+	case 1:
+		return DECORATE_SHORT_REFS;
+	case 0:
+		return 0;
+	default:
+		break;
+	}
+	if (!strcmp(value, "full"))
+		return DECORATE_FULL_REFS;
+	else if (!strcmp(value, "short"))
+		return DECORATE_SHORT_REFS;
+	else if (!strcmp(value, "auto"))
+		return auto_decoration_style();
+	/*
+	 * Please update _git_log() in git-completion.bash when you
+	 * add new decoration styles.
+	 */
+	return -1;
+}
+
+static int decorate_callback(const struct option *opt, const char *arg, int unset)
+{
+	if (unset)
+		decoration_style = 0;
+	else if (arg)
+		decoration_style = parse_decoration_style(arg);
+	else
+		decoration_style = DECORATE_SHORT_REFS;
+
+	if (decoration_style < 0)
+		die(_("invalid --decorate option: %s"), arg);
+
+	decoration_given = 1;
+
+	return 0;
+}
+
+static int log_line_range_callback(const struct option *option, const char *arg, int unset)
+{
+	struct line_opt_callback_data *data = option->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!arg)
+		return -1;
+
+	data->rev->line_level_traverse = 1;
+	string_list_append(&data->args, arg);
+
+	return 0;
+}
+
+static void init_log_defaults(void)
+{
+	init_grep_defaults(the_repository);
+	init_diff_ui_defaults();
+
+	decoration_style = auto_decoration_style();
+}
+
+static void cmd_log_init_defaults(struct rev_info *rev)
+{
+	if (fmt_pretty)
+		get_commit_format(fmt_pretty, rev);
+	if (default_follow)
+		rev->diffopt.flags.default_follow_renames = 1;
+	rev->verbose_header = 1;
+	rev->diffopt.flags.recursive = 1;
+	rev->diffopt.stat_width = -1; /* use full terminal width */
+	rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */
+	rev->abbrev_commit = default_abbrev_commit;
+	rev->show_root_diff = default_show_root;
+	rev->subject_prefix = fmt_patch_subject_prefix;
+	rev->show_signature = default_show_signature;
+	rev->diffopt.flags.allow_textconv = 1;
+
+	if (default_date_mode)
+		parse_date_format(default_date_mode, &rev->date_mode);
+}
+
+static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
+			 struct rev_info *rev, struct setup_revision_opt *opt)
+{
+	struct userformat_want w;
+	int quiet = 0, source = 0, mailmap;
+	static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP};
+	static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
+	static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
+	struct decoration_filter decoration_filter = {&decorate_refs_include,
+						      &decorate_refs_exclude};
+	static struct revision_sources revision_sources;
+
+	const struct option builtin_log_options[] = {
+		OPT__QUIET(&quiet, N_("suppress diff output")),
+		OPT_BOOL(0, "source", &source, N_("show source")),
+		OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
+		OPT_STRING_LIST(0, "decorate-refs", &decorate_refs_include,
+				N_("pattern"), N_("only decorate refs that match <pattern>")),
+		OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude,
+				N_("pattern"), N_("do not decorate refs that match <pattern>")),
+		{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
+		  PARSE_OPT_OPTARG, decorate_callback},
+		OPT_CALLBACK('L', NULL, &line_cb, "n,m:file",
+			     N_("Process line range n,m in file, counting from 1"),
+			     log_line_range_callback),
+		OPT_END()
+	};
+
+	line_cb.rev = rev;
+	line_cb.prefix = prefix;
+
+	mailmap = use_mailmap_config;
+	argc = parse_options(argc, argv, prefix,
+			     builtin_log_options, builtin_log_usage,
+			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
+			     PARSE_OPT_KEEP_DASHDASH);
+
+	if (quiet)
+		rev->diffopt.output_format |= DIFF_FORMAT_NO_OUTPUT;
+	argc = setup_revisions(argc, argv, rev, opt);
+
+	/* Any arguments at this point are not recognized */
+	if (argc > 1)
+		die(_("unrecognized argument: %s"), argv[1]);
+
+	memset(&w, 0, sizeof(w));
+	userformat_find_requirements(NULL, &w);
+
+	if (!rev->show_notes_given && (!rev->pretty_given || w.notes))
+		rev->show_notes = 1;
+	if (rev->show_notes)
+		init_display_notes(&rev->notes_opt);
+
+	if ((rev->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) ||
+	    rev->diffopt.filter || rev->diffopt.flags.follow_renames)
+		rev->always_show_header = 0;
+
+	if (source || w.source) {
+		init_revision_sources(&revision_sources);
+		rev->sources = &revision_sources;
+	}
+
+	if (mailmap) {
+		rev->mailmap = xcalloc(1, sizeof(struct string_list));
+		read_mailmap(rev->mailmap, NULL);
+	}
+
+	if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) {
+		/*
+		 * "log --pretty=raw" is special; ignore UI oriented
+		 * configuration variables such as decoration.
+		 */
+		if (!decoration_given)
+			decoration_style = 0;
+		if (!rev->abbrev_commit_given)
+			rev->abbrev_commit = 0;
+	}
+
+	if (decoration_style) {
+		rev->show_decorations = 1;
+		load_ref_decorations(&decoration_filter, decoration_style);
+	}
+
+	if (rev->line_level_traverse)
+		line_log_init(rev, line_cb.prefix, &line_cb.args);
+
+	setup_pager();
+}
+
+static void cmd_log_init(int argc, const char **argv, const char *prefix,
+			 struct rev_info *rev, struct setup_revision_opt *opt)
+{
+	cmd_log_init_defaults(rev);
+	cmd_log_init_finish(argc, argv, prefix, rev, opt);
+}
+
+/*
+ * This gives a rough estimate for how many commits we
+ * will print out in the list.
+ */
+static int estimate_commit_count(struct commit_list *list)
+{
+	int n = 0;
+
+	while (list) {
+		struct commit *commit = list->item;
+		unsigned int flags = commit->object.flags;
+		list = list->next;
+		if (!(flags & (TREESAME | UNINTERESTING)))
+			n++;
+	}
+	return n;
+}
+
+static void show_early_header(struct rev_info *rev, const char *stage, int nr)
+{
+	if (rev->shown_one) {
+		rev->shown_one = 0;
+		if (rev->commit_format != CMIT_FMT_ONELINE)
+			putchar(rev->diffopt.line_termination);
+	}
+	fprintf(rev->diffopt.file, _("Final output: %d %s\n"), nr, stage);
+}
+
+static struct itimerval early_output_timer;
+
+static void log_show_early(struct rev_info *revs, struct commit_list *list)
+{
+	int i = revs->early_output, close_file = revs->diffopt.close_file;
+	int show_header = 1;
+
+	revs->diffopt.close_file = 0;
+	sort_in_topological_order(&list, revs->sort_order);
+	while (list && i) {
+		struct commit *commit = list->item;
+		switch (simplify_commit(revs, commit)) {
+		case commit_show:
+			if (show_header) {
+				int n = estimate_commit_count(list);
+				show_early_header(revs, "incomplete", n);
+				show_header = 0;
+			}
+			log_tree_commit(revs, commit);
+			i--;
+			break;
+		case commit_ignore:
+			break;
+		case commit_error:
+			if (close_file)
+				fclose(revs->diffopt.file);
+			return;
+		}
+		list = list->next;
+	}
+
+	/* Did we already get enough commits for the early output? */
+	if (!i) {
+		if (close_file)
+			fclose(revs->diffopt.file);
+		return;
+	}
+
+	/*
+	 * ..if no, then repeat it twice a second until we
+	 * do.
+	 *
+	 * NOTE! We don't use "it_interval", because if the
+	 * reader isn't listening, we want our output to be
+	 * throttled by the writing, and not have the timer
+	 * trigger every second even if we're blocked on a
+	 * reader!
+	 */
+	early_output_timer.it_value.tv_sec = 0;
+	early_output_timer.it_value.tv_usec = 500000;
+	setitimer(ITIMER_REAL, &early_output_timer, NULL);
+}
+
+static void early_output(int signal)
+{
+	show_early_output = log_show_early;
+}
+
+static void setup_early_output(void)
+{
+	struct sigaction sa;
+
+	/*
+	 * Set up the signal handler, minimally intrusively:
+	 * we only set a single volatile integer word (not
+	 * using sigatomic_t - trying to avoid unnecessary
+	 * system dependencies and headers), and using
+	 * SA_RESTART.
+	 */
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = early_output;
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = SA_RESTART;
+	sigaction(SIGALRM, &sa, NULL);
+
+	/*
+	 * If we can get the whole output in less than a
+	 * tenth of a second, don't even bother doing the
+	 * early-output thing..
+	 *
+	 * This is a one-time-only trigger.
+	 */
+	early_output_timer.it_value.tv_sec = 0;
+	early_output_timer.it_value.tv_usec = 100000;
+	setitimer(ITIMER_REAL, &early_output_timer, NULL);
+}
+
+static void finish_early_output(struct rev_info *rev)
+{
+	int n = estimate_commit_count(rev->commits);
+	signal(SIGALRM, SIG_IGN);
+	show_early_header(rev, "done", n);
+}
+
+static int cmd_log_walk(struct rev_info *rev)
+{
+	struct commit *commit;
+	int saved_nrl = 0;
+	int saved_dcctc = 0, close_file = rev->diffopt.close_file;
+
+	if (rev->early_output)
+		setup_early_output();
+
+	if (prepare_revision_walk(rev))
+		die(_("revision walk setup failed"));
+
+	if (rev->early_output)
+		finish_early_output(rev);
+
+	/*
+	 * For --check and --exit-code, the exit code is based on CHECK_FAILED
+	 * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
+	 * retain that state information if replacing rev->diffopt in this loop
+	 */
+	rev->diffopt.close_file = 0;
+	while ((commit = get_revision(rev)) != NULL) {
+		if (!log_tree_commit(rev, commit) && rev->max_count >= 0)
+			/*
+			 * We decremented max_count in get_revision,
+			 * but we didn't actually show the commit.
+			 */
+			rev->max_count++;
+		if (!rev->reflog_info) {
+			/*
+			 * We may show a given commit multiple times when
+			 * walking the reflogs.
+			 */
+			free_commit_buffer(the_repository->parsed_objects,
+					   commit);
+			free_commit_list(commit->parents);
+			commit->parents = NULL;
+		}
+		if (saved_nrl < rev->diffopt.needed_rename_limit)
+			saved_nrl = rev->diffopt.needed_rename_limit;
+		if (rev->diffopt.degraded_cc_to_c)
+			saved_dcctc = 1;
+	}
+	rev->diffopt.degraded_cc_to_c = saved_dcctc;
+	rev->diffopt.needed_rename_limit = saved_nrl;
+	if (close_file)
+		fclose(rev->diffopt.file);
+
+	if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
+	    rev->diffopt.flags.check_failed) {
+		return 02;
+	}
+	return diff_result_code(&rev->diffopt, 0);
+}
+
+static int git_log_config(const char *var, const char *value, void *cb)
+{
+	const char *slot_name;
+
+	if (!strcmp(var, "format.pretty"))
+		return git_config_string(&fmt_pretty, var, value);
+	if (!strcmp(var, "format.subjectprefix"))
+		return git_config_string(&fmt_patch_subject_prefix, var, value);
+	if (!strcmp(var, "log.abbrevcommit")) {
+		default_abbrev_commit = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "log.date"))
+		return git_config_string(&default_date_mode, var, value);
+	if (!strcmp(var, "log.decorate")) {
+		decoration_style = parse_decoration_style(value);
+		if (decoration_style < 0)
+			decoration_style = 0; /* maybe warn? */
+		return 0;
+	}
+	if (!strcmp(var, "log.showroot")) {
+		default_show_root = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "log.follow")) {
+		default_follow = git_config_bool(var, value);
+		return 0;
+	}
+	if (skip_prefix(var, "color.decorate.", &slot_name))
+		return parse_decorate_color_config(var, slot_name, value);
+	if (!strcmp(var, "log.mailmap")) {
+		use_mailmap_config = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "log.showsignature")) {
+		default_show_signature = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (grep_config(var, value, cb) < 0)
+		return -1;
+	if (git_gpg_config(var, value, cb) < 0)
+		return -1;
+	return git_diff_ui_config(var, value, cb);
+}
+
+int cmd_whatchanged(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info rev;
+	struct setup_revision_opt opt;
+
+	init_log_defaults();
+	git_config(git_log_config, NULL);
+
+	repo_init_revisions(the_repository, &rev, prefix);
+	rev.diff = 1;
+	rev.simplify_history = 0;
+	memset(&opt, 0, sizeof(opt));
+	opt.def = "HEAD";
+	opt.revarg_opt = REVARG_COMMITTISH;
+	cmd_log_init(argc, argv, prefix, &rev, &opt);
+	if (!rev.diffopt.output_format)
+		rev.diffopt.output_format = DIFF_FORMAT_RAW;
+	return cmd_log_walk(&rev);
+}
+
+static void show_tagger(const char *buf, struct rev_info *rev)
+{
+	struct strbuf out = STRBUF_INIT;
+	struct pretty_print_context pp = {0};
+
+	pp.fmt = rev->commit_format;
+	pp.date_mode = rev->date_mode;
+	pp_user_info(&pp, "Tagger", &out, buf, get_log_output_encoding());
+	fprintf(rev->diffopt.file, "%s", out.buf);
+	strbuf_release(&out);
+}
+
+static int show_blob_object(const struct object_id *oid, struct rev_info *rev, const char *obj_name)
+{
+	struct object_id oidc;
+	struct object_context obj_context;
+	char *buf;
+	unsigned long size;
+
+	fflush(rev->diffopt.file);
+	if (!rev->diffopt.flags.textconv_set_via_cmdline ||
+	    !rev->diffopt.flags.allow_textconv)
+		return stream_blob_to_fd(1, oid, NULL, 0);
+
+	if (get_oid_with_context(the_repository, obj_name,
+				 GET_OID_RECORD_PATH,
+				 &oidc, &obj_context))
+		die(_("not a valid object name %s"), obj_name);
+	if (!obj_context.path ||
+	    !textconv_object(the_repository, obj_context.path,
+			     obj_context.mode, &oidc, 1, &buf, &size)) {
+		free(obj_context.path);
+		return stream_blob_to_fd(1, oid, NULL, 0);
+	}
+
+	if (!buf)
+		die(_("git show %s: bad file"), obj_name);
+
+	write_or_die(1, buf, size);
+	free(obj_context.path);
+	return 0;
+}
+
+static int show_tag_object(const struct object_id *oid, struct rev_info *rev)
+{
+	unsigned long size;
+	enum object_type type;
+	char *buf = read_object_file(oid, &type, &size);
+	int offset = 0;
+
+	if (!buf)
+		return error(_("could not read object %s"), oid_to_hex(oid));
+
+	assert(type == OBJ_TAG);
+	while (offset < size && buf[offset] != '\n') {
+		int new_offset = offset + 1;
+		const char *ident;
+		while (new_offset < size && buf[new_offset++] != '\n')
+			; /* do nothing */
+		if (skip_prefix(buf + offset, "tagger ", &ident))
+			show_tagger(ident, rev);
+		offset = new_offset;
+	}
+
+	if (offset < size)
+		fwrite(buf + offset, size - offset, 1, rev->diffopt.file);
+	free(buf);
+	return 0;
+}
+
+static int show_tree_object(const struct object_id *oid,
+		struct strbuf *base,
+		const char *pathname, unsigned mode, int stage, void *context)
+{
+	FILE *file = context;
+	fprintf(file, "%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
+	return 0;
+}
+
+static void show_setup_revisions_tweak(struct rev_info *rev,
+				       struct setup_revision_opt *opt)
+{
+	if (rev->ignore_merges) {
+		/* There was no "-m" on the command line */
+		rev->ignore_merges = 0;
+		if (!rev->first_parent_only && !rev->combine_merges) {
+			/* No "--first-parent", "-c", or "--cc" */
+			rev->combine_merges = 1;
+			rev->dense_combined_merges = 1;
+		}
+	}
+	if (!rev->diffopt.output_format)
+		rev->diffopt.output_format = DIFF_FORMAT_PATCH;
+}
+
+int cmd_show(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info rev;
+	struct object_array_entry *objects;
+	struct setup_revision_opt opt;
+	struct pathspec match_all;
+	int i, count, ret = 0;
+
+	init_log_defaults();
+	git_config(git_log_config, NULL);
+
+	memset(&match_all, 0, sizeof(match_all));
+	repo_init_revisions(the_repository, &rev, prefix);
+	rev.diff = 1;
+	rev.always_show_header = 1;
+	rev.no_walk = REVISION_WALK_NO_WALK_SORTED;
+	rev.diffopt.stat_width = -1; 	/* Scale to real terminal size */
+
+	memset(&opt, 0, sizeof(opt));
+	opt.def = "HEAD";
+	opt.tweak = show_setup_revisions_tweak;
+	cmd_log_init(argc, argv, prefix, &rev, &opt);
+
+	if (!rev.no_walk)
+		return cmd_log_walk(&rev);
+
+	count = rev.pending.nr;
+	objects = rev.pending.objects;
+	for (i = 0; i < count && !ret; i++) {
+		struct object *o = objects[i].item;
+		const char *name = objects[i].name;
+		switch (o->type) {
+		case OBJ_BLOB:
+			ret = show_blob_object(&o->oid, &rev, name);
+			break;
+		case OBJ_TAG: {
+			struct tag *t = (struct tag *)o;
+
+			if (rev.shown_one)
+				putchar('\n');
+			fprintf(rev.diffopt.file, "%stag %s%s\n",
+					diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
+					t->tag,
+					diff_get_color_opt(&rev.diffopt, DIFF_RESET));
+			ret = show_tag_object(&o->oid, &rev);
+			rev.shown_one = 1;
+			if (ret)
+				break;
+			o = parse_object(the_repository, &t->tagged->oid);
+			if (!o)
+				ret = error(_("could not read object %s"),
+					    oid_to_hex(&t->tagged->oid));
+			objects[i].item = o;
+			i--;
+			break;
+		}
+		case OBJ_TREE:
+			if (rev.shown_one)
+				putchar('\n');
+			fprintf(rev.diffopt.file, "%stree %s%s\n\n",
+					diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
+					name,
+					diff_get_color_opt(&rev.diffopt, DIFF_RESET));
+			read_tree_recursive(the_repository, (struct tree *)o, "",
+					    0, 0, &match_all, show_tree_object,
+					    rev.diffopt.file);
+			rev.shown_one = 1;
+			break;
+		case OBJ_COMMIT:
+			rev.pending.nr = rev.pending.alloc = 0;
+			rev.pending.objects = NULL;
+			add_object_array(o, name, &rev.pending);
+			ret = cmd_log_walk(&rev);
+			break;
+		default:
+			ret = error(_("unknown type: %d"), o->type);
+		}
+	}
+	free(objects);
+	return ret;
+}
+
+/*
+ * This is equivalent to "git log -g --abbrev-commit --pretty=oneline"
+ */
+int cmd_log_reflog(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info rev;
+	struct setup_revision_opt opt;
+
+	init_log_defaults();
+	git_config(git_log_config, NULL);
+
+	repo_init_revisions(the_repository, &rev, prefix);
+	init_reflog_walk(&rev.reflog_info);
+	rev.verbose_header = 1;
+	memset(&opt, 0, sizeof(opt));
+	opt.def = "HEAD";
+	cmd_log_init_defaults(&rev);
+	rev.abbrev_commit = 1;
+	rev.commit_format = CMIT_FMT_ONELINE;
+	rev.use_terminator = 1;
+	rev.always_show_header = 1;
+	cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
+
+	return cmd_log_walk(&rev);
+}
+
+static void log_setup_revisions_tweak(struct rev_info *rev,
+				      struct setup_revision_opt *opt)
+{
+	if (rev->diffopt.flags.default_follow_renames &&
+	    rev->prune_data.nr == 1)
+		rev->diffopt.flags.follow_renames = 1;
+
+	/* Turn --cc/-c into -p --cc/-c when -p was not given */
+	if (!rev->diffopt.output_format && rev->combine_merges)
+		rev->diffopt.output_format = DIFF_FORMAT_PATCH;
+
+	/* Turn -m on when --cc/-c was given */
+	if (rev->combine_merges)
+		rev->ignore_merges = 0;
+}
+
+int cmd_log(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info rev;
+	struct setup_revision_opt opt;
+
+	init_log_defaults();
+	git_config(git_log_config, NULL);
+
+	repo_init_revisions(the_repository, &rev, prefix);
+	rev.always_show_header = 1;
+	memset(&opt, 0, sizeof(opt));
+	opt.def = "HEAD";
+	opt.revarg_opt = REVARG_COMMITTISH;
+	opt.tweak = log_setup_revisions_tweak;
+	cmd_log_init(argc, argv, prefix, &rev, &opt);
+	return cmd_log_walk(&rev);
+}
+
+/* format-patch */
+
+static const char *fmt_patch_suffix = ".patch";
+static int numbered = 0;
+static int auto_number = 1;
+
+static char *default_attach = NULL;
+
+static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;
+static struct string_list extra_to = STRING_LIST_INIT_NODUP;
+static struct string_list extra_cc = STRING_LIST_INIT_NODUP;
+
+static void add_header(const char *value)
+{
+	struct string_list_item *item;
+	int len = strlen(value);
+	while (len && value[len - 1] == '\n')
+		len--;
+
+	if (!strncasecmp(value, "to: ", 4)) {
+		item = string_list_append(&extra_to, value + 4);
+		len -= 4;
+	} else if (!strncasecmp(value, "cc: ", 4)) {
+		item = string_list_append(&extra_cc, value + 4);
+		len -= 4;
+	} else {
+		item = string_list_append(&extra_hdr, value);
+	}
+
+	item->string[len] = '\0';
+}
+
+#define THREAD_SHALLOW 1
+#define THREAD_DEEP 2
+static int thread;
+static int do_signoff;
+static int base_auto;
+static char *from;
+static const char *signature = git_version_string;
+static const char *signature_file;
+static int config_cover_letter;
+static const char *config_output_directory;
+
+enum {
+	COVER_UNSET,
+	COVER_OFF,
+	COVER_ON,
+	COVER_AUTO
+};
+
+static int git_format_config(const char *var, const char *value, void *cb)
+{
+	struct rev_info *rev = cb;
+
+	if (!strcmp(var, "format.headers")) {
+		if (!value)
+			die(_("format.headers without value"));
+		add_header(value);
+		return 0;
+	}
+	if (!strcmp(var, "format.suffix"))
+		return git_config_string(&fmt_patch_suffix, var, value);
+	if (!strcmp(var, "format.to")) {
+		if (!value)
+			return config_error_nonbool(var);
+		string_list_append(&extra_to, value);
+		return 0;
+	}
+	if (!strcmp(var, "format.cc")) {
+		if (!value)
+			return config_error_nonbool(var);
+		string_list_append(&extra_cc, value);
+		return 0;
+	}
+	if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff") ||
+	    !strcmp(var, "color.ui") || !strcmp(var, "diff.submodule")) {
+		return 0;
+	}
+	if (!strcmp(var, "format.numbered")) {
+		if (value && !strcasecmp(value, "auto")) {
+			auto_number = 1;
+			return 0;
+		}
+		numbered = git_config_bool(var, value);
+		auto_number = auto_number && numbered;
+		return 0;
+	}
+	if (!strcmp(var, "format.attach")) {
+		if (value && *value)
+			default_attach = xstrdup(value);
+		else
+			default_attach = xstrdup(git_version_string);
+		return 0;
+	}
+	if (!strcmp(var, "format.thread")) {
+		if (value && !strcasecmp(value, "deep")) {
+			thread = THREAD_DEEP;
+			return 0;
+		}
+		if (value && !strcasecmp(value, "shallow")) {
+			thread = THREAD_SHALLOW;
+			return 0;
+		}
+		thread = git_config_bool(var, value) && THREAD_SHALLOW;
+		return 0;
+	}
+	if (!strcmp(var, "format.signoff")) {
+		do_signoff = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "format.signature"))
+		return git_config_string(&signature, var, value);
+	if (!strcmp(var, "format.signaturefile"))
+		return git_config_pathname(&signature_file, var, value);
+	if (!strcmp(var, "format.coverletter")) {
+		if (value && !strcasecmp(value, "auto")) {
+			config_cover_letter = COVER_AUTO;
+			return 0;
+		}
+		config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF;
+		return 0;
+	}
+	if (!strcmp(var, "format.outputdirectory"))
+		return git_config_string(&config_output_directory, var, value);
+	if (!strcmp(var, "format.useautobase")) {
+		base_auto = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "format.from")) {
+		int b = git_parse_maybe_bool(value);
+		free(from);
+		if (b < 0)
+			from = xstrdup(value);
+		else if (b)
+			from = xstrdup(git_committer_info(IDENT_NO_DATE));
+		else
+			from = NULL;
+		return 0;
+	}
+	if (!strcmp(var, "format.notes")) {
+		struct strbuf buf = STRBUF_INIT;
+		int b = git_parse_maybe_bool(value);
+		if (!b)
+			return 0;
+		rev->show_notes = 1;
+		if (b < 0) {
+			strbuf_addstr(&buf, value);
+			expand_notes_ref(&buf);
+			string_list_append(&rev->notes_opt.extra_notes_refs,
+					strbuf_detach(&buf, NULL));
+		} else {
+			rev->notes_opt.use_default_notes = 1;
+		}
+		return 0;
+	}
+
+	return git_log_config(var, value, cb);
+}
+
+static const char *output_directory = NULL;
+static int outdir_offset;
+
+static int open_next_file(struct commit *commit, const char *subject,
+			 struct rev_info *rev, int quiet)
+{
+	struct strbuf filename = STRBUF_INIT;
+	int suffix_len = strlen(rev->patch_suffix) + 1;
+
+	if (output_directory) {
+		strbuf_addstr(&filename, output_directory);
+		if (filename.len >=
+		    PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len) {
+			strbuf_release(&filename);
+			return error(_("name of output directory is too long"));
+		}
+		strbuf_complete(&filename, '/');
+	}
+
+	if (rev->numbered_files)
+		strbuf_addf(&filename, "%d", rev->nr);
+	else if (commit)
+		fmt_output_commit(&filename, commit, rev);
+	else
+		fmt_output_subject(&filename, subject, rev);
+
+	if (!quiet)
+		printf("%s\n", filename.buf + outdir_offset);
+
+	if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL) {
+		error_errno(_("cannot open patch file %s"), filename.buf);
+		strbuf_release(&filename);
+		return -1;
+	}
+
+	strbuf_release(&filename);
+	return 0;
+}
+
+static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
+{
+	struct rev_info check_rev;
+	struct commit *commit, *c1, *c2;
+	struct object *o1, *o2;
+	unsigned flags1, flags2;
+
+	if (rev->pending.nr != 2)
+		die(_("need exactly one range"));
+
+	o1 = rev->pending.objects[0].item;
+	o2 = rev->pending.objects[1].item;
+	flags1 = o1->flags;
+	flags2 = o2->flags;
+	c1 = lookup_commit_reference(the_repository, &o1->oid);
+	c2 = lookup_commit_reference(the_repository, &o2->oid);
+
+	if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
+		die(_("not a range"));
+
+	init_patch_ids(the_repository, ids);
+
+	/* given a range a..b get all patch ids for b..a */
+	repo_init_revisions(the_repository, &check_rev, rev->prefix);
+	check_rev.max_parents = 1;
+	o1->flags ^= UNINTERESTING;
+	o2->flags ^= UNINTERESTING;
+	add_pending_object(&check_rev, o1, "o1");
+	add_pending_object(&check_rev, o2, "o2");
+	if (prepare_revision_walk(&check_rev))
+		die(_("revision walk setup failed"));
+
+	while ((commit = get_revision(&check_rev)) != NULL) {
+		add_commit_patch_id(commit, ids);
+	}
+
+	/* reset for next revision walk */
+	clear_commit_marks(c1, SEEN | UNINTERESTING | SHOWN | ADDED);
+	clear_commit_marks(c2, SEEN | UNINTERESTING | SHOWN | ADDED);
+	o1->flags = flags1;
+	o2->flags = flags2;
+}
+
+static void gen_message_id(struct rev_info *info, char *base)
+{
+	struct strbuf buf = STRBUF_INIT;
+	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
+		    (timestamp_t) time(NULL),
+		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
+	info->message_id = strbuf_detach(&buf, NULL);
+}
+
+static void print_signature(FILE *file)
+{
+	if (!signature || !*signature)
+		return;
+
+	fprintf(file, "-- \n%s", signature);
+	if (signature[strlen(signature)-1] != '\n')
+		putc('\n', file);
+	putc('\n', file);
+}
+
+static void add_branch_description(struct strbuf *buf, const char *branch_name)
+{
+	struct strbuf desc = STRBUF_INIT;
+	if (!branch_name || !*branch_name)
+		return;
+	read_branch_desc(&desc, branch_name);
+	if (desc.len) {
+		strbuf_addch(buf, '\n');
+		strbuf_addbuf(buf, &desc);
+		strbuf_addch(buf, '\n');
+	}
+	strbuf_release(&desc);
+}
+
+static char *find_branch_name(struct rev_info *rev)
+{
+	int i, positive = -1;
+	struct object_id branch_oid;
+	const struct object_id *tip_oid;
+	const char *ref, *v;
+	char *full_ref, *branch = NULL;
+
+	for (i = 0; i < rev->cmdline.nr; i++) {
+		if (rev->cmdline.rev[i].flags & UNINTERESTING)
+			continue;
+		if (positive < 0)
+			positive = i;
+		else
+			return NULL;
+	}
+	if (positive < 0)
+		return NULL;
+	ref = rev->cmdline.rev[positive].name;
+	tip_oid = &rev->cmdline.rev[positive].item->oid;
+	if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref) &&
+	    skip_prefix(full_ref, "refs/heads/", &v) &&
+	    oideq(tip_oid, &branch_oid))
+		branch = xstrdup(v);
+	free(full_ref);
+	return branch;
+}
+
+static void show_diffstat(struct rev_info *rev,
+			  struct commit *origin, struct commit *head)
+{
+	struct diff_options opts;
+
+	memcpy(&opts, &rev->diffopt, sizeof(opts));
+	opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+	diff_setup_done(&opts);
+
+	diff_tree_oid(get_commit_tree_oid(origin),
+		      get_commit_tree_oid(head),
+		      "", &opts);
+	diffcore_std(&opts);
+	diff_flush(&opts);
+
+	fprintf(rev->diffopt.file, "\n");
+}
+
+static void make_cover_letter(struct rev_info *rev, int use_stdout,
+			      struct commit *origin,
+			      int nr, struct commit **list,
+			      const char *branch_name,
+			      int quiet)
+{
+	const char *committer;
+	const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
+	const char *msg;
+	struct shortlog log;
+	struct strbuf sb = STRBUF_INIT;
+	int i;
+	const char *encoding = "UTF-8";
+	int need_8bit_cte = 0;
+	struct pretty_print_context pp = {0};
+	struct commit *head = list[0];
+
+	if (!cmit_fmt_is_mail(rev->commit_format))
+		die(_("cover letter needs email format"));
+
+	committer = git_committer_info(0);
+
+	if (!use_stdout &&
+	    open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
+		die(_("failed to create cover-letter file"));
+
+	log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte, 0);
+
+	for (i = 0; !need_8bit_cte && i < nr; i++) {
+		const char *buf = get_commit_buffer(list[i], NULL);
+		if (has_non_ascii(buf))
+			need_8bit_cte = 1;
+		unuse_commit_buffer(list[i], buf);
+	}
+
+	if (!branch_name)
+		branch_name = find_branch_name(rev);
+
+	msg = body;
+	pp.fmt = CMIT_FMT_EMAIL;
+	pp.date_mode.type = DATE_RFC2822;
+	pp.rev = rev;
+	pp.print_email_subject = 1;
+	pp_user_info(&pp, NULL, &sb, committer, encoding);
+	pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
+	pp_remainder(&pp, &msg, &sb, 0);
+	add_branch_description(&sb, branch_name);
+	fprintf(rev->diffopt.file, "%s\n", sb.buf);
+
+	strbuf_release(&sb);
+
+	shortlog_init(&log);
+	log.wrap_lines = 1;
+	log.wrap = MAIL_DEFAULT_WRAP;
+	log.in1 = 2;
+	log.in2 = 4;
+	log.file = rev->diffopt.file;
+	for (i = 0; i < nr; i++)
+		shortlog_add_commit(&log, list[i]);
+
+	shortlog_output(&log);
+
+	/* We can only do diffstat with a unique reference point */
+	if (origin)
+		show_diffstat(rev, origin, head);
+
+	if (rev->idiff_oid1) {
+		fprintf_ln(rev->diffopt.file, "%s", rev->idiff_title);
+		show_interdiff(rev, 0);
+	}
+
+	if (rev->rdiff1) {
+		/*
+		 * Pass minimum required diff-options to range-diff; others
+		 * can be added later if deemed desirable.
+		 */
+		struct diff_options opts;
+		diff_setup(&opts);
+		opts.file = rev->diffopt.file;
+		opts.use_color = rev->diffopt.use_color;
+		diff_setup_done(&opts);
+		fprintf_ln(rev->diffopt.file, "%s", rev->rdiff_title);
+		show_range_diff(rev->rdiff1, rev->rdiff2,
+				rev->creation_factor, 1, &opts);
+	}
+}
+
+static const char *clean_message_id(const char *msg_id)
+{
+	char ch;
+	const char *a, *z, *m;
+
+	m = msg_id;
+	while ((ch = *m) && (isspace(ch) || (ch == '<')))
+		m++;
+	a = m;
+	z = NULL;
+	while ((ch = *m)) {
+		if (!isspace(ch) && (ch != '>'))
+			z = m;
+		m++;
+	}
+	if (!z)
+		die(_("insane in-reply-to: %s"), msg_id);
+	if (++z == m)
+		return a;
+	return xmemdupz(a, z - a);
+}
+
+static const char *set_outdir(const char *prefix, const char *output_directory)
+{
+	if (output_directory && is_absolute_path(output_directory))
+		return output_directory;
+
+	if (!prefix || !*prefix) {
+		if (output_directory)
+			return output_directory;
+		/* The user did not explicitly ask for "./" */
+		outdir_offset = 2;
+		return "./";
+	}
+
+	outdir_offset = strlen(prefix);
+	if (!output_directory)
+		return prefix;
+
+	return prefix_filename(prefix, output_directory);
+}
+
+static const char * const builtin_format_patch_usage[] = {
+	N_("git format-patch [<options>] [<since> | <revision-range>]"),
+	NULL
+};
+
+static int keep_subject = 0;
+
+static int keep_callback(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+	((struct rev_info *)opt->value)->total = -1;
+	keep_subject = 1;
+	return 0;
+}
+
+static int subject_prefix = 0;
+
+static int subject_prefix_callback(const struct option *opt, const char *arg,
+			    int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+	subject_prefix = 1;
+	((struct rev_info *)opt->value)->subject_prefix = arg;
+	return 0;
+}
+
+static int rfc_callback(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+	return subject_prefix_callback(opt, "RFC PATCH", unset);
+}
+
+static int numbered_cmdline_opt = 0;
+
+static int numbered_callback(const struct option *opt, const char *arg,
+			     int unset)
+{
+	BUG_ON_OPT_ARG(arg);
+	*(int *)opt->value = numbered_cmdline_opt = unset ? 0 : 1;
+	if (unset)
+		auto_number =  0;
+	return 0;
+}
+
+static int no_numbered_callback(const struct option *opt, const char *arg,
+				int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+	return numbered_callback(opt, arg, 1);
+}
+
+static int output_directory_callback(const struct option *opt, const char *arg,
+			      int unset)
+{
+	const char **dir = (const char **)opt->value;
+	BUG_ON_OPT_NEG(unset);
+	if (*dir)
+		die(_("two output directories?"));
+	*dir = arg;
+	return 0;
+}
+
+static int thread_callback(const struct option *opt, const char *arg, int unset)
+{
+	int *thread = (int *)opt->value;
+	if (unset)
+		*thread = 0;
+	else if (!arg || !strcmp(arg, "shallow"))
+		*thread = THREAD_SHALLOW;
+	else if (!strcmp(arg, "deep"))
+		*thread = THREAD_DEEP;
+	/*
+	 * Please update _git_formatpatch() in git-completion.bash
+	 * when you add new options.
+	 */
+	else
+		return 1;
+	return 0;
+}
+
+static int attach_callback(const struct option *opt, const char *arg, int unset)
+{
+	struct rev_info *rev = (struct rev_info *)opt->value;
+	if (unset)
+		rev->mime_boundary = NULL;
+	else if (arg)
+		rev->mime_boundary = arg;
+	else
+		rev->mime_boundary = git_version_string;
+	rev->no_inline = unset ? 0 : 1;
+	return 0;
+}
+
+static int inline_callback(const struct option *opt, const char *arg, int unset)
+{
+	struct rev_info *rev = (struct rev_info *)opt->value;
+	if (unset)
+		rev->mime_boundary = NULL;
+	else if (arg)
+		rev->mime_boundary = arg;
+	else
+		rev->mime_boundary = git_version_string;
+	rev->no_inline = 0;
+	return 0;
+}
+
+static int header_callback(const struct option *opt, const char *arg, int unset)
+{
+	if (unset) {
+		string_list_clear(&extra_hdr, 0);
+		string_list_clear(&extra_to, 0);
+		string_list_clear(&extra_cc, 0);
+	} else {
+	    add_header(arg);
+	}
+	return 0;
+}
+
+static int to_callback(const struct option *opt, const char *arg, int unset)
+{
+	if (unset)
+		string_list_clear(&extra_to, 0);
+	else
+		string_list_append(&extra_to, arg);
+	return 0;
+}
+
+static int cc_callback(const struct option *opt, const char *arg, int unset)
+{
+	if (unset)
+		string_list_clear(&extra_cc, 0);
+	else
+		string_list_append(&extra_cc, arg);
+	return 0;
+}
+
+static int from_callback(const struct option *opt, const char *arg, int unset)
+{
+	char **from = opt->value;
+
+	free(*from);
+
+	if (unset)
+		*from = NULL;
+	else if (arg)
+		*from = xstrdup(arg);
+	else
+		*from = xstrdup(git_committer_info(IDENT_NO_DATE));
+	return 0;
+}
+
+struct base_tree_info {
+	struct object_id base_commit;
+	int nr_patch_id, alloc_patch_id;
+	struct object_id *patch_id;
+};
+
+static struct commit *get_base_commit(const char *base_commit,
+				      struct commit **list,
+				      int total)
+{
+	struct commit *base = NULL;
+	struct commit **rev;
+	int i = 0, rev_nr = 0;
+
+	if (base_commit && strcmp(base_commit, "auto")) {
+		base = lookup_commit_reference_by_name(base_commit);
+		if (!base)
+			die(_("unknown commit %s"), base_commit);
+	} else if ((base_commit && !strcmp(base_commit, "auto")) || base_auto) {
+		struct branch *curr_branch = branch_get(NULL);
+		const char *upstream = branch_get_upstream(curr_branch, NULL);
+		if (upstream) {
+			struct commit_list *base_list;
+			struct commit *commit;
+			struct object_id oid;
+
+			if (get_oid(upstream, &oid))
+				die(_("failed to resolve '%s' as a valid ref"), upstream);
+			commit = lookup_commit_or_die(&oid, "upstream base");
+			base_list = get_merge_bases_many(commit, total, list);
+			/* There should be one and only one merge base. */
+			if (!base_list || base_list->next)
+				die(_("could not find exact merge base"));
+			base = base_list->item;
+			free_commit_list(base_list);
+		} else {
+			die(_("failed to get upstream, if you want to record base commit automatically,\n"
+			      "please use git branch --set-upstream-to to track a remote branch.\n"
+			      "Or you could specify base commit by --base=<base-commit-id> manually"));
+		}
+	}
+
+	ALLOC_ARRAY(rev, total);
+	for (i = 0; i < total; i++)
+		rev[i] = list[i];
+
+	rev_nr = total;
+	/*
+	 * Get merge base through pair-wise computations
+	 * and store it in rev[0].
+	 */
+	while (rev_nr > 1) {
+		for (i = 0; i < rev_nr / 2; i++) {
+			struct commit_list *merge_base;
+			merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);
+			if (!merge_base || merge_base->next)
+				die(_("failed to find exact merge base"));
+
+			rev[i] = merge_base->item;
+		}
+
+		if (rev_nr % 2)
+			rev[i] = rev[2 * i];
+		rev_nr = DIV_ROUND_UP(rev_nr, 2);
+	}
+
+	if (!in_merge_bases(base, rev[0]))
+		die(_("base commit should be the ancestor of revision list"));
+
+	for (i = 0; i < total; i++) {
+		if (base == list[i])
+			die(_("base commit shouldn't be in revision list"));
+	}
+
+	free(rev);
+	return base;
+}
+
+define_commit_slab(commit_base, int);
+
+static void prepare_bases(struct base_tree_info *bases,
+			  struct commit *base,
+			  struct commit **list,
+			  int total)
+{
+	struct commit *commit;
+	struct rev_info revs;
+	struct diff_options diffopt;
+	struct commit_base commit_base;
+	int i;
+
+	if (!base)
+		return;
+
+	init_commit_base(&commit_base);
+	repo_diff_setup(the_repository, &diffopt);
+	diffopt.flags.recursive = 1;
+	diff_setup_done(&diffopt);
+
+	oidcpy(&bases->base_commit, &base->object.oid);
+
+	repo_init_revisions(the_repository, &revs, NULL);
+	revs.max_parents = 1;
+	revs.topo_order = 1;
+	for (i = 0; i < total; i++) {
+		list[i]->object.flags &= ~UNINTERESTING;
+		add_pending_object(&revs, &list[i]->object, "rev_list");
+		*commit_base_at(&commit_base, list[i]) = 1;
+	}
+	base->object.flags |= UNINTERESTING;
+	add_pending_object(&revs, &base->object, "base");
+
+	if (prepare_revision_walk(&revs))
+		die(_("revision walk setup failed"));
+	/*
+	 * Traverse the commits list, get prerequisite patch ids
+	 * and stuff them in bases structure.
+	 */
+	while ((commit = get_revision(&revs)) != NULL) {
+		struct object_id oid;
+		struct object_id *patch_id;
+		if (*commit_base_at(&commit_base, commit))
+			continue;
+		if (commit_patch_id(commit, &diffopt, &oid, 0, 1))
+			die(_("cannot get patch id"));
+		ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id);
+		patch_id = bases->patch_id + bases->nr_patch_id;
+		oidcpy(patch_id, &oid);
+		bases->nr_patch_id++;
+	}
+	clear_commit_base(&commit_base);
+}
+
+static void print_bases(struct base_tree_info *bases, FILE *file)
+{
+	int i;
+
+	/* Only do this once, either for the cover or for the first one */
+	if (is_null_oid(&bases->base_commit))
+		return;
+
+	/* Show the base commit */
+	fprintf(file, "\nbase-commit: %s\n", oid_to_hex(&bases->base_commit));
+
+	/* Show the prerequisite patches */
+	for (i = bases->nr_patch_id - 1; i >= 0; i--)
+		fprintf(file, "prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
+
+	free(bases->patch_id);
+	bases->nr_patch_id = 0;
+	bases->alloc_patch_id = 0;
+	oidclr(&bases->base_commit);
+}
+
+static const char *diff_title(struct strbuf *sb, int reroll_count,
+		       const char *generic, const char *rerolled)
+{
+	if (reroll_count <= 0)
+		strbuf_addstr(sb, generic);
+	else /* RFC may be v0, so allow -v1 to diff against v0 */
+		strbuf_addf(sb, rerolled, reroll_count - 1);
+	return sb->buf;
+}
+
+static void infer_range_diff_ranges(struct strbuf *r1,
+				    struct strbuf *r2,
+				    const char *prev,
+				    struct commit *origin,
+				    struct commit *head)
+{
+	const char *head_oid = oid_to_hex(&head->object.oid);
+
+	if (!strstr(prev, "..")) {
+		strbuf_addf(r1, "%s..%s", head_oid, prev);
+		strbuf_addf(r2, "%s..%s", prev, head_oid);
+	} else if (!origin) {
+		die(_("failed to infer range-diff ranges"));
+	} else {
+		strbuf_addstr(r1, prev);
+		strbuf_addf(r2, "%s..%s",
+			    oid_to_hex(&origin->object.oid), head_oid);
+	}
+}
+
+int cmd_format_patch(int argc, const char **argv, const char *prefix)
+{
+	struct commit *commit;
+	struct commit **list = NULL;
+	struct rev_info rev;
+	struct setup_revision_opt s_r_opt;
+	int nr = 0, total, i;
+	int use_stdout = 0;
+	int start_number = -1;
+	int just_numbers = 0;
+	int ignore_if_in_upstream = 0;
+	int cover_letter = -1;
+	int boundary_count = 0;
+	int no_binary_diff = 0;
+	int zero_commit = 0;
+	struct commit *origin = NULL;
+	const char *in_reply_to = NULL;
+	struct patch_ids ids;
+	struct strbuf buf = STRBUF_INIT;
+	int use_patch_format = 0;
+	int quiet = 0;
+	int reroll_count = -1;
+	char *branch_name = NULL;
+	char *base_commit = NULL;
+	struct base_tree_info bases;
+	int show_progress = 0;
+	struct progress *progress = NULL;
+	struct oid_array idiff_prev = OID_ARRAY_INIT;
+	struct strbuf idiff_title = STRBUF_INIT;
+	const char *rdiff_prev = NULL;
+	struct strbuf rdiff1 = STRBUF_INIT;
+	struct strbuf rdiff2 = STRBUF_INIT;
+	struct strbuf rdiff_title = STRBUF_INIT;
+	int creation_factor = -1;
+
+	const struct option builtin_format_patch_options[] = {
+		{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
+			    N_("use [PATCH n/m] even with a single patch"),
+			    PARSE_OPT_NOARG, numbered_callback },
+		{ OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL,
+			    N_("use [PATCH] even with multiple patches"),
+			    PARSE_OPT_NOARG | PARSE_OPT_NONEG, no_numbered_callback },
+		OPT_BOOL('s', "signoff", &do_signoff, N_("add Signed-off-by:")),
+		OPT_BOOL(0, "stdout", &use_stdout,
+			    N_("print patches to standard out")),
+		OPT_BOOL(0, "cover-letter", &cover_letter,
+			    N_("generate a cover letter")),
+		OPT_BOOL(0, "numbered-files", &just_numbers,
+			    N_("use simple number sequence for output file names")),
+		OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"),
+			    N_("use <sfx> instead of '.patch'")),
+		OPT_INTEGER(0, "start-number", &start_number,
+			    N_("start numbering patches at <n> instead of 1")),
+		OPT_INTEGER('v', "reroll-count", &reroll_count,
+			    N_("mark the series as Nth re-roll")),
+		{ OPTION_CALLBACK, 0, "rfc", &rev, NULL,
+			    N_("Use [RFC PATCH] instead of [PATCH]"),
+			    PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback },
+		{ OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"),
+			    N_("Use [<prefix>] instead of [PATCH]"),
+			    PARSE_OPT_NONEG, subject_prefix_callback },
+		{ OPTION_CALLBACK, 'o', "output-directory", &output_directory,
+			    N_("dir"), N_("store resulting files in <dir>"),
+			    PARSE_OPT_NONEG, output_directory_callback },
+		{ OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL,
+			    N_("don't strip/add [PATCH]"),
+			    PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback },
+		OPT_BOOL(0, "no-binary", &no_binary_diff,
+			 N_("don't output binary diffs")),
+		OPT_BOOL(0, "zero-commit", &zero_commit,
+			 N_("output all-zero hash in From header")),
+		OPT_BOOL(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
+			 N_("don't include a patch matching a commit upstream")),
+		OPT_SET_INT_F('p', "no-stat", &use_patch_format,
+			      N_("show patch format instead of default (patch + stat)"),
+			      1, PARSE_OPT_NONEG),
+		OPT_GROUP(N_("Messaging")),
+		{ OPTION_CALLBACK, 0, "add-header", NULL, N_("header"),
+			    N_("add email header"), 0, header_callback },
+		{ OPTION_CALLBACK, 0, "to", NULL, N_("email"), N_("add To: header"),
+			    0, to_callback },
+		{ OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"),
+			    0, cc_callback },
+		{ OPTION_CALLBACK, 0, "from", &from, N_("ident"),
+			    N_("set From address to <ident> (or committer ident if absent)"),
+			    PARSE_OPT_OPTARG, from_callback },
+		OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
+			    N_("make first mail a reply to <message-id>")),
+		{ OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"),
+			    N_("attach the patch"), PARSE_OPT_OPTARG,
+			    attach_callback },
+		{ OPTION_CALLBACK, 0, "inline", &rev, N_("boundary"),
+			    N_("inline the patch"),
+			    PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
+			    inline_callback },
+		{ OPTION_CALLBACK, 0, "thread", &thread, N_("style"),
+			    N_("enable message threading, styles: shallow, deep"),
+			    PARSE_OPT_OPTARG, thread_callback },
+		OPT_STRING(0, "signature", &signature, N_("signature"),
+			    N_("add a signature")),
+		OPT_STRING(0, "base", &base_commit, N_("base-commit"),
+			   N_("add prerequisite tree info to the patch series")),
+		OPT_FILENAME(0, "signature-file", &signature_file,
+				N_("add a signature from a file")),
+		OPT__QUIET(&quiet, N_("don't print the patch filenames")),
+		OPT_BOOL(0, "progress", &show_progress,
+			 N_("show progress while generating patches")),
+		OPT_CALLBACK(0, "interdiff", &idiff_prev, N_("rev"),
+			     N_("show changes against <rev> in cover letter or single patch"),
+			     parse_opt_object_name),
+		OPT_STRING(0, "range-diff", &rdiff_prev, N_("refspec"),
+			   N_("show changes against <refspec> in cover letter or single patch")),
+		OPT_INTEGER(0, "creation-factor", &creation_factor,
+			    N_("percentage by which creation is weighted")),
+		OPT_END()
+	};
+
+	extra_hdr.strdup_strings = 1;
+	extra_to.strdup_strings = 1;
+	extra_cc.strdup_strings = 1;
+	init_log_defaults();
+	repo_init_revisions(the_repository, &rev, prefix);
+	git_config(git_format_config, &rev);
+	rev.commit_format = CMIT_FMT_EMAIL;
+	rev.expand_tabs_in_log_default = 0;
+	rev.verbose_header = 1;
+	rev.diff = 1;
+	rev.max_parents = 1;
+	rev.diffopt.flags.recursive = 1;
+	rev.subject_prefix = fmt_patch_subject_prefix;
+	memset(&s_r_opt, 0, sizeof(s_r_opt));
+	s_r_opt.def = "HEAD";
+	s_r_opt.revarg_opt = REVARG_COMMITTISH;
+
+	if (default_attach) {
+		rev.mime_boundary = default_attach;
+		rev.no_inline = 1;
+	}
+
+	/*
+	 * Parse the arguments before setup_revisions(), or something
+	 * like "git format-patch -o a123 HEAD^.." may fail; a123 is
+	 * possibly a valid SHA1.
+	 */
+	argc = parse_options(argc, argv, prefix, builtin_format_patch_options,
+			     builtin_format_patch_usage,
+			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
+			     PARSE_OPT_KEEP_DASHDASH);
+
+	if (0 < reroll_count) {
+		struct strbuf sprefix = STRBUF_INIT;
+		strbuf_addf(&sprefix, "%s v%d",
+			    rev.subject_prefix, reroll_count);
+		rev.reroll_count = reroll_count;
+		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
+	}
+
+	for (i = 0; i < extra_hdr.nr; i++) {
+		strbuf_addstr(&buf, extra_hdr.items[i].string);
+		strbuf_addch(&buf, '\n');
+	}
+
+	if (extra_to.nr)
+		strbuf_addstr(&buf, "To: ");
+	for (i = 0; i < extra_to.nr; i++) {
+		if (i)
+			strbuf_addstr(&buf, "    ");
+		strbuf_addstr(&buf, extra_to.items[i].string);
+		if (i + 1 < extra_to.nr)
+			strbuf_addch(&buf, ',');
+		strbuf_addch(&buf, '\n');
+	}
+
+	if (extra_cc.nr)
+		strbuf_addstr(&buf, "Cc: ");
+	for (i = 0; i < extra_cc.nr; i++) {
+		if (i)
+			strbuf_addstr(&buf, "    ");
+		strbuf_addstr(&buf, extra_cc.items[i].string);
+		if (i + 1 < extra_cc.nr)
+			strbuf_addch(&buf, ',');
+		strbuf_addch(&buf, '\n');
+	}
+
+	rev.extra_headers = strbuf_detach(&buf, NULL);
+
+	if (from) {
+		if (split_ident_line(&rev.from_ident, from, strlen(from)))
+			die(_("invalid ident line: %s"), from);
+	}
+
+	if (start_number < 0)
+		start_number = 1;
+
+	/*
+	 * If numbered is set solely due to format.numbered in config,
+	 * and it would conflict with --keep-subject (-k) from the
+	 * command line, reset "numbered".
+	 */
+	if (numbered && keep_subject && !numbered_cmdline_opt)
+		numbered = 0;
+
+	if (numbered && keep_subject)
+		die(_("-n and -k are mutually exclusive"));
+	if (keep_subject && subject_prefix)
+		die(_("--subject-prefix/--rfc and -k are mutually exclusive"));
+	rev.preserve_subject = keep_subject;
+
+	argc = setup_revisions(argc, argv, &rev, &s_r_opt);
+	if (argc > 1)
+		die(_("unrecognized argument: %s"), argv[1]);
+
+	if (rev.diffopt.output_format & DIFF_FORMAT_NAME)
+		die(_("--name-only does not make sense"));
+	if (rev.diffopt.output_format & DIFF_FORMAT_NAME_STATUS)
+		die(_("--name-status does not make sense"));
+	if (rev.diffopt.output_format & DIFF_FORMAT_CHECKDIFF)
+		die(_("--check does not make sense"));
+
+	if (!use_patch_format &&
+		(!rev.diffopt.output_format ||
+		 rev.diffopt.output_format == DIFF_FORMAT_PATCH))
+		rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY;
+	if (!rev.diffopt.stat_width)
+		rev.diffopt.stat_width = MAIL_DEFAULT_WRAP;
+
+	/* Always generate a patch */
+	rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
+
+	rev.zero_commit = zero_commit;
+
+	if (!rev.diffopt.flags.text && !no_binary_diff)
+		rev.diffopt.flags.binary = 1;
+
+	if (rev.show_notes)
+		init_display_notes(&rev.notes_opt);
+
+	if (!output_directory && !use_stdout)
+		output_directory = config_output_directory;
+
+	if (!use_stdout)
+		output_directory = set_outdir(prefix, output_directory);
+	else
+		setup_pager();
+
+	if (output_directory) {
+		if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)
+			rev.diffopt.use_color = GIT_COLOR_NEVER;
+		if (use_stdout)
+			die(_("standard output, or directory, which one?"));
+		if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
+			die_errno(_("could not create directory '%s'"),
+				  output_directory);
+	}
+
+	if (rev.pending.nr == 1) {
+		int check_head = 0;
+
+		if (rev.max_count < 0 && !rev.show_root_diff) {
+			/*
+			 * This is traditional behaviour of "git format-patch
+			 * origin" that prepares what the origin side still
+			 * does not have.
+			 */
+			rev.pending.objects[0].item->flags |= UNINTERESTING;
+			add_head_to_pending(&rev);
+			check_head = 1;
+		}
+		/*
+		 * Otherwise, it is "format-patch -22 HEAD", and/or
+		 * "format-patch --root HEAD".  The user wants
+		 * get_revision() to do the usual traversal.
+		 */
+
+		if (!strcmp(rev.pending.objects[0].name, "HEAD"))
+			check_head = 1;
+
+		if (check_head) {
+			const char *ref, *v;
+			ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+						 NULL, NULL);
+			if (ref && skip_prefix(ref, "refs/heads/", &v))
+				branch_name = xstrdup(v);
+			else
+				branch_name = xstrdup(""); /* no branch */
+		}
+	}
+
+	/*
+	 * We cannot move this anywhere earlier because we do want to
+	 * know if --root was given explicitly from the command line.
+	 */
+	rev.show_root_diff = 1;
+
+	if (ignore_if_in_upstream) {
+		/* Don't say anything if head and upstream are the same. */
+		if (rev.pending.nr == 2) {
+			struct object_array_entry *o = rev.pending.objects;
+			if (oideq(&o[0].item->oid, &o[1].item->oid))
+				goto done;
+		}
+		get_patch_ids(&rev, &ids);
+	}
+
+	if (prepare_revision_walk(&rev))
+		die(_("revision walk setup failed"));
+	rev.boundary = 1;
+	while ((commit = get_revision(&rev)) != NULL) {
+		if (commit->object.flags & BOUNDARY) {
+			boundary_count++;
+			origin = (boundary_count == 1) ? commit : NULL;
+			continue;
+		}
+
+		if (ignore_if_in_upstream && has_commit_patch_id(commit, &ids))
+			continue;
+
+		nr++;
+		REALLOC_ARRAY(list, nr);
+		list[nr - 1] = commit;
+	}
+	if (nr == 0)
+		/* nothing to do */
+		goto done;
+	total = nr;
+	if (cover_letter == -1) {
+		if (config_cover_letter == COVER_AUTO)
+			cover_letter = (total > 1);
+		else
+			cover_letter = (config_cover_letter == COVER_ON);
+	}
+	if (!keep_subject && auto_number && (total > 1 || cover_letter))
+		numbered = 1;
+	if (numbered)
+		rev.total = total + start_number - 1;
+
+	if (idiff_prev.nr) {
+		if (!cover_letter && total != 1)
+			die(_("--interdiff requires --cover-letter or single patch"));
+		rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
+		rev.idiff_oid2 = get_commit_tree_oid(list[0]);
+		rev.idiff_title = diff_title(&idiff_title, reroll_count,
+					     _("Interdiff:"),
+					     _("Interdiff against v%d:"));
+	}
+
+	if (creation_factor < 0)
+		creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT;
+	else if (!rdiff_prev)
+		die(_("--creation-factor requires --range-diff"));
+
+	if (rdiff_prev) {
+		if (!cover_letter && total != 1)
+			die(_("--range-diff requires --cover-letter or single patch"));
+
+		infer_range_diff_ranges(&rdiff1, &rdiff2, rdiff_prev,
+					origin, list[0]);
+		rev.rdiff1 = rdiff1.buf;
+		rev.rdiff2 = rdiff2.buf;
+		rev.creation_factor = creation_factor;
+		rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
+					     _("Range-diff:"),
+					     _("Range-diff against v%d:"));
+	}
+
+	if (!signature) {
+		; /* --no-signature inhibits all signatures */
+	} else if (signature && signature != git_version_string) {
+		; /* non-default signature already set */
+	} else if (signature_file) {
+		struct strbuf buf = STRBUF_INIT;
+
+		if (strbuf_read_file(&buf, signature_file, 128) < 0)
+			die_errno(_("unable to read signature file '%s'"), signature_file);
+		signature = strbuf_detach(&buf, NULL);
+	}
+
+	memset(&bases, 0, sizeof(bases));
+	if (base_commit || base_auto) {
+		struct commit *base = get_base_commit(base_commit, list, nr);
+		reset_revision_walk();
+		clear_object_flags(UNINTERESTING);
+		prepare_bases(&bases, base, list, nr);
+	}
+
+	if (in_reply_to || thread || cover_letter)
+		rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
+	if (in_reply_to) {
+		const char *msgid = clean_message_id(in_reply_to);
+		string_list_append(rev.ref_message_ids, msgid);
+	}
+	rev.numbered_files = just_numbers;
+	rev.patch_suffix = fmt_patch_suffix;
+	if (cover_letter) {
+		if (thread)
+			gen_message_id(&rev, "cover");
+		make_cover_letter(&rev, use_stdout,
+				  origin, nr, list, branch_name, quiet);
+		print_bases(&bases, rev.diffopt.file);
+		print_signature(rev.diffopt.file);
+		total++;
+		start_number--;
+		/* interdiff/range-diff in cover-letter; omit from patches */
+		rev.idiff_oid1 = NULL;
+		rev.rdiff1 = NULL;
+	}
+	rev.add_signoff = do_signoff;
+
+	if (show_progress)
+		progress = start_delayed_progress(_("Generating patches"), total);
+	while (0 <= --nr) {
+		int shown;
+		display_progress(progress, total - nr);
+		commit = list[nr];
+		rev.nr = total - nr + (start_number - 1);
+		/* Make the second and subsequent mails replies to the first */
+		if (thread) {
+			/* Have we already had a message ID? */
+			if (rev.message_id) {
+				/*
+				 * For deep threading: make every mail
+				 * a reply to the previous one, no
+				 * matter what other options are set.
+				 *
+				 * For shallow threading:
+				 *
+				 * Without --cover-letter and
+				 * --in-reply-to, make every mail a
+				 * reply to the one before.
+				 *
+				 * With --in-reply-to but no
+				 * --cover-letter, make every mail a
+				 * reply to the <reply-to>.
+				 *
+				 * With --cover-letter, make every
+				 * mail but the cover letter a reply
+				 * to the cover letter.  The cover
+				 * letter is a reply to the
+				 * --in-reply-to, if specified.
+				 */
+				if (thread == THREAD_SHALLOW
+				    && rev.ref_message_ids->nr > 0
+				    && (!cover_letter || rev.nr > 1))
+					free(rev.message_id);
+				else
+					string_list_append(rev.ref_message_ids,
+							   rev.message_id);
+			}
+			gen_message_id(&rev, oid_to_hex(&commit->object.oid));
+		}
+
+		if (!use_stdout &&
+		    open_next_file(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
+			die(_("failed to create output files"));
+		shown = log_tree_commit(&rev, commit);
+		free_commit_buffer(the_repository->parsed_objects,
+				   commit);
+
+		/* We put one extra blank line between formatted
+		 * patches and this flag is used by log-tree code
+		 * to see if it needs to emit a LF before showing
+		 * the log; when using one file per patch, we do
+		 * not want the extra blank line.
+		 */
+		if (!use_stdout)
+			rev.shown_one = 0;
+		if (shown) {
+			print_bases(&bases, rev.diffopt.file);
+			if (rev.mime_boundary)
+				fprintf(rev.diffopt.file, "\n--%s%s--\n\n\n",
+				       mime_boundary_leader,
+				       rev.mime_boundary);
+			else
+				print_signature(rev.diffopt.file);
+		}
+		if (!use_stdout)
+			fclose(rev.diffopt.file);
+	}
+	stop_progress(&progress);
+	free(list);
+	free(branch_name);
+	string_list_clear(&extra_to, 0);
+	string_list_clear(&extra_cc, 0);
+	string_list_clear(&extra_hdr, 0);
+	if (ignore_if_in_upstream)
+		free_patch_ids(&ids);
+
+done:
+	oid_array_clear(&idiff_prev);
+	strbuf_release(&idiff_title);
+	strbuf_release(&rdiff1);
+	strbuf_release(&rdiff2);
+	strbuf_release(&rdiff_title);
+	return 0;
+}
+
+static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
+{
+	struct object_id oid;
+	if (get_oid(arg, &oid) == 0) {
+		struct commit *commit = lookup_commit_reference(the_repository,
+								&oid);
+		if (commit) {
+			commit->object.flags |= flags;
+			add_pending_object(revs, &commit->object, arg);
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static const char * const cherry_usage[] = {
+	N_("git cherry [-v] [<upstream> [<head> [<limit>]]]"),
+	NULL
+};
+
+static void print_commit(char sign, struct commit *commit, int verbose,
+			 int abbrev, FILE *file)
+{
+	if (!verbose) {
+		fprintf(file, "%c %s\n", sign,
+		       find_unique_abbrev(&commit->object.oid, abbrev));
+	} else {
+		struct strbuf buf = STRBUF_INIT;
+		pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
+		fprintf(file, "%c %s %s\n", sign,
+		       find_unique_abbrev(&commit->object.oid, abbrev),
+		       buf.buf);
+		strbuf_release(&buf);
+	}
+}
+
+int cmd_cherry(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info revs;
+	struct patch_ids ids;
+	struct commit *commit;
+	struct commit_list *list = NULL;
+	struct branch *current_branch;
+	const char *upstream;
+	const char *head = "HEAD";
+	const char *limit = NULL;
+	int verbose = 0, abbrev = 0;
+
+	struct option options[] = {
+		OPT__ABBREV(&abbrev),
+		OPT__VERBOSE(&verbose, N_("be verbose")),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options, cherry_usage, 0);
+
+	switch (argc) {
+	case 3:
+		limit = argv[2];
+		/* FALLTHROUGH */
+	case 2:
+		head = argv[1];
+		/* FALLTHROUGH */
+	case 1:
+		upstream = argv[0];
+		break;
+	default:
+		current_branch = branch_get(NULL);
+		upstream = branch_get_upstream(current_branch, NULL);
+		if (!upstream) {
+			fprintf(stderr, _("Could not find a tracked"
+					" remote branch, please"
+					" specify <upstream> manually.\n"));
+			usage_with_options(cherry_usage, options);
+		}
+	}
+
+	repo_init_revisions(the_repository, &revs, prefix);
+	revs.max_parents = 1;
+
+	if (add_pending_commit(head, &revs, 0))
+		die(_("unknown commit %s"), head);
+	if (add_pending_commit(upstream, &revs, UNINTERESTING))
+		die(_("unknown commit %s"), upstream);
+
+	/* Don't say anything if head and upstream are the same. */
+	if (revs.pending.nr == 2) {
+		struct object_array_entry *o = revs.pending.objects;
+		if (oideq(&o[0].item->oid, &o[1].item->oid))
+			return 0;
+	}
+
+	get_patch_ids(&revs, &ids);
+
+	if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
+		die(_("unknown commit %s"), limit);
+
+	/* reverse the list of commits */
+	if (prepare_revision_walk(&revs))
+		die(_("revision walk setup failed"));
+	while ((commit = get_revision(&revs)) != NULL) {
+		commit_list_insert(commit, &list);
+	}
+
+	while (list) {
+		char sign = '+';
+
+		commit = list->item;
+		if (has_commit_patch_id(commit, &ids))
+			sign = '-';
+		print_commit(sign, commit, verbose, abbrev, revs.diffopt.file);
+		list = list->next;
+	}
+
+	free_patch_ids(&ids);
+	return 0;
+}
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
new file mode 100644
index 0000000000..670e8fb93c
--- /dev/null
+++ b/builtin/ls-files.c
@@ -0,0 +1,692 @@
+/*
+ * This merges the file listing in the directory cache index
+ * with the actual working directory list, and shows different
+ * combinations of the two.
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+#include "repository.h"
+#include "config.h"
+#include "quote.h"
+#include "dir.h"
+#include "builtin.h"
+#include "tree.h"
+#include "parse-options.h"
+#include "resolve-undo.h"
+#include "string-list.h"
+#include "pathspec.h"
+#include "run-command.h"
+#include "submodule.h"
+#include "submodule-config.h"
+
+static int abbrev;
+static int show_deleted;
+static int show_cached;
+static int show_others;
+static int show_stage;
+static int show_unmerged;
+static int show_resolve_undo;
+static int show_modified;
+static int show_killed;
+static int show_valid_bit;
+static int show_fsmonitor_bit;
+static int line_terminator = '\n';
+static int debug_mode;
+static int show_eol;
+static int recurse_submodules;
+
+static const char *prefix;
+static int max_prefix_len;
+static int prefix_len;
+static struct pathspec pathspec;
+static int error_unmatch;
+static char *ps_matched;
+static const char *with_tree;
+static int exc_given;
+static int exclude_args;
+
+static const char *tag_cached = "";
+static const char *tag_unmerged = "";
+static const char *tag_removed = "";
+static const char *tag_other = "";
+static const char *tag_killed = "";
+static const char *tag_modified = "";
+static const char *tag_skip_worktree = "";
+static const char *tag_resolve_undo = "";
+
+static void write_eolinfo(const struct index_state *istate,
+			  const struct cache_entry *ce, const char *path)
+{
+	if (show_eol) {
+		struct stat st;
+		const char *i_txt = "";
+		const char *w_txt = "";
+		const char *a_txt = get_convert_attr_ascii(istate, path);
+		if (ce && S_ISREG(ce->ce_mode))
+			i_txt = get_cached_convert_stats_ascii(istate,
+							       ce->name);
+		if (!lstat(path, &st) && S_ISREG(st.st_mode))
+			w_txt = get_wt_convert_stats_ascii(path);
+		printf("i/%-5s w/%-5s attr/%-17s\t", i_txt, w_txt, a_txt);
+	}
+}
+
+static void write_name(const char *name)
+{
+	/*
+	 * With "--full-name", prefix_len=0; this caller needs to pass
+	 * an empty string in that case (a NULL is good for "").
+	 */
+	write_name_quoted_relative(name, prefix_len ? prefix : NULL,
+				   stdout, line_terminator);
+}
+
+static const char *get_tag(const struct cache_entry *ce, const char *tag)
+{
+	static char alttag[4];
+
+	if (tag && *tag && ((show_valid_bit && (ce->ce_flags & CE_VALID)) ||
+		(show_fsmonitor_bit && (ce->ce_flags & CE_FSMONITOR_VALID)))) {
+		memcpy(alttag, tag, 3);
+
+		if (isalpha(tag[0])) {
+			alttag[0] = tolower(tag[0]);
+		} else if (tag[0] == '?') {
+			alttag[0] = '!';
+		} else {
+			alttag[0] = 'v';
+			alttag[1] = tag[0];
+			alttag[2] = ' ';
+			alttag[3] = 0;
+		}
+
+		tag = alttag;
+	}
+
+	return tag;
+}
+
+static void print_debug(const struct cache_entry *ce)
+{
+	if (debug_mode) {
+		const struct stat_data *sd = &ce->ce_stat_data;
+
+		printf("  ctime: %u:%u\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
+		printf("  mtime: %u:%u\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
+		printf("  dev: %u\tino: %u\n", sd->sd_dev, sd->sd_ino);
+		printf("  uid: %u\tgid: %u\n", sd->sd_uid, sd->sd_gid);
+		printf("  size: %u\tflags: %x\n", sd->sd_size, ce->ce_flags);
+	}
+}
+
+static void show_dir_entry(const struct index_state *istate,
+			   const char *tag, struct dir_entry *ent)
+{
+	int len = max_prefix_len;
+
+	if (len > ent->len)
+		die("git ls-files: internal error - directory entry not superset of prefix");
+
+	if (!dir_path_match(istate, ent, &pathspec, len, ps_matched))
+		return;
+
+	fputs(tag, stdout);
+	write_eolinfo(istate, NULL, ent->name);
+	write_name(ent->name);
+}
+
+static void show_other_files(const struct index_state *istate,
+			     const struct dir_struct *dir)
+{
+	int i;
+
+	for (i = 0; i < dir->nr; i++) {
+		struct dir_entry *ent = dir->entries[i];
+		if (!index_name_is_other(istate, ent->name, ent->len))
+			continue;
+		show_dir_entry(istate, tag_other, ent);
+	}
+}
+
+static void show_killed_files(const struct index_state *istate,
+			      const struct dir_struct *dir)
+{
+	int i;
+	for (i = 0; i < dir->nr; i++) {
+		struct dir_entry *ent = dir->entries[i];
+		char *cp, *sp;
+		int pos, len, killed = 0;
+
+		for (cp = ent->name; cp - ent->name < ent->len; cp = sp + 1) {
+			sp = strchr(cp, '/');
+			if (!sp) {
+				/* If ent->name is prefix of an entry in the
+				 * cache, it will be killed.
+				 */
+				pos = index_name_pos(istate, ent->name, ent->len);
+				if (0 <= pos)
+					BUG("killed-file %.*s not found",
+						ent->len, ent->name);
+				pos = -pos - 1;
+				while (pos < istate->cache_nr &&
+				       ce_stage(istate->cache[pos]))
+					pos++; /* skip unmerged */
+				if (istate->cache_nr <= pos)
+					break;
+				/* pos points at a name immediately after
+				 * ent->name in the cache.  Does it expect
+				 * ent->name to be a directory?
+				 */
+				len = ce_namelen(istate->cache[pos]);
+				if ((ent->len < len) &&
+				    !strncmp(istate->cache[pos]->name,
+					     ent->name, ent->len) &&
+				    istate->cache[pos]->name[ent->len] == '/')
+					killed = 1;
+				break;
+			}
+			if (0 <= index_name_pos(istate, ent->name, sp - ent->name)) {
+				/* If any of the leading directories in
+				 * ent->name is registered in the cache,
+				 * ent->name will be killed.
+				 */
+				killed = 1;
+				break;
+			}
+		}
+		if (killed)
+			show_dir_entry(istate, tag_killed, dir->entries[i]);
+	}
+}
+
+static void show_files(struct repository *repo, struct dir_struct *dir);
+
+static void show_submodule(struct repository *superproject,
+			   struct dir_struct *dir, const char *path)
+{
+	struct repository subrepo;
+	const struct submodule *sub = submodule_from_path(superproject,
+							  &null_oid, path);
+
+	if (repo_submodule_init(&subrepo, superproject, sub))
+		return;
+
+	if (repo_read_index(&subrepo) < 0)
+		die("index file corrupt");
+
+	show_files(&subrepo, dir);
+
+	repo_clear(&subrepo);
+}
+
+static void show_ce(struct repository *repo, struct dir_struct *dir,
+		    const struct cache_entry *ce, const char *fullname,
+		    const char *tag)
+{
+	if (max_prefix_len > strlen(fullname))
+		die("git ls-files: internal error - cache entry not superset of prefix");
+
+	if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
+	    is_submodule_active(repo, ce->name)) {
+		show_submodule(repo, dir, ce->name);
+	} else if (match_pathspec(repo->index, &pathspec, fullname, strlen(fullname),
+				  max_prefix_len, ps_matched,
+				  S_ISDIR(ce->ce_mode) ||
+				  S_ISGITLINK(ce->ce_mode))) {
+		tag = get_tag(ce, tag);
+
+		if (!show_stage) {
+			fputs(tag, stdout);
+		} else {
+			printf("%s%06o %s %d\t",
+			       tag,
+			       ce->ce_mode,
+			       find_unique_abbrev(&ce->oid, abbrev),
+			       ce_stage(ce));
+		}
+		write_eolinfo(repo->index, ce, fullname);
+		write_name(fullname);
+		print_debug(ce);
+	}
+}
+
+static void show_ru_info(const struct index_state *istate)
+{
+	struct string_list_item *item;
+
+	if (!istate->resolve_undo)
+		return;
+
+	for_each_string_list_item(item, istate->resolve_undo) {
+		const char *path = item->string;
+		struct resolve_undo_info *ui = item->util;
+		int i, len;
+
+		len = strlen(path);
+		if (len < max_prefix_len)
+			continue; /* outside of the prefix */
+		if (!match_pathspec(istate, &pathspec, path, len,
+				    max_prefix_len, ps_matched, 0))
+			continue; /* uninterested */
+		for (i = 0; i < 3; i++) {
+			if (!ui->mode[i])
+				continue;
+			printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
+			       find_unique_abbrev(&ui->oid[i], abbrev),
+			       i + 1);
+			write_name(path);
+		}
+	}
+}
+
+static int ce_excluded(struct dir_struct *dir, struct index_state *istate,
+		       const char *fullname, const struct cache_entry *ce)
+{
+	int dtype = ce_to_dtype(ce);
+	return is_excluded(dir, istate, fullname, &dtype);
+}
+
+static void construct_fullname(struct strbuf *out, const struct repository *repo,
+			       const struct cache_entry *ce)
+{
+	strbuf_reset(out);
+	if (repo->submodule_prefix)
+		strbuf_addstr(out, repo->submodule_prefix);
+	strbuf_addstr(out, ce->name);
+}
+
+static void show_files(struct repository *repo, struct dir_struct *dir)
+{
+	int i;
+	struct strbuf fullname = STRBUF_INIT;
+
+	/* For cached/deleted files we don't need to even do the readdir */
+	if (show_others || show_killed) {
+		if (!show_others)
+			dir->flags |= DIR_COLLECT_KILLED_ONLY;
+		fill_directory(dir, repo->index, &pathspec);
+		if (show_others)
+			show_other_files(repo->index, dir);
+		if (show_killed)
+			show_killed_files(repo->index, dir);
+	}
+	if (show_cached || show_stage) {
+		for (i = 0; i < repo->index->cache_nr; i++) {
+			const struct cache_entry *ce = repo->index->cache[i];
+
+			construct_fullname(&fullname, repo, ce);
+
+			if ((dir->flags & DIR_SHOW_IGNORED) &&
+			    !ce_excluded(dir, repo->index, fullname.buf, ce))
+				continue;
+			if (show_unmerged && !ce_stage(ce))
+				continue;
+			if (ce->ce_flags & CE_UPDATE)
+				continue;
+			show_ce(repo, dir, ce, fullname.buf,
+				ce_stage(ce) ? tag_unmerged :
+				(ce_skip_worktree(ce) ? tag_skip_worktree :
+				 tag_cached));
+		}
+	}
+	if (show_deleted || show_modified) {
+		for (i = 0; i < repo->index->cache_nr; i++) {
+			const struct cache_entry *ce = repo->index->cache[i];
+			struct stat st;
+			int err;
+
+			construct_fullname(&fullname, repo, ce);
+
+			if ((dir->flags & DIR_SHOW_IGNORED) &&
+			    !ce_excluded(dir, repo->index, fullname.buf, ce))
+				continue;
+			if (ce->ce_flags & CE_UPDATE)
+				continue;
+			if (ce_skip_worktree(ce))
+				continue;
+			err = lstat(fullname.buf, &st);
+			if (show_deleted && err)
+				show_ce(repo, dir, ce, fullname.buf, tag_removed);
+			if (show_modified && ie_modified(repo->index, ce, &st, 0))
+				show_ce(repo, dir, ce, fullname.buf, tag_modified);
+		}
+	}
+
+	strbuf_release(&fullname);
+}
+
+/*
+ * Prune the index to only contain stuff starting with "prefix"
+ */
+static void prune_index(struct index_state *istate,
+			const char *prefix, size_t prefixlen)
+{
+	int pos;
+	unsigned int first, last;
+
+	if (!prefix || !istate->cache_nr)
+		return;
+	pos = index_name_pos(istate, prefix, prefixlen);
+	if (pos < 0)
+		pos = -pos-1;
+	first = pos;
+	last = istate->cache_nr;
+	while (last > first) {
+		int next = first + ((last - first) >> 1);
+		const struct cache_entry *ce = istate->cache[next];
+		if (!strncmp(ce->name, prefix, prefixlen)) {
+			first = next+1;
+			continue;
+		}
+		last = next;
+	}
+	MOVE_ARRAY(istate->cache, istate->cache + pos, last - pos);
+	istate->cache_nr = last - pos;
+}
+
+static int get_common_prefix_len(const char *common_prefix)
+{
+	int common_prefix_len;
+
+	if (!common_prefix)
+		return 0;
+
+	common_prefix_len = strlen(common_prefix);
+
+	/*
+	 * If the prefix has a trailing slash, strip it so that submodules wont
+	 * be pruned from the index.
+	 */
+	if (common_prefix[common_prefix_len - 1] == '/')
+		common_prefix_len--;
+
+	return common_prefix_len;
+}
+
+/*
+ * Read the tree specified with --with-tree option
+ * (typically, HEAD) into stage #1 and then
+ * squash them down to stage #0.  This is used for
+ * --error-unmatch to list and check the path patterns
+ * that were given from the command line.  We are not
+ * going to write this index out.
+ */
+void overlay_tree_on_index(struct index_state *istate,
+			   const char *tree_name, const char *prefix)
+{
+	struct tree *tree;
+	struct object_id oid;
+	struct pathspec pathspec;
+	struct cache_entry *last_stage0 = NULL;
+	int i;
+
+	if (get_oid(tree_name, &oid))
+		die("tree-ish %s not found.", tree_name);
+	tree = parse_tree_indirect(&oid);
+	if (!tree)
+		die("bad tree-ish %s", tree_name);
+
+	/* Hoist the unmerged entries up to stage #3 to make room */
+	for (i = 0; i < istate->cache_nr; i++) {
+		struct cache_entry *ce = istate->cache[i];
+		if (!ce_stage(ce))
+			continue;
+		ce->ce_flags |= CE_STAGEMASK;
+	}
+
+	if (prefix) {
+		static const char *(matchbuf[1]);
+		matchbuf[0] = NULL;
+		parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC,
+			       PATHSPEC_PREFER_CWD, prefix, matchbuf);
+	} else
+		memset(&pathspec, 0, sizeof(pathspec));
+	if (read_tree(the_repository, tree, 1, &pathspec, istate))
+		die("unable to read tree entries %s", tree_name);
+
+	for (i = 0; i < istate->cache_nr; i++) {
+		struct cache_entry *ce = istate->cache[i];
+		switch (ce_stage(ce)) {
+		case 0:
+			last_stage0 = ce;
+			/* fallthru */
+		default:
+			continue;
+		case 1:
+			/*
+			 * If there is stage #0 entry for this, we do not
+			 * need to show it.  We use CE_UPDATE bit to mark
+			 * such an entry.
+			 */
+			if (last_stage0 &&
+			    !strcmp(last_stage0->name, ce->name))
+				ce->ce_flags |= CE_UPDATE;
+		}
+	}
+}
+
+static const char * const ls_files_usage[] = {
+	N_("git ls-files [<options>] [<file>...]"),
+	NULL
+};
+
+static int option_parse_exclude(const struct option *opt,
+				const char *arg, int unset)
+{
+	struct string_list *exclude_list = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	exc_given = 1;
+	string_list_append(exclude_list, arg);
+
+	return 0;
+}
+
+static int option_parse_exclude_from(const struct option *opt,
+				     const char *arg, int unset)
+{
+	struct dir_struct *dir = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	exc_given = 1;
+	add_excludes_from_file(dir, arg);
+
+	return 0;
+}
+
+static int option_parse_exclude_standard(const struct option *opt,
+					 const char *arg, int unset)
+{
+	struct dir_struct *dir = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+
+	exc_given = 1;
+	setup_standard_excludes(dir);
+
+	return 0;
+}
+
+int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
+{
+	int require_work_tree = 0, show_tag = 0, i;
+	const char *max_prefix;
+	struct dir_struct dir;
+	struct exclude_list *el;
+	struct string_list exclude_list = STRING_LIST_INIT_NODUP;
+	struct option builtin_ls_files_options[] = {
+		/* Think twice before adding "--nul" synonym to this */
+		OPT_SET_INT('z', NULL, &line_terminator,
+			N_("paths are separated with NUL character"), '\0'),
+		OPT_BOOL('t', NULL, &show_tag,
+			N_("identify the file status with tags")),
+		OPT_BOOL('v', NULL, &show_valid_bit,
+			N_("use lowercase letters for 'assume unchanged' files")),
+		OPT_BOOL('f', NULL, &show_fsmonitor_bit,
+			N_("use lowercase letters for 'fsmonitor clean' files")),
+		OPT_BOOL('c', "cached", &show_cached,
+			N_("show cached files in the output (default)")),
+		OPT_BOOL('d', "deleted", &show_deleted,
+			N_("show deleted files in the output")),
+		OPT_BOOL('m', "modified", &show_modified,
+			N_("show modified files in the output")),
+		OPT_BOOL('o', "others", &show_others,
+			N_("show other files in the output")),
+		OPT_BIT('i', "ignored", &dir.flags,
+			N_("show ignored files in the output"),
+			DIR_SHOW_IGNORED),
+		OPT_BOOL('s', "stage", &show_stage,
+			N_("show staged contents' object name in the output")),
+		OPT_BOOL('k', "killed", &show_killed,
+			N_("show files on the filesystem that need to be removed")),
+		OPT_BIT(0, "directory", &dir.flags,
+			N_("show 'other' directories' names only"),
+			DIR_SHOW_OTHER_DIRECTORIES),
+		OPT_BOOL(0, "eol", &show_eol, N_("show line endings of files")),
+		OPT_NEGBIT(0, "empty-directory", &dir.flags,
+			N_("don't show empty directories"),
+			DIR_HIDE_EMPTY_DIRECTORIES),
+		OPT_BOOL('u', "unmerged", &show_unmerged,
+			N_("show unmerged files in the output")),
+		OPT_BOOL(0, "resolve-undo", &show_resolve_undo,
+			    N_("show resolve-undo information")),
+		{ OPTION_CALLBACK, 'x', "exclude", &exclude_list, N_("pattern"),
+			N_("skip files matching pattern"),
+			PARSE_OPT_NONEG, option_parse_exclude },
+		{ OPTION_CALLBACK, 'X', "exclude-from", &dir, N_("file"),
+			N_("exclude patterns are read from <file>"),
+			PARSE_OPT_NONEG, option_parse_exclude_from },
+		OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, N_("file"),
+			N_("read additional per-directory exclude patterns in <file>")),
+		{ OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
+			N_("add the standard git exclusions"),
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+			option_parse_exclude_standard },
+		OPT_SET_INT_F(0, "full-name", &prefix_len,
+			      N_("make the output relative to the project top directory"),
+			      0, PARSE_OPT_NONEG),
+		OPT_BOOL(0, "recurse-submodules", &recurse_submodules,
+			N_("recurse through submodules")),
+		OPT_BOOL(0, "error-unmatch", &error_unmatch,
+			N_("if any <file> is not in the index, treat this as an error")),
+		OPT_STRING(0, "with-tree", &with_tree, N_("tree-ish"),
+			N_("pretend that paths removed since <tree-ish> are still present")),
+		OPT__ABBREV(&abbrev),
+		OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")),
+		OPT_END()
+	};
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(ls_files_usage, builtin_ls_files_options);
+
+	memset(&dir, 0, sizeof(dir));
+	prefix = cmd_prefix;
+	if (prefix)
+		prefix_len = strlen(prefix);
+	git_config(git_default_config, NULL);
+
+	if (repo_read_index(the_repository) < 0)
+		die("index file corrupt");
+
+	argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
+			ls_files_usage, 0);
+	el = add_exclude_list(&dir, EXC_CMDL, "--exclude option");
+	for (i = 0; i < exclude_list.nr; i++) {
+		add_exclude(exclude_list.items[i].string, "", 0, el, --exclude_args);
+	}
+	if (show_tag || show_valid_bit || show_fsmonitor_bit) {
+		tag_cached = "H ";
+		tag_unmerged = "M ";
+		tag_removed = "R ";
+		tag_modified = "C ";
+		tag_other = "? ";
+		tag_killed = "K ";
+		tag_skip_worktree = "S ";
+		tag_resolve_undo = "U ";
+	}
+	if (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)
+		require_work_tree = 1;
+	if (show_unmerged)
+		/*
+		 * There's no point in showing unmerged unless
+		 * you also show the stage information.
+		 */
+		show_stage = 1;
+	if (dir.exclude_per_dir)
+		exc_given = 1;
+
+	if (require_work_tree && !is_inside_work_tree())
+		setup_work_tree();
+
+	if (recurse_submodules &&
+	    (show_stage || show_deleted || show_others || show_unmerged ||
+	     show_killed || show_modified || show_resolve_undo || with_tree))
+		die("ls-files --recurse-submodules unsupported mode");
+
+	if (recurse_submodules && error_unmatch)
+		die("ls-files --recurse-submodules does not support "
+		    "--error-unmatch");
+
+	parse_pathspec(&pathspec, 0,
+		       PATHSPEC_PREFER_CWD,
+		       prefix, argv);
+
+	/*
+	 * Find common prefix for all pathspec's
+	 * This is used as a performance optimization which unfortunately cannot
+	 * be done when recursing into submodules because when a pathspec is
+	 * given which spans repository boundaries you can't simply remove the
+	 * submodule entry because the pathspec may match something inside the
+	 * submodule.
+	 */
+	if (recurse_submodules)
+		max_prefix = NULL;
+	else
+		max_prefix = common_prefix(&pathspec);
+	max_prefix_len = get_common_prefix_len(max_prefix);
+
+	prune_index(the_repository->index, max_prefix, max_prefix_len);
+
+	/* Treat unmatching pathspec elements as errors */
+	if (pathspec.nr && error_unmatch)
+		ps_matched = xcalloc(pathspec.nr, 1);
+
+	if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given)
+		die("ls-files --ignored needs some exclude pattern");
+
+	/* With no flags, we default to showing the cached files */
+	if (!(show_stage || show_deleted || show_others || show_unmerged ||
+	      show_killed || show_modified || show_resolve_undo))
+		show_cached = 1;
+
+	if (with_tree) {
+		/*
+		 * Basic sanity check; show-stages and show-unmerged
+		 * would not make any sense with this option.
+		 */
+		if (show_stage || show_unmerged)
+			die("ls-files --with-tree is incompatible with -s or -u");
+		overlay_tree_on_index(the_repository->index, with_tree, max_prefix);
+	}
+
+	show_files(the_repository, &dir);
+
+	if (show_resolve_undo)
+		show_ru_info(the_repository->index);
+
+	if (ps_matched) {
+		int bad;
+		bad = report_path_error(ps_matched, &pathspec);
+		if (bad)
+			fprintf(stderr, "Did you forget to 'git add'?\n");
+
+		return bad ? 1 : 0;
+	}
+
+	UNLEAK(dir);
+	return 0;
+}
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
new file mode 100644
index 0000000000..6ef519514b
--- /dev/null
+++ b/builtin/ls-remote.c
@@ -0,0 +1,152 @@
+#include "builtin.h"
+#include "cache.h"
+#include "transport.h"
+#include "ref-filter.h"
+#include "remote.h"
+#include "refs.h"
+
+static const char * const ls_remote_usage[] = {
+	N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
+	   "                     [-q | --quiet] [--exit-code] [--get-url]\n"
+	   "                     [--symref] [<repository> [<refs>...]]"),
+	NULL
+};
+
+/*
+ * Is there one among the list of patterns that match the tail part
+ * of the path?
+ */
+static int tail_match(const char **pattern, const char *path)
+{
+	const char *p;
+	char *pathbuf;
+
+	if (!pattern)
+		return 1; /* no restriction */
+
+	pathbuf = xstrfmt("/%s", path);
+	while ((p = *(pattern++)) != NULL) {
+		if (!wildmatch(p, pathbuf, 0)) {
+			free(pathbuf);
+			return 1;
+		}
+	}
+	free(pathbuf);
+	return 0;
+}
+
+int cmd_ls_remote(int argc, const char **argv, const char *prefix)
+{
+	const char *dest = NULL;
+	unsigned flags = 0;
+	int get_url = 0;
+	int quiet = 0;
+	int status = 0;
+	int show_symref_target = 0;
+	const char *uploadpack = NULL;
+	const char **pattern = NULL;
+	struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
+	int i;
+	struct string_list server_options = STRING_LIST_INIT_DUP;
+
+	struct remote *remote;
+	struct transport *transport;
+	const struct ref *ref;
+	struct ref_array ref_array;
+	static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+
+	struct option options[] = {
+		OPT__QUIET(&quiet, N_("do not print remote URL")),
+		OPT_STRING(0, "upload-pack", &uploadpack, N_("exec"),
+			   N_("path of git-upload-pack on the remote host")),
+		{ OPTION_STRING, 0, "exec", &uploadpack, N_("exec"),
+			   N_("path of git-upload-pack on the remote host"),
+			   PARSE_OPT_HIDDEN },
+		OPT_BIT('t', "tags", &flags, N_("limit to tags"), REF_TAGS),
+		OPT_BIT('h', "heads", &flags, N_("limit to heads"), REF_HEADS),
+		OPT_BIT(0, "refs", &flags, N_("do not show peeled tags"), REF_NORMAL),
+		OPT_BOOL(0, "get-url", &get_url,
+			 N_("take url.<base>.insteadOf into account")),
+		OPT_REF_SORT(sorting_tail),
+		OPT_SET_INT_F(0, "exit-code", &status,
+			      N_("exit with exit code 2 if no matching refs are found"),
+			      2, PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL(0, "symref", &show_symref_target,
+			 N_("show underlying ref in addition to the object pointed by it")),
+		OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
+		OPT_END()
+	};
+
+	memset(&ref_array, 0, sizeof(ref_array));
+
+	argc = parse_options(argc, argv, prefix, options, ls_remote_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+	dest = argv[0];
+
+	if (argc > 1) {
+		int i;
+		pattern = xcalloc(argc, sizeof(const char *));
+		for (i = 1; i < argc; i++) {
+			pattern[i - 1] = xstrfmt("*/%s", argv[i]);
+		}
+	}
+
+	if (flags & REF_TAGS)
+		argv_array_push(&ref_prefixes, "refs/tags/");
+	if (flags & REF_HEADS)
+		argv_array_push(&ref_prefixes, "refs/heads/");
+
+	remote = remote_get(dest);
+	if (!remote) {
+		if (dest)
+			die("bad repository '%s'", dest);
+		die("No remote configured to list refs from.");
+	}
+	if (!remote->url_nr)
+		die("remote %s has no configured URL", dest);
+
+	if (get_url) {
+		printf("%s\n", *remote->url);
+		UNLEAK(sorting);
+		return 0;
+	}
+
+	transport = transport_get(remote, NULL);
+	if (uploadpack != NULL)
+		transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
+	if (server_options.nr)
+		transport->server_options = &server_options;
+
+	ref = transport_get_remote_refs(transport, &ref_prefixes);
+	if (transport_disconnect(transport)) {
+		UNLEAK(sorting);
+		return 1;
+	}
+
+	if (!dest && !quiet)
+		fprintf(stderr, "From %s\n", *remote->url);
+	for ( ; ref; ref = ref->next) {
+		struct ref_array_item *item;
+		if (!check_ref_type(ref, flags))
+			continue;
+		if (!tail_match(pattern, ref->name))
+			continue;
+		item = ref_array_push(&ref_array, ref->name, &ref->old_oid);
+		item->symref = xstrdup_or_null(ref->symref);
+	}
+
+	if (sorting)
+		ref_array_sort(sorting, &ref_array);
+
+	for (i = 0; i < ref_array.nr; i++) {
+		const struct ref_array_item *ref = ref_array.items[i];
+		if (show_symref_target && ref->symref)
+			printf("ref: %s\t%s\n", ref->symref, ref->refname);
+		printf("%s\t%s\n", oid_to_hex(&ref->objectname), ref->refname);
+		status = 0; /* we found something */
+	}
+
+	UNLEAK(sorting);
+	ref_array_clear(&ref_array);
+	return status;
+}
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
new file mode 100644
index 0000000000..7cad3f24eb
--- /dev/null
+++ b/builtin/ls-tree.c
@@ -0,0 +1,190 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+#include "config.h"
+#include "object-store.h"
+#include "blob.h"
+#include "tree.h"
+#include "commit.h"
+#include "quote.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "pathspec.h"
+
+static int line_termination = '\n';
+#define LS_RECURSIVE 1
+#define LS_TREE_ONLY 2
+#define LS_SHOW_TREES 4
+#define LS_NAME_ONLY 8
+#define LS_SHOW_SIZE 16
+static int abbrev;
+static int ls_options;
+static struct pathspec pathspec;
+static int chomp_prefix;
+static const char *ls_tree_prefix;
+
+static const  char * const ls_tree_usage[] = {
+	N_("git ls-tree [<options>] <tree-ish> [<path>...]"),
+	NULL
+};
+
+static int show_recursive(const char *base, int baselen, const char *pathname)
+{
+	int i;
+
+	if (ls_options & LS_RECURSIVE)
+		return 1;
+
+	if (!pathspec.nr)
+		return 0;
+
+	for (i = 0; i < pathspec.nr; i++) {
+		const char *spec = pathspec.items[i].match;
+		int len, speclen;
+
+		if (strncmp(base, spec, baselen))
+			continue;
+		len = strlen(pathname);
+		spec += baselen;
+		speclen = strlen(spec);
+		if (speclen <= len)
+			continue;
+		if (spec[len] && spec[len] != '/')
+			continue;
+		if (memcmp(pathname, spec, len))
+			continue;
+		return 1;
+	}
+	return 0;
+}
+
+static int show_tree(const struct object_id *oid, struct strbuf *base,
+		const char *pathname, unsigned mode, int stage, void *context)
+{
+	int retval = 0;
+	int baselen;
+	const char *type = blob_type;
+
+	if (S_ISGITLINK(mode)) {
+		/*
+		 * Maybe we want to have some recursive version here?
+		 *
+		 * Something similar to this incomplete example:
+		 *
+		if (show_subprojects(base, baselen, pathname))
+			retval = READ_TREE_RECURSIVE;
+		 *
+		 */
+		type = commit_type;
+	} else if (S_ISDIR(mode)) {
+		if (show_recursive(base->buf, base->len, pathname)) {
+			retval = READ_TREE_RECURSIVE;
+			if (!(ls_options & LS_SHOW_TREES))
+				return retval;
+		}
+		type = tree_type;
+	}
+	else if (ls_options & LS_TREE_ONLY)
+		return 0;
+
+	if (!(ls_options & LS_NAME_ONLY)) {
+		if (ls_options & LS_SHOW_SIZE) {
+			char size_text[24];
+			if (!strcmp(type, blob_type)) {
+				unsigned long size;
+				if (oid_object_info(the_repository, oid, &size) == OBJ_BAD)
+					xsnprintf(size_text, sizeof(size_text),
+						  "BAD");
+				else
+					xsnprintf(size_text, sizeof(size_text),
+						  "%"PRIuMAX, (uintmax_t)size);
+			} else
+				xsnprintf(size_text, sizeof(size_text), "-");
+			printf("%06o %s %s %7s\t", mode, type,
+			       find_unique_abbrev(oid, abbrev),
+			       size_text);
+		} else
+			printf("%06o %s %s\t", mode, type,
+			       find_unique_abbrev(oid, abbrev));
+	}
+	baselen = base->len;
+	strbuf_addstr(base, pathname);
+	write_name_quoted_relative(base->buf,
+				   chomp_prefix ? ls_tree_prefix : NULL,
+				   stdout, line_termination);
+	strbuf_setlen(base, baselen);
+	return retval;
+}
+
+int cmd_ls_tree(int argc, const char **argv, const char *prefix)
+{
+	struct object_id oid;
+	struct tree *tree;
+	int i, full_tree = 0;
+	const struct option ls_tree_options[] = {
+		OPT_BIT('d', NULL, &ls_options, N_("only show trees"),
+			LS_TREE_ONLY),
+		OPT_BIT('r', NULL, &ls_options, N_("recurse into subtrees"),
+			LS_RECURSIVE),
+		OPT_BIT('t', NULL, &ls_options, N_("show trees when recursing"),
+			LS_SHOW_TREES),
+		OPT_SET_INT('z', NULL, &line_termination,
+			    N_("terminate entries with NUL byte"), 0),
+		OPT_BIT('l', "long", &ls_options, N_("include object size"),
+			LS_SHOW_SIZE),
+		OPT_BIT(0, "name-only", &ls_options, N_("list only filenames"),
+			LS_NAME_ONLY),
+		OPT_BIT(0, "name-status", &ls_options, N_("list only filenames"),
+			LS_NAME_ONLY),
+		OPT_SET_INT(0, "full-name", &chomp_prefix,
+			    N_("use full path names"), 0),
+		OPT_BOOL(0, "full-tree", &full_tree,
+			 N_("list entire tree; not just current directory "
+			    "(implies --full-name)")),
+		OPT__ABBREV(&abbrev),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+	ls_tree_prefix = prefix;
+	if (prefix && *prefix)
+		chomp_prefix = strlen(prefix);
+
+	argc = parse_options(argc, argv, prefix, ls_tree_options,
+			     ls_tree_usage, 0);
+	if (full_tree) {
+		ls_tree_prefix = prefix = NULL;
+		chomp_prefix = 0;
+	}
+	/* -d -r should imply -t, but -d by itself should not have to. */
+	if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
+	    ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
+		ls_options |= LS_SHOW_TREES;
+
+	if (argc < 1)
+		usage_with_options(ls_tree_usage, ls_tree_options);
+	if (get_oid(argv[0], &oid))
+		die("Not a valid object name %s", argv[0]);
+
+	/*
+	 * show_recursive() rolls its own matching code and is
+	 * generally ignorant of 'struct pathspec'. The magic mask
+	 * cannot be lifted until it is converted to use
+	 * match_pathspec() or tree_entry_interesting()
+	 */
+	parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC &
+				  ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
+		       PATHSPEC_PREFER_CWD,
+		       prefix, argv + 1);
+	for (i = 0; i < pathspec.nr; i++)
+		pathspec.items[i].nowildcard_len = pathspec.items[i].len;
+	pathspec.has_wildcard = 0;
+	tree = parse_tree_indirect(&oid);
+	if (!tree)
+		die("not a tree object");
+	return !!read_tree_recursive(the_repository, tree, "", 0, 0,
+				     &pathspec, show_tree, NULL);
+}
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
new file mode 100644
index 0000000000..cfb667a594
--- /dev/null
+++ b/builtin/mailinfo.c
@@ -0,0 +1,65 @@
+/*
+ * Another stupid program, this one parsing the headers of an
+ * email to figure out authorship and subject
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "utf8.h"
+#include "strbuf.h"
+#include "mailinfo.h"
+
+static const char mailinfo_usage[] =
+	"git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
+
+int cmd_mailinfo(int argc, const char **argv, const char *prefix)
+{
+	const char *def_charset;
+	struct mailinfo mi;
+	int status;
+	char *msgfile, *patchfile;
+
+	setup_mailinfo(&mi);
+
+	def_charset = get_commit_output_encoding();
+	mi.metainfo_charset = def_charset;
+
+	while (1 < argc && argv[1][0] == '-') {
+		if (!strcmp(argv[1], "-k"))
+			mi.keep_subject = 1;
+		else if (!strcmp(argv[1], "-b"))
+			mi.keep_non_patch_brackets_in_subject = 1;
+		else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
+			mi.add_message_id = 1;
+		else if (!strcmp(argv[1], "-u"))
+			mi.metainfo_charset = def_charset;
+		else if (!strcmp(argv[1], "-n"))
+			mi.metainfo_charset = NULL;
+		else if (starts_with(argv[1], "--encoding="))
+			mi.metainfo_charset = argv[1] + 11;
+		else if (!strcmp(argv[1], "--scissors"))
+			mi.use_scissors = 1;
+		else if (!strcmp(argv[1], "--no-scissors"))
+			mi.use_scissors = 0;
+		else if (!strcmp(argv[1], "--no-inbody-headers"))
+			mi.use_inbody_headers = 0;
+		else
+			usage(mailinfo_usage);
+		argc--; argv++;
+	}
+
+	if (argc != 3)
+		usage(mailinfo_usage);
+
+	mi.input = stdin;
+	mi.output = stdout;
+
+	msgfile = prefix_filename(prefix, argv[1]);
+	patchfile = prefix_filename(prefix, argv[2]);
+
+	status = !!mailinfo(&mi, msgfile, patchfile);
+	clear_mailinfo(&mi);
+
+	free(msgfile);
+	free(patchfile);
+	return status;
+}
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
new file mode 100644
index 0000000000..664400b816
--- /dev/null
+++ b/builtin/mailsplit.c
@@ -0,0 +1,369 @@
+/*
+ * Totally braindamaged mbox splitter program.
+ *
+ * It just splits a mbox into a list of files: "0001" "0002" ..
+ * so you can process them further from there.
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "string-list.h"
+#include "strbuf.h"
+
+static const char git_mailsplit_usage[] =
+"git mailsplit [-d<prec>] [-f<n>] [-b] [--keep-cr] -o<directory> [(<mbox>|<Maildir>)...]";
+
+static int is_from_line(const char *line, int len)
+{
+	const char *colon;
+
+	if (len < 20 || memcmp("From ", line, 5))
+		return 0;
+
+	colon = line + len - 2;
+	line += 5;
+	for (;;) {
+		if (colon < line)
+			return 0;
+		if (*--colon == ':')
+			break;
+	}
+
+	if (!isdigit(colon[-4]) ||
+	    !isdigit(colon[-2]) ||
+	    !isdigit(colon[-1]) ||
+	    !isdigit(colon[ 1]) ||
+	    !isdigit(colon[ 2]))
+		return 0;
+
+	/* year */
+	if (strtol(colon+3, NULL, 10) <= 90)
+		return 0;
+
+	/* Ok, close enough */
+	return 1;
+}
+
+static struct strbuf buf = STRBUF_INIT;
+static int keep_cr;
+static int mboxrd;
+
+static int is_gtfrom(const struct strbuf *buf)
+{
+	size_t min = strlen(">From ");
+	size_t ngt;
+
+	if (buf->len < min)
+		return 0;
+
+	ngt = strspn(buf->buf, ">");
+	return ngt && starts_with(buf->buf + ngt, "From ");
+}
+
+/* Called with the first line (potentially partial)
+ * already in buf[] -- normally that should begin with
+ * the Unix "From " line.  Write it into the specified
+ * file.
+ */
+static int split_one(FILE *mbox, const char *name, int allow_bare)
+{
+	FILE *output;
+	int fd;
+	int status = 0;
+	int is_bare = !is_from_line(buf.buf, buf.len);
+
+	if (is_bare && !allow_bare) {
+		fprintf(stderr, "corrupt mailbox\n");
+		exit(1);
+	}
+	fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
+	if (fd < 0)
+		die_errno("cannot open output file '%s'", name);
+	output = xfdopen(fd, "w");
+
+	/* Copy it out, while searching for a line that begins with
+	 * "From " and having something that looks like a date format.
+	 */
+	for (;;) {
+		if (!keep_cr && buf.len > 1 && buf.buf[buf.len-1] == '\n' &&
+			buf.buf[buf.len-2] == '\r') {
+			strbuf_setlen(&buf, buf.len-2);
+			strbuf_addch(&buf, '\n');
+		}
+
+		if (mboxrd && is_gtfrom(&buf))
+			strbuf_remove(&buf, 0, 1);
+
+		if (fwrite(buf.buf, 1, buf.len, output) != buf.len)
+			die_errno("cannot write output");
+
+		if (strbuf_getwholeline(&buf, mbox, '\n')) {
+			if (feof(mbox)) {
+				status = 1;
+				break;
+			}
+			die_errno("cannot read mbox");
+		}
+		if (!is_bare && is_from_line(buf.buf, buf.len))
+			break; /* done with one message */
+	}
+	fclose(output);
+	return status;
+}
+
+static int populate_maildir_list(struct string_list *list, const char *path)
+{
+	DIR *dir;
+	struct dirent *dent;
+	char *name = NULL;
+	char *subs[] = { "cur", "new", NULL };
+	char **sub;
+	int ret = -1;
+
+	for (sub = subs; *sub; ++sub) {
+		free(name);
+		name = xstrfmt("%s/%s", path, *sub);
+		if ((dir = opendir(name)) == NULL) {
+			if (errno == ENOENT)
+				continue;
+			error_errno("cannot opendir %s", name);
+			goto out;
+		}
+
+		while ((dent = readdir(dir)) != NULL) {
+			if (dent->d_name[0] == '.')
+				continue;
+			free(name);
+			name = xstrfmt("%s/%s", *sub, dent->d_name);
+			string_list_insert(list, name);
+		}
+
+		closedir(dir);
+	}
+
+	ret = 0;
+
+out:
+	free(name);
+	return ret;
+}
+
+static int maildir_filename_cmp(const char *a, const char *b)
+{
+	while (*a && *b) {
+		if (isdigit(*a) && isdigit(*b)) {
+			long int na, nb;
+			na = strtol(a, (char **)&a, 10);
+			nb = strtol(b, (char **)&b, 10);
+			if (na != nb)
+				return na - nb;
+			/* strtol advanced our pointers */
+		}
+		else {
+			if (*a != *b)
+				return (unsigned char)*a - (unsigned char)*b;
+			a++;
+			b++;
+		}
+	}
+	return (unsigned char)*a - (unsigned char)*b;
+}
+
+static int split_maildir(const char *maildir, const char *dir,
+	int nr_prec, int skip)
+{
+	char *file = NULL;
+	FILE *f = NULL;
+	int ret = -1;
+	int i;
+	struct string_list list = STRING_LIST_INIT_DUP;
+
+	list.cmp = maildir_filename_cmp;
+
+	if (populate_maildir_list(&list, maildir) < 0)
+		goto out;
+
+	for (i = 0; i < list.nr; i++) {
+		char *name;
+
+		free(file);
+		file = xstrfmt("%s/%s", maildir, list.items[i].string);
+
+		f = fopen(file, "r");
+		if (!f) {
+			error_errno("cannot open mail %s", file);
+			goto out;
+		}
+
+		if (strbuf_getwholeline(&buf, f, '\n')) {
+			error_errno("cannot read mail %s", file);
+			goto out;
+		}
+
+		name = xstrfmt("%s/%0*d", dir, nr_prec, ++skip);
+		split_one(f, name, 1);
+		free(name);
+
+		fclose(f);
+		f = NULL;
+	}
+
+	ret = skip;
+out:
+	if (f)
+		fclose(f);
+	free(file);
+	string_list_clear(&list, 1);
+	return ret;
+}
+
+static int split_mbox(const char *file, const char *dir, int allow_bare,
+		      int nr_prec, int skip)
+{
+	int ret = -1;
+	int peek;
+
+	FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
+	int file_done = 0;
+
+	if (!f) {
+		error_errno("cannot open mbox %s", file);
+		goto out;
+	}
+
+	do {
+		peek = fgetc(f);
+		if (peek == EOF) {
+			if (f == stdin)
+				/* empty stdin is OK */
+				ret = skip;
+			else {
+				fclose(f);
+				error(_("empty mbox: '%s'"), file);
+			}
+			goto out;
+		}
+	} while (isspace(peek));
+	ungetc(peek, f);
+
+	if (strbuf_getwholeline(&buf, f, '\n')) {
+		/* empty stdin is OK */
+		if (f != stdin) {
+			error("cannot read mbox %s", file);
+			goto out;
+		}
+		file_done = 1;
+	}
+
+	while (!file_done) {
+		char *name = xstrfmt("%s/%0*d", dir, nr_prec, ++skip);
+		file_done = split_one(f, name, allow_bare);
+		free(name);
+	}
+
+	if (f != stdin)
+		fclose(f);
+
+	ret = skip;
+out:
+	return ret;
+}
+
+int cmd_mailsplit(int argc, const char **argv, const char *prefix)
+{
+	int nr = 0, nr_prec = 4, num = 0;
+	int allow_bare = 0;
+	const char *dir = NULL;
+	const char **argp;
+	static const char *stdin_only[] = { "-", NULL };
+
+	for (argp = argv+1; *argp; argp++) {
+		const char *arg = *argp;
+
+		if (arg[0] != '-')
+			break;
+		/* do flags here */
+		if ( arg[1] == 'd' ) {
+			nr_prec = strtol(arg+2, NULL, 10);
+			if (nr_prec < 3 || 10 <= nr_prec)
+				usage(git_mailsplit_usage);
+			continue;
+		} else if ( arg[1] == 'f' ) {
+			nr = strtol(arg+2, NULL, 10);
+		} else if ( arg[1] == 'h' ) {
+			usage(git_mailsplit_usage);
+		} else if ( arg[1] == 'b' && !arg[2] ) {
+			allow_bare = 1;
+		} else if (!strcmp(arg, "--keep-cr")) {
+			keep_cr = 1;
+		} else if ( arg[1] == 'o' && arg[2] ) {
+			dir = arg+2;
+		} else if (!strcmp(arg, "--mboxrd")) {
+			mboxrd = 1;
+		} else if ( arg[1] == '-' && !arg[2] ) {
+			argp++;	/* -- marks end of options */
+			break;
+		} else {
+			die("unknown option: %s", arg);
+		}
+	}
+
+	if ( !dir ) {
+		/* Backwards compatibility: if no -o specified, accept
+		   <mbox> <dir> or just <dir> */
+		switch (argc - (argp-argv)) {
+		case 1:
+			dir = argp[0];
+			argp = stdin_only;
+			break;
+		case 2:
+			stdin_only[0] = argp[0];
+			dir = argp[1];
+			argp = stdin_only;
+			break;
+		default:
+			usage(git_mailsplit_usage);
+		}
+	} else {
+		/* New usage: if no more argument, parse stdin */
+		if ( !*argp )
+			argp = stdin_only;
+	}
+
+	while (*argp) {
+		const char *arg = *argp++;
+		struct stat argstat;
+		int ret = 0;
+
+		if (arg[0] == '-' && arg[1] == 0) {
+			ret = split_mbox(arg, dir, allow_bare, nr_prec, nr);
+			if (ret < 0) {
+				error("cannot split patches from stdin");
+				return 1;
+			}
+			num += (ret - nr);
+			nr = ret;
+			continue;
+		}
+
+		if (stat(arg, &argstat) == -1) {
+			error_errno("cannot stat %s", arg);
+			return 1;
+		}
+
+		if (S_ISDIR(argstat.st_mode))
+			ret = split_maildir(arg, dir, nr_prec, nr);
+		else
+			ret = split_mbox(arg, dir, allow_bare, nr_prec, nr);
+
+		if (ret < 0) {
+			error("cannot split patches from %s", arg);
+			return 1;
+		}
+		num += (ret - nr);
+		nr = ret;
+	}
+
+	printf("%d\n", num);
+
+	return 0;
+}
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
new file mode 100644
index 0000000000..e3f8da13b6
--- /dev/null
+++ b/builtin/merge-base.c
@@ -0,0 +1,198 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "commit.h"
+#include "refs.h"
+#include "diff.h"
+#include "revision.h"
+#include "parse-options.h"
+#include "repository.h"
+#include "commit-reach.h"
+
+static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
+{
+	struct commit_list *result, *r;
+
+	result = get_merge_bases_many_dirty(rev[0], rev_nr - 1, rev + 1);
+
+	if (!result)
+		return 1;
+
+	for (r = result; r; r = r->next) {
+		printf("%s\n", oid_to_hex(&r->item->object.oid));
+		if (!show_all)
+			break;
+	}
+
+	free_commit_list(result);
+	return 0;
+}
+
+static const char * const merge_base_usage[] = {
+	N_("git merge-base [-a | --all] <commit> <commit>..."),
+	N_("git merge-base [-a | --all] --octopus <commit>..."),
+	N_("git merge-base --independent <commit>..."),
+	N_("git merge-base --is-ancestor <commit> <commit>"),
+	N_("git merge-base --fork-point <ref> [<commit>]"),
+	NULL
+};
+
+static struct commit *get_commit_reference(const char *arg)
+{
+	struct object_id revkey;
+	struct commit *r;
+
+	if (get_oid(arg, &revkey))
+		die("Not a valid object name %s", arg);
+	r = lookup_commit_reference(the_repository, &revkey);
+	if (!r)
+		die("Not a valid commit name %s", arg);
+
+	return r;
+}
+
+static int handle_independent(int count, const char **args)
+{
+	struct commit_list *revs = NULL, *rev;
+	int i;
+
+	for (i = count - 1; i >= 0; i--)
+		commit_list_insert(get_commit_reference(args[i]), &revs);
+
+	reduce_heads_replace(&revs);
+
+	if (!revs)
+		return 1;
+
+	for (rev = revs; rev; rev = rev->next)
+		printf("%s\n", oid_to_hex(&rev->item->object.oid));
+
+	free_commit_list(revs);
+	return 0;
+}
+
+static int handle_octopus(int count, const char **args, int show_all)
+{
+	struct commit_list *revs = NULL;
+	struct commit_list *result, *rev;
+	int i;
+
+	for (i = count - 1; i >= 0; i--)
+		commit_list_insert(get_commit_reference(args[i]), &revs);
+
+	result = get_octopus_merge_bases(revs);
+	free_commit_list(revs);
+	reduce_heads_replace(&result);
+
+	if (!result)
+		return 1;
+
+	for (rev = result; rev; rev = rev->next) {
+		printf("%s\n", oid_to_hex(&rev->item->object.oid));
+		if (!show_all)
+			break;
+	}
+
+	free_commit_list(result);
+	return 0;
+}
+
+static int handle_is_ancestor(int argc, const char **argv)
+{
+	struct commit *one, *two;
+
+	if (argc != 2)
+		die("--is-ancestor takes exactly two commits");
+	one = get_commit_reference(argv[0]);
+	two = get_commit_reference(argv[1]);
+	if (in_merge_bases(one, two))
+		return 0;
+	else
+		return 1;
+}
+
+static int handle_fork_point(int argc, const char **argv)
+{
+	struct object_id oid;
+	char *refname;
+	struct commit *derived, *fork_point;
+	const char *commitname;
+
+	switch (dwim_ref(argv[0], strlen(argv[0]), &oid, &refname)) {
+	case 0:
+		die("No such ref: '%s'", argv[0]);
+	case 1:
+		break; /* good */
+	default:
+		die("Ambiguous refname: '%s'", argv[0]);
+	}
+
+	commitname = (argc == 2) ? argv[1] : "HEAD";
+	if (get_oid(commitname, &oid))
+		die("Not a valid object name: '%s'", commitname);
+
+	derived = lookup_commit_reference(the_repository, &oid);
+
+	fork_point = get_fork_point(refname, derived);
+
+	if (!fork_point)
+		return 1;
+
+	printf("%s\n", oid_to_hex(&fork_point->object.oid));
+	return 0;
+}
+
+int cmd_merge_base(int argc, const char **argv, const char *prefix)
+{
+	struct commit **rev;
+	int rev_nr = 0;
+	int show_all = 0;
+	int cmdmode = 0;
+
+	struct option options[] = {
+		OPT_BOOL('a', "all", &show_all, N_("output all common ancestors")),
+		OPT_CMDMODE(0, "octopus", &cmdmode,
+			    N_("find ancestors for a single n-way merge"), 'o'),
+		OPT_CMDMODE(0, "independent", &cmdmode,
+			    N_("list revs not reachable from others"), 'r'),
+		OPT_CMDMODE(0, "is-ancestor", &cmdmode,
+			    N_("is the first one ancestor of the other?"), 'a'),
+		OPT_CMDMODE(0, "fork-point", &cmdmode,
+			    N_("find where <commit> forked from reflog of <ref>"), 'f'),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0);
+
+	if (cmdmode == 'a') {
+		if (argc < 2)
+			usage_with_options(merge_base_usage, options);
+		if (show_all)
+			die("--is-ancestor cannot be used with --all");
+		return handle_is_ancestor(argc, argv);
+	}
+
+	if (cmdmode == 'r' && show_all)
+		die("--independent cannot be used with --all");
+
+	if (cmdmode == 'o')
+		return handle_octopus(argc, argv, show_all);
+
+	if (cmdmode == 'r')
+		return handle_independent(argc, argv);
+
+	if (cmdmode == 'f') {
+		if (argc < 1 || 2 < argc)
+			usage_with_options(merge_base_usage, options);
+		return handle_fork_point(argc, argv);
+	}
+
+	if (argc < 2)
+		usage_with_options(merge_base_usage, options);
+
+	ALLOC_ARRAY(rev, argc);
+	while (argc-- > 0)
+		rev[rev_nr++] = get_commit_reference(*argv++);
+	return show_merge_base(rev, rev_nr, show_all);
+}
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
new file mode 100644
index 0000000000..06a2f90c48
--- /dev/null
+++ b/builtin/merge-file.c
@@ -0,0 +1,118 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "xdiff/xdiff.h"
+#include "xdiff-interface.h"
+#include "parse-options.h"
+
+static const char *const merge_file_usage[] = {
+	N_("git merge-file [<options>] [-L <name1> [-L <orig> [-L <name2>]]] <file1> <orig-file> <file2>"),
+	NULL
+};
+
+static int label_cb(const struct option *opt, const char *arg, int unset)
+{
+	static int label_count = 0;
+	const char **names = (const char **)opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (label_count >= 3)
+		return error("too many labels on the command line");
+	names[label_count++] = arg;
+	return 0;
+}
+
+int cmd_merge_file(int argc, const char **argv, const char *prefix)
+{
+	const char *names[3] = { NULL, NULL, NULL };
+	mmfile_t mmfs[3];
+	mmbuffer_t result = {NULL, 0};
+	xmparam_t xmp = {{0}};
+	int ret = 0, i = 0, to_stdout = 0;
+	int quiet = 0;
+	struct option options[] = {
+		OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
+		OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
+		OPT_SET_INT(0, "ours", &xmp.favor, N_("for conflicts, use our version"),
+			    XDL_MERGE_FAVOR_OURS),
+		OPT_SET_INT(0, "theirs", &xmp.favor, N_("for conflicts, use their version"),
+			    XDL_MERGE_FAVOR_THEIRS),
+		OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"),
+			    XDL_MERGE_FAVOR_UNION),
+		OPT_INTEGER(0, "marker-size", &xmp.marker_size,
+			    N_("for conflicts, use this marker size")),
+		OPT__QUIET(&quiet, N_("do not warn about conflicts")),
+		OPT_CALLBACK('L', NULL, names, N_("name"),
+			     N_("set labels for file1/orig-file/file2"), &label_cb),
+		OPT_END(),
+	};
+
+	xmp.level = XDL_MERGE_ZEALOUS_ALNUM;
+	xmp.style = 0;
+	xmp.favor = 0;
+
+	if (startup_info->have_repository) {
+		/* Read the configuration file */
+		git_config(git_xmerge_config, NULL);
+		if (0 <= git_xmerge_style)
+			xmp.style = git_xmerge_style;
+	}
+
+	argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0);
+	if (argc != 3)
+		usage_with_options(merge_file_usage, options);
+	if (quiet) {
+		if (!freopen("/dev/null", "w", stderr))
+			return error_errno("failed to redirect stderr to /dev/null");
+	}
+
+	for (i = 0; i < 3; i++) {
+		char *fname;
+		int ret;
+
+		if (!names[i])
+			names[i] = argv[i];
+
+		fname = prefix_filename(prefix, argv[i]);
+		ret = read_mmfile(mmfs + i, fname);
+		free(fname);
+		if (ret)
+			return -1;
+
+		if (mmfs[i].size > MAX_XDIFF_SIZE ||
+		    buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
+			return error("Cannot merge binary files: %s",
+					argv[i]);
+	}
+
+	xmp.ancestor = names[1];
+	xmp.file1 = names[0];
+	xmp.file2 = names[2];
+	ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result);
+
+	for (i = 0; i < 3; i++)
+		free(mmfs[i].ptr);
+
+	if (ret >= 0) {
+		const char *filename = argv[0];
+		char *fpath = prefix_filename(prefix, argv[0]);
+		FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
+
+		if (!f)
+			ret = error_errno("Could not open %s for writing",
+					  filename);
+		else if (result.size &&
+			 fwrite(result.ptr, result.size, 1, f) != 1)
+			ret = error_errno("Could not write to %s", filename);
+		else if (fclose(f))
+			ret = error_errno("Could not close %s", filename);
+		free(result.ptr);
+		free(fpath);
+	}
+
+	if (ret > 127)
+		ret = 127;
+
+	return ret;
+}
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
new file mode 100644
index 0000000000..38ea6ad6ca
--- /dev/null
+++ b/builtin/merge-index.c
@@ -0,0 +1,111 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "run-command.h"
+
+static const char *pgm;
+static int one_shot, quiet;
+static int err;
+
+static int merge_entry(int pos, const char *path)
+{
+	int found;
+	const char *arguments[] = { pgm, "", "", "", path, "", "", "", NULL };
+	char hexbuf[4][GIT_MAX_HEXSZ + 1];
+	char ownbuf[4][60];
+
+	if (pos >= active_nr)
+		die("git merge-index: %s not in the cache", path);
+	found = 0;
+	do {
+		const struct cache_entry *ce = active_cache[pos];
+		int stage = ce_stage(ce);
+
+		if (strcmp(ce->name, path))
+			break;
+		found++;
+		oid_to_hex_r(hexbuf[stage], &ce->oid);
+		xsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), "%o", ce->ce_mode);
+		arguments[stage] = hexbuf[stage];
+		arguments[stage + 4] = ownbuf[stage];
+	} while (++pos < active_nr);
+	if (!found)
+		die("git merge-index: %s not in the cache", path);
+
+	if (run_command_v_opt(arguments, 0)) {
+		if (one_shot)
+			err++;
+		else {
+			if (!quiet)
+				die("merge program failed");
+			exit(1);
+		}
+	}
+	return found;
+}
+
+static void merge_one_path(const char *path)
+{
+	int pos = cache_name_pos(path, strlen(path));
+
+	/*
+	 * If it already exists in the cache as stage0, it's
+	 * already merged and there is nothing to do.
+	 */
+	if (pos < 0)
+		merge_entry(-pos-1, path);
+}
+
+static void merge_all(void)
+{
+	int i;
+	for (i = 0; i < active_nr; i++) {
+		const struct cache_entry *ce = active_cache[i];
+		if (!ce_stage(ce))
+			continue;
+		i += merge_entry(i, ce->name)-1;
+	}
+}
+
+int cmd_merge_index(int argc, const char **argv, const char *prefix)
+{
+	int i, force_file = 0;
+
+	/* Without this we cannot rely on waitpid() to tell
+	 * what happened to our children.
+	 */
+	signal(SIGCHLD, SIG_DFL);
+
+	if (argc < 3)
+		usage("git merge-index [-o] [-q] <merge-program> (-a | [--] [<filename>...])");
+
+	read_cache();
+
+	i = 1;
+	if (!strcmp(argv[i], "-o")) {
+		one_shot = 1;
+		i++;
+	}
+	if (!strcmp(argv[i], "-q")) {
+		quiet = 1;
+		i++;
+	}
+	pgm = argv[i++];
+	for (; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!force_file && *arg == '-') {
+			if (!strcmp(arg, "--")) {
+				force_file = 1;
+				continue;
+			}
+			if (!strcmp(arg, "-a")) {
+				merge_all();
+				continue;
+			}
+			die("git merge-index: unknown option %s", arg);
+		}
+		merge_one_path(arg);
+	}
+	if (err && !quiet)
+		die("merge program failed");
+	return err;
+}
diff --git a/builtin/merge-ours.c b/builtin/merge-ours.c
new file mode 100644
index 0000000000..4594507420
--- /dev/null
+++ b/builtin/merge-ours.c
@@ -0,0 +1,33 @@
+/*
+ * Implementation of git-merge-ours.sh as builtin
+ *
+ * Copyright (c) 2007 Thomas Harning Jr
+ * Original:
+ * Original Copyright (c) 2005 Junio C Hamano
+ *
+ * Pretend we resolved the heads, but declare our tree trumps everybody else.
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "git-compat-util.h"
+#include "builtin.h"
+#include "diff.h"
+
+static const char builtin_merge_ours_usage[] =
+	"git merge-ours <base>... -- HEAD <remote>...";
+
+int cmd_merge_ours(int argc, const char **argv, const char *prefix)
+{
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(builtin_merge_ours_usage);
+
+	/*
+	 * The contents of the current index becomes the tree we
+	 * commit.  The index must match HEAD, or this merge cannot go
+	 * through.
+	 */
+	if (read_cache() < 0)
+		die_errno("read_cache failed");
+	if (index_differs_from(the_repository, "HEAD", NULL, 0))
+		exit(2);
+	exit(0);
+}
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
new file mode 100644
index 0000000000..5b910e351e
--- /dev/null
+++ b/builtin/merge-recursive.c
@@ -0,0 +1,88 @@
+#include "builtin.h"
+#include "commit.h"
+#include "tag.h"
+#include "merge-recursive.h"
+#include "xdiff-interface.h"
+
+static const char builtin_merge_recursive_usage[] =
+	"git %s <base>... -- <head> <remote> ...";
+
+static char *better_branch_name(const char *branch)
+{
+	static char githead_env[8 + GIT_MAX_HEXSZ + 1];
+	char *name;
+
+	if (strlen(branch) != the_hash_algo->hexsz)
+		return xstrdup(branch);
+	xsnprintf(githead_env, sizeof(githead_env), "GITHEAD_%s", branch);
+	name = getenv(githead_env);
+	return xstrdup(name ? name : branch);
+}
+
+int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
+{
+	const struct object_id *bases[21];
+	unsigned bases_count = 0;
+	int i, failed;
+	struct object_id h1, h2;
+	struct merge_options o;
+	char *better1, *better2;
+	struct commit *result;
+
+	init_merge_options(&o, the_repository);
+	if (argv[0] && ends_with(argv[0], "-subtree"))
+		o.subtree_shift = "";
+
+	if (argc < 4)
+		usagef(builtin_merge_recursive_usage, argv[0]);
+
+	for (i = 1; i < argc; ++i) {
+		const char *arg = argv[i];
+
+		if (starts_with(arg, "--")) {
+			if (!arg[2])
+				break;
+			if (parse_merge_opt(&o, arg + 2))
+				die(_("unknown option %s"), arg);
+			continue;
+		}
+		if (bases_count < ARRAY_SIZE(bases)-1) {
+			struct object_id *oid = xmalloc(sizeof(struct object_id));
+			if (get_oid(argv[i], oid))
+				die(_("could not parse object '%s'"), argv[i]);
+			bases[bases_count++] = oid;
+		}
+		else
+			warning(Q_("cannot handle more than %d base. "
+				   "Ignoring %s.",
+				   "cannot handle more than %d bases. "
+				   "Ignoring %s.",
+				    (int)ARRAY_SIZE(bases)-1),
+				(int)ARRAY_SIZE(bases)-1, argv[i]);
+	}
+	if (argc - i != 3) /* "--" "<head>" "<remote>" */
+		die(_("not handling anything other than two heads merge."));
+
+	o.branch1 = argv[++i];
+	o.branch2 = argv[++i];
+
+	if (get_oid(o.branch1, &h1))
+		die(_("could not resolve ref '%s'"), o.branch1);
+	if (get_oid(o.branch2, &h2))
+		die(_("could not resolve ref '%s'"), o.branch2);
+
+	o.branch1 = better1 = better_branch_name(o.branch1);
+	o.branch2 = better2 = better_branch_name(o.branch2);
+
+	if (o.verbosity >= 3)
+		printf(_("Merging %s with %s\n"), o.branch1, o.branch2);
+
+	failed = merge_recursive_generic(&o, &h1, &h2, bases_count, bases, &result);
+
+	free(better1);
+	free(better2);
+
+	if (failed < 0)
+		return 128; /* die() error code */
+	return failed;
+}
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
new file mode 100644
index 0000000000..97b54caeb9
--- /dev/null
+++ b/builtin/merge-tree.c
@@ -0,0 +1,389 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "tree-walk.h"
+#include "xdiff-interface.h"
+#include "object-store.h"
+#include "repository.h"
+#include "blob.h"
+#include "exec-cmd.h"
+#include "merge-blobs.h"
+
+static const char merge_tree_usage[] = "git merge-tree <base-tree> <branch1> <branch2>";
+
+struct merge_list {
+	struct merge_list *next;
+	struct merge_list *link;	/* other stages for this object */
+
+	unsigned int stage : 2;
+	unsigned int mode;
+	const char *path;
+	struct blob *blob;
+};
+
+static struct merge_list *merge_result, **merge_result_end = &merge_result;
+
+static void add_merge_entry(struct merge_list *entry)
+{
+	*merge_result_end = entry;
+	merge_result_end = &entry->next;
+}
+
+static void merge_trees(struct tree_desc t[3], const char *base);
+
+static const char *explanation(struct merge_list *entry)
+{
+	switch (entry->stage) {
+	case 0:
+		return "merged";
+	case 3:
+		return "added in remote";
+	case 2:
+		if (entry->link)
+			return "added in both";
+		return "added in local";
+	}
+
+	/* Existed in base */
+	entry = entry->link;
+	if (!entry)
+		return "removed in both";
+
+	if (entry->link)
+		return "changed in both";
+
+	if (entry->stage == 3)
+		return "removed in local";
+	return "removed in remote";
+}
+
+static void *result(struct merge_list *entry, unsigned long *size)
+{
+	enum object_type type;
+	struct blob *base, *our, *their;
+	const char *path = entry->path;
+
+	if (!entry->stage)
+		return read_object_file(&entry->blob->object.oid, &type, size);
+	base = NULL;
+	if (entry->stage == 1) {
+		base = entry->blob;
+		entry = entry->link;
+	}
+	our = NULL;
+	if (entry && entry->stage == 2) {
+		our = entry->blob;
+		entry = entry->link;
+	}
+	their = NULL;
+	if (entry)
+		their = entry->blob;
+	return merge_blobs(the_repository->index, path,
+			   base, our, their, size);
+}
+
+static void *origin(struct merge_list *entry, unsigned long *size)
+{
+	enum object_type type;
+	while (entry) {
+		if (entry->stage == 2)
+			return read_object_file(&entry->blob->object.oid,
+						&type, size);
+		entry = entry->link;
+	}
+	return NULL;
+}
+
+static int show_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+{
+	int i;
+	for (i = 0; i < nbuf; i++)
+		printf("%.*s", (int) mb[i].size, mb[i].ptr);
+	return 0;
+}
+
+static void show_diff(struct merge_list *entry)
+{
+	unsigned long size;
+	mmfile_t src, dst;
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
+	xdemitcb_t ecb;
+
+	xpp.flags = 0;
+	memset(&xecfg, 0, sizeof(xecfg));
+	xecfg.ctxlen = 3;
+	ecb.out_hunk = NULL;
+	ecb.out_line = show_outf;
+	ecb.priv = NULL;
+
+	src.ptr = origin(entry, &size);
+	if (!src.ptr)
+		size = 0;
+	src.size = size;
+	dst.ptr = result(entry, &size);
+	if (!dst.ptr)
+		size = 0;
+	dst.size = size;
+	if (xdi_diff(&src, &dst, &xpp, &xecfg, &ecb))
+		die("unable to generate diff");
+	free(src.ptr);
+	free(dst.ptr);
+}
+
+static void show_result_list(struct merge_list *entry)
+{
+	printf("%s\n", explanation(entry));
+	do {
+		struct merge_list *link = entry->link;
+		static const char *desc[4] = { "result", "base", "our", "their" };
+		printf("  %-6s %o %s %s\n", desc[entry->stage], entry->mode, oid_to_hex(&entry->blob->object.oid), entry->path);
+		entry = link;
+	} while (entry);
+}
+
+static void show_result(void)
+{
+	struct merge_list *walk;
+
+	walk = merge_result;
+	while (walk) {
+		show_result_list(walk);
+		show_diff(walk);
+		walk = walk->next;
+	}
+}
+
+/* An empty entry never compares same, not even to another empty entry */
+static int same_entry(struct name_entry *a, struct name_entry *b)
+{
+	return	!is_null_oid(&a->oid) &&
+		!is_null_oid(&b->oid) &&
+		oideq(&a->oid, &b->oid) &&
+		a->mode == b->mode;
+}
+
+static int both_empty(struct name_entry *a, struct name_entry *b)
+{
+	return is_null_oid(&a->oid) && is_null_oid(&b->oid);
+}
+
+static struct merge_list *create_entry(unsigned stage, unsigned mode, const struct object_id *oid, const char *path)
+{
+	struct merge_list *res = xcalloc(1, sizeof(*res));
+
+	res->stage = stage;
+	res->path = path;
+	res->mode = mode;
+	res->blob = lookup_blob(the_repository, oid);
+	return res;
+}
+
+static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
+{
+	char *path = xmallocz(traverse_path_len(info, n) + the_hash_algo->rawsz);
+	return make_traverse_path(path, info, n);
+}
+
+static void resolve(const struct traverse_info *info, struct name_entry *ours, struct name_entry *result)
+{
+	struct merge_list *orig, *final;
+	const char *path;
+
+	/* If it's already ours, don't bother showing it */
+	if (!ours)
+		return;
+
+	path = traverse_path(info, result);
+	orig = create_entry(2, ours->mode, &ours->oid, path);
+	final = create_entry(0, result->mode, &result->oid, path);
+
+	final->link = orig;
+
+	add_merge_entry(final);
+}
+
+static void unresolved_directory(const struct traverse_info *info,
+				 struct name_entry n[3])
+{
+	struct repository *r = the_repository;
+	char *newbase;
+	struct name_entry *p;
+	struct tree_desc t[3];
+	void *buf0, *buf1, *buf2;
+
+	for (p = n; p < n + 3; p++) {
+		if (p->mode && S_ISDIR(p->mode))
+			break;
+	}
+	if (n + 3 <= p)
+		return; /* there is no tree here */
+
+	newbase = traverse_path(info, p);
+
+#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL)
+	buf0 = fill_tree_descriptor(r, t + 0, ENTRY_OID(n + 0));
+	buf1 = fill_tree_descriptor(r, t + 1, ENTRY_OID(n + 1));
+	buf2 = fill_tree_descriptor(r, t + 2, ENTRY_OID(n + 2));
+#undef ENTRY_OID
+
+	merge_trees(t, newbase);
+
+	free(buf0);
+	free(buf1);
+	free(buf2);
+	free(newbase);
+}
+
+
+static struct merge_list *link_entry(unsigned stage, const struct traverse_info *info, struct name_entry *n, struct merge_list *entry)
+{
+	const char *path;
+	struct merge_list *link;
+
+	if (!n->mode)
+		return entry;
+	if (entry)
+		path = entry->path;
+	else
+		path = traverse_path(info, n);
+	link = create_entry(stage, n->mode, &n->oid, path);
+	link->link = entry;
+	return link;
+}
+
+static void unresolved(const struct traverse_info *info, struct name_entry n[3])
+{
+	struct merge_list *entry = NULL;
+	int i;
+	unsigned dirmask = 0, mask = 0;
+
+	for (i = 0; i < 3; i++) {
+		mask |= (1 << i);
+		/*
+		 * Treat missing entries as directories so that we return
+		 * after unresolved_directory has handled this.
+		 */
+		if (!n[i].mode || S_ISDIR(n[i].mode))
+			dirmask |= (1 << i);
+	}
+
+	unresolved_directory(info, n);
+
+	if (dirmask == mask)
+		return;
+
+	if (n[2].mode && !S_ISDIR(n[2].mode))
+		entry = link_entry(3, info, n + 2, entry);
+	if (n[1].mode && !S_ISDIR(n[1].mode))
+		entry = link_entry(2, info, n + 1, entry);
+	if (n[0].mode && !S_ISDIR(n[0].mode))
+		entry = link_entry(1, info, n + 0, entry);
+
+	add_merge_entry(entry);
+}
+
+/*
+ * Merge two trees together (t[1] and t[2]), using a common base (t[0])
+ * as the origin.
+ *
+ * This walks the (sorted) trees in lock-step, checking every possible
+ * name. Note that directories automatically sort differently from other
+ * files (see "base_name_compare"), so you'll never see file/directory
+ * conflicts, because they won't ever compare the same.
+ *
+ * IOW, if a directory changes to a filename, it will automatically be
+ * seen as the directory going away, and the filename being created.
+ *
+ * Think of this as a three-way diff.
+ *
+ * The output will be either:
+ *  - successful merge
+ *	 "0 mode sha1 filename"
+ *    NOTE NOTE NOTE! FIXME! We really really need to walk the index
+ *    in parallel with this too!
+ *
+ *  - conflict:
+ *	"1 mode sha1 filename"
+ *	"2 mode sha1 filename"
+ *	"3 mode sha1 filename"
+ *    where not all of the 1/2/3 lines may exist, of course.
+ *
+ * The successful merge rules are the same as for the three-way merge
+ * in git-read-tree.
+ */
+static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info)
+{
+	/* Same in both? */
+	if (same_entry(entry+1, entry+2) || both_empty(entry+1, entry+2)) {
+		/* Modified, added or removed identically */
+		resolve(info, NULL, entry+1);
+		return mask;
+	}
+
+	if (same_entry(entry+0, entry+1)) {
+		if (!is_null_oid(&entry[2].oid) && !S_ISDIR(entry[2].mode)) {
+			/* We did not touch, they modified -- take theirs */
+			resolve(info, entry+1, entry+2);
+			return mask;
+		}
+		/*
+		 * If we did not touch a directory but they made it
+		 * into a file, we fall through and unresolved()
+		 * recurses down.  Likewise for the opposite case.
+		 */
+	}
+
+	if (same_entry(entry+0, entry+2) || both_empty(entry+0, entry+2)) {
+		/* We added, modified or removed, they did not touch -- take ours */
+		resolve(info, NULL, entry+1);
+		return mask;
+	}
+
+	unresolved(info, entry);
+	return mask;
+}
+
+static void merge_trees(struct tree_desc t[3], const char *base)
+{
+	struct traverse_info info;
+
+	setup_traverse_info(&info, base);
+	info.fn = threeway_callback;
+	traverse_trees(&the_index, 3, t, &info);
+}
+
+static void *get_tree_descriptor(struct repository *r,
+				 struct tree_desc *desc,
+				 const char *rev)
+{
+	struct object_id oid;
+	void *buf;
+
+	if (repo_get_oid(r, rev, &oid))
+		die("unknown rev %s", rev);
+	buf = fill_tree_descriptor(r, desc, &oid);
+	if (!buf)
+		die("%s is not a tree", rev);
+	return buf;
+}
+
+int cmd_merge_tree(int argc, const char **argv, const char *prefix)
+{
+	struct repository *r = the_repository;
+	struct tree_desc t[3];
+	void *buf1, *buf2, *buf3;
+
+	if (argc != 4)
+		usage(merge_tree_usage);
+
+	buf1 = get_tree_descriptor(r, t+0, argv[1]);
+	buf2 = get_tree_descriptor(r, t+1, argv[2]);
+	buf3 = get_tree_descriptor(r, t+2, argv[3]);
+	merge_trees(t, "");
+	free(buf1);
+	free(buf2);
+	free(buf3);
+
+	show_result();
+	return 0;
+}
diff --git a/builtin/merge.c b/builtin/merge.c
new file mode 100644
index 0000000000..e2ccbc44e2
--- /dev/null
+++ b/builtin/merge.c
@@ -0,0 +1,1680 @@
+/*
+ * Builtin "git merge"
+ *
+ * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>
+ *
+ * Based on git-merge.sh by Junio C Hamano.
+ */
+
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "parse-options.h"
+#include "builtin.h"
+#include "lockfile.h"
+#include "run-command.h"
+#include "diff.h"
+#include "refs.h"
+#include "refspec.h"
+#include "commit.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "unpack-trees.h"
+#include "cache-tree.h"
+#include "dir.h"
+#include "utf8.h"
+#include "log-tree.h"
+#include "color.h"
+#include "rerere.h"
+#include "help.h"
+#include "merge-recursive.h"
+#include "resolve-undo.h"
+#include "remote.h"
+#include "fmt-merge-msg.h"
+#include "gpg-interface.h"
+#include "sequencer.h"
+#include "string-list.h"
+#include "packfile.h"
+#include "tag.h"
+#include "alias.h"
+#include "branch.h"
+#include "commit-reach.h"
+#include "wt-status.h"
+
+#define DEFAULT_TWOHEAD (1<<0)
+#define DEFAULT_OCTOPUS (1<<1)
+#define NO_FAST_FORWARD (1<<2)
+#define NO_TRIVIAL      (1<<3)
+
+struct strategy {
+	const char *name;
+	unsigned attr;
+};
+
+static const char * const builtin_merge_usage[] = {
+	N_("git merge [<options>] [<commit>...]"),
+	N_("git merge --abort"),
+	N_("git merge --continue"),
+	NULL
+};
+
+static int show_diffstat = 1, shortlog_len = -1, squash;
+static int option_commit = -1;
+static int option_edit = -1;
+static int allow_trivial = 1, have_message, verify_signatures;
+static int overwrite_ignore = 1;
+static struct strbuf merge_msg = STRBUF_INIT;
+static struct strategy **use_strategies;
+static size_t use_strategies_nr, use_strategies_alloc;
+static const char **xopts;
+static size_t xopts_nr, xopts_alloc;
+static const char *branch;
+static char *branch_mergeoptions;
+static int option_renormalize;
+static int verbosity;
+static int allow_rerere_auto;
+static int abort_current_merge;
+static int quit_current_merge;
+static int continue_current_merge;
+static int allow_unrelated_histories;
+static int show_progress = -1;
+static int default_to_upstream = 1;
+static int signoff;
+static const char *sign_commit;
+static int verify_msg = 1;
+
+static struct strategy all_strategy[] = {
+	{ "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
+	{ "octopus",    DEFAULT_OCTOPUS },
+	{ "resolve",    0 },
+	{ "ours",       NO_FAST_FORWARD | NO_TRIVIAL },
+	{ "subtree",    NO_FAST_FORWARD | NO_TRIVIAL },
+};
+
+static const char *pull_twohead, *pull_octopus;
+
+enum ff_type {
+	FF_NO,
+	FF_ALLOW,
+	FF_ONLY
+};
+
+static enum ff_type fast_forward = FF_ALLOW;
+
+static const char *cleanup_arg;
+static enum commit_msg_cleanup_mode cleanup_mode;
+
+static int option_parse_message(const struct option *opt,
+				const char *arg, int unset)
+{
+	struct strbuf *buf = opt->value;
+
+	if (unset)
+		strbuf_setlen(buf, 0);
+	else if (arg) {
+		strbuf_addf(buf, "%s%s", buf->len ? "\n\n" : "", arg);
+		have_message = 1;
+	} else
+		return error(_("switch `m' requires a value"));
+	return 0;
+}
+
+static enum parse_opt_result option_read_message(struct parse_opt_ctx_t *ctx,
+						 const struct option *opt,
+						 const char *arg_not_used,
+						 int unset)
+{
+	struct strbuf *buf = opt->value;
+	const char *arg;
+
+	BUG_ON_OPT_ARG(arg_not_used);
+	if (unset)
+		BUG("-F cannot be negated");
+
+	if (ctx->opt) {
+		arg = ctx->opt;
+		ctx->opt = NULL;
+	} else if (ctx->argc > 1) {
+		ctx->argc--;
+		arg = *++ctx->argv;
+	} else
+		return error(_("option `%s' requires a value"), opt->long_name);
+
+	if (buf->len)
+		strbuf_addch(buf, '\n');
+	if (ctx->prefix && !is_absolute_path(arg))
+		arg = prefix_filename(ctx->prefix, arg);
+	if (strbuf_read_file(buf, arg, 0) < 0)
+		return error(_("could not read file '%s'"), arg);
+	have_message = 1;
+
+	return 0;
+}
+
+static struct strategy *get_strategy(const char *name)
+{
+	int i;
+	struct strategy *ret;
+	static struct cmdnames main_cmds, other_cmds;
+	static int loaded;
+
+	if (!name)
+		return NULL;
+
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+		if (!strcmp(name, all_strategy[i].name))
+			return &all_strategy[i];
+
+	if (!loaded) {
+		struct cmdnames not_strategies;
+		loaded = 1;
+
+		memset(&not_strategies, 0, sizeof(struct cmdnames));
+		load_command_list("git-merge-", &main_cmds, &other_cmds);
+		for (i = 0; i < main_cmds.cnt; i++) {
+			int j, found = 0;
+			struct cmdname *ent = main_cmds.names[i];
+			for (j = 0; j < ARRAY_SIZE(all_strategy); j++)
+				if (!strncmp(ent->name, all_strategy[j].name, ent->len)
+						&& !all_strategy[j].name[ent->len])
+					found = 1;
+			if (!found)
+				add_cmdname(&not_strategies, ent->name, ent->len);
+		}
+		exclude_cmds(&main_cmds, &not_strategies);
+	}
+	if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) {
+		fprintf(stderr, _("Could not find merge strategy '%s'.\n"), name);
+		fprintf(stderr, _("Available strategies are:"));
+		for (i = 0; i < main_cmds.cnt; i++)
+			fprintf(stderr, " %s", main_cmds.names[i]->name);
+		fprintf(stderr, ".\n");
+		if (other_cmds.cnt) {
+			fprintf(stderr, _("Available custom strategies are:"));
+			for (i = 0; i < other_cmds.cnt; i++)
+				fprintf(stderr, " %s", other_cmds.names[i]->name);
+			fprintf(stderr, ".\n");
+		}
+		exit(1);
+	}
+
+	ret = xcalloc(1, sizeof(struct strategy));
+	ret->name = xstrdup(name);
+	ret->attr = NO_TRIVIAL;
+	return ret;
+}
+
+static void append_strategy(struct strategy *s)
+{
+	ALLOC_GROW(use_strategies, use_strategies_nr + 1, use_strategies_alloc);
+	use_strategies[use_strategies_nr++] = s;
+}
+
+static int option_parse_strategy(const struct option *opt,
+				 const char *name, int unset)
+{
+	if (unset)
+		return 0;
+
+	append_strategy(get_strategy(name));
+	return 0;
+}
+
+static int option_parse_x(const struct option *opt,
+			  const char *arg, int unset)
+{
+	if (unset)
+		return 0;
+
+	ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
+	xopts[xopts_nr++] = xstrdup(arg);
+	return 0;
+}
+
+static int option_parse_n(const struct option *opt,
+			  const char *arg, int unset)
+{
+	BUG_ON_OPT_ARG(arg);
+	show_diffstat = unset;
+	return 0;
+}
+
+static struct option builtin_merge_options[] = {
+	{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,
+		N_("do not show a diffstat at the end of the merge"),
+		PARSE_OPT_NOARG, option_parse_n },
+	OPT_BOOL(0, "stat", &show_diffstat,
+		N_("show a diffstat at the end of the merge")),
+	OPT_BOOL(0, "summary", &show_diffstat, N_("(synonym to --stat)")),
+	{ OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"),
+	  N_("add (at most <n>) entries from shortlog to merge commit message"),
+	  PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
+	OPT_BOOL(0, "squash", &squash,
+		N_("create a single commit instead of doing a merge")),
+	OPT_BOOL(0, "commit", &option_commit,
+		N_("perform a commit if the merge succeeds (default)")),
+	OPT_BOOL('e', "edit", &option_edit,
+		N_("edit message before committing")),
+	OPT_CLEANUP(&cleanup_arg),
+	OPT_SET_INT(0, "ff", &fast_forward, N_("allow fast-forward (default)"), FF_ALLOW),
+	OPT_SET_INT_F(0, "ff-only", &fast_forward,
+		      N_("abort if fast-forward is not possible"),
+		      FF_ONLY, PARSE_OPT_NONEG),
+	OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
+	OPT_BOOL(0, "verify-signatures", &verify_signatures,
+		N_("verify that the named commit has a valid GPG signature")),
+	OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"),
+		N_("merge strategy to use"), option_parse_strategy),
+	OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"),
+		N_("option for selected merge strategy"), option_parse_x),
+	OPT_CALLBACK('m', "message", &merge_msg, N_("message"),
+		N_("merge commit message (for a non-fast-forward merge)"),
+		option_parse_message),
+	{ OPTION_LOWLEVEL_CALLBACK, 'F', "file", &merge_msg, N_("path"),
+		N_("read message from file"), PARSE_OPT_NONEG,
+		NULL, 0, option_read_message },
+	OPT__VERBOSITY(&verbosity),
+	OPT_BOOL(0, "abort", &abort_current_merge,
+		N_("abort the current in-progress merge")),
+	OPT_BOOL(0, "quit", &quit_current_merge,
+		N_("--abort but leave index and working tree alone")),
+	OPT_BOOL(0, "continue", &continue_current_merge,
+		N_("continue the current in-progress merge")),
+	OPT_BOOL(0, "allow-unrelated-histories", &allow_unrelated_histories,
+		 N_("allow merging unrelated histories")),
+	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
+	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
+	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+	OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
+	OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
+	OPT_BOOL(0, "verify", &verify_msg, N_("verify commit-msg hook")),
+	OPT_END()
+};
+
+static int save_state(struct object_id *stash)
+{
+	int len;
+	struct child_process cp = CHILD_PROCESS_INIT;
+	struct strbuf buffer = STRBUF_INIT;
+	const char *argv[] = {"stash", "create", NULL};
+	int rc = -1;
+
+	cp.argv = argv;
+	cp.out = -1;
+	cp.git_cmd = 1;
+
+	if (start_command(&cp))
+		die(_("could not run stash."));
+	len = strbuf_read(&buffer, cp.out, 1024);
+	close(cp.out);
+
+	if (finish_command(&cp) || len < 0)
+		die(_("stash failed"));
+	else if (!len)		/* no changes */
+		goto out;
+	strbuf_setlen(&buffer, buffer.len-1);
+	if (get_oid(buffer.buf, stash))
+		die(_("not a valid object: %s"), buffer.buf);
+	rc = 0;
+out:
+	strbuf_release(&buffer);
+	return rc;
+}
+
+static void read_empty(const struct object_id *oid, int verbose)
+{
+	int i = 0;
+	const char *args[7];
+
+	args[i++] = "read-tree";
+	if (verbose)
+		args[i++] = "-v";
+	args[i++] = "-m";
+	args[i++] = "-u";
+	args[i++] = empty_tree_oid_hex();
+	args[i++] = oid_to_hex(oid);
+	args[i] = NULL;
+
+	if (run_command_v_opt(args, RUN_GIT_CMD))
+		die(_("read-tree failed"));
+}
+
+static void reset_hard(const struct object_id *oid, int verbose)
+{
+	int i = 0;
+	const char *args[6];
+
+	args[i++] = "read-tree";
+	if (verbose)
+		args[i++] = "-v";
+	args[i++] = "--reset";
+	args[i++] = "-u";
+	args[i++] = oid_to_hex(oid);
+	args[i] = NULL;
+
+	if (run_command_v_opt(args, RUN_GIT_CMD))
+		die(_("read-tree failed"));
+}
+
+static void restore_state(const struct object_id *head,
+			  const struct object_id *stash)
+{
+	struct strbuf sb = STRBUF_INIT;
+	const char *args[] = { "stash", "apply", NULL, NULL };
+
+	if (is_null_oid(stash))
+		return;
+
+	reset_hard(head, 1);
+
+	args[2] = oid_to_hex(stash);
+
+	/*
+	 * It is OK to ignore error here, for example when there was
+	 * nothing to restore.
+	 */
+	run_command_v_opt(args, RUN_GIT_CMD);
+
+	strbuf_release(&sb);
+	refresh_cache(REFRESH_QUIET);
+}
+
+/* This is called when no merge was necessary. */
+static void finish_up_to_date(const char *msg)
+{
+	if (verbosity >= 0)
+		printf("%s%s\n", squash ? _(" (nothing to squash)") : "", msg);
+	remove_merge_branch_state(the_repository);
+}
+
+static void squash_message(struct commit *commit, struct commit_list *remoteheads)
+{
+	struct rev_info rev;
+	struct strbuf out = STRBUF_INIT;
+	struct commit_list *j;
+	struct pretty_print_context ctx = {0};
+
+	printf(_("Squash commit -- not updating HEAD\n"));
+
+	repo_init_revisions(the_repository, &rev, NULL);
+	rev.ignore_merges = 1;
+	rev.commit_format = CMIT_FMT_MEDIUM;
+
+	commit->object.flags |= UNINTERESTING;
+	add_pending_object(&rev, &commit->object, NULL);
+
+	for (j = remoteheads; j; j = j->next)
+		add_pending_object(&rev, &j->item->object, NULL);
+
+	setup_revisions(0, NULL, &rev, NULL);
+	if (prepare_revision_walk(&rev))
+		die(_("revision walk setup failed"));
+
+	ctx.abbrev = rev.abbrev;
+	ctx.date_mode = rev.date_mode;
+	ctx.fmt = rev.commit_format;
+
+	strbuf_addstr(&out, "Squashed commit of the following:\n");
+	while ((commit = get_revision(&rev)) != NULL) {
+		strbuf_addch(&out, '\n');
+		strbuf_addf(&out, "commit %s\n",
+			oid_to_hex(&commit->object.oid));
+		pretty_print_commit(&ctx, commit, &out);
+	}
+	write_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);
+	strbuf_release(&out);
+}
+
+static void finish(struct commit *head_commit,
+		   struct commit_list *remoteheads,
+		   const struct object_id *new_head, const char *msg)
+{
+	struct strbuf reflog_message = STRBUF_INIT;
+	const struct object_id *head = &head_commit->object.oid;
+
+	if (!msg)
+		strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
+	else {
+		if (verbosity >= 0)
+			printf("%s\n", msg);
+		strbuf_addf(&reflog_message, "%s: %s",
+			getenv("GIT_REFLOG_ACTION"), msg);
+	}
+	if (squash) {
+		squash_message(head_commit, remoteheads);
+	} else {
+		if (verbosity >= 0 && !merge_msg.len)
+			printf(_("No merge message -- not updating HEAD\n"));
+		else {
+			const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+			update_ref(reflog_message.buf, "HEAD", new_head, head,
+				   0, UPDATE_REFS_DIE_ON_ERR);
+			/*
+			 * We ignore errors in 'gc --auto', since the
+			 * user should see them.
+			 */
+			close_object_store(the_repository->objects);
+			run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+		}
+	}
+	if (new_head && show_diffstat) {
+		struct diff_options opts;
+		repo_diff_setup(the_repository, &opts);
+		opts.stat_width = -1; /* use full terminal width */
+		opts.stat_graph_width = -1; /* respect statGraphWidth config */
+		opts.output_format |=
+			DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+		opts.detect_rename = DIFF_DETECT_RENAME;
+		diff_setup_done(&opts);
+		diff_tree_oid(head, new_head, "", &opts);
+		diffcore_std(&opts);
+		diff_flush(&opts);
+	}
+
+	/* Run a post-merge hook */
+	run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL);
+
+	strbuf_release(&reflog_message);
+}
+
+/* Get the name for the merge commit's message. */
+static void merge_name(const char *remote, struct strbuf *msg)
+{
+	struct commit *remote_head;
+	struct object_id branch_head;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf bname = STRBUF_INIT;
+	struct merge_remote_desc *desc;
+	const char *ptr;
+	char *found_ref;
+	int len, early;
+
+	strbuf_branchname(&bname, remote, 0);
+	remote = bname.buf;
+
+	oidclr(&branch_head);
+	remote_head = get_merge_parent(remote);
+	if (!remote_head)
+		die(_("'%s' does not point to a commit"), remote);
+
+	if (dwim_ref(remote, strlen(remote), &branch_head, &found_ref) > 0) {
+		if (starts_with(found_ref, "refs/heads/")) {
+			strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
+				    oid_to_hex(&branch_head), remote);
+			goto cleanup;
+		}
+		if (starts_with(found_ref, "refs/tags/")) {
+			strbuf_addf(msg, "%s\t\ttag '%s' of .\n",
+				    oid_to_hex(&branch_head), remote);
+			goto cleanup;
+		}
+		if (starts_with(found_ref, "refs/remotes/")) {
+			strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
+				    oid_to_hex(&branch_head), remote);
+			goto cleanup;
+		}
+	}
+
+	/* See if remote matches <name>^^^.. or <name>~<number> */
+	for (len = 0, ptr = remote + strlen(remote);
+	     remote < ptr && ptr[-1] == '^';
+	     ptr--)
+		len++;
+	if (len)
+		early = 1;
+	else {
+		early = 0;
+		ptr = strrchr(remote, '~');
+		if (ptr) {
+			int seen_nonzero = 0;
+
+			len++; /* count ~ */
+			while (*++ptr && isdigit(*ptr)) {
+				seen_nonzero |= (*ptr != '0');
+				len++;
+			}
+			if (*ptr)
+				len = 0; /* not ...~<number> */
+			else if (seen_nonzero)
+				early = 1;
+			else if (len == 1)
+				early = 1; /* "name~" is "name~1"! */
+		}
+	}
+	if (len) {
+		struct strbuf truname = STRBUF_INIT;
+		strbuf_addf(&truname, "refs/heads/%s", remote);
+		strbuf_setlen(&truname, truname.len - len);
+		if (ref_exists(truname.buf)) {
+			strbuf_addf(msg,
+				    "%s\t\tbranch '%s'%s of .\n",
+				    oid_to_hex(&remote_head->object.oid),
+				    truname.buf + 11,
+				    (early ? " (early part)" : ""));
+			strbuf_release(&truname);
+			goto cleanup;
+		}
+		strbuf_release(&truname);
+	}
+
+	desc = merge_remote_util(remote_head);
+	if (desc && desc->obj && desc->obj->type == OBJ_TAG) {
+		strbuf_addf(msg, "%s\t\t%s '%s'\n",
+			    oid_to_hex(&desc->obj->oid),
+			    type_name(desc->obj->type),
+			    remote);
+		goto cleanup;
+	}
+
+	strbuf_addf(msg, "%s\t\tcommit '%s'\n",
+		oid_to_hex(&remote_head->object.oid), remote);
+cleanup:
+	strbuf_release(&buf);
+	strbuf_release(&bname);
+}
+
+static void parse_branch_merge_options(char *bmo)
+{
+	const char **argv;
+	int argc;
+
+	if (!bmo)
+		return;
+	argc = split_cmdline(bmo, &argv);
+	if (argc < 0)
+		die(_("Bad branch.%s.mergeoptions string: %s"), branch,
+		    _(split_cmdline_strerror(argc)));
+	REALLOC_ARRAY(argv, argc + 2);
+	MOVE_ARRAY(argv + 1, argv, argc + 1);
+	argc++;
+	argv[0] = "branch.*.mergeoptions";
+	parse_options(argc, argv, NULL, builtin_merge_options,
+		      builtin_merge_usage, 0);
+	free(argv);
+}
+
+static int git_merge_config(const char *k, const char *v, void *cb)
+{
+	int status;
+
+	if (branch && starts_with(k, "branch.") &&
+		starts_with(k + 7, branch) &&
+		!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
+		free(branch_mergeoptions);
+		branch_mergeoptions = xstrdup(v);
+		return 0;
+	}
+
+	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+		show_diffstat = git_config_bool(k, v);
+	else if (!strcmp(k, "merge.verifysignatures"))
+		verify_signatures = git_config_bool(k, v);
+	else if (!strcmp(k, "pull.twohead"))
+		return git_config_string(&pull_twohead, k, v);
+	else if (!strcmp(k, "pull.octopus"))
+		return git_config_string(&pull_octopus, k, v);
+	else if (!strcmp(k, "commit.cleanup"))
+		return git_config_string(&cleanup_arg, k, v);
+	else if (!strcmp(k, "merge.renormalize"))
+		option_renormalize = git_config_bool(k, v);
+	else if (!strcmp(k, "merge.ff")) {
+		int boolval = git_parse_maybe_bool(v);
+		if (0 <= boolval) {
+			fast_forward = boolval ? FF_ALLOW : FF_NO;
+		} else if (v && !strcmp(v, "only")) {
+			fast_forward = FF_ONLY;
+		} /* do not barf on values from future versions of git */
+		return 0;
+	} else if (!strcmp(k, "merge.defaulttoupstream")) {
+		default_to_upstream = git_config_bool(k, v);
+		return 0;
+	} else if (!strcmp(k, "commit.gpgsign")) {
+		sign_commit = git_config_bool(k, v) ? "" : NULL;
+		return 0;
+	}
+
+	status = fmt_merge_msg_config(k, v, cb);
+	if (status)
+		return status;
+	status = git_gpg_config(k, v, NULL);
+	if (status)
+		return status;
+	return git_diff_ui_config(k, v, cb);
+}
+
+static int read_tree_trivial(struct object_id *common, struct object_id *head,
+			     struct object_id *one)
+{
+	int i, nr_trees = 0;
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct tree_desc t[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 2;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.trivial_merges_only = 1;
+	opts.merge = 1;
+	trees[nr_trees] = parse_tree_indirect(common);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(one);
+	if (!trees[nr_trees++])
+		return -1;
+	opts.fn = threeway_merge;
+	cache_tree_free(&active_cache_tree);
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	return 0;
+}
+
+static void write_tree_trivial(struct object_id *oid)
+{
+	if (write_cache_as_tree(oid, 0, NULL))
+		die(_("git write-tree failed to write a tree"));
+}
+
+static int try_merge_strategy(const char *strategy, struct commit_list *common,
+			      struct commit_list *remoteheads,
+			      struct commit *head)
+{
+	struct lock_file lock = LOCK_INIT;
+	const char *head_arg = "HEAD";
+
+	hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
+	refresh_cache(REFRESH_QUIET);
+	if (write_locked_index(&the_index, &lock,
+			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
+		return error(_("Unable to write index."));
+
+	if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
+		int clean, x;
+		struct commit *result;
+		struct commit_list *reversed = NULL;
+		struct merge_options o;
+		struct commit_list *j;
+
+		if (remoteheads->next) {
+			error(_("Not handling anything other than two heads merge."));
+			return 2;
+		}
+
+		init_merge_options(&o, the_repository);
+		if (!strcmp(strategy, "subtree"))
+			o.subtree_shift = "";
+
+		o.renormalize = option_renormalize;
+		o.show_rename_progress =
+			show_progress == -1 ? isatty(2) : show_progress;
+
+		for (x = 0; x < xopts_nr; x++)
+			if (parse_merge_opt(&o, xopts[x]))
+				die(_("Unknown option for merge-recursive: -X%s"), xopts[x]);
+
+		o.branch1 = head_arg;
+		o.branch2 = merge_remote_util(remoteheads->item)->name;
+
+		for (j = common; j; j = j->next)
+			commit_list_insert(j->item, &reversed);
+
+		hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
+		clean = merge_recursive(&o, head,
+				remoteheads->item, reversed, &result);
+		if (clean < 0)
+			exit(128);
+		if (write_locked_index(&the_index, &lock,
+				       COMMIT_LOCK | SKIP_IF_UNCHANGED))
+			die(_("unable to write %s"), get_index_file());
+		return clean ? 0 : 1;
+	} else {
+		return try_merge_command(the_repository,
+					 strategy, xopts_nr, xopts,
+					 common, head_arg, remoteheads);
+	}
+}
+
+static void count_diff_files(struct diff_queue_struct *q,
+			     struct diff_options *opt, void *data)
+{
+	int *count = data;
+
+	(*count) += q->nr;
+}
+
+static int count_unmerged_entries(void)
+{
+	int i, ret = 0;
+
+	for (i = 0; i < active_nr; i++)
+		if (ce_stage(active_cache[i]))
+			ret++;
+
+	return ret;
+}
+
+static void add_strategies(const char *string, unsigned attr)
+{
+	int i;
+
+	if (string) {
+		struct string_list list = STRING_LIST_INIT_DUP;
+		struct string_list_item *item;
+		string_list_split(&list, string, ' ', -1);
+		for_each_string_list_item(item, &list)
+			append_strategy(get_strategy(item->string));
+		string_list_clear(&list, 0);
+		return;
+	}
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+		if (all_strategy[i].attr & attr)
+			append_strategy(&all_strategy[i]);
+
+}
+
+static void read_merge_msg(struct strbuf *msg)
+{
+	const char *filename = git_path_merge_msg(the_repository);
+	strbuf_reset(msg);
+	if (strbuf_read_file(msg, filename, 0) < 0)
+		die_errno(_("Could not read from '%s'"), filename);
+}
+
+static void write_merge_state(struct commit_list *);
+static void abort_commit(struct commit_list *remoteheads, const char *err_msg)
+{
+	if (err_msg)
+		error("%s", err_msg);
+	fprintf(stderr,
+		_("Not committing merge; use 'git commit' to complete the merge.\n"));
+	write_merge_state(remoteheads);
+	exit(1);
+}
+
+static const char merge_editor_comment[] =
+N_("Please enter a commit message to explain why this merge is necessary,\n"
+   "especially if it merges an updated upstream into a topic branch.\n"
+   "\n");
+
+static const char scissors_editor_comment[] =
+N_("An empty message aborts the commit.\n");
+
+static const char no_scissors_editor_comment[] =
+N_("Lines starting with '%c' will be ignored, and an empty message aborts\n"
+   "the commit.\n");
+
+static void write_merge_heads(struct commit_list *);
+static void prepare_to_commit(struct commit_list *remoteheads)
+{
+	struct strbuf msg = STRBUF_INIT;
+	strbuf_addbuf(&msg, &merge_msg);
+	if (squash)
+		BUG("the control must not reach here under --squash");
+	if (0 < option_edit) {
+		strbuf_addch(&msg, '\n');
+		if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
+			wt_status_append_cut_line(&msg);
+			strbuf_commented_addf(&msg, "\n");
+		}
+		strbuf_commented_addf(&msg, _(merge_editor_comment));
+		strbuf_commented_addf(&msg, _(cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS ?
+			scissors_editor_comment :
+			no_scissors_editor_comment), comment_line_char);
+	}
+	if (signoff)
+		append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
+	write_merge_heads(remoteheads);
+	write_file_buf(git_path_merge_msg(the_repository), msg.buf, msg.len);
+	if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg",
+			    git_path_merge_msg(the_repository), "merge", NULL))
+		abort_commit(remoteheads, NULL);
+	if (0 < option_edit) {
+		if (launch_editor(git_path_merge_msg(the_repository), NULL, NULL))
+			abort_commit(remoteheads, NULL);
+	}
+
+	if (verify_msg && run_commit_hook(0 < option_edit, get_index_file(),
+					  "commit-msg",
+					  git_path_merge_msg(the_repository), NULL))
+		abort_commit(remoteheads, NULL);
+
+	read_merge_msg(&msg);
+	cleanup_message(&msg, cleanup_mode, 0);
+	if (!msg.len)
+		abort_commit(remoteheads, _("Empty commit message."));
+	strbuf_release(&merge_msg);
+	strbuf_addbuf(&merge_msg, &msg);
+	strbuf_release(&msg);
+}
+
+static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
+{
+	struct object_id result_tree, result_commit;
+	struct commit_list *parents, **pptr = &parents;
+	struct lock_file lock = LOCK_INIT;
+
+	hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
+	refresh_cache(REFRESH_QUIET);
+	if (write_locked_index(&the_index, &lock,
+			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
+		return error(_("Unable to write index."));
+
+	write_tree_trivial(&result_tree);
+	printf(_("Wonderful.\n"));
+	pptr = commit_list_append(head, pptr);
+	pptr = commit_list_append(remoteheads->item, pptr);
+	prepare_to_commit(remoteheads);
+	if (commit_tree(merge_msg.buf, merge_msg.len, &result_tree, parents,
+			&result_commit, NULL, sign_commit))
+		die(_("failed to write commit object"));
+	finish(head, remoteheads, &result_commit, "In-index merge");
+	remove_merge_branch_state(the_repository);
+	return 0;
+}
+
+static int finish_automerge(struct commit *head,
+			    int head_subsumed,
+			    struct commit_list *common,
+			    struct commit_list *remoteheads,
+			    struct object_id *result_tree,
+			    const char *wt_strategy)
+{
+	struct commit_list *parents = NULL;
+	struct strbuf buf = STRBUF_INIT;
+	struct object_id result_commit;
+
+	write_tree_trivial(result_tree);
+	free_commit_list(common);
+	parents = remoteheads;
+	if (!head_subsumed || fast_forward == FF_NO)
+		commit_list_insert(head, &parents);
+	prepare_to_commit(remoteheads);
+	if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
+			&result_commit, NULL, sign_commit))
+		die(_("failed to write commit object"));
+	strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
+	finish(head, remoteheads, &result_commit, buf.buf);
+	strbuf_release(&buf);
+	remove_merge_branch_state(the_repository);
+	return 0;
+}
+
+static int suggest_conflicts(void)
+{
+	const char *filename;
+	FILE *fp;
+	struct strbuf msgbuf = STRBUF_INIT;
+
+	filename = git_path_merge_msg(the_repository);
+	fp = xfopen(filename, "a");
+
+	/*
+	 * We can't use cleanup_mode because if we're not using the editor,
+	 * get_cleanup_mode will return COMMIT_MSG_CLEANUP_SPACE instead, even
+	 * though the message is meant to be processed later by git-commit.
+	 * Thus, we will get the cleanup mode which is returned when we _are_
+	 * using an editor.
+	 */
+	append_conflicts_hint(&the_index, &msgbuf,
+			      get_cleanup_mode(cleanup_arg, 1));
+	fputs(msgbuf.buf, fp);
+	strbuf_release(&msgbuf);
+	fclose(fp);
+	repo_rerere(the_repository, allow_rerere_auto);
+	printf(_("Automatic merge failed; "
+			"fix conflicts and then commit the result.\n"));
+	return 1;
+}
+
+static int evaluate_result(void)
+{
+	int cnt = 0;
+	struct rev_info rev;
+
+	/* Check how many files differ. */
+	repo_init_revisions(the_repository, &rev, "");
+	setup_revisions(0, NULL, &rev, NULL);
+	rev.diffopt.output_format |=
+		DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = count_diff_files;
+	rev.diffopt.format_callback_data = &cnt;
+	run_diff_files(&rev, 0);
+
+	/*
+	 * Check how many unmerged entries are
+	 * there.
+	 */
+	cnt += count_unmerged_entries();
+
+	return cnt;
+}
+
+/*
+ * Pretend as if the user told us to merge with the remote-tracking
+ * branch we have for the upstream of the current branch
+ */
+static int setup_with_upstream(const char ***argv)
+{
+	struct branch *branch = branch_get(NULL);
+	int i;
+	const char **args;
+
+	if (!branch)
+		die(_("No current branch."));
+	if (!branch->remote_name)
+		die(_("No remote for the current branch."));
+	if (!branch->merge_nr)
+		die(_("No default upstream defined for the current branch."));
+
+	args = xcalloc(st_add(branch->merge_nr, 1), sizeof(char *));
+	for (i = 0; i < branch->merge_nr; i++) {
+		if (!branch->merge[i]->dst)
+			die(_("No remote-tracking branch for %s from %s"),
+			    branch->merge[i]->src, branch->remote_name);
+		args[i] = branch->merge[i]->dst;
+	}
+	args[i] = NULL;
+	*argv = args;
+	return i;
+}
+
+static void write_merge_heads(struct commit_list *remoteheads)
+{
+	struct commit_list *j;
+	struct strbuf buf = STRBUF_INIT;
+
+	for (j = remoteheads; j; j = j->next) {
+		struct object_id *oid;
+		struct commit *c = j->item;
+		struct merge_remote_desc *desc;
+
+		desc = merge_remote_util(c);
+		if (desc && desc->obj) {
+			oid = &desc->obj->oid;
+		} else {
+			oid = &c->object.oid;
+		}
+		strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
+	}
+	write_file_buf(git_path_merge_head(the_repository), buf.buf, buf.len);
+
+	strbuf_reset(&buf);
+	if (fast_forward == FF_NO)
+		strbuf_addstr(&buf, "no-ff");
+	write_file_buf(git_path_merge_mode(the_repository), buf.buf, buf.len);
+	strbuf_release(&buf);
+}
+
+static void write_merge_state(struct commit_list *remoteheads)
+{
+	write_merge_heads(remoteheads);
+	strbuf_addch(&merge_msg, '\n');
+	write_file_buf(git_path_merge_msg(the_repository), merge_msg.buf,
+		       merge_msg.len);
+}
+
+static int default_edit_option(void)
+{
+	static const char name[] = "GIT_MERGE_AUTOEDIT";
+	const char *e = getenv(name);
+	struct stat st_stdin, st_stdout;
+
+	if (have_message)
+		/* an explicit -m msg without --[no-]edit */
+		return 0;
+
+	if (e) {
+		int v = git_parse_maybe_bool(e);
+		if (v < 0)
+			die(_("Bad value '%s' in environment '%s'"), e, name);
+		return v;
+	}
+
+	/* Use editor if stdin and stdout are the same and is a tty */
+	return (!fstat(0, &st_stdin) &&
+		!fstat(1, &st_stdout) &&
+		isatty(0) && isatty(1) &&
+		st_stdin.st_dev == st_stdout.st_dev &&
+		st_stdin.st_ino == st_stdout.st_ino &&
+		st_stdin.st_mode == st_stdout.st_mode);
+}
+
+static struct commit_list *reduce_parents(struct commit *head_commit,
+					  int *head_subsumed,
+					  struct commit_list *remoteheads)
+{
+	struct commit_list *parents, **remotes;
+
+	/*
+	 * Is the current HEAD reachable from another commit being
+	 * merged?  If so we do not want to record it as a parent of
+	 * the resulting merge, unless --no-ff is given.  We will flip
+	 * this variable to 0 when we find HEAD among the independent
+	 * tips being merged.
+	 */
+	*head_subsumed = 1;
+
+	/* Find what parents to record by checking independent ones. */
+	parents = reduce_heads(remoteheads);
+	free_commit_list(remoteheads);
+
+	remoteheads = NULL;
+	remotes = &remoteheads;
+	while (parents) {
+		struct commit *commit = pop_commit(&parents);
+		if (commit == head_commit)
+			*head_subsumed = 0;
+		else
+			remotes = &commit_list_insert(commit, remotes)->next;
+	}
+	return remoteheads;
+}
+
+static void prepare_merge_message(struct strbuf *merge_names, struct strbuf *merge_msg)
+{
+	struct fmt_merge_msg_opts opts;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.add_title = !have_message;
+	opts.shortlog_len = shortlog_len;
+	opts.credit_people = (0 < option_edit);
+
+	fmt_merge_msg(merge_names, merge_msg, &opts);
+	if (merge_msg->len)
+		strbuf_setlen(merge_msg, merge_msg->len - 1);
+}
+
+static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge_names)
+{
+	const char *filename;
+	int fd, pos, npos;
+	struct strbuf fetch_head_file = STRBUF_INIT;
+	const unsigned hexsz = the_hash_algo->hexsz;
+
+	if (!merge_names)
+		merge_names = &fetch_head_file;
+
+	filename = git_path_fetch_head(the_repository);
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		die_errno(_("could not open '%s' for reading"), filename);
+
+	if (strbuf_read(merge_names, fd, 0) < 0)
+		die_errno(_("could not read '%s'"), filename);
+	if (close(fd) < 0)
+		die_errno(_("could not close '%s'"), filename);
+
+	for (pos = 0; pos < merge_names->len; pos = npos) {
+		struct object_id oid;
+		char *ptr;
+		struct commit *commit;
+
+		ptr = strchr(merge_names->buf + pos, '\n');
+		if (ptr)
+			npos = ptr - merge_names->buf + 1;
+		else
+			npos = merge_names->len;
+
+		if (npos - pos < hexsz + 2 ||
+		    get_oid_hex(merge_names->buf + pos, &oid))
+			commit = NULL; /* bad */
+		else if (memcmp(merge_names->buf + pos + hexsz, "\t\t", 2))
+			continue; /* not-for-merge */
+		else {
+			char saved = merge_names->buf[pos + hexsz];
+			merge_names->buf[pos + hexsz] = '\0';
+			commit = get_merge_parent(merge_names->buf + pos);
+			merge_names->buf[pos + hexsz] = saved;
+		}
+		if (!commit) {
+			if (ptr)
+				*ptr = '\0';
+			die(_("not something we can merge in %s: %s"),
+			    filename, merge_names->buf + pos);
+		}
+		remotes = &commit_list_insert(commit, remotes)->next;
+	}
+
+	if (merge_names == &fetch_head_file)
+		strbuf_release(&fetch_head_file);
+}
+
+static struct commit_list *collect_parents(struct commit *head_commit,
+					   int *head_subsumed,
+					   int argc, const char **argv,
+					   struct strbuf *merge_msg)
+{
+	int i;
+	struct commit_list *remoteheads = NULL;
+	struct commit_list **remotes = &remoteheads;
+	struct strbuf merge_names = STRBUF_INIT, *autogen = NULL;
+
+	if (merge_msg && (!have_message || shortlog_len))
+		autogen = &merge_names;
+
+	if (head_commit)
+		remotes = &commit_list_insert(head_commit, remotes)->next;
+
+	if (argc == 1 && !strcmp(argv[0], "FETCH_HEAD")) {
+		handle_fetch_head(remotes, autogen);
+		remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads);
+	} else {
+		for (i = 0; i < argc; i++) {
+			struct commit *commit = get_merge_parent(argv[i]);
+			if (!commit)
+				help_unknown_ref(argv[i], "merge",
+						 _("not something we can merge"));
+			remotes = &commit_list_insert(commit, remotes)->next;
+		}
+		remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads);
+		if (autogen) {
+			struct commit_list *p;
+			for (p = remoteheads; p; p = p->next)
+				merge_name(merge_remote_util(p->item)->name, autogen);
+		}
+	}
+
+	if (autogen) {
+		prepare_merge_message(autogen, merge_msg);
+		strbuf_release(autogen);
+	}
+
+	return remoteheads;
+}
+
+static int merging_a_throwaway_tag(struct commit *commit)
+{
+	char *tag_ref;
+	struct object_id oid;
+	int is_throwaway_tag = 0;
+
+	/* Are we merging a tag? */
+	if (!merge_remote_util(commit) ||
+	    !merge_remote_util(commit)->obj ||
+	    merge_remote_util(commit)->obj->type != OBJ_TAG)
+		return is_throwaway_tag;
+
+	/*
+	 * Now we know we are merging a tag object.  Are we downstream
+	 * and following the tags from upstream?  If so, we must have
+	 * the tag object pointed at by "refs/tags/$T" where $T is the
+	 * tagname recorded in the tag object.  We want to allow such
+	 * a "just to catch up" merge to fast-forward.
+	 *
+	 * Otherwise, we are playing an integrator's role, making a
+	 * merge with a throw-away tag from a contributor with
+	 * something like "git pull $contributor $signed_tag".
+	 * We want to forbid such a merge from fast-forwarding
+	 * by default; otherwise we would not keep the signature
+	 * anywhere.
+	 */
+	tag_ref = xstrfmt("refs/tags/%s",
+			  ((struct tag *)merge_remote_util(commit)->obj)->tag);
+	if (!read_ref(tag_ref, &oid) &&
+	    oideq(&oid, &merge_remote_util(commit)->obj->oid))
+		is_throwaway_tag = 0;
+	else
+		is_throwaway_tag = 1;
+	free(tag_ref);
+	return is_throwaway_tag;
+}
+
+int cmd_merge(int argc, const char **argv, const char *prefix)
+{
+	struct object_id result_tree, stash, head_oid;
+	struct commit *head_commit;
+	struct strbuf buf = STRBUF_INIT;
+	int i, ret = 0, head_subsumed;
+	int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
+	struct commit_list *common = NULL;
+	const char *best_strategy = NULL, *wt_strategy = NULL;
+	struct commit_list *remoteheads, *p;
+	void *branch_to_free;
+	int orig_argc = argc;
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(builtin_merge_usage, builtin_merge_options);
+
+	/*
+	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
+	 * current branch.
+	 */
+	branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL);
+	if (branch)
+		skip_prefix(branch, "refs/heads/", &branch);
+
+	init_diff_ui_defaults();
+	git_config(git_merge_config, NULL);
+
+	if (!branch || is_null_oid(&head_oid))
+		head_commit = NULL;
+	else
+		head_commit = lookup_commit_or_die(&head_oid, "HEAD");
+
+	if (branch_mergeoptions)
+		parse_branch_merge_options(branch_mergeoptions);
+	argc = parse_options(argc, argv, prefix, builtin_merge_options,
+			builtin_merge_usage, 0);
+	if (shortlog_len < 0)
+		shortlog_len = (merge_log_config > 0) ? merge_log_config : 0;
+
+	if (verbosity < 0 && show_progress == -1)
+		show_progress = 0;
+
+	if (abort_current_merge) {
+		int nargc = 2;
+		const char *nargv[] = {"reset", "--merge", NULL};
+
+		if (orig_argc != 2)
+			usage_msg_opt(_("--abort expects no arguments"),
+			      builtin_merge_usage, builtin_merge_options);
+
+		if (!file_exists(git_path_merge_head(the_repository)))
+			die(_("There is no merge to abort (MERGE_HEAD missing)."));
+
+		/* Invoke 'git reset --merge' */
+		ret = cmd_reset(nargc, nargv, prefix);
+		goto done;
+	}
+
+	if (quit_current_merge) {
+		if (orig_argc != 2)
+			usage_msg_opt(_("--quit expects no arguments"),
+				      builtin_merge_usage,
+				      builtin_merge_options);
+
+		remove_merge_branch_state(the_repository);
+		goto done;
+	}
+
+	if (continue_current_merge) {
+		int nargc = 1;
+		const char *nargv[] = {"commit", NULL};
+
+		if (orig_argc != 2)
+			usage_msg_opt(_("--continue expects no arguments"),
+			      builtin_merge_usage, builtin_merge_options);
+
+		if (!file_exists(git_path_merge_head(the_repository)))
+			die(_("There is no merge in progress (MERGE_HEAD missing)."));
+
+		/* Invoke 'git commit' */
+		ret = cmd_commit(nargc, nargv, prefix);
+		goto done;
+	}
+
+	if (read_cache_unmerged())
+		die_resolve_conflict("merge");
+
+	if (file_exists(git_path_merge_head(the_repository))) {
+		/*
+		 * There is no unmerged entry, don't advise 'git
+		 * add/rm <file>', just 'git commit'.
+		 */
+		if (advice_resolve_conflict)
+			die(_("You have not concluded your merge (MERGE_HEAD exists).\n"
+				  "Please, commit your changes before you merge."));
+		else
+			die(_("You have not concluded your merge (MERGE_HEAD exists)."));
+	}
+	if (file_exists(git_path_cherry_pick_head(the_repository))) {
+		if (advice_resolve_conflict)
+			die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+			    "Please, commit your changes before you merge."));
+		else
+			die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
+	}
+	resolve_undo_clear();
+
+	if (option_edit < 0)
+		option_edit = default_edit_option();
+
+	cleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit);
+
+	if (verbosity < 0)
+		show_diffstat = 0;
+
+	if (squash) {
+		if (fast_forward == FF_NO)
+			die(_("You cannot combine --squash with --no-ff."));
+		if (option_commit > 0)
+			die(_("You cannot combine --squash with --commit."));
+		/*
+		 * squash can now silently disable option_commit - this is not
+		 * a problem as it is only overriding the default, not a user
+		 * supplied option.
+		 */
+		option_commit = 0;
+	}
+
+	if (option_commit < 0)
+		option_commit = 1;
+
+	if (!argc) {
+		if (default_to_upstream)
+			argc = setup_with_upstream(&argv);
+		else
+			die(_("No commit specified and merge.defaultToUpstream not set."));
+	} else if (argc == 1 && !strcmp(argv[0], "-")) {
+		argv[0] = "@{-1}";
+	}
+
+	if (!argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	if (!head_commit) {
+		/*
+		 * If the merged head is a valid one there is no reason
+		 * to forbid "git merge" into a branch yet to be born.
+		 * We do the same for "git pull".
+		 */
+		struct object_id *remote_head_oid;
+		if (squash)
+			die(_("Squash commit into empty head not supported yet"));
+		if (fast_forward == FF_NO)
+			die(_("Non-fast-forward commit does not make sense into "
+			    "an empty head"));
+		remoteheads = collect_parents(head_commit, &head_subsumed,
+					      argc, argv, NULL);
+		if (!remoteheads)
+			die(_("%s - not something we can merge"), argv[0]);
+		if (remoteheads->next)
+			die(_("Can merge only exactly one commit into empty head"));
+
+		if (verify_signatures)
+			verify_merge_signature(remoteheads->item, verbosity);
+
+		remote_head_oid = &remoteheads->item->object.oid;
+		read_empty(remote_head_oid, 0);
+		update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0,
+			   UPDATE_REFS_DIE_ON_ERR);
+		goto done;
+	}
+
+	/*
+	 * All the rest are the commits being merged; prepare
+	 * the standard merge summary message to be appended
+	 * to the given message.
+	 */
+	remoteheads = collect_parents(head_commit, &head_subsumed,
+				      argc, argv, &merge_msg);
+
+	if (!head_commit || !argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	if (verify_signatures) {
+		for (p = remoteheads; p; p = p->next) {
+			verify_merge_signature(p->item, verbosity);
+		}
+	}
+
+	strbuf_addstr(&buf, "merge");
+	for (p = remoteheads; p; p = p->next)
+		strbuf_addf(&buf, " %s", merge_remote_util(p->item)->name);
+	setenv("GIT_REFLOG_ACTION", buf.buf, 0);
+	strbuf_reset(&buf);
+
+	for (p = remoteheads; p; p = p->next) {
+		struct commit *commit = p->item;
+		strbuf_addf(&buf, "GITHEAD_%s",
+			    oid_to_hex(&commit->object.oid));
+		setenv(buf.buf, merge_remote_util(commit)->name, 1);
+		strbuf_reset(&buf);
+		if (fast_forward != FF_ONLY && merging_a_throwaway_tag(commit))
+			fast_forward = FF_NO;
+	}
+
+	if (!use_strategies) {
+		if (!remoteheads)
+			; /* already up-to-date */
+		else if (!remoteheads->next)
+			add_strategies(pull_twohead, DEFAULT_TWOHEAD);
+		else
+			add_strategies(pull_octopus, DEFAULT_OCTOPUS);
+	}
+
+	for (i = 0; i < use_strategies_nr; i++) {
+		if (use_strategies[i]->attr & NO_FAST_FORWARD)
+			fast_forward = FF_NO;
+		if (use_strategies[i]->attr & NO_TRIVIAL)
+			allow_trivial = 0;
+	}
+
+	if (!remoteheads)
+		; /* already up-to-date */
+	else if (!remoteheads->next)
+		common = get_merge_bases(head_commit, remoteheads->item);
+	else {
+		struct commit_list *list = remoteheads;
+		commit_list_insert(head_commit, &list);
+		common = get_octopus_merge_bases(list);
+		free(list);
+	}
+
+	update_ref("updating ORIG_HEAD", "ORIG_HEAD",
+		   &head_commit->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+
+	if (remoteheads && !common) {
+		/* No common ancestors found. */
+		if (!allow_unrelated_histories)
+			die(_("refusing to merge unrelated histories"));
+		/* otherwise, we need a real merge. */
+	} else if (!remoteheads ||
+		 (!remoteheads->next && !common->next &&
+		  common->item == remoteheads->item)) {
+		/*
+		 * If head can reach all the merge then we are up to date.
+		 * but first the most common case of merging one remote.
+		 */
+		finish_up_to_date(_("Already up to date."));
+		goto done;
+	} else if (fast_forward != FF_NO && !remoteheads->next &&
+			!common->next &&
+			oideq(&common->item->object.oid, &head_commit->object.oid)) {
+		/* Again the most common case of merging one remote. */
+		struct strbuf msg = STRBUF_INIT;
+		struct commit *commit;
+
+		if (verbosity >= 0) {
+			printf(_("Updating %s..%s\n"),
+			       find_unique_abbrev(&head_commit->object.oid,
+						  DEFAULT_ABBREV),
+			       find_unique_abbrev(&remoteheads->item->object.oid,
+						  DEFAULT_ABBREV));
+		}
+		strbuf_addstr(&msg, "Fast-forward");
+		if (have_message)
+			strbuf_addstr(&msg,
+				" (no commit created; -m option ignored)");
+		commit = remoteheads->item;
+		if (!commit) {
+			ret = 1;
+			goto done;
+		}
+
+		if (checkout_fast_forward(the_repository,
+					  &head_commit->object.oid,
+					  &commit->object.oid,
+					  overwrite_ignore)) {
+			ret = 1;
+			goto done;
+		}
+
+		finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
+		remove_merge_branch_state(the_repository);
+		goto done;
+	} else if (!remoteheads->next && common->next)
+		;
+		/*
+		 * We are not doing octopus and not fast-forward.  Need
+		 * a real merge.
+		 */
+	else if (!remoteheads->next && !common->next && option_commit) {
+		/*
+		 * We are not doing octopus, not fast-forward, and have
+		 * only one common.
+		 */
+		refresh_cache(REFRESH_QUIET);
+		if (allow_trivial && fast_forward != FF_ONLY) {
+			/* See if it is really trivial. */
+			git_committer_info(IDENT_STRICT);
+			printf(_("Trying really trivial in-index merge...\n"));
+			if (!read_tree_trivial(&common->item->object.oid,
+					       &head_commit->object.oid,
+					       &remoteheads->item->object.oid)) {
+				ret = merge_trivial(head_commit, remoteheads);
+				goto done;
+			}
+			printf(_("Nope.\n"));
+		}
+	} else {
+		/*
+		 * An octopus.  If we can reach all the remote we are up
+		 * to date.
+		 */
+		int up_to_date = 1;
+		struct commit_list *j;
+
+		for (j = remoteheads; j; j = j->next) {
+			struct commit_list *common_one;
+
+			/*
+			 * Here we *have* to calculate the individual
+			 * merge_bases again, otherwise "git merge HEAD^
+			 * HEAD^^" would be missed.
+			 */
+			common_one = get_merge_bases(head_commit, j->item);
+			if (!oideq(&common_one->item->object.oid, &j->item->object.oid)) {
+				up_to_date = 0;
+				break;
+			}
+		}
+		if (up_to_date) {
+			finish_up_to_date(_("Already up to date. Yeeah!"));
+			goto done;
+		}
+	}
+
+	if (fast_forward == FF_ONLY)
+		die(_("Not possible to fast-forward, aborting."));
+
+	/* We are going to make a new commit. */
+	git_committer_info(IDENT_STRICT);
+
+	/*
+	 * At this point, we need a real merge.  No matter what strategy
+	 * we use, it would operate on the index, possibly affecting the
+	 * working tree, and when resolved cleanly, have the desired
+	 * tree in the index -- this means that the index must be in
+	 * sync with the head commit.  The strategies are responsible
+	 * to ensure this.
+	 */
+	if (use_strategies_nr == 1 ||
+	    /*
+	     * Stash away the local changes so that we can try more than one.
+	     */
+	    save_state(&stash))
+		oidclr(&stash);
+
+	for (i = 0; !merge_was_ok && i < use_strategies_nr; i++) {
+		int ret, cnt;
+		if (i) {
+			printf(_("Rewinding the tree to pristine...\n"));
+			restore_state(&head_commit->object.oid, &stash);
+		}
+		if (use_strategies_nr != 1)
+			printf(_("Trying merge strategy %s...\n"),
+				use_strategies[i]->name);
+		/*
+		 * Remember which strategy left the state in the working
+		 * tree.
+		 */
+		wt_strategy = use_strategies[i]->name;
+
+		ret = try_merge_strategy(use_strategies[i]->name,
+					 common, remoteheads,
+					 head_commit);
+		/*
+		 * The backend exits with 1 when conflicts are
+		 * left to be resolved, with 2 when it does not
+		 * handle the given merge at all.
+		 */
+		if (ret < 2) {
+			if (!ret) {
+				if (option_commit) {
+					/* Automerge succeeded. */
+					automerge_was_ok = 1;
+					break;
+				}
+				merge_was_ok = 1;
+			}
+			cnt = evaluate_result();
+			if (best_cnt <= 0 || cnt <= best_cnt) {
+				best_strategy = use_strategies[i]->name;
+				best_cnt = cnt;
+			}
+		}
+	}
+
+	/*
+	 * If we have a resulting tree, that means the strategy module
+	 * auto resolved the merge cleanly.
+	 */
+	if (automerge_was_ok) {
+		ret = finish_automerge(head_commit, head_subsumed,
+				       common, remoteheads,
+				       &result_tree, wt_strategy);
+		goto done;
+	}
+
+	/*
+	 * Pick the result from the best strategy and have the user fix
+	 * it up.
+	 */
+	if (!best_strategy) {
+		restore_state(&head_commit->object.oid, &stash);
+		if (use_strategies_nr > 1)
+			fprintf(stderr,
+				_("No merge strategy handled the merge.\n"));
+		else
+			fprintf(stderr, _("Merge with strategy %s failed.\n"),
+				use_strategies[0]->name);
+		ret = 2;
+		goto done;
+	} else if (best_strategy == wt_strategy)
+		; /* We already have its result in the working tree. */
+	else {
+		printf(_("Rewinding the tree to pristine...\n"));
+		restore_state(&head_commit->object.oid, &stash);
+		printf(_("Using the %s to prepare resolving by hand.\n"),
+			best_strategy);
+		try_merge_strategy(best_strategy, common, remoteheads,
+				   head_commit);
+	}
+
+	if (squash)
+		finish(head_commit, remoteheads, NULL, NULL);
+	else
+		write_merge_state(remoteheads);
+
+	if (merge_was_ok)
+		fprintf(stderr, _("Automatic merge went well; "
+			"stopped before committing as requested\n"));
+	else
+		ret = suggest_conflicts();
+
+done:
+	free(branch_to_free);
+	return ret;
+}
diff --git a/builtin/mktag.c b/builtin/mktag.c
new file mode 100644
index 0000000000..6fb7dc8578
--- /dev/null
+++ b/builtin/mktag.c
@@ -0,0 +1,176 @@
+#include "builtin.h"
+#include "tag.h"
+#include "replace-object.h"
+#include "object-store.h"
+
+/*
+ * A signature file has a very simple fixed format: four lines
+ * of "object <sha1>" + "type <typename>" + "tag <tagname>" +
+ * "tagger <committer>", followed by a blank line, a free-form tag
+ * message and a signature block that git itself doesn't care about,
+ * but that can be verified with gpg or similar.
+ *
+ * The first four lines are guaranteed to be at least 83 bytes:
+ * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the
+ * shortest possible type-line, "tag .\n" at 6 bytes is the shortest
+ * single-character-tag line, and "tagger . <> 0 +0000\n" at 20 bytes is
+ * the shortest possible tagger-line.
+ */
+
+/*
+ * We refuse to tag something we can't verify. Just because.
+ */
+static int verify_object(const struct object_id *oid, const char *expected_type)
+{
+	int ret = -1;
+	enum object_type type;
+	unsigned long size;
+	void *buffer = read_object_file(oid, &type, &size);
+	const struct object_id *repl = lookup_replace_object(the_repository, oid);
+
+	if (buffer) {
+		if (type == type_from_string(expected_type))
+			ret = check_object_signature(repl, buffer, size, expected_type);
+		free(buffer);
+	}
+	return ret;
+}
+
+static int verify_tag(char *buffer, unsigned long size)
+{
+	int typelen;
+	char type[20];
+	struct object_id oid;
+	const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;
+	size_t len;
+
+	if (size < 84)
+		return error("wanna fool me ? you obviously got the size wrong !");
+
+	buffer[size] = 0;
+
+	/* Verify object line */
+	object = buffer;
+	if (memcmp(object, "object ", 7))
+		return error("char%d: does not start with \"object \"", 0);
+
+	if (parse_oid_hex(object + 7, &oid, &p))
+		return error("char%d: could not get SHA1 hash", 7);
+
+	/* Verify type line */
+	type_line = p + 1;
+	if (memcmp(type_line - 1, "\ntype ", 6))
+		return error("char%d: could not find \"\\ntype \"", 47);
+
+	/* Verify tag-line */
+	tag_line = strchr(type_line, '\n');
+	if (!tag_line)
+		return error("char%"PRIuMAX": could not find next \"\\n\"",
+				(uintmax_t) (type_line - buffer));
+	tag_line++;
+	if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n')
+		return error("char%"PRIuMAX": no \"tag \" found",
+				(uintmax_t) (tag_line - buffer));
+
+	/* Get the actual type */
+	typelen = tag_line - type_line - strlen("type \n");
+	if (typelen >= sizeof(type))
+		return error("char%"PRIuMAX": type too long",
+				(uintmax_t) (type_line+5 - buffer));
+
+	memcpy(type, type_line+5, typelen);
+	type[typelen] = 0;
+
+	/* Verify that the object matches */
+	if (verify_object(&oid, type))
+		return error("char%d: could not verify object %s", 7, oid_to_hex(&oid));
+
+	/* Verify the tag-name: we don't allow control characters or spaces in it */
+	tag_line += 4;
+	for (;;) {
+		unsigned char c = *tag_line++;
+		if (c == '\n')
+			break;
+		if (c > ' ')
+			continue;
+		return error("char%"PRIuMAX": could not verify tag name",
+				(uintmax_t) (tag_line - buffer));
+	}
+
+	/* Verify the tagger line */
+	tagger_line = tag_line;
+
+	if (memcmp(tagger_line, "tagger ", 7))
+		return error("char%"PRIuMAX": could not find \"tagger \"",
+			(uintmax_t) (tagger_line - buffer));
+
+	/*
+	 * Check for correct form for name and email
+	 * i.e. " <" followed by "> " on _this_ line
+	 * No angle brackets within the name or email address fields.
+	 * No spaces within the email address field.
+	 */
+	tagger_line += 7;
+	if (!(lb = strstr(tagger_line, " <")) || !(rb = strstr(lb+2, "> ")) ||
+		strpbrk(tagger_line, "<>\n") != lb+1 ||
+		strpbrk(lb+2, "><\n ") != rb)
+		return error("char%"PRIuMAX": malformed tagger field",
+			(uintmax_t) (tagger_line - buffer));
+
+	/* Check for author name, at least one character, space is acceptable */
+	if (lb == tagger_line)
+		return error("char%"PRIuMAX": missing tagger name",
+			(uintmax_t) (tagger_line - buffer));
+
+	/* timestamp, 1 or more digits followed by space */
+	tagger_line = rb + 2;
+	if (!(len = strspn(tagger_line, "0123456789")))
+		return error("char%"PRIuMAX": missing tag timestamp",
+			(uintmax_t) (tagger_line - buffer));
+	tagger_line += len;
+	if (*tagger_line != ' ')
+		return error("char%"PRIuMAX": malformed tag timestamp",
+			(uintmax_t) (tagger_line - buffer));
+	tagger_line++;
+
+	/* timezone, 5 digits [+-]hhmm, max. 1400 */
+	if (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&
+	      strspn(tagger_line+1, "0123456789") == 4 &&
+	      tagger_line[5] == '\n' && atoi(tagger_line+1) <= 1400))
+		return error("char%"PRIuMAX": malformed tag timezone",
+			(uintmax_t) (tagger_line - buffer));
+	tagger_line += 6;
+
+	/* Verify the blank line separating the header from the body */
+	if (*tagger_line != '\n')
+		return error("char%"PRIuMAX": trailing garbage in tag header",
+			(uintmax_t) (tagger_line - buffer));
+
+	/* The actual stuff afterwards we don't care about.. */
+	return 0;
+}
+
+int cmd_mktag(int argc, const char **argv, const char *prefix)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct object_id result;
+
+	if (argc != 1)
+		usage("git mktag");
+
+	if (strbuf_read(&buf, 0, 4096) < 0) {
+		die_errno("could not read from stdin");
+	}
+
+	/* Verify it for some basic sanity: it needs to start with
+	   "object <sha1>\ntype\ntagger " */
+	if (verify_tag(buf.buf, buf.len) < 0)
+		die("invalid tag signature file");
+
+	if (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)
+		die("unable to write tag file");
+
+	strbuf_release(&buf);
+	printf("%s\n", oid_to_hex(&result));
+	return 0;
+}
diff --git a/builtin/mktree.c b/builtin/mktree.c
new file mode 100644
index 0000000000..891991b00d
--- /dev/null
+++ b/builtin/mktree.c
@@ -0,0 +1,193 @@
+/*
+ * GIT - the stupid content tracker
+ *
+ * Copyright (c) Junio C Hamano, 2006, 2009
+ */
+#include "builtin.h"
+#include "quote.h"
+#include "tree.h"
+#include "parse-options.h"
+#include "object-store.h"
+
+static struct treeent {
+	unsigned mode;
+	struct object_id oid;
+	int len;
+	char name[FLEX_ARRAY];
+} **entries;
+static int alloc, used;
+
+static void append_to_tree(unsigned mode, struct object_id *oid, char *path)
+{
+	struct treeent *ent;
+	size_t len = strlen(path);
+	if (strchr(path, '/'))
+		die("path %s contains slash", path);
+
+	FLEX_ALLOC_MEM(ent, name, path, len);
+	ent->mode = mode;
+	ent->len = len;
+	oidcpy(&ent->oid, oid);
+
+	ALLOC_GROW(entries, used + 1, alloc);
+	entries[used++] = ent;
+}
+
+static int ent_compare(const void *a_, const void *b_)
+{
+	struct treeent *a = *(struct treeent **)a_;
+	struct treeent *b = *(struct treeent **)b_;
+	return base_name_compare(a->name, a->len, a->mode,
+				 b->name, b->len, b->mode);
+}
+
+static void write_tree(struct object_id *oid)
+{
+	struct strbuf buf;
+	size_t size;
+	int i;
+
+	QSORT(entries, used, ent_compare);
+	for (size = i = 0; i < used; i++)
+		size += 32 + entries[i]->len;
+
+	strbuf_init(&buf, size);
+	for (i = 0; i < used; i++) {
+		struct treeent *ent = entries[i];
+		strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0');
+		strbuf_add(&buf, ent->oid.hash, the_hash_algo->rawsz);
+	}
+
+	write_object_file(buf.buf, buf.len, tree_type, oid);
+	strbuf_release(&buf);
+}
+
+static const char *mktree_usage[] = {
+	N_("git mktree [-z] [--missing] [--batch]"),
+	NULL
+};
+
+static void mktree_line(char *buf, int nul_term_line, int allow_missing)
+{
+	char *ptr, *ntr;
+	const char *p;
+	unsigned mode;
+	enum object_type mode_type; /* object type derived from mode */
+	enum object_type obj_type; /* object type derived from sha */
+	char *path, *to_free = NULL;
+	struct object_id oid;
+
+	ptr = buf;
+	/*
+	 * Read non-recursive ls-tree output format:
+	 *     mode SP type SP sha1 TAB name
+	 */
+	mode = strtoul(ptr, &ntr, 8);
+	if (ptr == ntr || !ntr || *ntr != ' ')
+		die("input format error: %s", buf);
+	ptr = ntr + 1; /* type */
+	ntr = strchr(ptr, ' ');
+	if (!ntr || parse_oid_hex(ntr + 1, &oid, &p) ||
+	    *p != '\t')
+		die("input format error: %s", buf);
+
+	/* It is perfectly normal if we do not have a commit from a submodule */
+	if (S_ISGITLINK(mode))
+		allow_missing = 1;
+
+
+	*ntr++ = 0; /* now at the beginning of SHA1 */
+
+	path = (char *)p + 1;  /* at the beginning of name */
+	if (!nul_term_line && path[0] == '"') {
+		struct strbuf p_uq = STRBUF_INIT;
+		if (unquote_c_style(&p_uq, path, NULL))
+			die("invalid quoting");
+		path = to_free = strbuf_detach(&p_uq, NULL);
+	}
+
+	/*
+	 * Object type is redundantly derivable three ways.
+	 * These should all agree.
+	 */
+	mode_type = object_type(mode);
+	if (mode_type != type_from_string(ptr)) {
+		die("entry '%s' object type (%s) doesn't match mode type (%s)",
+			path, ptr, type_name(mode_type));
+	}
+
+	/* Check the type of object identified by sha1 */
+	obj_type = oid_object_info(the_repository, &oid, NULL);
+	if (obj_type < 0) {
+		if (allow_missing) {
+			; /* no problem - missing objects are presumed to be of the right type */
+		} else {
+			die("entry '%s' object %s is unavailable", path, oid_to_hex(&oid));
+		}
+	} else {
+		if (obj_type != mode_type) {
+			/*
+			 * The object exists but is of the wrong type.
+			 * This is a problem regardless of allow_missing
+			 * because the new tree entry will never be correct.
+			 */
+			die("entry '%s' object %s is a %s but specified type was (%s)",
+				path, oid_to_hex(&oid), type_name(obj_type), type_name(mode_type));
+		}
+	}
+
+	append_to_tree(mode, &oid, path);
+	free(to_free);
+}
+
+int cmd_mktree(int ac, const char **av, const char *prefix)
+{
+	struct strbuf sb = STRBUF_INIT;
+	struct object_id oid;
+	int nul_term_line = 0;
+	int allow_missing = 0;
+	int is_batch_mode = 0;
+	int got_eof = 0;
+	strbuf_getline_fn getline_fn;
+
+	const struct option option[] = {
+		OPT_BOOL('z', NULL, &nul_term_line, N_("input is NUL terminated")),
+		OPT_SET_INT( 0 , "missing", &allow_missing, N_("allow missing objects"), 1),
+		OPT_SET_INT( 0 , "batch", &is_batch_mode, N_("allow creation of more than one tree"), 1),
+		OPT_END()
+	};
+
+	ac = parse_options(ac, av, prefix, option, mktree_usage, 0);
+	getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
+
+	while (!got_eof) {
+		while (1) {
+			if (getline_fn(&sb, stdin) == EOF) {
+				got_eof = 1;
+				break;
+			}
+			if (sb.buf[0] == '\0') {
+				/* empty lines denote tree boundaries in batch mode */
+				if (is_batch_mode)
+					break;
+				die("input format error: (blank line only valid in batch mode)");
+			}
+			mktree_line(sb.buf, nul_term_line, allow_missing);
+		}
+		if (is_batch_mode && got_eof && used < 1) {
+			/*
+			 * Execution gets here if the last tree entry is terminated with a
+			 * new-line.  The final new-line has been made optional to be
+			 * consistent with the original non-batch behaviour of mktree.
+			 */
+			; /* skip creating an empty tree */
+		} else {
+			write_tree(&oid);
+			puts(oid_to_hex(&oid));
+			fflush(stdout);
+		}
+		used=0; /* reset tree entry buffer for re-use in batch mode */
+	}
+	strbuf_release(&sb);
+	exit(0);
+}
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
new file mode 100644
index 0000000000..b1ea1a6aa1
--- /dev/null
+++ b/builtin/multi-pack-index.c
@@ -0,0 +1,62 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "parse-options.h"
+#include "midx.h"
+#include "trace2.h"
+
+static char const * const builtin_multi_pack_index_usage[] = {
+	N_("git multi-pack-index [--object-dir=<dir>] (write|verify|expire|repack --batch-size=<size>)"),
+	NULL
+};
+
+static struct opts_multi_pack_index {
+	const char *object_dir;
+	unsigned long batch_size;
+} opts;
+
+int cmd_multi_pack_index(int argc, const char **argv,
+			 const char *prefix)
+{
+	static struct option builtin_multi_pack_index_options[] = {
+		OPT_FILENAME(0, "object-dir", &opts.object_dir,
+		  N_("object directory containing set of packfile and pack-index pairs")),
+		OPT_MAGNITUDE(0, "batch-size", &opts.batch_size,
+		  N_("during repack, collect pack-files of smaller size into a batch that is larger than this size")),
+		OPT_END(),
+	};
+
+	git_config(git_default_config, NULL);
+
+	argc = parse_options(argc, argv, prefix,
+			     builtin_multi_pack_index_options,
+			     builtin_multi_pack_index_usage, 0);
+
+	if (!opts.object_dir)
+		opts.object_dir = get_object_directory();
+
+	if (argc == 0)
+		usage_with_options(builtin_multi_pack_index_usage,
+				   builtin_multi_pack_index_options);
+
+	if (argc > 1) {
+		die(_("too many arguments"));
+		return 1;
+	}
+
+	trace2_cmd_mode(argv[0]);
+
+	if (!strcmp(argv[0], "repack"))
+		return midx_repack(the_repository, opts.object_dir, (size_t)opts.batch_size);
+	if (opts.batch_size)
+		die(_("--batch-size option is only for 'repack' subcommand"));
+
+	if (!strcmp(argv[0], "write"))
+		return write_midx_file(opts.object_dir);
+	if (!strcmp(argv[0], "verify"))
+		return verify_midx_file(the_repository, opts.object_dir);
+	if (!strcmp(argv[0], "expire"))
+		return expire_midx_packs(the_repository, opts.object_dir);
+
+	die(_("unrecognized subcommand: %s"), argv[0]);
+}
diff --git a/builtin/mv.c b/builtin/mv.c
new file mode 100644
index 0000000000..be15ba7044
--- /dev/null
+++ b/builtin/mv.c
@@ -0,0 +1,304 @@
+/*
+ * "git mv" builtin command
+ *
+ * Copyright (C) 2006 Johannes Schindelin
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "config.h"
+#include "pathspec.h"
+#include "lockfile.h"
+#include "dir.h"
+#include "cache-tree.h"
+#include "string-list.h"
+#include "parse-options.h"
+#include "submodule.h"
+
+static const char * const builtin_mv_usage[] = {
+	N_("git mv [<options>] <source>... <destination>"),
+	NULL
+};
+
+#define DUP_BASENAME 1
+#define KEEP_TRAILING_SLASH 2
+
+static const char **internal_prefix_pathspec(const char *prefix,
+					     const char **pathspec,
+					     int count, unsigned flags)
+{
+	int i;
+	const char **result;
+	int prefixlen = prefix ? strlen(prefix) : 0;
+	ALLOC_ARRAY(result, count + 1);
+
+	/* Create an intermediate copy of the pathspec based on the flags */
+	for (i = 0; i < count; i++) {
+		int length = strlen(pathspec[i]);
+		int to_copy = length;
+		char *it;
+		while (!(flags & KEEP_TRAILING_SLASH) &&
+		       to_copy > 0 && is_dir_sep(pathspec[i][to_copy - 1]))
+			to_copy--;
+
+		it = xmemdupz(pathspec[i], to_copy);
+		if (flags & DUP_BASENAME) {
+			result[i] = xstrdup(basename(it));
+			free(it);
+		} else {
+			result[i] = it;
+		}
+	}
+	result[count] = NULL;
+
+	/* Prefix the pathspec and free the old intermediate strings */
+	for (i = 0; i < count; i++) {
+		const char *match = prefix_path(prefix, prefixlen, result[i]);
+		free((char *) result[i]);
+		result[i] = match;
+	}
+
+	return result;
+}
+
+static const char *add_slash(const char *path)
+{
+	size_t len = strlen(path);
+	if (path[len - 1] != '/') {
+		char *with_slash = xmalloc(st_add(len, 2));
+		memcpy(with_slash, path, len);
+		with_slash[len++] = '/';
+		with_slash[len] = 0;
+		return with_slash;
+	}
+	return path;
+}
+
+#define SUBMODULE_WITH_GITDIR ((const char *)1)
+
+static void prepare_move_submodule(const char *src, int first,
+				   const char **submodule_gitfile)
+{
+	struct strbuf submodule_dotgit = STRBUF_INIT;
+	if (!S_ISGITLINK(active_cache[first]->ce_mode))
+		die(_("Directory %s is in index and no submodule?"), src);
+	if (!is_staging_gitmodules_ok(&the_index))
+		die(_("Please stage your changes to .gitmodules or stash them to proceed"));
+	strbuf_addf(&submodule_dotgit, "%s/.git", src);
+	*submodule_gitfile = read_gitfile(submodule_dotgit.buf);
+	if (*submodule_gitfile)
+		*submodule_gitfile = xstrdup(*submodule_gitfile);
+	else
+		*submodule_gitfile = SUBMODULE_WITH_GITDIR;
+	strbuf_release(&submodule_dotgit);
+}
+
+static int index_range_of_same_dir(const char *src, int length,
+				   int *first_p, int *last_p)
+{
+	const char *src_w_slash = add_slash(src);
+	int first, last, len_w_slash = length + 1;
+
+	first = cache_name_pos(src_w_slash, len_w_slash);
+	if (first >= 0)
+		die(_("%.*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;
+	}
+	if (src_w_slash != src)
+		free((char *)src_w_slash);
+	*first_p = first;
+	*last_p = last;
+	return last - first;
+}
+
+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;
+}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
new file mode 100644
index 0000000000..c785fe16ba
--- /dev/null
+++ b/builtin/name-rev.c
@@ -0,0 +1,517 @@
+#include "builtin.h"
+#include "cache.h"
+#include "repository.h"
+#include "config.h"
+#include "commit.h"
+#include "tag.h"
+#include "refs.h"
+#include "parse-options.h"
+#include "sha1-lookup.h"
+#include "commit-slab.h"
+
+#define CUTOFF_DATE_SLOP 86400 /* one day */
+
+typedef struct rev_name {
+	const char *tip_name;
+	timestamp_t taggerdate;
+	int generation;
+	int distance;
+	int from_tag;
+} rev_name;
+
+define_commit_slab(commit_rev_name, struct rev_name *);
+
+static timestamp_t cutoff = TIME_MAX;
+static struct commit_rev_name rev_names;
+
+/* How many generations are maximally preferred over _one_ merge traversal? */
+#define MERGE_TRAVERSAL_WEIGHT 65535
+
+static struct rev_name *get_commit_rev_name(struct commit *commit)
+{
+	struct rev_name **slot = commit_rev_name_peek(&rev_names, commit);
+
+	return slot ? *slot : NULL;
+}
+
+static void set_commit_rev_name(struct commit *commit, struct rev_name *name)
+{
+	*commit_rev_name_at(&rev_names, commit) = name;
+}
+
+static int is_better_name(struct rev_name *name,
+			  timestamp_t taggerdate,
+			  int distance,
+			  int from_tag)
+{
+	/*
+	 * When comparing names based on tags, prefer names
+	 * based on the older tag, even if it is farther away.
+	 */
+	if (from_tag && name->from_tag)
+		return (name->taggerdate > taggerdate ||
+			(name->taggerdate == taggerdate &&
+			 name->distance > distance));
+
+	/*
+	 * We know that at least one of them is a non-tag at this point.
+	 * favor a tag over a non-tag.
+	 */
+	if (name->from_tag != from_tag)
+		return from_tag;
+
+	/*
+	 * We are now looking at two non-tags.  Tiebreak to favor
+	 * shorter hops.
+	 */
+	if (name->distance != distance)
+		return name->distance > distance;
+
+	/* ... or tiebreak to favor older date */
+	if (name->taggerdate != taggerdate)
+		return name->taggerdate > taggerdate;
+
+	/* keep the current one if we cannot decide */
+	return 0;
+}
+
+static void name_rev(struct commit *commit,
+		const char *tip_name, timestamp_t taggerdate,
+		int generation, int distance, int from_tag,
+		int deref)
+{
+	struct rev_name *name = get_commit_rev_name(commit);
+	struct commit_list *parents;
+	int parent_number = 1;
+	char *to_free = NULL;
+
+	parse_commit(commit);
+
+	if (commit->date < cutoff)
+		return;
+
+	if (deref) {
+		tip_name = to_free = xstrfmt("%s^0", tip_name);
+
+		if (generation)
+			die("generation: %d, but deref?", generation);
+	}
+
+	if (name == NULL) {
+		name = xmalloc(sizeof(rev_name));
+		set_commit_rev_name(commit, name);
+		goto copy_data;
+	} else if (is_better_name(name, taggerdate, distance, from_tag)) {
+copy_data:
+		name->tip_name = tip_name;
+		name->taggerdate = taggerdate;
+		name->generation = generation;
+		name->distance = distance;
+		name->from_tag = from_tag;
+	} else {
+		free(to_free);
+		return;
+	}
+
+	for (parents = commit->parents;
+			parents;
+			parents = parents->next, parent_number++) {
+		if (parent_number > 1) {
+			size_t len;
+			char *new_name;
+
+			strip_suffix(tip_name, "^0", &len);
+			if (generation > 0)
+				new_name = xstrfmt("%.*s~%d^%d", (int)len, tip_name,
+						   generation, parent_number);
+			else
+				new_name = xstrfmt("%.*s^%d", (int)len, tip_name,
+						   parent_number);
+
+			name_rev(parents->item, new_name, taggerdate, 0,
+				 distance + MERGE_TRAVERSAL_WEIGHT,
+				 from_tag, 0);
+		} else {
+			name_rev(parents->item, tip_name, taggerdate,
+				 generation + 1, distance + 1,
+				 from_tag, 0);
+		}
+	}
+}
+
+static int subpath_matches(const char *path, const char *filter)
+{
+	const char *subpath = path;
+
+	while (subpath) {
+		if (!wildmatch(filter, subpath, 0))
+			return subpath - path;
+		subpath = strchr(subpath, '/');
+		if (subpath)
+			subpath++;
+	}
+	return -1;
+}
+
+static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
+{
+	if (shorten_unambiguous)
+		refname = shorten_unambiguous_ref(refname, 0);
+	else if (starts_with(refname, "refs/heads/"))
+		refname = refname + 11;
+	else if (starts_with(refname, "refs/"))
+		refname = refname + 5;
+	return refname;
+}
+
+struct name_ref_data {
+	int tags_only;
+	int name_only;
+	struct string_list ref_filters;
+	struct string_list exclude_filters;
+};
+
+static struct tip_table {
+	struct tip_table_entry {
+		struct object_id oid;
+		const char *refname;
+	} *table;
+	int nr;
+	int alloc;
+	int sorted;
+} tip_table;
+
+static void add_to_tip_table(const struct object_id *oid, const char *refname,
+			     int shorten_unambiguous)
+{
+	refname = name_ref_abbrev(refname, shorten_unambiguous);
+
+	ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
+	oidcpy(&tip_table.table[tip_table.nr].oid, oid);
+	tip_table.table[tip_table.nr].refname = xstrdup(refname);
+	tip_table.nr++;
+	tip_table.sorted = 0;
+}
+
+static int tipcmp(const void *a_, const void *b_)
+{
+	const struct tip_table_entry *a = a_, *b = b_;
+	return oidcmp(&a->oid, &b->oid);
+}
+
+static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data)
+{
+	struct object *o = parse_object(the_repository, oid);
+	struct name_ref_data *data = cb_data;
+	int can_abbreviate_output = data->tags_only && data->name_only;
+	int deref = 0;
+	timestamp_t taggerdate = TIME_MAX;
+
+	if (data->tags_only && !starts_with(path, "refs/tags/"))
+		return 0;
+
+	if (data->exclude_filters.nr) {
+		struct string_list_item *item;
+
+		for_each_string_list_item(item, &data->exclude_filters) {
+			if (subpath_matches(path, item->string) >= 0)
+				return 0;
+		}
+	}
+
+	if (data->ref_filters.nr) {
+		struct string_list_item *item;
+		int matched = 0;
+
+		/* See if any of the patterns match. */
+		for_each_string_list_item(item, &data->ref_filters) {
+			/*
+			 * Check all patterns even after finding a match, so
+			 * that we can see if a match with a subpath exists.
+			 * When a user asked for 'refs/tags/v*' and 'v1.*',
+			 * both of which match, the user is showing her
+			 * willingness to accept a shortened output by having
+			 * the 'v1.*' in the acceptable refnames, so we
+			 * shouldn't stop when seeing 'refs/tags/v1.4' matches
+			 * 'refs/tags/v*'.  We should show it as 'v1.4'.
+			 */
+			switch (subpath_matches(path, item->string)) {
+			case -1: /* did not match */
+				break;
+			case 0: /* matched fully */
+				matched = 1;
+				break;
+			default: /* matched subpath */
+				matched = 1;
+				can_abbreviate_output = 1;
+				break;
+			}
+		}
+
+		/* If none of the patterns matched, stop now */
+		if (!matched)
+			return 0;
+	}
+
+	add_to_tip_table(oid, path, can_abbreviate_output);
+
+	while (o && o->type == OBJ_TAG) {
+		struct tag *t = (struct tag *) o;
+		if (!t->tagged)
+			break; /* broken repository */
+		o = parse_object(the_repository, &t->tagged->oid);
+		deref = 1;
+		taggerdate = t->date;
+	}
+	if (o && o->type == OBJ_COMMIT) {
+		struct commit *commit = (struct commit *)o;
+		int from_tag = starts_with(path, "refs/tags/");
+
+		if (taggerdate == TIME_MAX)
+			taggerdate = ((struct commit *)o)->date;
+		path = name_ref_abbrev(path, can_abbreviate_output);
+		name_rev(commit, xstrdup(path), taggerdate, 0, 0,
+			 from_tag, deref);
+	}
+	return 0;
+}
+
+static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
+{
+	struct tip_table_entry *table = table_;
+	return table[ix].oid.hash;
+}
+
+static const char *get_exact_ref_match(const struct object *o)
+{
+	int found;
+
+	if (!tip_table.table || !tip_table.nr)
+		return NULL;
+
+	if (!tip_table.sorted) {
+		QSORT(tip_table.table, tip_table.nr, tipcmp);
+		tip_table.sorted = 1;
+	}
+
+	found = sha1_pos(o->oid.hash, tip_table.table, tip_table.nr,
+			 nth_tip_table_ent);
+	if (0 <= found)
+		return tip_table.table[found].refname;
+	return NULL;
+}
+
+/* may return a constant string or use "buf" as scratch space */
+static const char *get_rev_name(const struct object *o, struct strbuf *buf)
+{
+	struct rev_name *n;
+	struct commit *c;
+
+	if (o->type != OBJ_COMMIT)
+		return get_exact_ref_match(o);
+	c = (struct commit *) o;
+	n = get_commit_rev_name(c);
+	if (!n)
+		return NULL;
+
+	if (!n->generation)
+		return n->tip_name;
+	else {
+		int len = strlen(n->tip_name);
+		if (len > 2 && !strcmp(n->tip_name + len - 2, "^0"))
+			len -= 2;
+		strbuf_reset(buf);
+		strbuf_addf(buf, "%.*s~%d", len, n->tip_name, n->generation);
+		return buf->buf;
+	}
+}
+
+static void show_name(const struct object *obj,
+		      const char *caller_name,
+		      int always, int allow_undefined, int name_only)
+{
+	const char *name;
+	const struct object_id *oid = &obj->oid;
+	struct strbuf buf = STRBUF_INIT;
+
+	if (!name_only)
+		printf("%s ", caller_name ? caller_name : oid_to_hex(oid));
+	name = get_rev_name(obj, &buf);
+	if (name)
+		printf("%s\n", name);
+	else if (allow_undefined)
+		printf("undefined\n");
+	else if (always)
+		printf("%s\n", find_unique_abbrev(oid, DEFAULT_ABBREV));
+	else
+		die("cannot describe '%s'", oid_to_hex(oid));
+	strbuf_release(&buf);
+}
+
+static char const * const name_rev_usage[] = {
+	N_("git name-rev [<options>] <commit>..."),
+	N_("git name-rev [<options>] --all"),
+	N_("git name-rev [<options>] --stdin"),
+	NULL
+};
+
+static void name_rev_line(char *p, struct name_ref_data *data)
+{
+	struct strbuf buf = STRBUF_INIT;
+	int counter = 0;
+	char *p_start;
+	const unsigned hexsz = the_hash_algo->hexsz;
+
+	for (p_start = p; *p; p++) {
+#define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
+		if (!ishex(*p))
+			counter = 0;
+		else if (++counter == hexsz &&
+			 !ishex(*(p+1))) {
+			struct object_id oid;
+			const char *name = NULL;
+			char c = *(p+1);
+			int p_len = p - p_start + 1;
+
+			counter = 0;
+
+			*(p+1) = 0;
+			if (!get_oid(p - (hexsz - 1), &oid)) {
+				struct object *o =
+					lookup_object(the_repository, &oid);
+				if (o)
+					name = get_rev_name(o, &buf);
+			}
+			*(p+1) = c;
+
+			if (!name)
+				continue;
+
+			if (data->name_only)
+				printf("%.*s%s", p_len - hexsz, p_start, name);
+			else
+				printf("%.*s (%s)", p_len, p_start, name);
+			p_start = p + 1;
+		}
+	}
+
+	/* flush */
+	if (p_start != p)
+		fwrite(p_start, p - p_start, 1, stdout);
+
+	strbuf_release(&buf);
+}
+
+int cmd_name_rev(int argc, const char **argv, const char *prefix)
+{
+	struct object_array revs = OBJECT_ARRAY_INIT;
+	int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
+	struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
+	struct option opts[] = {
+		OPT_BOOL(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")),
+		OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
+		OPT_STRING_LIST(0, "refs", &data.ref_filters, N_("pattern"),
+				   N_("only use refs matching <pattern>")),
+		OPT_STRING_LIST(0, "exclude", &data.exclude_filters, N_("pattern"),
+				   N_("ignore refs matching <pattern>")),
+		OPT_GROUP(""),
+		OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
+		OPT_BOOL(0, "stdin", &transform_stdin, N_("read from stdin")),
+		OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
+		OPT_BOOL(0, "always",     &always,
+			   N_("show abbreviated commit object as fallback")),
+		{
+			/* A Hidden OPT_BOOL */
+			OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL,
+			N_("dereference tags in the input (internal use)"),
+			PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1,
+		},
+		OPT_END(),
+	};
+
+	init_commit_rev_name(&rev_names);
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
+	if (all + transform_stdin + !!argc > 1) {
+		error("Specify either a list, or --all, not both!");
+		usage_with_options(name_rev_usage, opts);
+	}
+	if (all || transform_stdin)
+		cutoff = 0;
+
+	for (; argc; argc--, argv++) {
+		struct object_id oid;
+		struct object *object;
+		struct commit *commit;
+
+		if (get_oid(*argv, &oid)) {
+			fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
+					*argv);
+			continue;
+		}
+
+		commit = NULL;
+		object = parse_object(the_repository, &oid);
+		if (object) {
+			struct object *peeled = deref_tag(the_repository,
+							  object, *argv, 0);
+			if (peeled && peeled->type == OBJ_COMMIT)
+				commit = (struct commit *)peeled;
+		}
+
+		if (!object) {
+			fprintf(stderr, "Could not get object for %s. Skipping.\n",
+					*argv);
+			continue;
+		}
+
+		if (commit) {
+			if (cutoff > commit->date)
+				cutoff = commit->date;
+		}
+
+		if (peel_tag) {
+			if (!commit) {
+				fprintf(stderr, "Could not get commit for %s. Skipping.\n",
+					*argv);
+				continue;
+			}
+			object = (struct object *)commit;
+		}
+		add_object_array(object, *argv, &revs);
+	}
+
+	if (cutoff)
+		cutoff = cutoff - CUTOFF_DATE_SLOP;
+	for_each_ref(name_ref, &data);
+
+	if (transform_stdin) {
+		char buffer[2048];
+
+		while (!feof(stdin)) {
+			char *p = fgets(buffer, sizeof(buffer), stdin);
+			if (!p)
+				break;
+			name_rev_line(p, &data);
+		}
+	} else if (all) {
+		int i, max;
+
+		max = get_max_object_index();
+		for (i = 0; i < max; i++) {
+			struct object *obj = get_indexed_object(i);
+			if (!obj || obj->type != OBJ_COMMIT)
+				continue;
+			show_name(obj, NULL,
+				  always, allow_undefined, data.name_only);
+		}
+	} else {
+		int i;
+		for (i = 0; i < revs.nr; i++)
+			show_name(revs.objects[i].item, revs.objects[i].name,
+				  always, allow_undefined, data.name_only);
+	}
+
+	UNLEAK(revs);
+	return 0;
+}
diff --git a/builtin/notes.c b/builtin/notes.c
new file mode 100644
index 0000000000..02e97f55c5
--- /dev/null
+++ b/builtin/notes.c
@@ -0,0 +1,1039 @@
+/*
+ * Builtin "git notes"
+ *
+ * Copyright (c) 2010 Johan Herland <johan@herland.net>
+ *
+ * Based on git-notes.sh by Johannes Schindelin,
+ * and builtin/tag.c by Kristian Høgsberg and Carlos Rica.
+ */
+
+#include "cache.h"
+#include "config.h"
+#include "builtin.h"
+#include "notes.h"
+#include "object-store.h"
+#include "repository.h"
+#include "blob.h"
+#include "pretty.h"
+#include "refs.h"
+#include "exec-cmd.h"
+#include "run-command.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "notes-merge.h"
+#include "notes-utils.h"
+#include "worktree.h"
+
+static const char * const git_notes_usage[] = {
+	N_("git notes [--ref <notes-ref>] [list [<object>]]"),
+	N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+	N_("git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"),
+	N_("git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+	N_("git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"),
+	N_("git notes [--ref <notes-ref>] show [<object>]"),
+	N_("git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"),
+	N_("git notes merge --commit [-v | -q]"),
+	N_("git notes merge --abort [-v | -q]"),
+	N_("git notes [--ref <notes-ref>] remove [<object>...]"),
+	N_("git notes [--ref <notes-ref>] prune [-n] [-v]"),
+	N_("git notes [--ref <notes-ref>] get-ref"),
+	NULL
+};
+
+static const char * const git_notes_list_usage[] = {
+	N_("git notes [list [<object>]]"),
+	NULL
+};
+
+static const char * const git_notes_add_usage[] = {
+	N_("git notes add [<options>] [<object>]"),
+	NULL
+};
+
+static const char * const git_notes_copy_usage[] = {
+	N_("git notes copy [<options>] <from-object> <to-object>"),
+	N_("git notes copy --stdin [<from-object> <to-object>]..."),
+	NULL
+};
+
+static const char * const git_notes_append_usage[] = {
+	N_("git notes append [<options>] [<object>]"),
+	NULL
+};
+
+static const char * const git_notes_edit_usage[] = {
+	N_("git notes edit [<object>]"),
+	NULL
+};
+
+static const char * const git_notes_show_usage[] = {
+	N_("git notes show [<object>]"),
+	NULL
+};
+
+static const char * const git_notes_merge_usage[] = {
+	N_("git notes merge [<options>] <notes-ref>"),
+	N_("git notes merge --commit [<options>]"),
+	N_("git notes merge --abort [<options>]"),
+	NULL
+};
+
+static const char * const git_notes_remove_usage[] = {
+	N_("git notes remove [<object>]"),
+	NULL
+};
+
+static const char * const git_notes_prune_usage[] = {
+	N_("git notes prune [<options>]"),
+	NULL
+};
+
+static const char * const git_notes_get_ref_usage[] = {
+	N_("git notes get-ref"),
+	NULL
+};
+
+static const char note_template[] =
+	N_("Write/edit the notes for the following object:");
+
+struct note_data {
+	int given;
+	int use_editor;
+	char *edit_path;
+	struct strbuf buf;
+};
+
+static void free_note_data(struct note_data *d)
+{
+	if (d->edit_path) {
+		unlink_or_warn(d->edit_path);
+		free(d->edit_path);
+	}
+	strbuf_release(&d->buf);
+}
+
+static int list_each_note(const struct object_id *object_oid,
+		const struct object_id *note_oid, char *note_path,
+		void *cb_data)
+{
+	printf("%s %s\n", oid_to_hex(note_oid), oid_to_hex(object_oid));
+	return 0;
+}
+
+static void copy_obj_to_fd(int fd, const struct object_id *oid)
+{
+	unsigned long size;
+	enum object_type type;
+	char *buf = read_object_file(oid, &type, &size);
+	if (buf) {
+		if (size)
+			write_or_die(fd, buf, size);
+		free(buf);
+	}
+}
+
+static void write_commented_object(int fd, const struct object_id *object)
+{
+	const char *show_args[5] =
+		{"show", "--stat", "--no-notes", oid_to_hex(object), NULL};
+	struct child_process show = CHILD_PROCESS_INIT;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf cbuf = STRBUF_INIT;
+
+	/* Invoke "git show --stat --no-notes $object" */
+	show.argv = show_args;
+	show.no_stdin = 1;
+	show.out = -1;
+	show.err = 0;
+	show.git_cmd = 1;
+	if (start_command(&show))
+		die(_("unable to start 'show' for object '%s'"),
+		    oid_to_hex(object));
+
+	if (strbuf_read(&buf, show.out, 0) < 0)
+		die_errno(_("could not read 'show' output"));
+	strbuf_add_commented_lines(&cbuf, buf.buf, buf.len);
+	write_or_die(fd, cbuf.buf, cbuf.len);
+
+	strbuf_release(&cbuf);
+	strbuf_release(&buf);
+
+	if (finish_command(&show))
+		die(_("failed to finish 'show' for object '%s'"),
+		    oid_to_hex(object));
+}
+
+static void prepare_note_data(const struct object_id *object, struct note_data *d,
+		const struct object_id *old_note)
+{
+	if (d->use_editor || !d->given) {
+		int fd;
+		struct strbuf buf = STRBUF_INIT;
+
+		/* write the template message before editing: */
+		d->edit_path = git_pathdup("NOTES_EDITMSG");
+		fd = open(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+		if (fd < 0)
+			die_errno(_("could not create file '%s'"), d->edit_path);
+
+		if (d->given)
+			write_or_die(fd, d->buf.buf, d->buf.len);
+		else if (old_note)
+			copy_obj_to_fd(fd, old_note);
+
+		strbuf_addch(&buf, '\n');
+		strbuf_add_commented_lines(&buf, "\n", strlen("\n"));
+		strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)));
+		strbuf_addch(&buf, '\n');
+		write_or_die(fd, buf.buf, buf.len);
+
+		write_commented_object(fd, object);
+
+		close(fd);
+		strbuf_release(&buf);
+		strbuf_reset(&d->buf);
+
+		if (launch_editor(d->edit_path, &d->buf, NULL)) {
+			die(_("please supply the note contents using either -m or -F option"));
+		}
+		strbuf_stripspace(&d->buf, 1);
+	}
+}
+
+static void write_note_data(struct note_data *d, struct object_id *oid)
+{
+	if (write_object_file(d->buf.buf, d->buf.len, blob_type, oid)) {
+		error(_("unable to write note object"));
+		if (d->edit_path)
+			error(_("the note contents have been left in %s"),
+				d->edit_path);
+		exit(128);
+	}
+}
+
+static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
+{
+	struct note_data *d = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	strbuf_grow(&d->buf, strlen(arg) + 2);
+	if (d->buf.len)
+		strbuf_addch(&d->buf, '\n');
+	strbuf_addstr(&d->buf, arg);
+	strbuf_stripspace(&d->buf, 0);
+
+	d->given = 1;
+	return 0;
+}
+
+static int parse_file_arg(const struct option *opt, const char *arg, int unset)
+{
+	struct note_data *d = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (d->buf.len)
+		strbuf_addch(&d->buf, '\n');
+	if (!strcmp(arg, "-")) {
+		if (strbuf_read(&d->buf, 0, 1024) < 0)
+			die_errno(_("cannot read '%s'"), arg);
+	} else if (strbuf_read_file(&d->buf, arg, 1024) < 0)
+		die_errno(_("could not open or read '%s'"), arg);
+	strbuf_stripspace(&d->buf, 0);
+
+	d->given = 1;
+	return 0;
+}
+
+static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
+{
+	struct note_data *d = opt->value;
+	char *buf;
+	struct object_id object;
+	enum object_type type;
+	unsigned long len;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (d->buf.len)
+		strbuf_addch(&d->buf, '\n');
+
+	if (get_oid(arg, &object))
+		die(_("failed to resolve '%s' as a valid ref."), arg);
+	if (!(buf = read_object_file(&object, &type, &len)))
+		die(_("failed to read object '%s'."), arg);
+	if (type != OBJ_BLOB) {
+		free(buf);
+		die(_("cannot read note data from non-blob object '%s'."), arg);
+	}
+	strbuf_add(&d->buf, buf, len);
+	free(buf);
+
+	d->given = 1;
+	return 0;
+}
+
+static int parse_reedit_arg(const struct option *opt, const char *arg, int unset)
+{
+	struct note_data *d = opt->value;
+	BUG_ON_OPT_NEG(unset);
+	d->use_editor = 1;
+	return parse_reuse_arg(opt, arg, unset);
+}
+
+static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct notes_rewrite_cfg *c = NULL;
+	struct notes_tree *t = NULL;
+	int ret = 0;
+	const char *msg = "Notes added by 'git notes copy'";
+
+	if (rewrite_cmd) {
+		c = init_copy_notes_for_rewrite(rewrite_cmd);
+		if (!c)
+			return 0;
+	} else {
+		init_notes(NULL, NULL, NULL, NOTES_INIT_WRITABLE);
+		t = &default_notes_tree;
+	}
+
+	while (strbuf_getline_lf(&buf, stdin) != EOF) {
+		struct object_id from_obj, to_obj;
+		struct strbuf **split;
+		int err;
+
+		split = strbuf_split(&buf, ' ');
+		if (!split[0] || !split[1])
+			die(_("malformed input line: '%s'."), buf.buf);
+		strbuf_rtrim(split[0]);
+		strbuf_rtrim(split[1]);
+		if (get_oid(split[0]->buf, &from_obj))
+			die(_("failed to resolve '%s' as a valid ref."), split[0]->buf);
+		if (get_oid(split[1]->buf, &to_obj))
+			die(_("failed to resolve '%s' as a valid ref."), split[1]->buf);
+
+		if (rewrite_cmd)
+			err = copy_note_for_rewrite(c, &from_obj, &to_obj);
+		else
+			err = copy_note(t, &from_obj, &to_obj, force,
+					combine_notes_overwrite);
+
+		if (err) {
+			error(_("failed to copy notes from '%s' to '%s'"),
+			      split[0]->buf, split[1]->buf);
+			ret = 1;
+		}
+
+		strbuf_list_free(split);
+	}
+
+	if (!rewrite_cmd) {
+		commit_notes(the_repository, t, msg);
+		free_notes(t);
+	} else {
+		finish_copy_notes_for_rewrite(the_repository, c, msg);
+	}
+	strbuf_release(&buf);
+	return ret;
+}
+
+static struct notes_tree *init_notes_check(const char *subcommand,
+					   int flags)
+{
+	struct notes_tree *t;
+	const char *ref;
+	init_notes(NULL, NULL, NULL, flags);
+	t = &default_notes_tree;
+
+	ref = (flags & NOTES_INIT_WRITABLE) ? t->update_ref : t->ref;
+	if (!starts_with(ref, "refs/notes/"))
+		/*
+		 * TRANSLATORS: the first %s will be replaced by a git
+		 * notes command: 'add', 'merge', 'remove', etc.
+		 */
+		die(_("refusing to %s notes in %s (outside of refs/notes/)"),
+		    subcommand, ref);
+	return t;
+}
+
+static int list(int argc, const char **argv, const char *prefix)
+{
+	struct notes_tree *t;
+	struct object_id object;
+	const struct object_id *note;
+	int retval = -1;
+	struct option options[] = {
+		OPT_END()
+	};
+
+	if (argc)
+		argc = parse_options(argc, argv, prefix, options,
+				     git_notes_list_usage, 0);
+
+	if (1 < argc) {
+		error(_("too many parameters"));
+		usage_with_options(git_notes_list_usage, options);
+	}
+
+	t = init_notes_check("list", 0);
+	if (argc) {
+		if (get_oid(argv[0], &object))
+			die(_("failed to resolve '%s' as a valid ref."), argv[0]);
+		note = get_note(t, &object);
+		if (note) {
+			puts(oid_to_hex(note));
+			retval = 0;
+		} else
+			retval = error(_("no note found for object %s."),
+				       oid_to_hex(&object));
+	} else
+		retval = for_each_note(t, 0, list_each_note, NULL);
+
+	free_notes(t);
+	return retval;
+}
+
+static int append_edit(int argc, const char **argv, const char *prefix);
+
+static int add(int argc, const char **argv, const char *prefix)
+{
+	int force = 0, allow_empty = 0;
+	const char *object_ref;
+	struct notes_tree *t;
+	struct object_id object, new_note;
+	const struct object_id *note;
+	struct note_data d = { 0, 0, NULL, STRBUF_INIT };
+	struct option options[] = {
+		{ OPTION_CALLBACK, 'm', "message", &d, N_("message"),
+			N_("note contents as a string"), PARSE_OPT_NONEG,
+			parse_msg_arg},
+		{ OPTION_CALLBACK, 'F', "file", &d, N_("file"),
+			N_("note contents in a file"), PARSE_OPT_NONEG,
+			parse_file_arg},
+		{ OPTION_CALLBACK, 'c', "reedit-message", &d, N_("object"),
+			N_("reuse and edit specified note object"), PARSE_OPT_NONEG,
+			parse_reedit_arg},
+		{ OPTION_CALLBACK, 'C', "reuse-message", &d, N_("object"),
+			N_("reuse specified note object"), PARSE_OPT_NONEG,
+			parse_reuse_arg},
+		OPT_BOOL(0, "allow-empty", &allow_empty,
+			N_("allow storing empty note")),
+		OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options, git_notes_add_usage,
+			     PARSE_OPT_KEEP_ARGV0);
+
+	if (2 < argc) {
+		error(_("too many parameters"));
+		usage_with_options(git_notes_add_usage, options);
+	}
+
+	object_ref = argc > 1 ? argv[1] : "HEAD";
+
+	if (get_oid(object_ref, &object))
+		die(_("failed to resolve '%s' as a valid ref."), object_ref);
+
+	t = init_notes_check("add", NOTES_INIT_WRITABLE);
+	note = get_note(t, &object);
+
+	if (note) {
+		if (!force) {
+			free_notes(t);
+			if (d.given) {
+				free_note_data(&d);
+				return error(_("Cannot add notes. "
+					"Found existing notes for object %s. "
+					"Use '-f' to overwrite existing notes"),
+					oid_to_hex(&object));
+			}
+			/*
+			 * Redirect to "edit" subcommand.
+			 *
+			 * We only end up here if none of -m/-F/-c/-C or -f are
+			 * given. The original args are therefore still in
+			 * argv[0-1].
+			 */
+			argv[0] = "edit";
+			return append_edit(argc, argv, prefix);
+		}
+		fprintf(stderr, _("Overwriting existing notes for object %s\n"),
+			oid_to_hex(&object));
+	}
+
+	prepare_note_data(&object, &d, note);
+	if (d.buf.len || allow_empty) {
+		write_note_data(&d, &new_note);
+		if (add_note(t, &object, &new_note, combine_notes_overwrite))
+			BUG("combine_notes_overwrite failed");
+		commit_notes(the_repository, t,
+			     "Notes added by 'git notes add'");
+	} else {
+		fprintf(stderr, _("Removing note for object %s\n"),
+			oid_to_hex(&object));
+		remove_note(t, object.hash);
+		commit_notes(the_repository, t,
+			     "Notes removed by 'git notes add'");
+	}
+
+	free_note_data(&d);
+	free_notes(t);
+	return 0;
+}
+
+static int copy(int argc, const char **argv, const char *prefix)
+{
+	int retval = 0, force = 0, from_stdin = 0;
+	const struct object_id *from_note, *note;
+	const char *object_ref;
+	struct object_id object, from_obj;
+	struct notes_tree *t;
+	const char *rewrite_cmd = NULL;
+	struct option options[] = {
+		OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL(0, "stdin", &from_stdin, N_("read objects from stdin")),
+		OPT_STRING(0, "for-rewrite", &rewrite_cmd, N_("command"),
+			   N_("load rewriting config for <command> (implies "
+			      "--stdin)")),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options, git_notes_copy_usage,
+			     0);
+
+	if (from_stdin || rewrite_cmd) {
+		if (argc) {
+			error(_("too many parameters"));
+			usage_with_options(git_notes_copy_usage, options);
+		} else {
+			return notes_copy_from_stdin(force, rewrite_cmd);
+		}
+	}
+
+	if (argc < 2) {
+		error(_("too few parameters"));
+		usage_with_options(git_notes_copy_usage, options);
+	}
+	if (2 < argc) {
+		error(_("too many parameters"));
+		usage_with_options(git_notes_copy_usage, options);
+	}
+
+	if (get_oid(argv[0], &from_obj))
+		die(_("failed to resolve '%s' as a valid ref."), argv[0]);
+
+	object_ref = 1 < argc ? argv[1] : "HEAD";
+
+	if (get_oid(object_ref, &object))
+		die(_("failed to resolve '%s' as a valid ref."), object_ref);
+
+	t = init_notes_check("copy", NOTES_INIT_WRITABLE);
+	note = get_note(t, &object);
+
+	if (note) {
+		if (!force) {
+			retval = error(_("Cannot copy notes. Found existing "
+				       "notes for object %s. Use '-f' to "
+				       "overwrite existing notes"),
+				       oid_to_hex(&object));
+			goto out;
+		}
+		fprintf(stderr, _("Overwriting existing notes for object %s\n"),
+			oid_to_hex(&object));
+	}
+
+	from_note = get_note(t, &from_obj);
+	if (!from_note) {
+		retval = error(_("missing notes on source object %s. Cannot "
+			       "copy."), oid_to_hex(&from_obj));
+		goto out;
+	}
+
+	if (add_note(t, &object, from_note, combine_notes_overwrite))
+		BUG("combine_notes_overwrite failed");
+	commit_notes(the_repository, t,
+		     "Notes added by 'git notes copy'");
+out:
+	free_notes(t);
+	return retval;
+}
+
+static int append_edit(int argc, const char **argv, const char *prefix)
+{
+	int allow_empty = 0;
+	const char *object_ref;
+	struct notes_tree *t;
+	struct object_id object, new_note;
+	const struct object_id *note;
+	char *logmsg;
+	const char * const *usage;
+	struct note_data d = { 0, 0, NULL, STRBUF_INIT };
+	struct option options[] = {
+		{ OPTION_CALLBACK, 'm', "message", &d, N_("message"),
+			N_("note contents as a string"), PARSE_OPT_NONEG,
+			parse_msg_arg},
+		{ OPTION_CALLBACK, 'F', "file", &d, N_("file"),
+			N_("note contents in a file"), PARSE_OPT_NONEG,
+			parse_file_arg},
+		{ OPTION_CALLBACK, 'c', "reedit-message", &d, N_("object"),
+			N_("reuse and edit specified note object"), PARSE_OPT_NONEG,
+			parse_reedit_arg},
+		{ OPTION_CALLBACK, 'C', "reuse-message", &d, N_("object"),
+			N_("reuse specified note object"), PARSE_OPT_NONEG,
+			parse_reuse_arg},
+		OPT_BOOL(0, "allow-empty", &allow_empty,
+			N_("allow storing empty note")),
+		OPT_END()
+	};
+	int edit = !strcmp(argv[0], "edit");
+
+	usage = edit ? git_notes_edit_usage : git_notes_append_usage;
+	argc = parse_options(argc, argv, prefix, options, usage,
+			     PARSE_OPT_KEEP_ARGV0);
+
+	if (2 < argc) {
+		error(_("too many parameters"));
+		usage_with_options(usage, options);
+	}
+
+	if (d.given && edit)
+		fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated "
+			"for the 'edit' subcommand.\n"
+			"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"));
+
+	object_ref = 1 < argc ? argv[1] : "HEAD";
+
+	if (get_oid(object_ref, &object))
+		die(_("failed to resolve '%s' as a valid ref."), object_ref);
+
+	t = init_notes_check(argv[0], NOTES_INIT_WRITABLE);
+	note = get_note(t, &object);
+
+	prepare_note_data(&object, &d, edit && note ? note : NULL);
+
+	if (note && !edit) {
+		/* Append buf to previous note contents */
+		unsigned long size;
+		enum object_type type;
+		char *prev_buf = read_object_file(note, &type, &size);
+
+		strbuf_grow(&d.buf, size + 1);
+		if (d.buf.len && prev_buf && size)
+			strbuf_insert(&d.buf, 0, "\n", 1);
+		if (prev_buf && size)
+			strbuf_insert(&d.buf, 0, prev_buf, size);
+		free(prev_buf);
+	}
+
+	if (d.buf.len || allow_empty) {
+		write_note_data(&d, &new_note);
+		if (add_note(t, &object, &new_note, combine_notes_overwrite))
+			BUG("combine_notes_overwrite failed");
+		logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]);
+	} else {
+		fprintf(stderr, _("Removing note for object %s\n"),
+			oid_to_hex(&object));
+		remove_note(t, object.hash);
+		logmsg = xstrfmt("Notes removed by 'git notes %s'", argv[0]);
+	}
+	commit_notes(the_repository, t, logmsg);
+
+	free(logmsg);
+	free_note_data(&d);
+	free_notes(t);
+	return 0;
+}
+
+static int show(int argc, const char **argv, const char *prefix)
+{
+	const char *object_ref;
+	struct notes_tree *t;
+	struct object_id object;
+	const struct object_id *note;
+	int retval;
+	struct option options[] = {
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options, git_notes_show_usage,
+			     0);
+
+	if (1 < argc) {
+		error(_("too many parameters"));
+		usage_with_options(git_notes_show_usage, options);
+	}
+
+	object_ref = argc ? argv[0] : "HEAD";
+
+	if (get_oid(object_ref, &object))
+		die(_("failed to resolve '%s' as a valid ref."), object_ref);
+
+	t = init_notes_check("show", 0);
+	note = get_note(t, &object);
+
+	if (!note)
+		retval = error(_("no note found for object %s."),
+			       oid_to_hex(&object));
+	else {
+		const char *show_args[3] = {"show", oid_to_hex(note), NULL};
+		retval = execv_git_cmd(show_args);
+	}
+	free_notes(t);
+	return retval;
+}
+
+static int merge_abort(struct notes_merge_options *o)
+{
+	int ret = 0;
+
+	/*
+	 * Remove .git/NOTES_MERGE_PARTIAL and .git/NOTES_MERGE_REF, and call
+	 * notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE.
+	 */
+
+	if (delete_ref(NULL, "NOTES_MERGE_PARTIAL", NULL, 0))
+		ret += error(_("failed to delete ref NOTES_MERGE_PARTIAL"));
+	if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF))
+		ret += error(_("failed to delete ref NOTES_MERGE_REF"));
+	if (notes_merge_abort(o))
+		ret += error(_("failed to remove 'git notes merge' worktree"));
+	return ret;
+}
+
+static int merge_commit(struct notes_merge_options *o)
+{
+	struct strbuf msg = STRBUF_INIT;
+	struct object_id oid, parent_oid;
+	struct notes_tree *t;
+	struct commit *partial;
+	struct pretty_print_context pretty_ctx;
+	void *local_ref_to_free;
+	int ret;
+
+	/*
+	 * Read partial merge result from .git/NOTES_MERGE_PARTIAL,
+	 * and target notes ref from .git/NOTES_MERGE_REF.
+	 */
+
+	if (get_oid("NOTES_MERGE_PARTIAL", &oid))
+		die(_("failed to read ref NOTES_MERGE_PARTIAL"));
+	else if (!(partial = lookup_commit_reference(the_repository, &oid)))
+		die(_("could not find commit from NOTES_MERGE_PARTIAL."));
+	else if (parse_commit(partial))
+		die(_("could not parse commit from NOTES_MERGE_PARTIAL."));
+
+	if (partial->parents)
+		oidcpy(&parent_oid, &partial->parents->item->object.oid);
+	else
+		oidclr(&parent_oid);
+
+	t = xcalloc(1, sizeof(struct notes_tree));
+	init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
+
+	o->local_ref = local_ref_to_free =
+		resolve_refdup("NOTES_MERGE_REF", 0, &oid, NULL);
+	if (!o->local_ref)
+		die(_("failed to resolve NOTES_MERGE_REF"));
+
+	if (notes_merge_commit(o, t, partial, &oid))
+		die(_("failed to finalize notes merge"));
+
+	/* Reuse existing commit message in reflog message */
+	memset(&pretty_ctx, 0, sizeof(pretty_ctx));
+	format_commit_message(partial, "%s", &msg, &pretty_ctx);
+	strbuf_trim(&msg);
+	strbuf_insert(&msg, 0, "notes: ", 7);
+	update_ref(msg.buf, o->local_ref, &oid,
+		   is_null_oid(&parent_oid) ? NULL : &parent_oid,
+		   0, UPDATE_REFS_DIE_ON_ERR);
+
+	free_notes(t);
+	strbuf_release(&msg);
+	ret = merge_abort(o);
+	free(local_ref_to_free);
+	return ret;
+}
+
+static int git_config_get_notes_strategy(const char *key,
+					 enum notes_merge_strategy *strategy)
+{
+	char *value;
+
+	if (git_config_get_string(key, &value))
+		return 1;
+	if (parse_notes_merge_strategy(value, strategy))
+		git_die_config(key, _("unknown notes merge strategy %s"), value);
+
+	free(value);
+	return 0;
+}
+
+static int merge(int argc, const char **argv, const char *prefix)
+{
+	struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT;
+	struct object_id result_oid;
+	struct notes_tree *t;
+	struct notes_merge_options o;
+	int do_merge = 0, do_commit = 0, do_abort = 0;
+	int verbosity = 0, result;
+	const char *strategy = NULL;
+	struct option options[] = {
+		OPT_GROUP(N_("General options")),
+		OPT__VERBOSITY(&verbosity),
+		OPT_GROUP(N_("Merge options")),
+		OPT_STRING('s', "strategy", &strategy, N_("strategy"),
+			   N_("resolve notes conflicts using the given strategy "
+			      "(manual/ours/theirs/union/cat_sort_uniq)")),
+		OPT_GROUP(N_("Committing unmerged notes")),
+		OPT_SET_INT_F(0, "commit", &do_commit,
+			      N_("finalize notes merge by committing unmerged notes"),
+			      1, PARSE_OPT_NONEG),
+		OPT_GROUP(N_("Aborting notes merge resolution")),
+		OPT_SET_INT_F(0, "abort", &do_abort,
+			      N_("abort notes merge"),
+			      1, PARSE_OPT_NONEG),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_notes_merge_usage, 0);
+
+	if (strategy || do_commit + do_abort == 0)
+		do_merge = 1;
+	if (do_merge + do_commit + do_abort != 1) {
+		error(_("cannot mix --commit, --abort or -s/--strategy"));
+		usage_with_options(git_notes_merge_usage, options);
+	}
+
+	if (do_merge && argc != 1) {
+		error(_("must specify a notes ref to merge"));
+		usage_with_options(git_notes_merge_usage, options);
+	} else if (!do_merge && argc) {
+		error(_("too many parameters"));
+		usage_with_options(git_notes_merge_usage, options);
+	}
+
+	init_notes_merge_options(the_repository, &o);
+	o.verbosity = verbosity + NOTES_MERGE_VERBOSITY_DEFAULT;
+
+	if (do_abort)
+		return merge_abort(&o);
+	if (do_commit)
+		return merge_commit(&o);
+
+	o.local_ref = default_notes_ref();
+	strbuf_addstr(&remote_ref, argv[0]);
+	expand_loose_notes_ref(&remote_ref);
+	o.remote_ref = remote_ref.buf;
+
+	t = init_notes_check("merge", NOTES_INIT_WRITABLE);
+
+	if (strategy) {
+		if (parse_notes_merge_strategy(strategy, &o.strategy)) {
+			error(_("unknown -s/--strategy: %s"), strategy);
+			usage_with_options(git_notes_merge_usage, options);
+		}
+	} else {
+		struct strbuf merge_key = STRBUF_INIT;
+		const char *short_ref = NULL;
+
+		if (!skip_prefix(o.local_ref, "refs/notes/", &short_ref))
+			BUG("local ref %s is outside of refs/notes/",
+			    o.local_ref);
+
+		strbuf_addf(&merge_key, "notes.%s.mergeStrategy", short_ref);
+
+		if (git_config_get_notes_strategy(merge_key.buf, &o.strategy))
+			git_config_get_notes_strategy("notes.mergeStrategy", &o.strategy);
+
+		strbuf_release(&merge_key);
+	}
+
+	strbuf_addf(&msg, "notes: Merged notes from %s into %s",
+		    remote_ref.buf, default_notes_ref());
+	strbuf_add(&(o.commit_msg), msg.buf + 7, msg.len - 7); /* skip "notes: " */
+
+	result = notes_merge(&o, t, &result_oid);
+
+	if (result >= 0) /* Merge resulted (trivially) in result_oid */
+		/* Update default notes ref with new commit */
+		update_ref(msg.buf, default_notes_ref(), &result_oid, NULL, 0,
+			   UPDATE_REFS_DIE_ON_ERR);
+	else { /* Merge has unresolved conflicts */
+		const struct worktree *wt;
+		/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
+		update_ref(msg.buf, "NOTES_MERGE_PARTIAL", &result_oid, NULL,
+			   0, UPDATE_REFS_DIE_ON_ERR);
+		/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
+		wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
+		if (wt)
+			die(_("a notes merge into %s is already in-progress at %s"),
+			    default_notes_ref(), wt->path);
+		if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
+			die(_("failed to store link to current notes ref (%s)"),
+			    default_notes_ref());
+		fprintf(stderr, _("Automatic notes merge failed. Fix conflicts in %s "
+				  "and commit the result with 'git notes merge --commit', "
+				  "or abort the merge with 'git notes merge --abort'.\n"),
+			git_path(NOTES_MERGE_WORKTREE));
+	}
+
+	free_notes(t);
+	strbuf_release(&remote_ref);
+	strbuf_release(&msg);
+	return result < 0; /* return non-zero on conflicts */
+}
+
+#define IGNORE_MISSING 1
+
+static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag)
+{
+	int status;
+	struct object_id oid;
+	if (get_oid(name, &oid))
+		return error(_("Failed to resolve '%s' as a valid ref."), name);
+	status = remove_note(t, oid.hash);
+	if (status)
+		fprintf(stderr, _("Object %s has no note\n"), name);
+	else
+		fprintf(stderr, _("Removing note for object %s\n"), name);
+	return (flag & IGNORE_MISSING) ? 0 : status;
+}
+
+static int remove_cmd(int argc, const char **argv, const char *prefix)
+{
+	unsigned flag = 0;
+	int from_stdin = 0;
+	struct option options[] = {
+		OPT_BIT(0, "ignore-missing", &flag,
+			N_("attempt to remove non-existent note is not an error"),
+			IGNORE_MISSING),
+		OPT_BOOL(0, "stdin", &from_stdin,
+			    N_("read object names from the standard input")),
+		OPT_END()
+	};
+	struct notes_tree *t;
+	int retval = 0;
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_notes_remove_usage, 0);
+
+	t = init_notes_check("remove", NOTES_INIT_WRITABLE);
+
+	if (!argc && !from_stdin) {
+		retval = remove_one_note(t, "HEAD", flag);
+	} else {
+		while (*argv) {
+			retval |= remove_one_note(t, *argv, flag);
+			argv++;
+		}
+	}
+	if (from_stdin) {
+		struct strbuf sb = STRBUF_INIT;
+		while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) {
+			strbuf_rtrim(&sb);
+			retval |= remove_one_note(t, sb.buf, flag);
+		}
+		strbuf_release(&sb);
+	}
+	if (!retval)
+		commit_notes(the_repository, t,
+			     "Notes removed by 'git notes remove'");
+	free_notes(t);
+	return retval;
+}
+
+static int prune(int argc, const char **argv, const char *prefix)
+{
+	struct notes_tree *t;
+	int show_only = 0, verbose = 0;
+	struct option options[] = {
+		OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
+		OPT__VERBOSE(&verbose, N_("report pruned notes")),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options, git_notes_prune_usage,
+			     0);
+
+	if (argc) {
+		error(_("too many parameters"));
+		usage_with_options(git_notes_prune_usage, options);
+	}
+
+	t = init_notes_check("prune", NOTES_INIT_WRITABLE);
+
+	prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) |
+		(show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) );
+	if (!show_only)
+		commit_notes(the_repository, t,
+			     "Notes removed by 'git notes prune'");
+	free_notes(t);
+	return 0;
+}
+
+static int get_ref(int argc, const char **argv, const char *prefix)
+{
+	struct option options[] = { OPT_END() };
+	argc = parse_options(argc, argv, prefix, options,
+			     git_notes_get_ref_usage, 0);
+
+	if (argc) {
+		error(_("too many parameters"));
+		usage_with_options(git_notes_get_ref_usage, options);
+	}
+
+	puts(default_notes_ref());
+	return 0;
+}
+
+int cmd_notes(int argc, const char **argv, const char *prefix)
+{
+	int result;
+	const char *override_notes_ref = NULL;
+	struct option options[] = {
+		OPT_STRING(0, "ref", &override_notes_ref, N_("notes-ref"),
+			   N_("use notes from <notes-ref>")),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, options, git_notes_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+
+	if (override_notes_ref) {
+		struct strbuf sb = STRBUF_INIT;
+		strbuf_addstr(&sb, override_notes_ref);
+		expand_notes_ref(&sb);
+		setenv("GIT_NOTES_REF", sb.buf, 1);
+		strbuf_release(&sb);
+	}
+
+	if (argc < 1 || !strcmp(argv[0], "list"))
+		result = list(argc, argv, prefix);
+	else if (!strcmp(argv[0], "add"))
+		result = add(argc, argv, prefix);
+	else if (!strcmp(argv[0], "copy"))
+		result = copy(argc, argv, prefix);
+	else if (!strcmp(argv[0], "append") || !strcmp(argv[0], "edit"))
+		result = append_edit(argc, argv, prefix);
+	else if (!strcmp(argv[0], "show"))
+		result = show(argc, argv, prefix);
+	else if (!strcmp(argv[0], "merge"))
+		result = merge(argc, argv, prefix);
+	else if (!strcmp(argv[0], "remove"))
+		result = remove_cmd(argc, argv, prefix);
+	else if (!strcmp(argv[0], "prune"))
+		result = prune(argc, argv, prefix);
+	else if (!strcmp(argv[0], "get-ref"))
+		result = get_ref(argc, argv, prefix);
+	else {
+		result = error(_("unknown subcommand: %s"), argv[0]);
+		usage_with_options(git_notes_usage, options);
+	}
+
+	return result ? 1 : 0;
+}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
new file mode 100644
index 0000000000..76ce906946
--- /dev/null
+++ b/builtin/pack-objects.c
@@ -0,0 +1,3531 @@
+#include "builtin.h"
+#include "cache.h"
+#include "repository.h"
+#include "config.h"
+#include "attr.h"
+#include "object.h"
+#include "blob.h"
+#include "commit.h"
+#include "tag.h"
+#include "tree.h"
+#include "delta.h"
+#include "pack.h"
+#include "pack-revindex.h"
+#include "csum-file.h"
+#include "tree-walk.h"
+#include "diff.h"
+#include "revision.h"
+#include "list-objects.h"
+#include "list-objects-filter.h"
+#include "list-objects-filter-options.h"
+#include "pack-objects.h"
+#include "progress.h"
+#include "refs.h"
+#include "streaming.h"
+#include "thread-utils.h"
+#include "pack-bitmap.h"
+#include "delta-islands.h"
+#include "reachable.h"
+#include "sha1-array.h"
+#include "argv-array.h"
+#include "list.h"
+#include "packfile.h"
+#include "object-store.h"
+#include "dir.h"
+#include "midx.h"
+#include "trace2.h"
+
+#define IN_PACK(obj) oe_in_pack(&to_pack, obj)
+#define SIZE(obj) oe_size(&to_pack, obj)
+#define SET_SIZE(obj,size) oe_set_size(&to_pack, obj, size)
+#define DELTA_SIZE(obj) oe_delta_size(&to_pack, obj)
+#define DELTA(obj) oe_delta(&to_pack, obj)
+#define DELTA_CHILD(obj) oe_delta_child(&to_pack, obj)
+#define DELTA_SIBLING(obj) oe_delta_sibling(&to_pack, obj)
+#define SET_DELTA(obj, val) oe_set_delta(&to_pack, obj, val)
+#define SET_DELTA_EXT(obj, oid) oe_set_delta_ext(&to_pack, obj, oid)
+#define SET_DELTA_SIZE(obj, val) oe_set_delta_size(&to_pack, obj, val)
+#define SET_DELTA_CHILD(obj, val) oe_set_delta_child(&to_pack, obj, val)
+#define SET_DELTA_SIBLING(obj, val) oe_set_delta_sibling(&to_pack, obj, val)
+
+static const char *pack_usage[] = {
+	N_("git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"),
+	N_("git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"),
+	NULL
+};
+
+/*
+ * Objects we are going to pack are collected in the `to_pack` structure.
+ * It contains an array (dynamically expanded) of the object data, and a map
+ * that can resolve SHA1s to their position in the array.
+ */
+static struct packing_data to_pack;
+
+static struct pack_idx_entry **written_list;
+static uint32_t nr_result, nr_written, nr_seen;
+static struct bitmap_index *bitmap_git;
+static uint32_t write_layer;
+
+static int non_empty;
+static int reuse_delta = 1, reuse_object = 1;
+static int keep_unreachable, unpack_unreachable, include_tag;
+static timestamp_t unpack_unreachable_expiration;
+static int pack_loose_unreachable;
+static int local;
+static int have_non_local_packs;
+static int incremental;
+static int ignore_packed_keep_on_disk;
+static int ignore_packed_keep_in_core;
+static int allow_ofs_delta;
+static struct pack_idx_option pack_idx_opts;
+static const char *base_name;
+static int progress = 1;
+static int window = 10;
+static unsigned long pack_size_limit;
+static int depth = 50;
+static int delta_search_threads;
+static int pack_to_stdout;
+static int sparse;
+static int thin;
+static int num_preferred_base;
+static struct progress *progress_state;
+
+static struct packed_git *reuse_packfile;
+static uint32_t reuse_packfile_objects;
+static off_t reuse_packfile_offset;
+
+static int use_bitmap_index_default = 1;
+static int use_bitmap_index = -1;
+static enum {
+	WRITE_BITMAP_FALSE = 0,
+	WRITE_BITMAP_QUIET,
+	WRITE_BITMAP_TRUE,
+} write_bitmap_index;
+static uint16_t write_bitmap_options = BITMAP_OPT_HASH_CACHE;
+
+static int exclude_promisor_objects;
+
+static int use_delta_islands;
+
+static unsigned long delta_cache_size = 0;
+static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE;
+static unsigned long cache_max_small_delta_size = 1000;
+
+static unsigned long window_memory_limit = 0;
+
+static struct list_objects_filter_options filter_options;
+
+enum missing_action {
+	MA_ERROR = 0,      /* fail if any missing objects are encountered */
+	MA_ALLOW_ANY,      /* silently allow ALL missing objects */
+	MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */
+};
+static enum missing_action arg_missing_action;
+static show_object_fn fn_show_object;
+
+/*
+ * stats
+ */
+static uint32_t written, written_delta;
+static uint32_t reused, reused_delta;
+
+/*
+ * Indexed commits
+ */
+static struct commit **indexed_commits;
+static unsigned int indexed_commits_nr;
+static unsigned int indexed_commits_alloc;
+
+static void index_commit_for_bitmap(struct commit *commit)
+{
+	if (indexed_commits_nr >= indexed_commits_alloc) {
+		indexed_commits_alloc = (indexed_commits_alloc + 32) * 2;
+		REALLOC_ARRAY(indexed_commits, indexed_commits_alloc);
+	}
+
+	indexed_commits[indexed_commits_nr++] = commit;
+}
+
+static void *get_delta(struct object_entry *entry)
+{
+	unsigned long size, base_size, delta_size;
+	void *buf, *base_buf, *delta_buf;
+	enum object_type type;
+
+	buf = read_object_file(&entry->idx.oid, &type, &size);
+	if (!buf)
+		die(_("unable to read %s"), oid_to_hex(&entry->idx.oid));
+	base_buf = read_object_file(&DELTA(entry)->idx.oid, &type,
+				    &base_size);
+	if (!base_buf)
+		die("unable to read %s",
+		    oid_to_hex(&DELTA(entry)->idx.oid));
+	delta_buf = diff_delta(base_buf, base_size,
+			       buf, size, &delta_size, 0);
+	/*
+	 * We succesfully computed this delta once but dropped it for
+	 * memory reasons. Something is very wrong if this time we
+	 * recompute and create a different delta.
+	 */
+	if (!delta_buf || delta_size != DELTA_SIZE(entry))
+		BUG("delta size changed");
+	free(buf);
+	free(base_buf);
+	return delta_buf;
+}
+
+static unsigned long do_compress(void **pptr, unsigned long size)
+{
+	git_zstream stream;
+	void *in, *out;
+	unsigned long maxsize;
+
+	git_deflate_init(&stream, pack_compression_level);
+	maxsize = git_deflate_bound(&stream, size);
+
+	in = *pptr;
+	out = xmalloc(maxsize);
+	*pptr = out;
+
+	stream.next_in = in;
+	stream.avail_in = size;
+	stream.next_out = out;
+	stream.avail_out = maxsize;
+	while (git_deflate(&stream, Z_FINISH) == Z_OK)
+		; /* nothing */
+	git_deflate_end(&stream);
+
+	free(in);
+	return stream.total_out;
+}
+
+static unsigned long write_large_blob_data(struct git_istream *st, struct hashfile *f,
+					   const struct object_id *oid)
+{
+	git_zstream stream;
+	unsigned char ibuf[1024 * 16];
+	unsigned char obuf[1024 * 16];
+	unsigned long olen = 0;
+
+	git_deflate_init(&stream, pack_compression_level);
+
+	for (;;) {
+		ssize_t readlen;
+		int zret = Z_OK;
+		readlen = read_istream(st, ibuf, sizeof(ibuf));
+		if (readlen == -1)
+			die(_("unable to read %s"), oid_to_hex(oid));
+
+		stream.next_in = ibuf;
+		stream.avail_in = readlen;
+		while ((stream.avail_in || readlen == 0) &&
+		       (zret == Z_OK || zret == Z_BUF_ERROR)) {
+			stream.next_out = obuf;
+			stream.avail_out = sizeof(obuf);
+			zret = git_deflate(&stream, readlen ? 0 : Z_FINISH);
+			hashwrite(f, obuf, stream.next_out - obuf);
+			olen += stream.next_out - obuf;
+		}
+		if (stream.avail_in)
+			die(_("deflate error (%d)"), zret);
+		if (readlen == 0) {
+			if (zret != Z_STREAM_END)
+				die(_("deflate error (%d)"), zret);
+			break;
+		}
+	}
+	git_deflate_end(&stream);
+	return olen;
+}
+
+/*
+ * we are going to reuse the existing object data as is.  make
+ * sure it is not corrupt.
+ */
+static int check_pack_inflate(struct packed_git *p,
+		struct pack_window **w_curs,
+		off_t offset,
+		off_t len,
+		unsigned long expect)
+{
+	git_zstream stream;
+	unsigned char fakebuf[4096], *in;
+	int st;
+
+	memset(&stream, 0, sizeof(stream));
+	git_inflate_init(&stream);
+	do {
+		in = use_pack(p, w_curs, offset, &stream.avail_in);
+		stream.next_in = in;
+		stream.next_out = fakebuf;
+		stream.avail_out = sizeof(fakebuf);
+		st = git_inflate(&stream, Z_FINISH);
+		offset += stream.next_in - in;
+	} while (st == Z_OK || st == Z_BUF_ERROR);
+	git_inflate_end(&stream);
+	return (st == Z_STREAM_END &&
+		stream.total_out == expect &&
+		stream.total_in == len) ? 0 : -1;
+}
+
+static void copy_pack_data(struct hashfile *f,
+		struct packed_git *p,
+		struct pack_window **w_curs,
+		off_t offset,
+		off_t len)
+{
+	unsigned char *in;
+	unsigned long avail;
+
+	while (len) {
+		in = use_pack(p, w_curs, offset, &avail);
+		if (avail > len)
+			avail = (unsigned long)len;
+		hashwrite(f, in, avail);
+		offset += avail;
+		len -= avail;
+	}
+}
+
+/* Return 0 if we will bust the pack-size limit */
+static unsigned long write_no_reuse_object(struct hashfile *f, struct object_entry *entry,
+					   unsigned long limit, int usable_delta)
+{
+	unsigned long size, datalen;
+	unsigned char header[MAX_PACK_OBJECT_HEADER],
+		      dheader[MAX_PACK_OBJECT_HEADER];
+	unsigned hdrlen;
+	enum object_type type;
+	void *buf;
+	struct git_istream *st = NULL;
+	const unsigned hashsz = the_hash_algo->rawsz;
+
+	if (!usable_delta) {
+		if (oe_type(entry) == OBJ_BLOB &&
+		    oe_size_greater_than(&to_pack, entry, big_file_threshold) &&
+		    (st = open_istream(&entry->idx.oid, &type, &size, NULL)) != NULL)
+			buf = NULL;
+		else {
+			buf = read_object_file(&entry->idx.oid, &type, &size);
+			if (!buf)
+				die(_("unable to read %s"),
+				    oid_to_hex(&entry->idx.oid));
+		}
+		/*
+		 * make sure no cached delta data remains from a
+		 * previous attempt before a pack split occurred.
+		 */
+		FREE_AND_NULL(entry->delta_data);
+		entry->z_delta_size = 0;
+	} else if (entry->delta_data) {
+		size = DELTA_SIZE(entry);
+		buf = entry->delta_data;
+		entry->delta_data = NULL;
+		type = (allow_ofs_delta && DELTA(entry)->idx.offset) ?
+			OBJ_OFS_DELTA : OBJ_REF_DELTA;
+	} else {
+		buf = get_delta(entry);
+		size = DELTA_SIZE(entry);
+		type = (allow_ofs_delta && DELTA(entry)->idx.offset) ?
+			OBJ_OFS_DELTA : OBJ_REF_DELTA;
+	}
+
+	if (st)	/* large blob case, just assume we don't compress well */
+		datalen = size;
+	else if (entry->z_delta_size)
+		datalen = entry->z_delta_size;
+	else
+		datalen = do_compress(&buf, size);
+
+	/*
+	 * The object header is a byte of 'type' followed by zero or
+	 * more bytes of length.
+	 */
+	hdrlen = encode_in_pack_object_header(header, sizeof(header),
+					      type, size);
+
+	if (type == OBJ_OFS_DELTA) {
+		/*
+		 * Deltas with relative base contain an additional
+		 * encoding of the relative offset for the delta
+		 * base from this object's position in the pack.
+		 */
+		off_t ofs = entry->idx.offset - DELTA(entry)->idx.offset;
+		unsigned pos = sizeof(dheader) - 1;
+		dheader[pos] = ofs & 127;
+		while (ofs >>= 7)
+			dheader[--pos] = 128 | (--ofs & 127);
+		if (limit && hdrlen + sizeof(dheader) - pos + datalen + hashsz >= limit) {
+			if (st)
+				close_istream(st);
+			free(buf);
+			return 0;
+		}
+		hashwrite(f, header, hdrlen);
+		hashwrite(f, dheader + pos, sizeof(dheader) - pos);
+		hdrlen += sizeof(dheader) - pos;
+	} else if (type == OBJ_REF_DELTA) {
+		/*
+		 * Deltas with a base reference contain
+		 * additional bytes for the base object ID.
+		 */
+		if (limit && hdrlen + hashsz + datalen + hashsz >= limit) {
+			if (st)
+				close_istream(st);
+			free(buf);
+			return 0;
+		}
+		hashwrite(f, header, hdrlen);
+		hashwrite(f, DELTA(entry)->idx.oid.hash, hashsz);
+		hdrlen += hashsz;
+	} else {
+		if (limit && hdrlen + datalen + hashsz >= limit) {
+			if (st)
+				close_istream(st);
+			free(buf);
+			return 0;
+		}
+		hashwrite(f, header, hdrlen);
+	}
+	if (st) {
+		datalen = write_large_blob_data(st, f, &entry->idx.oid);
+		close_istream(st);
+	} else {
+		hashwrite(f, buf, datalen);
+		free(buf);
+	}
+
+	return hdrlen + datalen;
+}
+
+/* Return 0 if we will bust the pack-size limit */
+static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry,
+				unsigned long limit, int usable_delta)
+{
+	struct packed_git *p = IN_PACK(entry);
+	struct pack_window *w_curs = NULL;
+	struct revindex_entry *revidx;
+	off_t offset;
+	enum object_type type = oe_type(entry);
+	off_t datalen;
+	unsigned char header[MAX_PACK_OBJECT_HEADER],
+		      dheader[MAX_PACK_OBJECT_HEADER];
+	unsigned hdrlen;
+	const unsigned hashsz = the_hash_algo->rawsz;
+	unsigned long entry_size = SIZE(entry);
+
+	if (DELTA(entry))
+		type = (allow_ofs_delta && DELTA(entry)->idx.offset) ?
+			OBJ_OFS_DELTA : OBJ_REF_DELTA;
+	hdrlen = encode_in_pack_object_header(header, sizeof(header),
+					      type, entry_size);
+
+	offset = entry->in_pack_offset;
+	revidx = find_pack_revindex(p, offset);
+	datalen = revidx[1].offset - offset;
+	if (!pack_to_stdout && p->index_version > 1 &&
+	    check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
+		error(_("bad packed object CRC for %s"),
+		      oid_to_hex(&entry->idx.oid));
+		unuse_pack(&w_curs);
+		return write_no_reuse_object(f, entry, limit, usable_delta);
+	}
+
+	offset += entry->in_pack_header_size;
+	datalen -= entry->in_pack_header_size;
+
+	if (!pack_to_stdout && p->index_version == 1 &&
+	    check_pack_inflate(p, &w_curs, offset, datalen, entry_size)) {
+		error(_("corrupt packed object for %s"),
+		      oid_to_hex(&entry->idx.oid));
+		unuse_pack(&w_curs);
+		return write_no_reuse_object(f, entry, limit, usable_delta);
+	}
+
+	if (type == OBJ_OFS_DELTA) {
+		off_t ofs = entry->idx.offset - DELTA(entry)->idx.offset;
+		unsigned pos = sizeof(dheader) - 1;
+		dheader[pos] = ofs & 127;
+		while (ofs >>= 7)
+			dheader[--pos] = 128 | (--ofs & 127);
+		if (limit && hdrlen + sizeof(dheader) - pos + datalen + hashsz >= limit) {
+			unuse_pack(&w_curs);
+			return 0;
+		}
+		hashwrite(f, header, hdrlen);
+		hashwrite(f, dheader + pos, sizeof(dheader) - pos);
+		hdrlen += sizeof(dheader) - pos;
+		reused_delta++;
+	} else if (type == OBJ_REF_DELTA) {
+		if (limit && hdrlen + hashsz + datalen + hashsz >= limit) {
+			unuse_pack(&w_curs);
+			return 0;
+		}
+		hashwrite(f, header, hdrlen);
+		hashwrite(f, DELTA(entry)->idx.oid.hash, hashsz);
+		hdrlen += hashsz;
+		reused_delta++;
+	} else {
+		if (limit && hdrlen + datalen + hashsz >= limit) {
+			unuse_pack(&w_curs);
+			return 0;
+		}
+		hashwrite(f, header, hdrlen);
+	}
+	copy_pack_data(f, p, &w_curs, offset, datalen);
+	unuse_pack(&w_curs);
+	reused++;
+	return hdrlen + datalen;
+}
+
+/* Return 0 if we will bust the pack-size limit */
+static off_t write_object(struct hashfile *f,
+			  struct object_entry *entry,
+			  off_t write_offset)
+{
+	unsigned long limit;
+	off_t len;
+	int usable_delta, to_reuse;
+
+	if (!pack_to_stdout)
+		crc32_begin(f);
+
+	/* apply size limit if limited packsize and not first object */
+	if (!pack_size_limit || !nr_written)
+		limit = 0;
+	else if (pack_size_limit <= write_offset)
+		/*
+		 * the earlier object did not fit the limit; avoid
+		 * mistaking this with unlimited (i.e. limit = 0).
+		 */
+		limit = 1;
+	else
+		limit = pack_size_limit - write_offset;
+
+	if (!DELTA(entry))
+		usable_delta = 0;	/* no delta */
+	else if (!pack_size_limit)
+	       usable_delta = 1;	/* unlimited packfile */
+	else if (DELTA(entry)->idx.offset == (off_t)-1)
+		usable_delta = 0;	/* base was written to another pack */
+	else if (DELTA(entry)->idx.offset)
+		usable_delta = 1;	/* base already exists in this pack */
+	else
+		usable_delta = 0;	/* base could end up in another pack */
+
+	if (!reuse_object)
+		to_reuse = 0;	/* explicit */
+	else if (!IN_PACK(entry))
+		to_reuse = 0;	/* can't reuse what we don't have */
+	else if (oe_type(entry) == OBJ_REF_DELTA ||
+		 oe_type(entry) == OBJ_OFS_DELTA)
+				/* check_object() decided it for us ... */
+		to_reuse = usable_delta;
+				/* ... but pack split may override that */
+	else if (oe_type(entry) != entry->in_pack_type)
+		to_reuse = 0;	/* pack has delta which is unusable */
+	else if (DELTA(entry))
+		to_reuse = 0;	/* we want to pack afresh */
+	else
+		to_reuse = 1;	/* we have it in-pack undeltified,
+				 * and we do not need to deltify it.
+				 */
+
+	if (!to_reuse)
+		len = write_no_reuse_object(f, entry, limit, usable_delta);
+	else
+		len = write_reuse_object(f, entry, limit, usable_delta);
+	if (!len)
+		return 0;
+
+	if (usable_delta)
+		written_delta++;
+	written++;
+	if (!pack_to_stdout)
+		entry->idx.crc32 = crc32_end(f);
+	return len;
+}
+
+enum write_one_status {
+	WRITE_ONE_SKIP = -1, /* already written */
+	WRITE_ONE_BREAK = 0, /* writing this will bust the limit; not written */
+	WRITE_ONE_WRITTEN = 1, /* normal */
+	WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */
+};
+
+static enum write_one_status write_one(struct hashfile *f,
+				       struct object_entry *e,
+				       off_t *offset)
+{
+	off_t size;
+	int recursing;
+
+	/*
+	 * we set offset to 1 (which is an impossible value) to mark
+	 * the fact that this object is involved in "write its base
+	 * first before writing a deltified object" recursion.
+	 */
+	recursing = (e->idx.offset == 1);
+	if (recursing) {
+		warning(_("recursive delta detected for object %s"),
+			oid_to_hex(&e->idx.oid));
+		return WRITE_ONE_RECURSIVE;
+	} else if (e->idx.offset || e->preferred_base) {
+		/* offset is non zero if object is written already. */
+		return WRITE_ONE_SKIP;
+	}
+
+	/* if we are deltified, write out base object first. */
+	if (DELTA(e)) {
+		e->idx.offset = 1; /* now recurse */
+		switch (write_one(f, DELTA(e), offset)) {
+		case WRITE_ONE_RECURSIVE:
+			/* we cannot depend on this one */
+			SET_DELTA(e, NULL);
+			break;
+		default:
+			break;
+		case WRITE_ONE_BREAK:
+			e->idx.offset = recursing;
+			return WRITE_ONE_BREAK;
+		}
+	}
+
+	e->idx.offset = *offset;
+	size = write_object(f, e, *offset);
+	if (!size) {
+		e->idx.offset = recursing;
+		return WRITE_ONE_BREAK;
+	}
+	written_list[nr_written++] = &e->idx;
+
+	/* make sure off_t is sufficiently large not to wrap */
+	if (signed_add_overflows(*offset, size))
+		die(_("pack too large for current definition of off_t"));
+	*offset += size;
+	return WRITE_ONE_WRITTEN;
+}
+
+static int mark_tagged(const char *path, const struct object_id *oid, int flag,
+		       void *cb_data)
+{
+	struct object_id peeled;
+	struct object_entry *entry = packlist_find(&to_pack, oid, NULL);
+
+	if (entry)
+		entry->tagged = 1;
+	if (!peel_ref(path, &peeled)) {
+		entry = packlist_find(&to_pack, &peeled, NULL);
+		if (entry)
+			entry->tagged = 1;
+	}
+	return 0;
+}
+
+static inline void add_to_write_order(struct object_entry **wo,
+			       unsigned int *endp,
+			       struct object_entry *e)
+{
+	if (e->filled || oe_layer(&to_pack, e) != write_layer)
+		return;
+	wo[(*endp)++] = e;
+	e->filled = 1;
+}
+
+static void add_descendants_to_write_order(struct object_entry **wo,
+					   unsigned int *endp,
+					   struct object_entry *e)
+{
+	int add_to_order = 1;
+	while (e) {
+		if (add_to_order) {
+			struct object_entry *s;
+			/* add this node... */
+			add_to_write_order(wo, endp, e);
+			/* all its siblings... */
+			for (s = DELTA_SIBLING(e); s; s = DELTA_SIBLING(s)) {
+				add_to_write_order(wo, endp, s);
+			}
+		}
+		/* drop down a level to add left subtree nodes if possible */
+		if (DELTA_CHILD(e)) {
+			add_to_order = 1;
+			e = DELTA_CHILD(e);
+		} else {
+			add_to_order = 0;
+			/* our sibling might have some children, it is next */
+			if (DELTA_SIBLING(e)) {
+				e = DELTA_SIBLING(e);
+				continue;
+			}
+			/* go back to our parent node */
+			e = DELTA(e);
+			while (e && !DELTA_SIBLING(e)) {
+				/* we're on the right side of a subtree, keep
+				 * going up until we can go right again */
+				e = DELTA(e);
+			}
+			if (!e) {
+				/* done- we hit our original root node */
+				return;
+			}
+			/* pass it off to sibling at this level */
+			e = DELTA_SIBLING(e);
+		}
+	};
+}
+
+static void add_family_to_write_order(struct object_entry **wo,
+				      unsigned int *endp,
+				      struct object_entry *e)
+{
+	struct object_entry *root;
+
+	for (root = e; DELTA(root); root = DELTA(root))
+		; /* nothing */
+	add_descendants_to_write_order(wo, endp, root);
+}
+
+static void compute_layer_order(struct object_entry **wo, unsigned int *wo_end)
+{
+	unsigned int i, last_untagged;
+	struct object_entry *objects = to_pack.objects;
+
+	for (i = 0; i < to_pack.nr_objects; i++) {
+		if (objects[i].tagged)
+			break;
+		add_to_write_order(wo, wo_end, &objects[i]);
+	}
+	last_untagged = i;
+
+	/*
+	 * Then fill all the tagged tips.
+	 */
+	for (; i < to_pack.nr_objects; i++) {
+		if (objects[i].tagged)
+			add_to_write_order(wo, wo_end, &objects[i]);
+	}
+
+	/*
+	 * And then all remaining commits and tags.
+	 */
+	for (i = last_untagged; i < to_pack.nr_objects; i++) {
+		if (oe_type(&objects[i]) != OBJ_COMMIT &&
+		    oe_type(&objects[i]) != OBJ_TAG)
+			continue;
+		add_to_write_order(wo, wo_end, &objects[i]);
+	}
+
+	/*
+	 * And then all the trees.
+	 */
+	for (i = last_untagged; i < to_pack.nr_objects; i++) {
+		if (oe_type(&objects[i]) != OBJ_TREE)
+			continue;
+		add_to_write_order(wo, wo_end, &objects[i]);
+	}
+
+	/*
+	 * Finally all the rest in really tight order
+	 */
+	for (i = last_untagged; i < to_pack.nr_objects; i++) {
+		if (!objects[i].filled && oe_layer(&to_pack, &objects[i]) == write_layer)
+			add_family_to_write_order(wo, wo_end, &objects[i]);
+	}
+}
+
+static struct object_entry **compute_write_order(void)
+{
+	uint32_t max_layers = 1;
+	unsigned int i, wo_end;
+
+	struct object_entry **wo;
+	struct object_entry *objects = to_pack.objects;
+
+	for (i = 0; i < to_pack.nr_objects; i++) {
+		objects[i].tagged = 0;
+		objects[i].filled = 0;
+		SET_DELTA_CHILD(&objects[i], NULL);
+		SET_DELTA_SIBLING(&objects[i], NULL);
+	}
+
+	/*
+	 * Fully connect delta_child/delta_sibling network.
+	 * Make sure delta_sibling is sorted in the original
+	 * recency order.
+	 */
+	for (i = to_pack.nr_objects; i > 0;) {
+		struct object_entry *e = &objects[--i];
+		if (!DELTA(e))
+			continue;
+		/* Mark me as the first child */
+		e->delta_sibling_idx = DELTA(e)->delta_child_idx;
+		SET_DELTA_CHILD(DELTA(e), e);
+	}
+
+	/*
+	 * Mark objects that are at the tip of tags.
+	 */
+	for_each_tag_ref(mark_tagged, NULL);
+
+	if (use_delta_islands)
+		max_layers = compute_pack_layers(&to_pack);
+
+	ALLOC_ARRAY(wo, to_pack.nr_objects);
+	wo_end = 0;
+
+	for (; write_layer < max_layers; ++write_layer)
+		compute_layer_order(wo, &wo_end);
+
+	if (wo_end != to_pack.nr_objects)
+		die(_("ordered %u objects, expected %"PRIu32),
+		    wo_end, to_pack.nr_objects);
+
+	return wo;
+}
+
+static off_t write_reused_pack(struct hashfile *f)
+{
+	unsigned char buffer[8192];
+	off_t to_write, total;
+	int fd;
+
+	if (!is_pack_valid(reuse_packfile))
+		die(_("packfile is invalid: %s"), reuse_packfile->pack_name);
+
+	fd = git_open(reuse_packfile->pack_name);
+	if (fd < 0)
+		die_errno(_("unable to open packfile for reuse: %s"),
+			  reuse_packfile->pack_name);
+
+	if (lseek(fd, sizeof(struct pack_header), SEEK_SET) == -1)
+		die_errno(_("unable to seek in reused packfile"));
+
+	if (reuse_packfile_offset < 0)
+		reuse_packfile_offset = reuse_packfile->pack_size - the_hash_algo->rawsz;
+
+	total = to_write = reuse_packfile_offset - sizeof(struct pack_header);
+
+	while (to_write) {
+		int read_pack = xread(fd, buffer, sizeof(buffer));
+
+		if (read_pack <= 0)
+			die_errno(_("unable to read from reused packfile"));
+
+		if (read_pack > to_write)
+			read_pack = to_write;
+
+		hashwrite(f, buffer, read_pack);
+		to_write -= read_pack;
+
+		/*
+		 * We don't know the actual number of objects written,
+		 * only how many bytes written, how many bytes total, and
+		 * how many objects total. So we can fake it by pretending all
+		 * objects we are writing are the same size. This gives us a
+		 * smooth progress meter, and at the end it matches the true
+		 * answer.
+		 */
+		written = reuse_packfile_objects *
+				(((double)(total - to_write)) / total);
+		display_progress(progress_state, written);
+	}
+
+	close(fd);
+	written = reuse_packfile_objects;
+	display_progress(progress_state, written);
+	return reuse_packfile_offset - sizeof(struct pack_header);
+}
+
+static const char no_split_warning[] = N_(
+"disabling bitmap writing, packs are split due to pack.packSizeLimit"
+);
+
+static void write_pack_file(void)
+{
+	uint32_t i = 0, j;
+	struct hashfile *f;
+	off_t offset;
+	uint32_t nr_remaining = nr_result;
+	time_t last_mtime = 0;
+	struct object_entry **write_order;
+
+	if (progress > pack_to_stdout)
+		progress_state = start_progress(_("Writing objects"), nr_result);
+	ALLOC_ARRAY(written_list, to_pack.nr_objects);
+	write_order = compute_write_order();
+
+	do {
+		struct object_id oid;
+		char *pack_tmp_name = NULL;
+
+		if (pack_to_stdout)
+			f = hashfd_throughput(1, "<stdout>", progress_state);
+		else
+			f = create_tmp_packfile(&pack_tmp_name);
+
+		offset = write_pack_header(f, nr_remaining);
+
+		if (reuse_packfile) {
+			off_t packfile_size;
+			assert(pack_to_stdout);
+
+			packfile_size = write_reused_pack(f);
+			offset += packfile_size;
+		}
+
+		nr_written = 0;
+		for (; i < to_pack.nr_objects; i++) {
+			struct object_entry *e = write_order[i];
+			if (write_one(f, e, &offset) == WRITE_ONE_BREAK)
+				break;
+			display_progress(progress_state, written);
+		}
+
+		/*
+		 * Did we write the wrong # entries in the header?
+		 * If so, rewrite it like in fast-import
+		 */
+		if (pack_to_stdout) {
+			finalize_hashfile(f, oid.hash, CSUM_HASH_IN_STREAM | CSUM_CLOSE);
+		} else if (nr_written == nr_remaining) {
+			finalize_hashfile(f, oid.hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
+		} else {
+			int fd = finalize_hashfile(f, oid.hash, 0);
+			fixup_pack_header_footer(fd, oid.hash, pack_tmp_name,
+						 nr_written, oid.hash, offset);
+			close(fd);
+			if (write_bitmap_index) {
+				if (write_bitmap_index != WRITE_BITMAP_QUIET)
+					warning(_(no_split_warning));
+				write_bitmap_index = 0;
+			}
+		}
+
+		if (!pack_to_stdout) {
+			struct stat st;
+			struct strbuf tmpname = STRBUF_INIT;
+
+			/*
+			 * Packs are runtime accessed in their mtime
+			 * order since newer packs are more likely to contain
+			 * younger objects.  So if we are creating multiple
+			 * packs then we should modify the mtime of later ones
+			 * to preserve this property.
+			 */
+			if (stat(pack_tmp_name, &st) < 0) {
+				warning_errno(_("failed to stat %s"), pack_tmp_name);
+			} else if (!last_mtime) {
+				last_mtime = st.st_mtime;
+			} else {
+				struct utimbuf utb;
+				utb.actime = st.st_atime;
+				utb.modtime = --last_mtime;
+				if (utime(pack_tmp_name, &utb) < 0)
+					warning_errno(_("failed utime() on %s"), pack_tmp_name);
+			}
+
+			strbuf_addf(&tmpname, "%s-", base_name);
+
+			if (write_bitmap_index) {
+				bitmap_writer_set_checksum(oid.hash);
+				bitmap_writer_build_type_index(
+					&to_pack, written_list, nr_written);
+			}
+
+			finish_tmp_packfile(&tmpname, pack_tmp_name,
+					    written_list, nr_written,
+					    &pack_idx_opts, oid.hash);
+
+			if (write_bitmap_index) {
+				strbuf_addf(&tmpname, "%s.bitmap", oid_to_hex(&oid));
+
+				stop_progress(&progress_state);
+
+				bitmap_writer_show_progress(progress);
+				bitmap_writer_reuse_bitmaps(&to_pack);
+				bitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1);
+				bitmap_writer_build(&to_pack);
+				bitmap_writer_finish(written_list, nr_written,
+						     tmpname.buf, write_bitmap_options);
+				write_bitmap_index = 0;
+			}
+
+			strbuf_release(&tmpname);
+			free(pack_tmp_name);
+			puts(oid_to_hex(&oid));
+		}
+
+		/* mark written objects as written to previous pack */
+		for (j = 0; j < nr_written; j++) {
+			written_list[j]->offset = (off_t)-1;
+		}
+		nr_remaining -= nr_written;
+	} while (nr_remaining && i < to_pack.nr_objects);
+
+	free(written_list);
+	free(write_order);
+	stop_progress(&progress_state);
+	if (written != nr_result)
+		die(_("wrote %"PRIu32" objects while expecting %"PRIu32),
+		    written, nr_result);
+	trace2_data_intmax("pack-objects", the_repository,
+			   "write_pack_file/wrote", nr_result);
+}
+
+static int no_try_delta(const char *path)
+{
+	static struct attr_check *check;
+
+	if (!check)
+		check = attr_check_initl("delta", NULL);
+	git_check_attr(the_repository->index, path, check);
+	if (ATTR_FALSE(check->items[0].value))
+		return 1;
+	return 0;
+}
+
+/*
+ * When adding an object, check whether we have already added it
+ * to our packing list. If so, we can skip. However, if we are
+ * being asked to excludei t, but the previous mention was to include
+ * it, make sure to adjust its flags and tweak our numbers accordingly.
+ *
+ * As an optimization, we pass out the index position where we would have
+ * found the item, since that saves us from having to look it up again a
+ * few lines later when we want to add the new entry.
+ */
+static int have_duplicate_entry(const struct object_id *oid,
+				int exclude,
+				uint32_t *index_pos)
+{
+	struct object_entry *entry;
+
+	entry = packlist_find(&to_pack, oid, index_pos);
+	if (!entry)
+		return 0;
+
+	if (exclude) {
+		if (!entry->preferred_base)
+			nr_result--;
+		entry->preferred_base = 1;
+	}
+
+	return 1;
+}
+
+static int want_found_object(int exclude, struct packed_git *p)
+{
+	if (exclude)
+		return 1;
+	if (incremental)
+		return 0;
+
+	/*
+	 * When asked to do --local (do not include an object that appears in a
+	 * pack we borrow from elsewhere) or --honor-pack-keep (do not include
+	 * an object that appears in a pack marked with .keep), finding a pack
+	 * that matches the criteria is sufficient for us to decide to omit it.
+	 * However, even if this pack does not satisfy the criteria, we need to
+	 * make sure no copy of this object appears in _any_ pack that makes us
+	 * to omit the object, so we need to check all the packs.
+	 *
+	 * We can however first check whether these options can possible matter;
+	 * if they do not matter we know we want the object in generated pack.
+	 * Otherwise, we signal "-1" at the end to tell the caller that we do
+	 * not know either way, and it needs to check more packs.
+	 */
+	if (!ignore_packed_keep_on_disk &&
+	    !ignore_packed_keep_in_core &&
+	    (!local || !have_non_local_packs))
+		return 1;
+
+	if (local && !p->pack_local)
+		return 0;
+	if (p->pack_local &&
+	    ((ignore_packed_keep_on_disk && p->pack_keep) ||
+	     (ignore_packed_keep_in_core && p->pack_keep_in_core)))
+		return 0;
+
+	/* we don't know yet; keep looking for more packs */
+	return -1;
+}
+
+/*
+ * Check whether we want the object in the pack (e.g., we do not want
+ * objects found in non-local stores if the "--local" option was used).
+ *
+ * If the caller already knows an existing pack it wants to take the object
+ * from, that is passed in *found_pack and *found_offset; otherwise this
+ * function finds if there is any pack that has the object and returns the pack
+ * and its offset in these variables.
+ */
+static int want_object_in_pack(const struct object_id *oid,
+			       int exclude,
+			       struct packed_git **found_pack,
+			       off_t *found_offset)
+{
+	int want;
+	struct list_head *pos;
+	struct multi_pack_index *m;
+
+	if (!exclude && local && has_loose_object_nonlocal(oid))
+		return 0;
+
+	/*
+	 * If we already know the pack object lives in, start checks from that
+	 * pack - in the usual case when neither --local was given nor .keep files
+	 * are present we will determine the answer right now.
+	 */
+	if (*found_pack) {
+		want = want_found_object(exclude, *found_pack);
+		if (want != -1)
+			return want;
+	}
+
+	for (m = get_multi_pack_index(the_repository); m; m = m->next) {
+		struct pack_entry e;
+		if (fill_midx_entry(the_repository, oid, &e, m)) {
+			struct packed_git *p = e.p;
+			off_t offset;
+
+			if (p == *found_pack)
+				offset = *found_offset;
+			else
+				offset = find_pack_entry_one(oid->hash, p);
+
+			if (offset) {
+				if (!*found_pack) {
+					if (!is_pack_valid(p))
+						continue;
+					*found_offset = offset;
+					*found_pack = p;
+				}
+				want = want_found_object(exclude, p);
+				if (want != -1)
+					return want;
+			}
+		}
+	}
+
+	list_for_each(pos, get_packed_git_mru(the_repository)) {
+		struct packed_git *p = list_entry(pos, struct packed_git, mru);
+		off_t offset;
+
+		if (p == *found_pack)
+			offset = *found_offset;
+		else
+			offset = find_pack_entry_one(oid->hash, p);
+
+		if (offset) {
+			if (!*found_pack) {
+				if (!is_pack_valid(p))
+					continue;
+				*found_offset = offset;
+				*found_pack = p;
+			}
+			want = want_found_object(exclude, p);
+			if (!exclude && want > 0)
+				list_move(&p->mru,
+					  get_packed_git_mru(the_repository));
+			if (want != -1)
+				return want;
+		}
+	}
+
+	return 1;
+}
+
+static void create_object_entry(const struct object_id *oid,
+				enum object_type type,
+				uint32_t hash,
+				int exclude,
+				int no_try_delta,
+				uint32_t index_pos,
+				struct packed_git *found_pack,
+				off_t found_offset)
+{
+	struct object_entry *entry;
+
+	entry = packlist_alloc(&to_pack, oid->hash, index_pos);
+	entry->hash = hash;
+	oe_set_type(entry, type);
+	if (exclude)
+		entry->preferred_base = 1;
+	else
+		nr_result++;
+	if (found_pack) {
+		oe_set_in_pack(&to_pack, entry, found_pack);
+		entry->in_pack_offset = found_offset;
+	}
+
+	entry->no_try_delta = no_try_delta;
+}
+
+static const char no_closure_warning[] = N_(
+"disabling bitmap writing, as some objects are not being packed"
+);
+
+static int add_object_entry(const struct object_id *oid, enum object_type type,
+			    const char *name, int exclude)
+{
+	struct packed_git *found_pack = NULL;
+	off_t found_offset = 0;
+	uint32_t index_pos;
+
+	display_progress(progress_state, ++nr_seen);
+
+	if (have_duplicate_entry(oid, exclude, &index_pos))
+		return 0;
+
+	if (!want_object_in_pack(oid, exclude, &found_pack, &found_offset)) {
+		/* The pack is missing an object, so it will not have closure */
+		if (write_bitmap_index) {
+			if (write_bitmap_index != WRITE_BITMAP_QUIET)
+				warning(_(no_closure_warning));
+			write_bitmap_index = 0;
+		}
+		return 0;
+	}
+
+	create_object_entry(oid, type, pack_name_hash(name),
+			    exclude, name && no_try_delta(name),
+			    index_pos, found_pack, found_offset);
+	return 1;
+}
+
+static int add_object_entry_from_bitmap(const struct object_id *oid,
+					enum object_type type,
+					int flags, uint32_t name_hash,
+					struct packed_git *pack, off_t offset)
+{
+	uint32_t index_pos;
+
+	display_progress(progress_state, ++nr_seen);
+
+	if (have_duplicate_entry(oid, 0, &index_pos))
+		return 0;
+
+	if (!want_object_in_pack(oid, 0, &pack, &offset))
+		return 0;
+
+	create_object_entry(oid, type, name_hash, 0, 0, index_pos, pack, offset);
+	return 1;
+}
+
+struct pbase_tree_cache {
+	struct object_id oid;
+	int ref;
+	int temporary;
+	void *tree_data;
+	unsigned long tree_size;
+};
+
+static struct pbase_tree_cache *(pbase_tree_cache[256]);
+static int pbase_tree_cache_ix(const struct object_id *oid)
+{
+	return oid->hash[0] % ARRAY_SIZE(pbase_tree_cache);
+}
+static int pbase_tree_cache_ix_incr(int ix)
+{
+	return (ix+1) % ARRAY_SIZE(pbase_tree_cache);
+}
+
+static struct pbase_tree {
+	struct pbase_tree *next;
+	/* This is a phony "cache" entry; we are not
+	 * going to evict it or find it through _get()
+	 * mechanism -- this is for the toplevel node that
+	 * would almost always change with any commit.
+	 */
+	struct pbase_tree_cache pcache;
+} *pbase_tree;
+
+static struct pbase_tree_cache *pbase_tree_get(const struct object_id *oid)
+{
+	struct pbase_tree_cache *ent, *nent;
+	void *data;
+	unsigned long size;
+	enum object_type type;
+	int neigh;
+	int my_ix = pbase_tree_cache_ix(oid);
+	int available_ix = -1;
+
+	/* pbase-tree-cache acts as a limited hashtable.
+	 * your object will be found at your index or within a few
+	 * slots after that slot if it is cached.
+	 */
+	for (neigh = 0; neigh < 8; neigh++) {
+		ent = pbase_tree_cache[my_ix];
+		if (ent && oideq(&ent->oid, oid)) {
+			ent->ref++;
+			return ent;
+		}
+		else if (((available_ix < 0) && (!ent || !ent->ref)) ||
+			 ((0 <= available_ix) &&
+			  (!ent && pbase_tree_cache[available_ix])))
+			available_ix = my_ix;
+		if (!ent)
+			break;
+		my_ix = pbase_tree_cache_ix_incr(my_ix);
+	}
+
+	/* Did not find one.  Either we got a bogus request or
+	 * we need to read and perhaps cache.
+	 */
+	data = read_object_file(oid, &type, &size);
+	if (!data)
+		return NULL;
+	if (type != OBJ_TREE) {
+		free(data);
+		return NULL;
+	}
+
+	/* We need to either cache or return a throwaway copy */
+
+	if (available_ix < 0)
+		ent = NULL;
+	else {
+		ent = pbase_tree_cache[available_ix];
+		my_ix = available_ix;
+	}
+
+	if (!ent) {
+		nent = xmalloc(sizeof(*nent));
+		nent->temporary = (available_ix < 0);
+	}
+	else {
+		/* evict and reuse */
+		free(ent->tree_data);
+		nent = ent;
+	}
+	oidcpy(&nent->oid, oid);
+	nent->tree_data = data;
+	nent->tree_size = size;
+	nent->ref = 1;
+	if (!nent->temporary)
+		pbase_tree_cache[my_ix] = nent;
+	return nent;
+}
+
+static void pbase_tree_put(struct pbase_tree_cache *cache)
+{
+	if (!cache->temporary) {
+		cache->ref--;
+		return;
+	}
+	free(cache->tree_data);
+	free(cache);
+}
+
+static int name_cmp_len(const char *name)
+{
+	int i;
+	for (i = 0; name[i] && name[i] != '\n' && name[i] != '/'; i++)
+		;
+	return i;
+}
+
+static void add_pbase_object(struct tree_desc *tree,
+			     const char *name,
+			     int cmplen,
+			     const char *fullname)
+{
+	struct name_entry entry;
+	int cmp;
+
+	while (tree_entry(tree,&entry)) {
+		if (S_ISGITLINK(entry.mode))
+			continue;
+		cmp = tree_entry_len(&entry) != cmplen ? 1 :
+		      memcmp(name, entry.path, cmplen);
+		if (cmp > 0)
+			continue;
+		if (cmp < 0)
+			return;
+		if (name[cmplen] != '/') {
+			add_object_entry(&entry.oid,
+					 object_type(entry.mode),
+					 fullname, 1);
+			return;
+		}
+		if (S_ISDIR(entry.mode)) {
+			struct tree_desc sub;
+			struct pbase_tree_cache *tree;
+			const char *down = name+cmplen+1;
+			int downlen = name_cmp_len(down);
+
+			tree = pbase_tree_get(&entry.oid);
+			if (!tree)
+				return;
+			init_tree_desc(&sub, tree->tree_data, tree->tree_size);
+
+			add_pbase_object(&sub, down, downlen, fullname);
+			pbase_tree_put(tree);
+		}
+	}
+}
+
+static unsigned *done_pbase_paths;
+static int done_pbase_paths_num;
+static int done_pbase_paths_alloc;
+static int done_pbase_path_pos(unsigned hash)
+{
+	int lo = 0;
+	int hi = done_pbase_paths_num;
+	while (lo < hi) {
+		int mi = lo + (hi - lo) / 2;
+		if (done_pbase_paths[mi] == hash)
+			return mi;
+		if (done_pbase_paths[mi] < hash)
+			hi = mi;
+		else
+			lo = mi + 1;
+	}
+	return -lo-1;
+}
+
+static int check_pbase_path(unsigned hash)
+{
+	int pos = done_pbase_path_pos(hash);
+	if (0 <= pos)
+		return 1;
+	pos = -pos - 1;
+	ALLOC_GROW(done_pbase_paths,
+		   done_pbase_paths_num + 1,
+		   done_pbase_paths_alloc);
+	done_pbase_paths_num++;
+	if (pos < done_pbase_paths_num)
+		MOVE_ARRAY(done_pbase_paths + pos + 1, done_pbase_paths + pos,
+			   done_pbase_paths_num - pos - 1);
+	done_pbase_paths[pos] = hash;
+	return 0;
+}
+
+static void add_preferred_base_object(const char *name)
+{
+	struct pbase_tree *it;
+	int cmplen;
+	unsigned hash = pack_name_hash(name);
+
+	if (!num_preferred_base || check_pbase_path(hash))
+		return;
+
+	cmplen = name_cmp_len(name);
+	for (it = pbase_tree; it; it = it->next) {
+		if (cmplen == 0) {
+			add_object_entry(&it->pcache.oid, OBJ_TREE, NULL, 1);
+		}
+		else {
+			struct tree_desc tree;
+			init_tree_desc(&tree, it->pcache.tree_data, it->pcache.tree_size);
+			add_pbase_object(&tree, name, cmplen, name);
+		}
+	}
+}
+
+static void add_preferred_base(struct object_id *oid)
+{
+	struct pbase_tree *it;
+	void *data;
+	unsigned long size;
+	struct object_id tree_oid;
+
+	if (window <= num_preferred_base++)
+		return;
+
+	data = read_object_with_reference(the_repository, oid,
+					  tree_type, &size, &tree_oid);
+	if (!data)
+		return;
+
+	for (it = pbase_tree; it; it = it->next) {
+		if (oideq(&it->pcache.oid, &tree_oid)) {
+			free(data);
+			return;
+		}
+	}
+
+	it = xcalloc(1, sizeof(*it));
+	it->next = pbase_tree;
+	pbase_tree = it;
+
+	oidcpy(&it->pcache.oid, &tree_oid);
+	it->pcache.tree_data = data;
+	it->pcache.tree_size = size;
+}
+
+static void cleanup_preferred_base(void)
+{
+	struct pbase_tree *it;
+	unsigned i;
+
+	it = pbase_tree;
+	pbase_tree = NULL;
+	while (it) {
+		struct pbase_tree *tmp = it;
+		it = tmp->next;
+		free(tmp->pcache.tree_data);
+		free(tmp);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(pbase_tree_cache); i++) {
+		if (!pbase_tree_cache[i])
+			continue;
+		free(pbase_tree_cache[i]->tree_data);
+		FREE_AND_NULL(pbase_tree_cache[i]);
+	}
+
+	FREE_AND_NULL(done_pbase_paths);
+	done_pbase_paths_num = done_pbase_paths_alloc = 0;
+}
+
+/*
+ * Return 1 iff the object specified by "delta" can be sent
+ * literally as a delta against the base in "base_sha1". If
+ * so, then *base_out will point to the entry in our packing
+ * list, or NULL if we must use the external-base list.
+ *
+ * Depth value does not matter - find_deltas() will
+ * never consider reused delta as the base object to
+ * deltify other objects against, in order to avoid
+ * circular deltas.
+ */
+static int can_reuse_delta(const unsigned char *base_sha1,
+			   struct object_entry *delta,
+			   struct object_entry **base_out)
+{
+	struct object_entry *base;
+	struct object_id base_oid;
+
+	if (!base_sha1)
+		return 0;
+
+	oidread(&base_oid, base_sha1);
+
+	/*
+	 * First see if we're already sending the base (or it's explicitly in
+	 * our "excluded" list).
+	 */
+	base = packlist_find(&to_pack, &base_oid, NULL);
+	if (base) {
+		if (!in_same_island(&delta->idx.oid, &base->idx.oid))
+			return 0;
+		*base_out = base;
+		return 1;
+	}
+
+	/*
+	 * Otherwise, reachability bitmaps may tell us if the receiver has it,
+	 * even if it was buried too deep in history to make it into the
+	 * packing list.
+	 */
+	if (thin && bitmap_has_oid_in_uninteresting(bitmap_git, &base_oid)) {
+		if (use_delta_islands) {
+			if (!in_same_island(&delta->idx.oid, &base_oid))
+				return 0;
+		}
+		*base_out = NULL;
+		return 1;
+	}
+
+	return 0;
+}
+
+static void check_object(struct object_entry *entry)
+{
+	unsigned long canonical_size;
+
+	if (IN_PACK(entry)) {
+		struct packed_git *p = IN_PACK(entry);
+		struct pack_window *w_curs = NULL;
+		const unsigned char *base_ref = NULL;
+		struct object_entry *base_entry;
+		unsigned long used, used_0;
+		unsigned long avail;
+		off_t ofs;
+		unsigned char *buf, c;
+		enum object_type type;
+		unsigned long in_pack_size;
+
+		buf = use_pack(p, &w_curs, entry->in_pack_offset, &avail);
+
+		/*
+		 * We want in_pack_type even if we do not reuse delta
+		 * since non-delta representations could still be reused.
+		 */
+		used = unpack_object_header_buffer(buf, avail,
+						   &type,
+						   &in_pack_size);
+		if (used == 0)
+			goto give_up;
+
+		if (type < 0)
+			BUG("invalid type %d", type);
+		entry->in_pack_type = type;
+
+		/*
+		 * Determine if this is a delta and if so whether we can
+		 * reuse it or not.  Otherwise let's find out as cheaply as
+		 * possible what the actual type and size for this object is.
+		 */
+		switch (entry->in_pack_type) {
+		default:
+			/* Not a delta hence we've already got all we need. */
+			oe_set_type(entry, entry->in_pack_type);
+			SET_SIZE(entry, in_pack_size);
+			entry->in_pack_header_size = used;
+			if (oe_type(entry) < OBJ_COMMIT || oe_type(entry) > OBJ_BLOB)
+				goto give_up;
+			unuse_pack(&w_curs);
+			return;
+		case OBJ_REF_DELTA:
+			if (reuse_delta && !entry->preferred_base)
+				base_ref = use_pack(p, &w_curs,
+						entry->in_pack_offset + used, NULL);
+			entry->in_pack_header_size = used + the_hash_algo->rawsz;
+			break;
+		case OBJ_OFS_DELTA:
+			buf = use_pack(p, &w_curs,
+				       entry->in_pack_offset + used, NULL);
+			used_0 = 0;
+			c = buf[used_0++];
+			ofs = c & 127;
+			while (c & 128) {
+				ofs += 1;
+				if (!ofs || MSB(ofs, 7)) {
+					error(_("delta base offset overflow in pack for %s"),
+					      oid_to_hex(&entry->idx.oid));
+					goto give_up;
+				}
+				c = buf[used_0++];
+				ofs = (ofs << 7) + (c & 127);
+			}
+			ofs = entry->in_pack_offset - ofs;
+			if (ofs <= 0 || ofs >= entry->in_pack_offset) {
+				error(_("delta base offset out of bound for %s"),
+				      oid_to_hex(&entry->idx.oid));
+				goto give_up;
+			}
+			if (reuse_delta && !entry->preferred_base) {
+				struct revindex_entry *revidx;
+				revidx = find_pack_revindex(p, ofs);
+				if (!revidx)
+					goto give_up;
+				base_ref = nth_packed_object_sha1(p, revidx->nr);
+			}
+			entry->in_pack_header_size = used + used_0;
+			break;
+		}
+
+		if (can_reuse_delta(base_ref, entry, &base_entry)) {
+			oe_set_type(entry, entry->in_pack_type);
+			SET_SIZE(entry, in_pack_size); /* delta size */
+			SET_DELTA_SIZE(entry, in_pack_size);
+
+			if (base_entry) {
+				SET_DELTA(entry, base_entry);
+				entry->delta_sibling_idx = base_entry->delta_child_idx;
+				SET_DELTA_CHILD(base_entry, entry);
+			} else {
+				SET_DELTA_EXT(entry, base_ref);
+			}
+
+			unuse_pack(&w_curs);
+			return;
+		}
+
+		if (oe_type(entry)) {
+			off_t delta_pos;
+
+			/*
+			 * This must be a delta and we already know what the
+			 * final object type is.  Let's extract the actual
+			 * object size from the delta header.
+			 */
+			delta_pos = entry->in_pack_offset + entry->in_pack_header_size;
+			canonical_size = get_size_from_delta(p, &w_curs, delta_pos);
+			if (canonical_size == 0)
+				goto give_up;
+			SET_SIZE(entry, canonical_size);
+			unuse_pack(&w_curs);
+			return;
+		}
+
+		/*
+		 * No choice but to fall back to the recursive delta walk
+		 * with oid_object_info() to find about the object type
+		 * at this point...
+		 */
+		give_up:
+		unuse_pack(&w_curs);
+	}
+
+	oe_set_type(entry,
+		    oid_object_info(the_repository, &entry->idx.oid, &canonical_size));
+	if (entry->type_valid) {
+		SET_SIZE(entry, canonical_size);
+	} else {
+		/*
+		 * Bad object type is checked in prepare_pack().  This is
+		 * to permit a missing preferred base object to be ignored
+		 * as a preferred base.  Doing so can result in a larger
+		 * pack file, but the transfer will still take place.
+		 */
+	}
+}
+
+static int pack_offset_sort(const void *_a, const void *_b)
+{
+	const struct object_entry *a = *(struct object_entry **)_a;
+	const struct object_entry *b = *(struct object_entry **)_b;
+	const struct packed_git *a_in_pack = IN_PACK(a);
+	const struct packed_git *b_in_pack = IN_PACK(b);
+
+	/* avoid filesystem trashing with loose objects */
+	if (!a_in_pack && !b_in_pack)
+		return oidcmp(&a->idx.oid, &b->idx.oid);
+
+	if (a_in_pack < b_in_pack)
+		return -1;
+	if (a_in_pack > b_in_pack)
+		return 1;
+	return a->in_pack_offset < b->in_pack_offset ? -1 :
+			(a->in_pack_offset > b->in_pack_offset);
+}
+
+/*
+ * Drop an on-disk delta we were planning to reuse. Naively, this would
+ * just involve blanking out the "delta" field, but we have to deal
+ * with some extra book-keeping:
+ *
+ *   1. Removing ourselves from the delta_sibling linked list.
+ *
+ *   2. Updating our size/type to the non-delta representation. These were
+ *      either not recorded initially (size) or overwritten with the delta type
+ *      (type) when check_object() decided to reuse the delta.
+ *
+ *   3. Resetting our delta depth, as we are now a base object.
+ */
+static void drop_reused_delta(struct object_entry *entry)
+{
+	unsigned *idx = &to_pack.objects[entry->delta_idx - 1].delta_child_idx;
+	struct object_info oi = OBJECT_INFO_INIT;
+	enum object_type type;
+	unsigned long size;
+
+	while (*idx) {
+		struct object_entry *oe = &to_pack.objects[*idx - 1];
+
+		if (oe == entry)
+			*idx = oe->delta_sibling_idx;
+		else
+			idx = &oe->delta_sibling_idx;
+	}
+	SET_DELTA(entry, NULL);
+	entry->depth = 0;
+
+	oi.sizep = &size;
+	oi.typep = &type;
+	if (packed_object_info(the_repository, IN_PACK(entry), entry->in_pack_offset, &oi) < 0) {
+		/*
+		 * We failed to get the info from this pack for some reason;
+		 * fall back to oid_object_info, which may find another copy.
+		 * And if that fails, the error will be recorded in oe_type(entry)
+		 * and dealt with in prepare_pack().
+		 */
+		oe_set_type(entry,
+			    oid_object_info(the_repository, &entry->idx.oid, &size));
+	} else {
+		oe_set_type(entry, type);
+	}
+	SET_SIZE(entry, size);
+}
+
+/*
+ * Follow the chain of deltas from this entry onward, throwing away any links
+ * that cause us to hit a cycle (as determined by the DFS state flags in
+ * the entries).
+ *
+ * We also detect too-long reused chains that would violate our --depth
+ * limit.
+ */
+static void break_delta_chains(struct object_entry *entry)
+{
+	/*
+	 * The actual depth of each object we will write is stored as an int,
+	 * as it cannot exceed our int "depth" limit. But before we break
+	 * changes based no that limit, we may potentially go as deep as the
+	 * number of objects, which is elsewhere bounded to a uint32_t.
+	 */
+	uint32_t total_depth;
+	struct object_entry *cur, *next;
+
+	for (cur = entry, total_depth = 0;
+	     cur;
+	     cur = DELTA(cur), total_depth++) {
+		if (cur->dfs_state == DFS_DONE) {
+			/*
+			 * We've already seen this object and know it isn't
+			 * part of a cycle. We do need to append its depth
+			 * to our count.
+			 */
+			total_depth += cur->depth;
+			break;
+		}
+
+		/*
+		 * We break cycles before looping, so an ACTIVE state (or any
+		 * other cruft which made its way into the state variable)
+		 * is a bug.
+		 */
+		if (cur->dfs_state != DFS_NONE)
+			BUG("confusing delta dfs state in first pass: %d",
+			    cur->dfs_state);
+
+		/*
+		 * Now we know this is the first time we've seen the object. If
+		 * it's not a delta, we're done traversing, but we'll mark it
+		 * done to save time on future traversals.
+		 */
+		if (!DELTA(cur)) {
+			cur->dfs_state = DFS_DONE;
+			break;
+		}
+
+		/*
+		 * Mark ourselves as active and see if the next step causes
+		 * us to cycle to another active object. It's important to do
+		 * this _before_ we loop, because it impacts where we make the
+		 * cut, and thus how our total_depth counter works.
+		 * E.g., We may see a partial loop like:
+		 *
+		 *   A -> B -> C -> D -> B
+		 *
+		 * Cutting B->C breaks the cycle. But now the depth of A is
+		 * only 1, and our total_depth counter is at 3. The size of the
+		 * error is always one less than the size of the cycle we
+		 * broke. Commits C and D were "lost" from A's chain.
+		 *
+		 * If we instead cut D->B, then the depth of A is correct at 3.
+		 * We keep all commits in the chain that we examined.
+		 */
+		cur->dfs_state = DFS_ACTIVE;
+		if (DELTA(cur)->dfs_state == DFS_ACTIVE) {
+			drop_reused_delta(cur);
+			cur->dfs_state = DFS_DONE;
+			break;
+		}
+	}
+
+	/*
+	 * And now that we've gone all the way to the bottom of the chain, we
+	 * need to clear the active flags and set the depth fields as
+	 * appropriate. Unlike the loop above, which can quit when it drops a
+	 * delta, we need to keep going to look for more depth cuts. So we need
+	 * an extra "next" pointer to keep going after we reset cur->delta.
+	 */
+	for (cur = entry; cur; cur = next) {
+		next = DELTA(cur);
+
+		/*
+		 * We should have a chain of zero or more ACTIVE states down to
+		 * a final DONE. We can quit after the DONE, because either it
+		 * has no bases, or we've already handled them in a previous
+		 * call.
+		 */
+		if (cur->dfs_state == DFS_DONE)
+			break;
+		else if (cur->dfs_state != DFS_ACTIVE)
+			BUG("confusing delta dfs state in second pass: %d",
+			    cur->dfs_state);
+
+		/*
+		 * If the total_depth is more than depth, then we need to snip
+		 * the chain into two or more smaller chains that don't exceed
+		 * the maximum depth. Most of the resulting chains will contain
+		 * (depth + 1) entries (i.e., depth deltas plus one base), and
+		 * the last chain (i.e., the one containing entry) will contain
+		 * whatever entries are left over, namely
+		 * (total_depth % (depth + 1)) of them.
+		 *
+		 * Since we are iterating towards decreasing depth, we need to
+		 * decrement total_depth as we go, and we need to write to the
+		 * entry what its final depth will be after all of the
+		 * snipping. Since we're snipping into chains of length (depth
+		 * + 1) entries, the final depth of an entry will be its
+		 * original depth modulo (depth + 1). Any time we encounter an
+		 * entry whose final depth is supposed to be zero, we snip it
+		 * from its delta base, thereby making it so.
+		 */
+		cur->depth = (total_depth--) % (depth + 1);
+		if (!cur->depth)
+			drop_reused_delta(cur);
+
+		cur->dfs_state = DFS_DONE;
+	}
+}
+
+static void get_object_details(void)
+{
+	uint32_t i;
+	struct object_entry **sorted_by_offset;
+
+	if (progress)
+		progress_state = start_progress(_("Counting objects"),
+						to_pack.nr_objects);
+
+	sorted_by_offset = xcalloc(to_pack.nr_objects, sizeof(struct object_entry *));
+	for (i = 0; i < to_pack.nr_objects; i++)
+		sorted_by_offset[i] = to_pack.objects + i;
+	QSORT(sorted_by_offset, to_pack.nr_objects, pack_offset_sort);
+
+	for (i = 0; i < to_pack.nr_objects; i++) {
+		struct object_entry *entry = sorted_by_offset[i];
+		check_object(entry);
+		if (entry->type_valid &&
+		    oe_size_greater_than(&to_pack, entry, big_file_threshold))
+			entry->no_try_delta = 1;
+		display_progress(progress_state, i + 1);
+	}
+	stop_progress(&progress_state);
+
+	/*
+	 * This must happen in a second pass, since we rely on the delta
+	 * information for the whole list being completed.
+	 */
+	for (i = 0; i < to_pack.nr_objects; i++)
+		break_delta_chains(&to_pack.objects[i]);
+
+	free(sorted_by_offset);
+}
+
+/*
+ * We search for deltas in a list sorted by type, by filename hash, and then
+ * by size, so that we see progressively smaller and smaller files.
+ * That's because we prefer deltas to be from the bigger file
+ * to the smaller -- deletes are potentially cheaper, but perhaps
+ * more importantly, the bigger file is likely the more recent
+ * one.  The deepest deltas are therefore the oldest objects which are
+ * less susceptible to be accessed often.
+ */
+static int type_size_sort(const void *_a, const void *_b)
+{
+	const struct object_entry *a = *(struct object_entry **)_a;
+	const struct object_entry *b = *(struct object_entry **)_b;
+	const enum object_type a_type = oe_type(a);
+	const enum object_type b_type = oe_type(b);
+	const unsigned long a_size = SIZE(a);
+	const unsigned long b_size = SIZE(b);
+
+	if (a_type > b_type)
+		return -1;
+	if (a_type < b_type)
+		return 1;
+	if (a->hash > b->hash)
+		return -1;
+	if (a->hash < b->hash)
+		return 1;
+	if (a->preferred_base > b->preferred_base)
+		return -1;
+	if (a->preferred_base < b->preferred_base)
+		return 1;
+	if (use_delta_islands) {
+		const int island_cmp = island_delta_cmp(&a->idx.oid, &b->idx.oid);
+		if (island_cmp)
+			return island_cmp;
+	}
+	if (a_size > b_size)
+		return -1;
+	if (a_size < b_size)
+		return 1;
+	return a < b ? -1 : (a > b);  /* newest first */
+}
+
+struct unpacked {
+	struct object_entry *entry;
+	void *data;
+	struct delta_index *index;
+	unsigned depth;
+};
+
+static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
+			   unsigned long delta_size)
+{
+	if (max_delta_cache_size && delta_cache_size + delta_size > max_delta_cache_size)
+		return 0;
+
+	if (delta_size < cache_max_small_delta_size)
+		return 1;
+
+	/* cache delta, if objects are large enough compared to delta size */
+	if ((src_size >> 20) + (trg_size >> 21) > (delta_size >> 10))
+		return 1;
+
+	return 0;
+}
+
+/* Protect delta_cache_size */
+static pthread_mutex_t cache_mutex;
+#define cache_lock()		pthread_mutex_lock(&cache_mutex)
+#define cache_unlock()		pthread_mutex_unlock(&cache_mutex)
+
+/*
+ * Protect object list partitioning (e.g. struct thread_param) and
+ * progress_state
+ */
+static pthread_mutex_t progress_mutex;
+#define progress_lock()		pthread_mutex_lock(&progress_mutex)
+#define progress_unlock()	pthread_mutex_unlock(&progress_mutex)
+
+/*
+ * Access to struct object_entry is unprotected since each thread owns
+ * a portion of the main object list. Just don't access object entries
+ * ahead in the list because they can be stolen and would need
+ * progress_mutex for protection.
+ */
+
+/*
+ * Return the size of the object without doing any delta
+ * reconstruction (so non-deltas are true object sizes, but deltas
+ * return the size of the delta data).
+ */
+unsigned long oe_get_size_slow(struct packing_data *pack,
+			       const struct object_entry *e)
+{
+	struct packed_git *p;
+	struct pack_window *w_curs;
+	unsigned char *buf;
+	enum object_type type;
+	unsigned long used, avail, size;
+
+	if (e->type_ != OBJ_OFS_DELTA && e->type_ != OBJ_REF_DELTA) {
+		packing_data_lock(&to_pack);
+		if (oid_object_info(the_repository, &e->idx.oid, &size) < 0)
+			die(_("unable to get size of %s"),
+			    oid_to_hex(&e->idx.oid));
+		packing_data_unlock(&to_pack);
+		return size;
+	}
+
+	p = oe_in_pack(pack, e);
+	if (!p)
+		BUG("when e->type is a delta, it must belong to a pack");
+
+	packing_data_lock(&to_pack);
+	w_curs = NULL;
+	buf = use_pack(p, &w_curs, e->in_pack_offset, &avail);
+	used = unpack_object_header_buffer(buf, avail, &type, &size);
+	if (used == 0)
+		die(_("unable to parse object header of %s"),
+		    oid_to_hex(&e->idx.oid));
+
+	unuse_pack(&w_curs);
+	packing_data_unlock(&to_pack);
+	return size;
+}
+
+static int try_delta(struct unpacked *trg, struct unpacked *src,
+		     unsigned max_depth, unsigned long *mem_usage)
+{
+	struct object_entry *trg_entry = trg->entry;
+	struct object_entry *src_entry = src->entry;
+	unsigned long trg_size, src_size, delta_size, sizediff, max_size, sz;
+	unsigned ref_depth;
+	enum object_type type;
+	void *delta_buf;
+
+	/* Don't bother doing diffs between different types */
+	if (oe_type(trg_entry) != oe_type(src_entry))
+		return -1;
+
+	/*
+	 * We do not bother to try a delta that we discarded on an
+	 * earlier try, but only when reusing delta data.  Note that
+	 * src_entry that is marked as the preferred_base should always
+	 * be considered, as even if we produce a suboptimal delta against
+	 * it, we will still save the transfer cost, as we already know
+	 * the other side has it and we won't send src_entry at all.
+	 */
+	if (reuse_delta && IN_PACK(trg_entry) &&
+	    IN_PACK(trg_entry) == IN_PACK(src_entry) &&
+	    !src_entry->preferred_base &&
+	    trg_entry->in_pack_type != OBJ_REF_DELTA &&
+	    trg_entry->in_pack_type != OBJ_OFS_DELTA)
+		return 0;
+
+	/* Let's not bust the allowed depth. */
+	if (src->depth >= max_depth)
+		return 0;
+
+	/* Now some size filtering heuristics. */
+	trg_size = SIZE(trg_entry);
+	if (!DELTA(trg_entry)) {
+		max_size = trg_size/2 - the_hash_algo->rawsz;
+		ref_depth = 1;
+	} else {
+		max_size = DELTA_SIZE(trg_entry);
+		ref_depth = trg->depth;
+	}
+	max_size = (uint64_t)max_size * (max_depth - src->depth) /
+						(max_depth - ref_depth + 1);
+	if (max_size == 0)
+		return 0;
+	src_size = SIZE(src_entry);
+	sizediff = src_size < trg_size ? trg_size - src_size : 0;
+	if (sizediff >= max_size)
+		return 0;
+	if (trg_size < src_size / 32)
+		return 0;
+
+	if (!in_same_island(&trg->entry->idx.oid, &src->entry->idx.oid))
+		return 0;
+
+	/* Load data if not already done */
+	if (!trg->data) {
+		packing_data_lock(&to_pack);
+		trg->data = read_object_file(&trg_entry->idx.oid, &type, &sz);
+		packing_data_unlock(&to_pack);
+		if (!trg->data)
+			die(_("object %s cannot be read"),
+			    oid_to_hex(&trg_entry->idx.oid));
+		if (sz != trg_size)
+			die(_("object %s inconsistent object length (%"PRIuMAX" vs %"PRIuMAX")"),
+			    oid_to_hex(&trg_entry->idx.oid), (uintmax_t)sz,
+			    (uintmax_t)trg_size);
+		*mem_usage += sz;
+	}
+	if (!src->data) {
+		packing_data_lock(&to_pack);
+		src->data = read_object_file(&src_entry->idx.oid, &type, &sz);
+		packing_data_unlock(&to_pack);
+		if (!src->data) {
+			if (src_entry->preferred_base) {
+				static int warned = 0;
+				if (!warned++)
+					warning(_("object %s cannot be read"),
+						oid_to_hex(&src_entry->idx.oid));
+				/*
+				 * Those objects are not included in the
+				 * resulting pack.  Be resilient and ignore
+				 * them if they can't be read, in case the
+				 * pack could be created nevertheless.
+				 */
+				return 0;
+			}
+			die(_("object %s cannot be read"),
+			    oid_to_hex(&src_entry->idx.oid));
+		}
+		if (sz != src_size)
+			die(_("object %s inconsistent object length (%"PRIuMAX" vs %"PRIuMAX")"),
+			    oid_to_hex(&src_entry->idx.oid), (uintmax_t)sz,
+			    (uintmax_t)src_size);
+		*mem_usage += sz;
+	}
+	if (!src->index) {
+		src->index = create_delta_index(src->data, src_size);
+		if (!src->index) {
+			static int warned = 0;
+			if (!warned++)
+				warning(_("suboptimal pack - out of memory"));
+			return 0;
+		}
+		*mem_usage += sizeof_delta_index(src->index);
+	}
+
+	delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size);
+	if (!delta_buf)
+		return 0;
+
+	if (DELTA(trg_entry)) {
+		/* Prefer only shallower same-sized deltas. */
+		if (delta_size == DELTA_SIZE(trg_entry) &&
+		    src->depth + 1 >= trg->depth) {
+			free(delta_buf);
+			return 0;
+		}
+	}
+
+	/*
+	 * Handle memory allocation outside of the cache
+	 * accounting lock.  Compiler will optimize the strangeness
+	 * away when NO_PTHREADS is defined.
+	 */
+	free(trg_entry->delta_data);
+	cache_lock();
+	if (trg_entry->delta_data) {
+		delta_cache_size -= DELTA_SIZE(trg_entry);
+		trg_entry->delta_data = NULL;
+	}
+	if (delta_cacheable(src_size, trg_size, delta_size)) {
+		delta_cache_size += delta_size;
+		cache_unlock();
+		trg_entry->delta_data = xrealloc(delta_buf, delta_size);
+	} else {
+		cache_unlock();
+		free(delta_buf);
+	}
+
+	SET_DELTA(trg_entry, src_entry);
+	SET_DELTA_SIZE(trg_entry, delta_size);
+	trg->depth = src->depth + 1;
+
+	return 1;
+}
+
+static unsigned int check_delta_limit(struct object_entry *me, unsigned int n)
+{
+	struct object_entry *child = DELTA_CHILD(me);
+	unsigned int m = n;
+	while (child) {
+		const unsigned int c = check_delta_limit(child, n + 1);
+		if (m < c)
+			m = c;
+		child = DELTA_SIBLING(child);
+	}
+	return m;
+}
+
+static unsigned long free_unpacked(struct unpacked *n)
+{
+	unsigned long freed_mem = sizeof_delta_index(n->index);
+	free_delta_index(n->index);
+	n->index = NULL;
+	if (n->data) {
+		freed_mem += SIZE(n->entry);
+		FREE_AND_NULL(n->data);
+	}
+	n->entry = NULL;
+	n->depth = 0;
+	return freed_mem;
+}
+
+static void find_deltas(struct object_entry **list, unsigned *list_size,
+			int window, int depth, unsigned *processed)
+{
+	uint32_t i, idx = 0, count = 0;
+	struct unpacked *array;
+	unsigned long mem_usage = 0;
+
+	array = xcalloc(window, sizeof(struct unpacked));
+
+	for (;;) {
+		struct object_entry *entry;
+		struct unpacked *n = array + idx;
+		int j, max_depth, best_base = -1;
+
+		progress_lock();
+		if (!*list_size) {
+			progress_unlock();
+			break;
+		}
+		entry = *list++;
+		(*list_size)--;
+		if (!entry->preferred_base) {
+			(*processed)++;
+			display_progress(progress_state, *processed);
+		}
+		progress_unlock();
+
+		mem_usage -= free_unpacked(n);
+		n->entry = entry;
+
+		while (window_memory_limit &&
+		       mem_usage > window_memory_limit &&
+		       count > 1) {
+			const uint32_t tail = (idx + window - count) % window;
+			mem_usage -= free_unpacked(array + tail);
+			count--;
+		}
+
+		/* We do not compute delta to *create* objects we are not
+		 * going to pack.
+		 */
+		if (entry->preferred_base)
+			goto next;
+
+		/*
+		 * If the current object is at pack edge, take the depth the
+		 * objects that depend on the current object into account
+		 * otherwise they would become too deep.
+		 */
+		max_depth = depth;
+		if (DELTA_CHILD(entry)) {
+			max_depth -= check_delta_limit(entry, 0);
+			if (max_depth <= 0)
+				goto next;
+		}
+
+		j = window;
+		while (--j > 0) {
+			int ret;
+			uint32_t other_idx = idx + j;
+			struct unpacked *m;
+			if (other_idx >= window)
+				other_idx -= window;
+			m = array + other_idx;
+			if (!m->entry)
+				break;
+			ret = try_delta(n, m, max_depth, &mem_usage);
+			if (ret < 0)
+				break;
+			else if (ret > 0)
+				best_base = other_idx;
+		}
+
+		/*
+		 * If we decided to cache the delta data, then it is best
+		 * to compress it right away.  First because we have to do
+		 * it anyway, and doing it here while we're threaded will
+		 * save a lot of time in the non threaded write phase,
+		 * as well as allow for caching more deltas within
+		 * the same cache size limit.
+		 * ...
+		 * But only if not writing to stdout, since in that case
+		 * the network is most likely throttling writes anyway,
+		 * and therefore it is best to go to the write phase ASAP
+		 * instead, as we can afford spending more time compressing
+		 * between writes at that moment.
+		 */
+		if (entry->delta_data && !pack_to_stdout) {
+			unsigned long size;
+
+			size = do_compress(&entry->delta_data, DELTA_SIZE(entry));
+			if (size < (1U << OE_Z_DELTA_BITS)) {
+				entry->z_delta_size = size;
+				cache_lock();
+				delta_cache_size -= DELTA_SIZE(entry);
+				delta_cache_size += entry->z_delta_size;
+				cache_unlock();
+			} else {
+				FREE_AND_NULL(entry->delta_data);
+				entry->z_delta_size = 0;
+			}
+		}
+
+		/* if we made n a delta, and if n is already at max
+		 * depth, leaving it in the window is pointless.  we
+		 * should evict it first.
+		 */
+		if (DELTA(entry) && max_depth <= n->depth)
+			continue;
+
+		/*
+		 * Move the best delta base up in the window, after the
+		 * currently deltified object, to keep it longer.  It will
+		 * be the first base object to be attempted next.
+		 */
+		if (DELTA(entry)) {
+			struct unpacked swap = array[best_base];
+			int dist = (window + idx - best_base) % window;
+			int dst = best_base;
+			while (dist--) {
+				int src = (dst + 1) % window;
+				array[dst] = array[src];
+				dst = src;
+			}
+			array[dst] = swap;
+		}
+
+		next:
+		idx++;
+		if (count + 1 < window)
+			count++;
+		if (idx >= window)
+			idx = 0;
+	}
+
+	for (i = 0; i < window; ++i) {
+		free_delta_index(array[i].index);
+		free(array[i].data);
+	}
+	free(array);
+}
+
+static void try_to_free_from_threads(size_t size)
+{
+	packing_data_lock(&to_pack);
+	release_pack_memory(size);
+	packing_data_unlock(&to_pack);
+}
+
+static try_to_free_t old_try_to_free_routine;
+
+/*
+ * The main object list is split into smaller lists, each is handed to
+ * one worker.
+ *
+ * The main thread waits on the condition that (at least) one of the workers
+ * has stopped working (which is indicated in the .working member of
+ * struct thread_params).
+ *
+ * When a work thread has completed its work, it sets .working to 0 and
+ * signals the main thread and waits on the condition that .data_ready
+ * becomes 1.
+ *
+ * The main thread steals half of the work from the worker that has
+ * most work left to hand it to the idle worker.
+ */
+
+struct thread_params {
+	pthread_t thread;
+	struct object_entry **list;
+	unsigned list_size;
+	unsigned remaining;
+	int window;
+	int depth;
+	int working;
+	int data_ready;
+	pthread_mutex_t mutex;
+	pthread_cond_t cond;
+	unsigned *processed;
+};
+
+static pthread_cond_t progress_cond;
+
+/*
+ * Mutex and conditional variable can't be statically-initialized on Windows.
+ */
+static void init_threaded_search(void)
+{
+	pthread_mutex_init(&cache_mutex, NULL);
+	pthread_mutex_init(&progress_mutex, NULL);
+	pthread_cond_init(&progress_cond, NULL);
+	old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
+}
+
+static void cleanup_threaded_search(void)
+{
+	set_try_to_free_routine(old_try_to_free_routine);
+	pthread_cond_destroy(&progress_cond);
+	pthread_mutex_destroy(&cache_mutex);
+	pthread_mutex_destroy(&progress_mutex);
+}
+
+static void *threaded_find_deltas(void *arg)
+{
+	struct thread_params *me = arg;
+
+	progress_lock();
+	while (me->remaining) {
+		progress_unlock();
+
+		find_deltas(me->list, &me->remaining,
+			    me->window, me->depth, me->processed);
+
+		progress_lock();
+		me->working = 0;
+		pthread_cond_signal(&progress_cond);
+		progress_unlock();
+
+		/*
+		 * We must not set ->data_ready before we wait on the
+		 * condition because the main thread may have set it to 1
+		 * before we get here. In order to be sure that new
+		 * work is available if we see 1 in ->data_ready, it
+		 * was initialized to 0 before this thread was spawned
+		 * and we reset it to 0 right away.
+		 */
+		pthread_mutex_lock(&me->mutex);
+		while (!me->data_ready)
+			pthread_cond_wait(&me->cond, &me->mutex);
+		me->data_ready = 0;
+		pthread_mutex_unlock(&me->mutex);
+
+		progress_lock();
+	}
+	progress_unlock();
+	/* leave ->working 1 so that this doesn't get more work assigned */
+	return NULL;
+}
+
+static void ll_find_deltas(struct object_entry **list, unsigned list_size,
+			   int window, int depth, unsigned *processed)
+{
+	struct thread_params *p;
+	int i, ret, active_threads = 0;
+
+	init_threaded_search();
+
+	if (delta_search_threads <= 1) {
+		find_deltas(list, &list_size, window, depth, processed);
+		cleanup_threaded_search();
+		return;
+	}
+	if (progress > pack_to_stdout)
+		fprintf_ln(stderr, _("Delta compression using up to %d threads"),
+			   delta_search_threads);
+	p = xcalloc(delta_search_threads, sizeof(*p));
+
+	/* Partition the work amongst work threads. */
+	for (i = 0; i < delta_search_threads; i++) {
+		unsigned sub_size = list_size / (delta_search_threads - i);
+
+		/* don't use too small segments or no deltas will be found */
+		if (sub_size < 2*window && i+1 < delta_search_threads)
+			sub_size = 0;
+
+		p[i].window = window;
+		p[i].depth = depth;
+		p[i].processed = processed;
+		p[i].working = 1;
+		p[i].data_ready = 0;
+
+		/* try to split chunks on "path" boundaries */
+		while (sub_size && sub_size < list_size &&
+		       list[sub_size]->hash &&
+		       list[sub_size]->hash == list[sub_size-1]->hash)
+			sub_size++;
+
+		p[i].list = list;
+		p[i].list_size = sub_size;
+		p[i].remaining = sub_size;
+
+		list += sub_size;
+		list_size -= sub_size;
+	}
+
+	/* Start work threads. */
+	for (i = 0; i < delta_search_threads; i++) {
+		if (!p[i].list_size)
+			continue;
+		pthread_mutex_init(&p[i].mutex, NULL);
+		pthread_cond_init(&p[i].cond, NULL);
+		ret = pthread_create(&p[i].thread, NULL,
+				     threaded_find_deltas, &p[i]);
+		if (ret)
+			die(_("unable to create thread: %s"), strerror(ret));
+		active_threads++;
+	}
+
+	/*
+	 * Now let's wait for work completion.  Each time a thread is done
+	 * with its work, we steal half of the remaining work from the
+	 * thread with the largest number of unprocessed objects and give
+	 * it to that newly idle thread.  This ensure good load balancing
+	 * until the remaining object list segments are simply too short
+	 * to be worth splitting anymore.
+	 */
+	while (active_threads) {
+		struct thread_params *target = NULL;
+		struct thread_params *victim = NULL;
+		unsigned sub_size = 0;
+
+		progress_lock();
+		for (;;) {
+			for (i = 0; !target && i < delta_search_threads; i++)
+				if (!p[i].working)
+					target = &p[i];
+			if (target)
+				break;
+			pthread_cond_wait(&progress_cond, &progress_mutex);
+		}
+
+		for (i = 0; i < delta_search_threads; i++)
+			if (p[i].remaining > 2*window &&
+			    (!victim || victim->remaining < p[i].remaining))
+				victim = &p[i];
+		if (victim) {
+			sub_size = victim->remaining / 2;
+			list = victim->list + victim->list_size - sub_size;
+			while (sub_size && list[0]->hash &&
+			       list[0]->hash == list[-1]->hash) {
+				list++;
+				sub_size--;
+			}
+			if (!sub_size) {
+				/*
+				 * It is possible for some "paths" to have
+				 * so many objects that no hash boundary
+				 * might be found.  Let's just steal the
+				 * exact half in that case.
+				 */
+				sub_size = victim->remaining / 2;
+				list -= sub_size;
+			}
+			target->list = list;
+			victim->list_size -= sub_size;
+			victim->remaining -= sub_size;
+		}
+		target->list_size = sub_size;
+		target->remaining = sub_size;
+		target->working = 1;
+		progress_unlock();
+
+		pthread_mutex_lock(&target->mutex);
+		target->data_ready = 1;
+		pthread_cond_signal(&target->cond);
+		pthread_mutex_unlock(&target->mutex);
+
+		if (!sub_size) {
+			pthread_join(target->thread, NULL);
+			pthread_cond_destroy(&target->cond);
+			pthread_mutex_destroy(&target->mutex);
+			active_threads--;
+		}
+	}
+	cleanup_threaded_search();
+	free(p);
+}
+
+static void add_tag_chain(const struct object_id *oid)
+{
+	struct tag *tag;
+
+	/*
+	 * We catch duplicates already in add_object_entry(), but we'd
+	 * prefer to do this extra check to avoid having to parse the
+	 * tag at all if we already know that it's being packed (e.g., if
+	 * it was included via bitmaps, we would not have parsed it
+	 * previously).
+	 */
+	if (packlist_find(&to_pack, oid, NULL))
+		return;
+
+	tag = lookup_tag(the_repository, oid);
+	while (1) {
+		if (!tag || parse_tag(tag) || !tag->tagged)
+			die(_("unable to pack objects reachable from tag %s"),
+			    oid_to_hex(oid));
+
+		add_object_entry(&tag->object.oid, OBJ_TAG, NULL, 0);
+
+		if (tag->tagged->type != OBJ_TAG)
+			return;
+
+		tag = (struct tag *)tag->tagged;
+	}
+}
+
+static int add_ref_tag(const char *path, const struct object_id *oid, int flag, void *cb_data)
+{
+	struct object_id peeled;
+
+	if (starts_with(path, "refs/tags/") && /* is a tag? */
+	    !peel_ref(path, &peeled)    && /* peelable? */
+	    packlist_find(&to_pack, &peeled, NULL))      /* object packed? */
+		add_tag_chain(oid);
+	return 0;
+}
+
+static void prepare_pack(int window, int depth)
+{
+	struct object_entry **delta_list;
+	uint32_t i, nr_deltas;
+	unsigned n;
+
+	if (use_delta_islands)
+		resolve_tree_islands(the_repository, progress, &to_pack);
+
+	get_object_details();
+
+	/*
+	 * If we're locally repacking then we need to be doubly careful
+	 * from now on in order to make sure no stealth corruption gets
+	 * propagated to the new pack.  Clients receiving streamed packs
+	 * should validate everything they get anyway so no need to incur
+	 * the additional cost here in that case.
+	 */
+	if (!pack_to_stdout)
+		do_check_packed_object_crc = 1;
+
+	if (!to_pack.nr_objects || !window || !depth)
+		return;
+
+	ALLOC_ARRAY(delta_list, to_pack.nr_objects);
+	nr_deltas = n = 0;
+
+	for (i = 0; i < to_pack.nr_objects; i++) {
+		struct object_entry *entry = to_pack.objects + i;
+
+		if (DELTA(entry))
+			/* This happens if we decided to reuse existing
+			 * delta from a pack.  "reuse_delta &&" is implied.
+			 */
+			continue;
+
+		if (!entry->type_valid ||
+		    oe_size_less_than(&to_pack, entry, 50))
+			continue;
+
+		if (entry->no_try_delta)
+			continue;
+
+		if (!entry->preferred_base) {
+			nr_deltas++;
+			if (oe_type(entry) < 0)
+				die(_("unable to get type of object %s"),
+				    oid_to_hex(&entry->idx.oid));
+		} else {
+			if (oe_type(entry) < 0) {
+				/*
+				 * This object is not found, but we
+				 * don't have to include it anyway.
+				 */
+				continue;
+			}
+		}
+
+		delta_list[n++] = entry;
+	}
+
+	if (nr_deltas && n > 1) {
+		unsigned nr_done = 0;
+		if (progress)
+			progress_state = start_progress(_("Compressing objects"),
+							nr_deltas);
+		QSORT(delta_list, n, type_size_sort);
+		ll_find_deltas(delta_list, n, window+1, depth, &nr_done);
+		stop_progress(&progress_state);
+		if (nr_done != nr_deltas)
+			die(_("inconsistency with delta count"));
+	}
+	free(delta_list);
+}
+
+static int git_pack_config(const char *k, const char *v, void *cb)
+{
+	if (!strcmp(k, "pack.window")) {
+		window = git_config_int(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "pack.windowmemory")) {
+		window_memory_limit = git_config_ulong(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "pack.depth")) {
+		depth = git_config_int(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "pack.deltacachesize")) {
+		max_delta_cache_size = git_config_int(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "pack.deltacachelimit")) {
+		cache_max_small_delta_size = git_config_int(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "pack.writebitmaphashcache")) {
+		if (git_config_bool(k, v))
+			write_bitmap_options |= BITMAP_OPT_HASH_CACHE;
+		else
+			write_bitmap_options &= ~BITMAP_OPT_HASH_CACHE;
+	}
+	if (!strcmp(k, "pack.usebitmaps")) {
+		use_bitmap_index_default = git_config_bool(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "pack.usesparse")) {
+		sparse = git_config_bool(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "pack.threads")) {
+		delta_search_threads = git_config_int(k, v);
+		if (delta_search_threads < 0)
+			die(_("invalid number of threads specified (%d)"),
+			    delta_search_threads);
+		if (!HAVE_THREADS && delta_search_threads != 1) {
+			warning(_("no threads support, ignoring %s"), k);
+			delta_search_threads = 0;
+		}
+		return 0;
+	}
+	if (!strcmp(k, "pack.indexversion")) {
+		pack_idx_opts.version = git_config_int(k, v);
+		if (pack_idx_opts.version > 2)
+			die(_("bad pack.indexversion=%"PRIu32),
+			    pack_idx_opts.version);
+		return 0;
+	}
+	return git_default_config(k, v, cb);
+}
+
+static void read_object_list_from_stdin(void)
+{
+	char line[GIT_MAX_HEXSZ + 1 + PATH_MAX + 2];
+	struct object_id oid;
+	const char *p;
+
+	for (;;) {
+		if (!fgets(line, sizeof(line), stdin)) {
+			if (feof(stdin))
+				break;
+			if (!ferror(stdin))
+				die("BUG: fgets returned NULL, not EOF, not error!");
+			if (errno != EINTR)
+				die_errno("fgets");
+			clearerr(stdin);
+			continue;
+		}
+		if (line[0] == '-') {
+			if (get_oid_hex(line+1, &oid))
+				die(_("expected edge object ID, got garbage:\n %s"),
+				    line);
+			add_preferred_base(&oid);
+			continue;
+		}
+		if (parse_oid_hex(line, &oid, &p))
+			die(_("expected object ID, got garbage:\n %s"), line);
+
+		add_preferred_base_object(p + 1);
+		add_object_entry(&oid, OBJ_NONE, p + 1, 0);
+	}
+}
+
+/* Remember to update object flag allocation in object.h */
+#define OBJECT_ADDED (1u<<20)
+
+static void show_commit(struct commit *commit, void *data)
+{
+	add_object_entry(&commit->object.oid, OBJ_COMMIT, NULL, 0);
+	commit->object.flags |= OBJECT_ADDED;
+
+	if (write_bitmap_index)
+		index_commit_for_bitmap(commit);
+
+	if (use_delta_islands)
+		propagate_island_marks(commit);
+}
+
+static void show_object(struct object *obj, const char *name, void *data)
+{
+	add_preferred_base_object(name);
+	add_object_entry(&obj->oid, obj->type, name, 0);
+	obj->flags |= OBJECT_ADDED;
+
+	if (use_delta_islands) {
+		const char *p;
+		unsigned depth;
+		struct object_entry *ent;
+
+		/* the empty string is a root tree, which is depth 0 */
+		depth = *name ? 1 : 0;
+		for (p = strchr(name, '/'); p; p = strchr(p + 1, '/'))
+			depth++;
+
+		ent = packlist_find(&to_pack, &obj->oid, NULL);
+		if (ent && depth > oe_tree_depth(&to_pack, ent))
+			oe_set_tree_depth(&to_pack, ent, depth);
+	}
+}
+
+static void show_object__ma_allow_any(struct object *obj, const char *name, void *data)
+{
+	assert(arg_missing_action == MA_ALLOW_ANY);
+
+	/*
+	 * Quietly ignore ALL missing objects.  This avoids problems with
+	 * staging them now and getting an odd error later.
+	 */
+	if (!has_object_file(&obj->oid))
+		return;
+
+	show_object(obj, name, data);
+}
+
+static void show_object__ma_allow_promisor(struct object *obj, const char *name, void *data)
+{
+	assert(arg_missing_action == MA_ALLOW_PROMISOR);
+
+	/*
+	 * Quietly ignore EXPECTED missing objects.  This avoids problems with
+	 * staging them now and getting an odd error later.
+	 */
+	if (!has_object_file(&obj->oid) && is_promisor_object(&obj->oid))
+		return;
+
+	show_object(obj, name, data);
+}
+
+static int option_parse_missing_action(const struct option *opt,
+				       const char *arg, int unset)
+{
+	assert(arg);
+	assert(!unset);
+
+	if (!strcmp(arg, "error")) {
+		arg_missing_action = MA_ERROR;
+		fn_show_object = show_object;
+		return 0;
+	}
+
+	if (!strcmp(arg, "allow-any")) {
+		arg_missing_action = MA_ALLOW_ANY;
+		fetch_if_missing = 0;
+		fn_show_object = show_object__ma_allow_any;
+		return 0;
+	}
+
+	if (!strcmp(arg, "allow-promisor")) {
+		arg_missing_action = MA_ALLOW_PROMISOR;
+		fetch_if_missing = 0;
+		fn_show_object = show_object__ma_allow_promisor;
+		return 0;
+	}
+
+	die(_("invalid value for --missing"));
+	return 0;
+}
+
+static void show_edge(struct commit *commit)
+{
+	add_preferred_base(&commit->object.oid);
+}
+
+struct in_pack_object {
+	off_t offset;
+	struct object *object;
+};
+
+struct in_pack {
+	unsigned int alloc;
+	unsigned int nr;
+	struct in_pack_object *array;
+};
+
+static void mark_in_pack_object(struct object *object, struct packed_git *p, struct in_pack *in_pack)
+{
+	in_pack->array[in_pack->nr].offset = find_pack_entry_one(object->oid.hash, p);
+	in_pack->array[in_pack->nr].object = object;
+	in_pack->nr++;
+}
+
+/*
+ * Compare the objects in the offset order, in order to emulate the
+ * "git rev-list --objects" output that produced the pack originally.
+ */
+static int ofscmp(const void *a_, const void *b_)
+{
+	struct in_pack_object *a = (struct in_pack_object *)a_;
+	struct in_pack_object *b = (struct in_pack_object *)b_;
+
+	if (a->offset < b->offset)
+		return -1;
+	else if (a->offset > b->offset)
+		return 1;
+	else
+		return oidcmp(&a->object->oid, &b->object->oid);
+}
+
+static void add_objects_in_unpacked_packs(void)
+{
+	struct packed_git *p;
+	struct in_pack in_pack;
+	uint32_t i;
+
+	memset(&in_pack, 0, sizeof(in_pack));
+
+	for (p = get_all_packs(the_repository); p; p = p->next) {
+		struct object_id oid;
+		struct object *o;
+
+		if (!p->pack_local || p->pack_keep || p->pack_keep_in_core)
+			continue;
+		if (open_pack_index(p))
+			die(_("cannot open pack index"));
+
+		ALLOC_GROW(in_pack.array,
+			   in_pack.nr + p->num_objects,
+			   in_pack.alloc);
+
+		for (i = 0; i < p->num_objects; i++) {
+			nth_packed_object_oid(&oid, p, i);
+			o = lookup_unknown_object(&oid);
+			if (!(o->flags & OBJECT_ADDED))
+				mark_in_pack_object(o, p, &in_pack);
+			o->flags |= OBJECT_ADDED;
+		}
+	}
+
+	if (in_pack.nr) {
+		QSORT(in_pack.array, in_pack.nr, ofscmp);
+		for (i = 0; i < in_pack.nr; i++) {
+			struct object *o = in_pack.array[i].object;
+			add_object_entry(&o->oid, o->type, "", 0);
+		}
+	}
+	free(in_pack.array);
+}
+
+static int add_loose_object(const struct object_id *oid, const char *path,
+			    void *data)
+{
+	enum object_type type = oid_object_info(the_repository, oid, NULL);
+
+	if (type < 0) {
+		warning(_("loose object at %s could not be examined"), path);
+		return 0;
+	}
+
+	add_object_entry(oid, type, "", 0);
+	return 0;
+}
+
+/*
+ * We actually don't even have to worry about reachability here.
+ * add_object_entry will weed out duplicates, so we just add every
+ * loose object we find.
+ */
+static void add_unreachable_loose_objects(void)
+{
+	for_each_loose_file_in_objdir(get_object_directory(),
+				      add_loose_object,
+				      NULL, NULL, NULL);
+}
+
+static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid)
+{
+	static struct packed_git *last_found = (void *)1;
+	struct packed_git *p;
+
+	p = (last_found != (void *)1) ? last_found :
+					get_all_packs(the_repository);
+
+	while (p) {
+		if ((!p->pack_local || p->pack_keep ||
+				p->pack_keep_in_core) &&
+			find_pack_entry_one(oid->hash, p)) {
+			last_found = p;
+			return 1;
+		}
+		if (p == last_found)
+			p = get_all_packs(the_repository);
+		else
+			p = p->next;
+		if (p == last_found)
+			p = p->next;
+	}
+	return 0;
+}
+
+/*
+ * Store a list of sha1s that are should not be discarded
+ * because they are either written too recently, or are
+ * reachable from another object that was.
+ *
+ * This is filled by get_object_list.
+ */
+static struct oid_array recent_objects;
+
+static int loosened_object_can_be_discarded(const struct object_id *oid,
+					    timestamp_t mtime)
+{
+	if (!unpack_unreachable_expiration)
+		return 0;
+	if (mtime > unpack_unreachable_expiration)
+		return 0;
+	if (oid_array_lookup(&recent_objects, oid) >= 0)
+		return 0;
+	return 1;
+}
+
+static void loosen_unused_packed_objects(void)
+{
+	struct packed_git *p;
+	uint32_t i;
+	struct object_id oid;
+
+	for (p = get_all_packs(the_repository); p; p = p->next) {
+		if (!p->pack_local || p->pack_keep || p->pack_keep_in_core)
+			continue;
+
+		if (open_pack_index(p))
+			die(_("cannot open pack index"));
+
+		for (i = 0; i < p->num_objects; i++) {
+			nth_packed_object_oid(&oid, p, i);
+			if (!packlist_find(&to_pack, &oid, NULL) &&
+			    !has_sha1_pack_kept_or_nonlocal(&oid) &&
+			    !loosened_object_can_be_discarded(&oid, p->mtime))
+				if (force_object_loose(&oid, p->mtime))
+					die(_("unable to force loose object"));
+		}
+	}
+}
+
+/*
+ * This tracks any options which pack-reuse code expects to be on, or which a
+ * reader of the pack might not understand, and which would therefore prevent
+ * blind reuse of what we have on disk.
+ */
+static int pack_options_allow_reuse(void)
+{
+	return pack_to_stdout &&
+	       allow_ofs_delta &&
+	       !ignore_packed_keep_on_disk &&
+	       !ignore_packed_keep_in_core &&
+	       (!local || !have_non_local_packs) &&
+	       !incremental;
+}
+
+static int get_object_list_from_bitmap(struct rev_info *revs)
+{
+	if (!(bitmap_git = prepare_bitmap_walk(revs)))
+		return -1;
+
+	if (pack_options_allow_reuse() &&
+	    !reuse_partial_packfile_from_bitmap(
+			bitmap_git,
+			&reuse_packfile,
+			&reuse_packfile_objects,
+			&reuse_packfile_offset)) {
+		assert(reuse_packfile_objects);
+		nr_result += reuse_packfile_objects;
+		display_progress(progress_state, nr_result);
+	}
+
+	traverse_bitmap_commit_list(bitmap_git, &add_object_entry_from_bitmap);
+	return 0;
+}
+
+static void record_recent_object(struct object *obj,
+				 const char *name,
+				 void *data)
+{
+	oid_array_append(&recent_objects, &obj->oid);
+}
+
+static void record_recent_commit(struct commit *commit, void *data)
+{
+	oid_array_append(&recent_objects, &commit->object.oid);
+}
+
+static void get_object_list(int ac, const char **av)
+{
+	struct rev_info revs;
+	struct setup_revision_opt s_r_opt = {
+		.allow_exclude_promisor_objects = 1,
+	};
+	char line[1000];
+	int flags = 0;
+	int save_warning;
+
+	repo_init_revisions(the_repository, &revs, NULL);
+	save_commit_buffer = 0;
+	setup_revisions(ac, av, &revs, &s_r_opt);
+
+	/* make sure shallows are read */
+	is_repository_shallow(the_repository);
+
+	save_warning = warn_on_object_refname_ambiguity;
+	warn_on_object_refname_ambiguity = 0;
+
+	while (fgets(line, sizeof(line), stdin) != NULL) {
+		int len = strlen(line);
+		if (len && line[len - 1] == '\n')
+			line[--len] = 0;
+		if (!len)
+			break;
+		if (*line == '-') {
+			if (!strcmp(line, "--not")) {
+				flags ^= UNINTERESTING;
+				write_bitmap_index = 0;
+				continue;
+			}
+			if (starts_with(line, "--shallow ")) {
+				struct object_id oid;
+				if (get_oid_hex(line + 10, &oid))
+					die("not an SHA-1 '%s'", line + 10);
+				register_shallow(the_repository, &oid);
+				use_bitmap_index = 0;
+				continue;
+			}
+			die(_("not a rev '%s'"), line);
+		}
+		if (handle_revision_arg(line, &revs, flags, REVARG_CANNOT_BE_FILENAME))
+			die(_("bad revision '%s'"), line);
+	}
+
+	warn_on_object_refname_ambiguity = save_warning;
+
+	if (use_bitmap_index && !get_object_list_from_bitmap(&revs))
+		return;
+
+	if (use_delta_islands)
+		load_delta_islands(the_repository, progress);
+
+	if (prepare_revision_walk(&revs))
+		die(_("revision walk setup failed"));
+	mark_edges_uninteresting(&revs, show_edge, sparse);
+
+	if (!fn_show_object)
+		fn_show_object = show_object;
+	traverse_commit_list_filtered(&filter_options, &revs,
+				      show_commit, fn_show_object, NULL,
+				      NULL);
+
+	if (unpack_unreachable_expiration) {
+		revs.ignore_missing_links = 1;
+		if (add_unseen_recent_objects_to_traversal(&revs,
+				unpack_unreachable_expiration))
+			die(_("unable to add recent objects"));
+		if (prepare_revision_walk(&revs))
+			die(_("revision walk setup failed"));
+		traverse_commit_list(&revs, record_recent_commit,
+				     record_recent_object, NULL);
+	}
+
+	if (keep_unreachable)
+		add_objects_in_unpacked_packs();
+	if (pack_loose_unreachable)
+		add_unreachable_loose_objects();
+	if (unpack_unreachable)
+		loosen_unused_packed_objects();
+
+	oid_array_clear(&recent_objects);
+}
+
+static void add_extra_kept_packs(const struct string_list *names)
+{
+	struct packed_git *p;
+
+	if (!names->nr)
+		return;
+
+	for (p = get_all_packs(the_repository); p; p = p->next) {
+		const char *name = basename(p->pack_name);
+		int i;
+
+		if (!p->pack_local)
+			continue;
+
+		for (i = 0; i < names->nr; i++)
+			if (!fspathcmp(name, names->items[i].string))
+				break;
+
+		if (i < names->nr) {
+			p->pack_keep_in_core = 1;
+			ignore_packed_keep_in_core = 1;
+			continue;
+		}
+	}
+}
+
+static int option_parse_index_version(const struct option *opt,
+				      const char *arg, int unset)
+{
+	char *c;
+	const char *val = arg;
+
+	BUG_ON_OPT_NEG(unset);
+
+	pack_idx_opts.version = strtoul(val, &c, 10);
+	if (pack_idx_opts.version > 2)
+		die(_("unsupported index version %s"), val);
+	if (*c == ',' && c[1])
+		pack_idx_opts.off32_limit = strtoul(c+1, &c, 0);
+	if (*c || pack_idx_opts.off32_limit & 0x80000000)
+		die(_("bad index version '%s'"), val);
+	return 0;
+}
+
+static int option_parse_unpack_unreachable(const struct option *opt,
+					   const char *arg, int unset)
+{
+	if (unset) {
+		unpack_unreachable = 0;
+		unpack_unreachable_expiration = 0;
+	}
+	else {
+		unpack_unreachable = 1;
+		if (arg)
+			unpack_unreachable_expiration = approxidate(arg);
+	}
+	return 0;
+}
+
+int cmd_pack_objects(int argc, const char **argv, const char *prefix)
+{
+	int use_internal_rev_list = 0;
+	int shallow = 0;
+	int all_progress_implied = 0;
+	struct argv_array rp = ARGV_ARRAY_INIT;
+	int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0;
+	int rev_list_index = 0;
+	struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
+	struct option pack_objects_options[] = {
+		OPT_SET_INT('q', "quiet", &progress,
+			    N_("do not show progress meter"), 0),
+		OPT_SET_INT(0, "progress", &progress,
+			    N_("show progress meter"), 1),
+		OPT_SET_INT(0, "all-progress", &progress,
+			    N_("show progress meter during object writing phase"), 2),
+		OPT_BOOL(0, "all-progress-implied",
+			 &all_progress_implied,
+			 N_("similar to --all-progress when progress meter is shown")),
+		{ OPTION_CALLBACK, 0, "index-version", NULL, N_("<version>[,<offset>]"),
+		  N_("write the pack index file in the specified idx format version"),
+		  PARSE_OPT_NONEG, option_parse_index_version },
+		OPT_MAGNITUDE(0, "max-pack-size", &pack_size_limit,
+			      N_("maximum size of each output pack file")),
+		OPT_BOOL(0, "local", &local,
+			 N_("ignore borrowed objects from alternate object store")),
+		OPT_BOOL(0, "incremental", &incremental,
+			 N_("ignore packed objects")),
+		OPT_INTEGER(0, "window", &window,
+			    N_("limit pack window by objects")),
+		OPT_MAGNITUDE(0, "window-memory", &window_memory_limit,
+			      N_("limit pack window by memory in addition to object limit")),
+		OPT_INTEGER(0, "depth", &depth,
+			    N_("maximum length of delta chain allowed in the resulting pack")),
+		OPT_BOOL(0, "reuse-delta", &reuse_delta,
+			 N_("reuse existing deltas")),
+		OPT_BOOL(0, "reuse-object", &reuse_object,
+			 N_("reuse existing objects")),
+		OPT_BOOL(0, "delta-base-offset", &allow_ofs_delta,
+			 N_("use OFS_DELTA objects")),
+		OPT_INTEGER(0, "threads", &delta_search_threads,
+			    N_("use threads when searching for best delta matches")),
+		OPT_BOOL(0, "non-empty", &non_empty,
+			 N_("do not create an empty pack output")),
+		OPT_BOOL(0, "revs", &use_internal_rev_list,
+			 N_("read revision arguments from standard input")),
+		OPT_SET_INT_F(0, "unpacked", &rev_list_unpacked,
+			      N_("limit the objects to those that are not yet packed"),
+			      1, PARSE_OPT_NONEG),
+		OPT_SET_INT_F(0, "all", &rev_list_all,
+			      N_("include objects reachable from any reference"),
+			      1, PARSE_OPT_NONEG),
+		OPT_SET_INT_F(0, "reflog", &rev_list_reflog,
+			      N_("include objects referred by reflog entries"),
+			      1, PARSE_OPT_NONEG),
+		OPT_SET_INT_F(0, "indexed-objects", &rev_list_index,
+			      N_("include objects referred to by the index"),
+			      1, PARSE_OPT_NONEG),
+		OPT_BOOL(0, "stdout", &pack_to_stdout,
+			 N_("output pack to stdout")),
+		OPT_BOOL(0, "include-tag", &include_tag,
+			 N_("include tag objects that refer to objects to be packed")),
+		OPT_BOOL(0, "keep-unreachable", &keep_unreachable,
+			 N_("keep unreachable objects")),
+		OPT_BOOL(0, "pack-loose-unreachable", &pack_loose_unreachable,
+			 N_("pack loose unreachable objects")),
+		{ OPTION_CALLBACK, 0, "unpack-unreachable", NULL, N_("time"),
+		  N_("unpack unreachable objects newer than <time>"),
+		  PARSE_OPT_OPTARG, option_parse_unpack_unreachable },
+		OPT_BOOL(0, "sparse", &sparse,
+			 N_("use the sparse reachability algorithm")),
+		OPT_BOOL(0, "thin", &thin,
+			 N_("create thin packs")),
+		OPT_BOOL(0, "shallow", &shallow,
+			 N_("create packs suitable for shallow fetches")),
+		OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep_on_disk,
+			 N_("ignore packs that have companion .keep file")),
+		OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"),
+				N_("ignore this pack")),
+		OPT_INTEGER(0, "compression", &pack_compression_level,
+			    N_("pack compression level")),
+		OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents,
+			    N_("do not hide commits by grafts"), 0),
+		OPT_BOOL(0, "use-bitmap-index", &use_bitmap_index,
+			 N_("use a bitmap index if available to speed up counting objects")),
+		OPT_SET_INT(0, "write-bitmap-index", &write_bitmap_index,
+			    N_("write a bitmap index together with the pack index"),
+			    WRITE_BITMAP_TRUE),
+		OPT_SET_INT_F(0, "write-bitmap-index-quiet",
+			      &write_bitmap_index,
+			      N_("write a bitmap index if possible"),
+			      WRITE_BITMAP_QUIET, PARSE_OPT_HIDDEN),
+		OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+		{ OPTION_CALLBACK, 0, "missing", NULL, N_("action"),
+		  N_("handling for missing objects"), PARSE_OPT_NONEG,
+		  option_parse_missing_action },
+		OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
+			 N_("do not pack objects in promisor packfiles")),
+		OPT_BOOL(0, "delta-islands", &use_delta_islands,
+			 N_("respect islands during delta compression")),
+		OPT_END(),
+	};
+
+	if (DFS_NUM_STATES > (1 << OE_DFS_STATE_BITS))
+		BUG("too many dfs states, increase OE_DFS_STATE_BITS");
+
+	read_replace_refs = 0;
+
+	sparse = git_env_bool("GIT_TEST_PACK_SPARSE", 0);
+	reset_pack_idx_option(&pack_idx_opts);
+	git_config(git_pack_config, NULL);
+
+	progress = isatty(2);
+	argc = parse_options(argc, argv, prefix, pack_objects_options,
+			     pack_usage, 0);
+
+	if (argc) {
+		base_name = argv[0];
+		argc--;
+	}
+	if (pack_to_stdout != !base_name || argc)
+		usage_with_options(pack_usage, pack_objects_options);
+
+	if (depth >= (1 << OE_DEPTH_BITS)) {
+		warning(_("delta chain depth %d is too deep, forcing %d"),
+			depth, (1 << OE_DEPTH_BITS) - 1);
+		depth = (1 << OE_DEPTH_BITS) - 1;
+	}
+	if (cache_max_small_delta_size >= (1U << OE_Z_DELTA_BITS)) {
+		warning(_("pack.deltaCacheLimit is too high, forcing %d"),
+			(1U << OE_Z_DELTA_BITS) - 1);
+		cache_max_small_delta_size = (1U << OE_Z_DELTA_BITS) - 1;
+	}
+
+	argv_array_push(&rp, "pack-objects");
+	if (thin) {
+		use_internal_rev_list = 1;
+		argv_array_push(&rp, shallow
+				? "--objects-edge-aggressive"
+				: "--objects-edge");
+	} else
+		argv_array_push(&rp, "--objects");
+
+	if (rev_list_all) {
+		use_internal_rev_list = 1;
+		argv_array_push(&rp, "--all");
+	}
+	if (rev_list_reflog) {
+		use_internal_rev_list = 1;
+		argv_array_push(&rp, "--reflog");
+	}
+	if (rev_list_index) {
+		use_internal_rev_list = 1;
+		argv_array_push(&rp, "--indexed-objects");
+	}
+	if (rev_list_unpacked) {
+		use_internal_rev_list = 1;
+		argv_array_push(&rp, "--unpacked");
+	}
+
+	if (exclude_promisor_objects) {
+		use_internal_rev_list = 1;
+		fetch_if_missing = 0;
+		argv_array_push(&rp, "--exclude-promisor-objects");
+	}
+	if (unpack_unreachable || keep_unreachable || pack_loose_unreachable)
+		use_internal_rev_list = 1;
+
+	if (!reuse_object)
+		reuse_delta = 0;
+	if (pack_compression_level == -1)
+		pack_compression_level = Z_DEFAULT_COMPRESSION;
+	else if (pack_compression_level < 0 || pack_compression_level > Z_BEST_COMPRESSION)
+		die(_("bad pack compression level %d"), pack_compression_level);
+
+	if (!delta_search_threads)	/* --threads=0 means autodetect */
+		delta_search_threads = online_cpus();
+
+	if (!HAVE_THREADS && delta_search_threads != 1)
+		warning(_("no threads support, ignoring --threads"));
+	if (!pack_to_stdout && !pack_size_limit)
+		pack_size_limit = pack_size_limit_cfg;
+	if (pack_to_stdout && pack_size_limit)
+		die(_("--max-pack-size cannot be used to build a pack for transfer"));
+	if (pack_size_limit && pack_size_limit < 1024*1024) {
+		warning(_("minimum pack size limit is 1 MiB"));
+		pack_size_limit = 1024*1024;
+	}
+
+	if (!pack_to_stdout && thin)
+		die(_("--thin cannot be used to build an indexable pack"));
+
+	if (keep_unreachable && unpack_unreachable)
+		die(_("--keep-unreachable and --unpack-unreachable are incompatible"));
+	if (!rev_list_all || !rev_list_reflog || !rev_list_index)
+		unpack_unreachable_expiration = 0;
+
+	if (filter_options.choice) {
+		if (!pack_to_stdout)
+			die(_("cannot use --filter without --stdout"));
+		use_bitmap_index = 0;
+	}
+
+	/*
+	 * "soft" reasons not to use bitmaps - for on-disk repack by default we want
+	 *
+	 * - to produce good pack (with bitmap index not-yet-packed objects are
+	 *   packed in suboptimal order).
+	 *
+	 * - to use more robust pack-generation codepath (avoiding possible
+	 *   bugs in bitmap code and possible bitmap index corruption).
+	 */
+	if (!pack_to_stdout)
+		use_bitmap_index_default = 0;
+
+	if (use_bitmap_index < 0)
+		use_bitmap_index = use_bitmap_index_default;
+
+	/* "hard" reasons not to use bitmaps; these just won't work at all */
+	if (!use_internal_rev_list || (!pack_to_stdout && write_bitmap_index) || is_repository_shallow(the_repository))
+		use_bitmap_index = 0;
+
+	if (pack_to_stdout || !rev_list_all)
+		write_bitmap_index = 0;
+
+	if (use_delta_islands)
+		argv_array_push(&rp, "--topo-order");
+
+	if (progress && all_progress_implied)
+		progress = 2;
+
+	add_extra_kept_packs(&keep_pack_list);
+	if (ignore_packed_keep_on_disk) {
+		struct packed_git *p;
+		for (p = get_all_packs(the_repository); p; p = p->next)
+			if (p->pack_local && p->pack_keep)
+				break;
+		if (!p) /* no keep-able packs found */
+			ignore_packed_keep_on_disk = 0;
+	}
+	if (local) {
+		/*
+		 * unlike ignore_packed_keep_on_disk above, we do not
+		 * want to unset "local" based on looking at packs, as
+		 * it also covers non-local objects
+		 */
+		struct packed_git *p;
+		for (p = get_all_packs(the_repository); p; p = p->next) {
+			if (!p->pack_local) {
+				have_non_local_packs = 1;
+				break;
+			}
+		}
+	}
+
+	trace2_region_enter("pack-objects", "enumerate-objects",
+			    the_repository);
+	prepare_packing_data(the_repository, &to_pack);
+
+	if (progress)
+		progress_state = start_progress(_("Enumerating objects"), 0);
+	if (!use_internal_rev_list)
+		read_object_list_from_stdin();
+	else {
+		get_object_list(rp.argc, rp.argv);
+		argv_array_clear(&rp);
+	}
+	cleanup_preferred_base();
+	if (include_tag && nr_result)
+		for_each_ref(add_ref_tag, NULL);
+	stop_progress(&progress_state);
+	trace2_region_leave("pack-objects", "enumerate-objects",
+			    the_repository);
+
+	if (non_empty && !nr_result)
+		return 0;
+	if (nr_result) {
+		trace2_region_enter("pack-objects", "prepare-pack",
+				    the_repository);
+		prepare_pack(window, depth);
+		trace2_region_leave("pack-objects", "prepare-pack",
+				    the_repository);
+	}
+
+	trace2_region_enter("pack-objects", "write-pack-file", the_repository);
+	write_pack_file();
+	trace2_region_leave("pack-objects", "write-pack-file", the_repository);
+
+	if (progress)
+		fprintf_ln(stderr,
+			   _("Total %"PRIu32" (delta %"PRIu32"),"
+			     " reused %"PRIu32" (delta %"PRIu32")"),
+			   written, written_delta, reused, reused_delta);
+	return 0;
+}
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
new file mode 100644
index 0000000000..178e3409b7
--- /dev/null
+++ b/builtin/pack-redundant.c
@@ -0,0 +1,653 @@
+/*
+*
+* Copyright 2005, Lukas Sandstrom <lukass@etek.chalmers.se>
+*
+* This file is licensed under the GPL v2.
+*
+*/
+
+#include "builtin.h"
+#include "repository.h"
+#include "packfile.h"
+#include "object-store.h"
+
+#define BLKSIZE 512
+
+static const char pack_redundant_usage[] =
+"git pack-redundant [--verbose] [--alt-odb] (--all | <filename.pack>...)";
+
+static int load_all_packs, verbose, alt_odb;
+
+struct llist_item {
+	struct llist_item *next;
+	const struct object_id *oid;
+};
+static struct llist {
+	struct llist_item *front;
+	struct llist_item *back;
+	size_t size;
+} *all_objects; /* all objects which must be present in local packfiles */
+
+static struct pack_list {
+	struct pack_list *next;
+	struct packed_git *pack;
+	struct llist *unique_objects;
+	struct llist *remaining_objects;
+	size_t all_objects_size;
+} *local_packs = NULL, *altodb_packs = NULL;
+
+static struct llist_item *free_nodes;
+
+static inline void llist_item_put(struct llist_item *item)
+{
+	item->next = free_nodes;
+	free_nodes = item;
+}
+
+static inline struct llist_item *llist_item_get(void)
+{
+	struct llist_item *new_item;
+	if ( free_nodes ) {
+		new_item = free_nodes;
+		free_nodes = free_nodes->next;
+	} else {
+		int i = 1;
+		ALLOC_ARRAY(new_item, BLKSIZE);
+		for (; i < BLKSIZE; i++)
+			llist_item_put(&new_item[i]);
+	}
+	return new_item;
+}
+
+static inline void llist_init(struct llist **list)
+{
+	*list = xmalloc(sizeof(struct llist));
+	(*list)->front = (*list)->back = NULL;
+	(*list)->size = 0;
+}
+
+static struct llist * llist_copy(struct llist *list)
+{
+	struct llist *ret;
+	struct llist_item *new_item, *old_item, *prev;
+
+	llist_init(&ret);
+
+	if ((ret->size = list->size) == 0)
+		return ret;
+
+	new_item = ret->front = llist_item_get();
+	new_item->oid = list->front->oid;
+
+	old_item = list->front->next;
+	while (old_item) {
+		prev = new_item;
+		new_item = llist_item_get();
+		prev->next = new_item;
+		new_item->oid = old_item->oid;
+		old_item = old_item->next;
+	}
+	new_item->next = NULL;
+	ret->back = new_item;
+
+	return ret;
+}
+
+static inline struct llist_item *llist_insert(struct llist *list,
+					      struct llist_item *after,
+					      const struct object_id *oid)
+{
+	struct llist_item *new_item = llist_item_get();
+	new_item->oid = oid;
+	new_item->next = NULL;
+
+	if (after != NULL) {
+		new_item->next = after->next;
+		after->next = new_item;
+		if (after == list->back)
+			list->back = new_item;
+	} else {/* insert in front */
+		if (list->size == 0)
+			list->back = new_item;
+		else
+			new_item->next = list->front;
+		list->front = new_item;
+	}
+	list->size++;
+	return new_item;
+}
+
+static inline struct llist_item *llist_insert_back(struct llist *list,
+						   const struct object_id *oid)
+{
+	return llist_insert(list, list->back, oid);
+}
+
+static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,
+			const struct object_id *oid, struct llist_item *hint)
+{
+	struct llist_item *prev = NULL, *l;
+
+	l = (hint == NULL) ? list->front : hint;
+	while (l) {
+		int cmp = oidcmp(l->oid, oid);
+		if (cmp > 0) { /* we insert before this entry */
+			return llist_insert(list, prev, oid);
+		}
+		if (!cmp) { /* already exists */
+			return l;
+		}
+		prev = l;
+		l = l->next;
+	}
+	/* insert at the end */
+	return llist_insert_back(list, oid);
+}
+
+/* returns a pointer to an item in front of sha1 */
+static inline struct llist_item * llist_sorted_remove(struct llist *list, const struct object_id *oid, struct llist_item *hint)
+{
+	struct llist_item *prev, *l;
+
+redo_from_start:
+	l = (hint == NULL) ? list->front : hint;
+	prev = NULL;
+	while (l) {
+		const int cmp = oidcmp(l->oid, oid);
+		if (cmp > 0) /* not in list, since sorted */
+			return prev;
+		if (!cmp) { /* found */
+			if (prev == NULL) {
+				if (hint != NULL && hint != list->front) {
+					/* we don't know the previous element */
+					hint = NULL;
+					goto redo_from_start;
+				}
+				list->front = l->next;
+			} else
+				prev->next = l->next;
+			if (l == list->back)
+				list->back = prev;
+			llist_item_put(l);
+			list->size--;
+			return prev;
+		}
+		prev = l;
+		l = l->next;
+	}
+	return prev;
+}
+
+/* computes A\B */
+static void llist_sorted_difference_inplace(struct llist *A,
+				     struct llist *B)
+{
+	struct llist_item *hint, *b;
+
+	hint = NULL;
+	b = B->front;
+
+	while (b) {
+		hint = llist_sorted_remove(A, b->oid, hint);
+		b = b->next;
+	}
+}
+
+static inline struct pack_list * pack_list_insert(struct pack_list **pl,
+					   struct pack_list *entry)
+{
+	struct pack_list *p = xmalloc(sizeof(struct pack_list));
+	memcpy(p, entry, sizeof(struct pack_list));
+	p->next = *pl;
+	*pl = p;
+	return p;
+}
+
+static inline size_t pack_list_size(struct pack_list *pl)
+{
+	size_t ret = 0;
+	while (pl) {
+		ret++;
+		pl = pl->next;
+	}
+	return ret;
+}
+
+static struct pack_list * pack_list_difference(const struct pack_list *A,
+					       const struct pack_list *B)
+{
+	struct pack_list *ret;
+	const struct pack_list *pl;
+
+	if (A == NULL)
+		return NULL;
+
+	pl = B;
+	while (pl != NULL) {
+		if (A->pack == pl->pack)
+			return pack_list_difference(A->next, B);
+		pl = pl->next;
+	}
+	ret = xmalloc(sizeof(struct pack_list));
+	memcpy(ret, A, sizeof(struct pack_list));
+	ret->next = pack_list_difference(A->next, B);
+	return ret;
+}
+
+static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
+{
+	unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step;
+	const unsigned char *p1_base, *p2_base;
+	struct llist_item *p1_hint = NULL, *p2_hint = NULL;
+	const unsigned int hashsz = the_hash_algo->rawsz;
+
+	if (!p1->unique_objects)
+		p1->unique_objects = llist_copy(p1->remaining_objects);
+	if (!p2->unique_objects)
+		p2->unique_objects = llist_copy(p2->remaining_objects);
+
+	p1_base = p1->pack->index_data;
+	p2_base = p2->pack->index_data;
+	p1_base += 256 * 4 + ((p1->pack->index_version < 2) ? 4 : 8);
+	p2_base += 256 * 4 + ((p2->pack->index_version < 2) ? 4 : 8);
+	p1_step = hashsz + ((p1->pack->index_version < 2) ? 4 : 0);
+	p2_step = hashsz + ((p2->pack->index_version < 2) ? 4 : 0);
+
+	while (p1_off < p1->pack->num_objects * p1_step &&
+	       p2_off < p2->pack->num_objects * p2_step)
+	{
+		const int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
+		/* cmp ~ p1 - p2 */
+		if (cmp == 0) {
+			p1_hint = llist_sorted_remove(p1->unique_objects,
+					(const struct object_id *)(p1_base + p1_off),
+					p1_hint);
+			p2_hint = llist_sorted_remove(p2->unique_objects,
+					(const struct object_id *)(p1_base + p1_off),
+					p2_hint);
+			p1_off += p1_step;
+			p2_off += p2_step;
+			continue;
+		}
+		if (cmp < 0) { /* p1 has the object, p2 doesn't */
+			p1_off += p1_step;
+		} else { /* p2 has the object, p1 doesn't */
+			p2_off += p2_step;
+		}
+	}
+}
+
+static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
+{
+	size_t ret = 0;
+	unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step;
+	const unsigned char *p1_base, *p2_base;
+	const unsigned int hashsz = the_hash_algo->rawsz;
+
+	p1_base = p1->index_data;
+	p2_base = p2->index_data;
+	p1_base += 256 * 4 + ((p1->index_version < 2) ? 4 : 8);
+	p2_base += 256 * 4 + ((p2->index_version < 2) ? 4 : 8);
+	p1_step = hashsz + ((p1->index_version < 2) ? 4 : 0);
+	p2_step = hashsz + ((p2->index_version < 2) ? 4 : 0);
+
+	while (p1_off < p1->num_objects * p1_step &&
+	       p2_off < p2->num_objects * p2_step)
+	{
+		int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
+		/* cmp ~ p1 - p2 */
+		if (cmp == 0) {
+			ret++;
+			p1_off += p1_step;
+			p2_off += p2_step;
+			continue;
+		}
+		if (cmp < 0) { /* p1 has the object, p2 doesn't */
+			p1_off += p1_step;
+		} else { /* p2 has the object, p1 doesn't */
+			p2_off += p2_step;
+		}
+	}
+	return ret;
+}
+
+/* another O(n^2) function ... */
+static size_t get_pack_redundancy(struct pack_list *pl)
+{
+	struct pack_list *subset;
+	size_t ret = 0;
+
+	if (pl == NULL)
+		return 0;
+
+	while ((subset = pl->next)) {
+		while (subset) {
+			ret += sizeof_union(pl->pack, subset->pack);
+			subset = subset->next;
+		}
+		pl = pl->next;
+	}
+	return ret;
+}
+
+static inline off_t pack_set_bytecount(struct pack_list *pl)
+{
+	off_t ret = 0;
+	while (pl) {
+		ret += pl->pack->pack_size;
+		ret += pl->pack->index_size;
+		pl = pl->next;
+	}
+	return ret;
+}
+
+static int cmp_remaining_objects(const void *a, const void *b)
+{
+	struct pack_list *pl_a = *((struct pack_list **)a);
+	struct pack_list *pl_b = *((struct pack_list **)b);
+
+	if (pl_a->remaining_objects->size == pl_b->remaining_objects->size) {
+		/* have the same remaining_objects, big pack first */
+		if (pl_a->all_objects_size == pl_b->all_objects_size)
+			return 0;
+		else if (pl_a->all_objects_size < pl_b->all_objects_size)
+			return 1;
+		else
+			return -1;
+	} else if (pl_a->remaining_objects->size < pl_b->remaining_objects->size) {
+		/* sort by remaining objects, more objects first */
+		return 1;
+	} else {
+		return -1;
+	}
+}
+
+/* Sort pack_list, greater size of remaining_objects first */
+static void sort_pack_list(struct pack_list **pl)
+{
+	struct pack_list **ary, *p;
+	int i;
+	size_t n = pack_list_size(*pl);
+
+	if (n < 2)
+		return;
+
+	/* prepare an array of packed_list for easier sorting */
+	ary = xcalloc(n, sizeof(struct pack_list *));
+	for (n = 0, p = *pl; p; p = p->next)
+		ary[n++] = p;
+
+	QSORT(ary, n, cmp_remaining_objects);
+
+	/* link them back again */
+	for (i = 0; i < n - 1; i++)
+		ary[i]->next = ary[i + 1];
+	ary[n - 1]->next = NULL;
+	*pl = ary[0];
+
+	free(ary);
+}
+
+
+static void minimize(struct pack_list **min)
+{
+	struct pack_list *pl, *unique = NULL, *non_unique = NULL;
+	struct llist *missing, *unique_pack_objects;
+
+	pl = local_packs;
+	while (pl) {
+		if (pl->unique_objects->size)
+			pack_list_insert(&unique, pl);
+		else
+			pack_list_insert(&non_unique, pl);
+		pl = pl->next;
+	}
+	/* find out which objects are missing from the set of unique packs */
+	missing = llist_copy(all_objects);
+	pl = unique;
+	while (pl) {
+		llist_sorted_difference_inplace(missing, pl->remaining_objects);
+		pl = pl->next;
+	}
+
+	*min = unique;
+
+	/* return if there are no objects missing from the unique set */
+	if (missing->size == 0) {
+		free(missing);
+		return;
+	}
+
+	unique_pack_objects = llist_copy(all_objects);
+	llist_sorted_difference_inplace(unique_pack_objects, missing);
+
+	/* remove unique pack objects from the non_unique packs */
+	pl = non_unique;
+	while (pl) {
+		llist_sorted_difference_inplace(pl->remaining_objects, unique_pack_objects);
+		pl = pl->next;
+	}
+
+	while (non_unique) {
+		/* sort the non_unique packs, greater size of remaining_objects first */
+		sort_pack_list(&non_unique);
+		if (non_unique->remaining_objects->size == 0)
+			break;
+
+		pack_list_insert(min, non_unique);
+
+		for (pl = non_unique->next; pl && pl->remaining_objects->size > 0;  pl = pl->next)
+			llist_sorted_difference_inplace(pl->remaining_objects, non_unique->remaining_objects);
+
+		non_unique = non_unique->next;
+	}
+}
+
+static void load_all_objects(void)
+{
+	struct pack_list *pl = local_packs;
+	struct llist_item *hint, *l;
+
+	llist_init(&all_objects);
+
+	while (pl) {
+		hint = NULL;
+		l = pl->remaining_objects->front;
+		while (l) {
+			hint = llist_insert_sorted_unique(all_objects,
+							  l->oid, hint);
+			l = l->next;
+		}
+		pl = pl->next;
+	}
+	/* remove objects present in remote packs */
+	pl = altodb_packs;
+	while (pl) {
+		llist_sorted_difference_inplace(all_objects, pl->remaining_objects);
+		pl = pl->next;
+	}
+}
+
+/* this scales like O(n^2) */
+static void cmp_local_packs(void)
+{
+	struct pack_list *subset, *pl = local_packs;
+
+	while ((subset = pl)) {
+		while ((subset = subset->next))
+			cmp_two_packs(pl, subset);
+		pl = pl->next;
+	}
+}
+
+static void scan_alt_odb_packs(void)
+{
+	struct pack_list *local, *alt;
+
+	alt = altodb_packs;
+	while (alt) {
+		local = local_packs;
+		while (local) {
+			llist_sorted_difference_inplace(local->remaining_objects,
+							alt->remaining_objects);
+			local = local->next;
+		}
+		alt = alt->next;
+	}
+}
+
+static struct pack_list * add_pack(struct packed_git *p)
+{
+	struct pack_list l;
+	unsigned long off = 0, step;
+	const unsigned char *base;
+
+	if (!p->pack_local && !(alt_odb || verbose))
+		return NULL;
+
+	l.pack = p;
+	llist_init(&l.remaining_objects);
+
+	if (open_pack_index(p))
+		return NULL;
+
+	base = p->index_data;
+	base += 256 * 4 + ((p->index_version < 2) ? 4 : 8);
+	step = the_hash_algo->rawsz + ((p->index_version < 2) ? 4 : 0);
+	while (off < p->num_objects * step) {
+		llist_insert_back(l.remaining_objects, (const struct object_id *)(base + off));
+		off += step;
+	}
+	l.all_objects_size = l.remaining_objects->size;
+	l.unique_objects = NULL;
+	if (p->pack_local)
+		return pack_list_insert(&local_packs, &l);
+	else
+		return pack_list_insert(&altodb_packs, &l);
+}
+
+static struct pack_list * add_pack_file(const char *filename)
+{
+	struct packed_git *p = get_all_packs(the_repository);
+
+	if (strlen(filename) < 40)
+		die("Bad pack filename: %s", filename);
+
+	while (p) {
+		if (strstr(p->pack_name, filename))
+			return add_pack(p);
+		p = p->next;
+	}
+	die("Filename %s not found in packed_git", filename);
+}
+
+static void load_all(void)
+{
+	struct packed_git *p = get_all_packs(the_repository);
+
+	while (p) {
+		add_pack(p);
+		p = p->next;
+	}
+}
+
+int cmd_pack_redundant(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct pack_list *min = NULL, *red, *pl;
+	struct llist *ignore;
+	struct object_id *oid;
+	char buf[GIT_MAX_HEXSZ + 2]; /* hex hash + \n + \0 */
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(pack_redundant_usage);
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "--")) {
+			i++;
+			break;
+		}
+		if (!strcmp(arg, "--all")) {
+			load_all_packs = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--verbose")) {
+			verbose = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--alt-odb")) {
+			alt_odb = 1;
+			continue;
+		}
+		if (*arg == '-')
+			usage(pack_redundant_usage);
+		else
+			break;
+	}
+
+	if (load_all_packs)
+		load_all();
+	else
+		while (*(argv + i) != NULL)
+			add_pack_file(*(argv + i++));
+
+	if (local_packs == NULL)
+		die("Zero packs found!");
+
+	load_all_objects();
+
+	if (alt_odb)
+		scan_alt_odb_packs();
+
+	/* ignore objects given on stdin */
+	llist_init(&ignore);
+	if (!isatty(0)) {
+		while (fgets(buf, sizeof(buf), stdin)) {
+			oid = xmalloc(sizeof(*oid));
+			if (get_oid_hex(buf, oid))
+				die("Bad object ID on stdin: %s", buf);
+			llist_insert_sorted_unique(ignore, oid, NULL);
+		}
+	}
+	llist_sorted_difference_inplace(all_objects, ignore);
+	pl = local_packs;
+	while (pl) {
+		llist_sorted_difference_inplace(pl->remaining_objects, ignore);
+		pl = pl->next;
+	}
+
+	cmp_local_packs();
+
+	minimize(&min);
+
+	if (verbose) {
+		fprintf(stderr, "There are %lu packs available in alt-odbs.\n",
+			(unsigned long)pack_list_size(altodb_packs));
+		fprintf(stderr, "The smallest (bytewise) set of packs is:\n");
+		pl = min;
+		while (pl) {
+			fprintf(stderr, "\t%s\n", pl->pack->pack_name);
+			pl = pl->next;
+		}
+		fprintf(stderr, "containing %lu duplicate objects "
+				"with a total size of %lukb.\n",
+			(unsigned long)get_pack_redundancy(min),
+			(unsigned long)pack_set_bytecount(min)/1024);
+		fprintf(stderr, "A total of %lu unique objects were considered.\n",
+			(unsigned long)all_objects->size);
+		fprintf(stderr, "Redundant packs (with indexes):\n");
+	}
+	pl = red = pack_list_difference(local_packs, min);
+	while (pl) {
+		printf("%s\n%s\n",
+		       sha1_pack_index_name(pl->pack->hash),
+		       pl->pack->pack_name);
+		pl = pl->next;
+	}
+	if (verbose)
+		fprintf(stderr, "%luMB of redundant packs in total.\n",
+			(unsigned long)pack_set_bytecount(red)/(1024*1024));
+
+	return 0;
+}
diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c
new file mode 100644
index 0000000000..cfbd5c36c7
--- /dev/null
+++ b/builtin/pack-refs.c
@@ -0,0 +1,24 @@
+#include "builtin.h"
+#include "config.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "repository.h"
+
+static char const * const pack_refs_usage[] = {
+	N_("git pack-refs [<options>]"),
+	NULL
+};
+
+int cmd_pack_refs(int argc, const char **argv, const char *prefix)
+{
+	unsigned int flags = PACK_REFS_PRUNE;
+	struct option opts[] = {
+		OPT_BIT(0, "all",   &flags, N_("pack everything"), PACK_REFS_ALL),
+		OPT_BIT(0, "prune", &flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE),
+		OPT_END(),
+	};
+	git_config(git_default_config, NULL);
+	if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))
+		usage_with_options(pack_refs_usage, opts);
+	return refs_pack_refs(get_main_ref_store(the_repository), flags);
+}
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
new file mode 100644
index 0000000000..bd28b80b2d
--- /dev/null
+++ b/builtin/patch-id.c
@@ -0,0 +1,184 @@
+#include "builtin.h"
+#include "config.h"
+#include "diff.h"
+
+static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
+{
+	char name[50];
+
+	if (!patchlen)
+		return;
+
+	memcpy(name, oid_to_hex(id), GIT_SHA1_HEXSZ + 1);
+	printf("%s %s\n", oid_to_hex(result), name);
+}
+
+static int remove_space(char *line)
+{
+	char *src = line;
+	char *dst = line;
+	unsigned char c;
+
+	while ((c = *src++) != '\0') {
+		if (!isspace(c))
+			*dst++ = c;
+	}
+	return dst - line;
+}
+
+static int scan_hunk_header(const char *p, int *p_before, int *p_after)
+{
+	static const char digits[] = "0123456789";
+	const char *q, *r;
+	int n;
+
+	q = p + 4;
+	n = strspn(q, digits);
+	if (q[n] == ',') {
+		q += n + 1;
+		n = strspn(q, digits);
+	}
+	if (n == 0 || q[n] != ' ' || q[n+1] != '+')
+		return 0;
+
+	r = q + n + 2;
+	n = strspn(r, digits);
+	if (r[n] == ',') {
+		r += n + 1;
+		n = strspn(r, digits);
+	}
+	if (n == 0)
+		return 0;
+
+	*p_before = atoi(q);
+	*p_after = atoi(r);
+	return 1;
+}
+
+static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
+			   struct strbuf *line_buf, int stable)
+{
+	int patchlen = 0, found_next = 0;
+	int before = -1, after = -1;
+	git_SHA_CTX ctx;
+
+	git_SHA1_Init(&ctx);
+	oidclr(result);
+
+	while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
+		char *line = line_buf->buf;
+		const char *p = line;
+		int len;
+
+		if (!skip_prefix(line, "diff-tree ", &p) &&
+		    !skip_prefix(line, "commit ", &p) &&
+		    !skip_prefix(line, "From ", &p) &&
+		    starts_with(line, "\\ ") && 12 < strlen(line))
+			continue;
+
+		if (!get_oid_hex(p, next_oid)) {
+			found_next = 1;
+			break;
+		}
+
+		/* Ignore commit comments */
+		if (!patchlen && !starts_with(line, "diff "))
+			continue;
+
+		/* Parsing diff header?  */
+		if (before == -1) {
+			if (starts_with(line, "index "))
+				continue;
+			else if (starts_with(line, "--- "))
+				before = after = 1;
+			else if (!isalpha(line[0]))
+				break;
+		}
+
+		/* Looking for a valid hunk header?  */
+		if (before == 0 && after == 0) {
+			if (starts_with(line, "@@ -")) {
+				/* Parse next hunk, but ignore line numbers.  */
+				scan_hunk_header(line, &before, &after);
+				continue;
+			}
+
+			/* Split at the end of the patch.  */
+			if (!starts_with(line, "diff "))
+				break;
+
+			/* Else we're parsing another header.  */
+			if (stable)
+				flush_one_hunk(result, &ctx);
+			before = after = -1;
+		}
+
+		/* If we get here, we're inside a hunk.  */
+		if (line[0] == '-' || line[0] == ' ')
+			before--;
+		if (line[0] == '+' || line[0] == ' ')
+			after--;
+
+		/* Compute the sha without whitespace */
+		len = remove_space(line);
+		patchlen += len;
+		git_SHA1_Update(&ctx, line, len);
+	}
+
+	if (!found_next)
+		oidclr(next_oid);
+
+	flush_one_hunk(result, &ctx);
+
+	return patchlen;
+}
+
+static void generate_id_list(int stable)
+{
+	struct object_id oid, n, result;
+	int patchlen;
+	struct strbuf line_buf = STRBUF_INIT;
+
+	oidclr(&oid);
+	while (!feof(stdin)) {
+		patchlen = get_one_patchid(&n, &result, &line_buf, stable);
+		flush_current_id(patchlen, &oid, &result);
+		oidcpy(&oid, &n);
+	}
+	strbuf_release(&line_buf);
+}
+
+static const char patch_id_usage[] = "git patch-id [--stable | --unstable]";
+
+static int git_patch_id_config(const char *var, const char *value, void *cb)
+{
+	int *stable = cb;
+
+	if (!strcmp(var, "patchid.stable")) {
+		*stable = git_config_bool(var, value);
+		return 0;
+	}
+
+	return git_default_config(var, value, cb);
+}
+
+int cmd_patch_id(int argc, const char **argv, const char *prefix)
+{
+	int stable = -1;
+
+	git_config(git_patch_id_config, &stable);
+
+	/* If nothing is set, default to unstable. */
+	if (stable < 0)
+		stable = 0;
+
+	if (argc == 2 && !strcmp(argv[1], "--stable"))
+		stable = 1;
+	else if (argc == 2 && !strcmp(argv[1], "--unstable"))
+		stable = 0;
+	else if (argc != 1)
+		usage(patch_id_usage);
+
+	generate_id_list(stable);
+	return 0;
+}
diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c
new file mode 100644
index 0000000000..48c5e78e33
--- /dev/null
+++ b/builtin/prune-packed.c
@@ -0,0 +1,73 @@
+#include "builtin.h"
+#include "cache.h"
+#include "progress.h"
+#include "parse-options.h"
+#include "packfile.h"
+#include "object-store.h"
+
+static const char * const prune_packed_usage[] = {
+	N_("git prune-packed [-n | --dry-run] [-q | --quiet]"),
+	NULL
+};
+
+static struct progress *progress;
+
+static int prune_subdir(unsigned int nr, const char *path, void *data)
+{
+	int *opts = data;
+	display_progress(progress, nr + 1);
+	if (!(*opts & PRUNE_PACKED_DRY_RUN))
+		rmdir(path);
+	return 0;
+}
+
+static int prune_object(const struct object_id *oid, const char *path,
+			 void *data)
+{
+	int *opts = data;
+
+	if (!has_object_pack(oid))
+		return 0;
+
+	if (*opts & PRUNE_PACKED_DRY_RUN)
+		printf("rm -f %s\n", path);
+	else
+		unlink_or_warn(path);
+	return 0;
+}
+
+void prune_packed_objects(int opts)
+{
+	if (opts & PRUNE_PACKED_VERBOSE)
+		progress = start_delayed_progress(_("Removing duplicate objects"), 256);
+
+	for_each_loose_file_in_objdir(get_object_directory(),
+				      prune_object, NULL, prune_subdir, &opts);
+
+	/* Ensure we show 100% before finishing progress */
+	display_progress(progress, 256);
+	stop_progress(&progress);
+}
+
+int cmd_prune_packed(int argc, const char **argv, const char *prefix)
+{
+	int opts = isatty(2) ? PRUNE_PACKED_VERBOSE : 0;
+	const struct option prune_packed_options[] = {
+		OPT_BIT('n', "dry-run", &opts, N_("dry run"),
+			PRUNE_PACKED_DRY_RUN),
+		OPT_NEGBIT('q', "quiet", &opts, N_("be quiet"),
+			   PRUNE_PACKED_VERBOSE),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, prune_packed_options,
+			     prune_packed_usage, 0);
+
+	if (argc > 0)
+		usage_msg_opt(_("too many arguments"),
+			      prune_packed_usage,
+			      prune_packed_options);
+
+	prune_packed_objects(opts);
+	return 0;
+}
diff --git a/builtin/prune.c b/builtin/prune.c
new file mode 100644
index 0000000000..2b76872ad2
--- /dev/null
+++ b/builtin/prune.c
@@ -0,0 +1,187 @@
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "builtin.h"
+#include "reachable.h"
+#include "parse-options.h"
+#include "progress.h"
+#include "object-store.h"
+
+static const char * const prune_usage[] = {
+	N_("git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"),
+	NULL
+};
+static int show_only;
+static int verbose;
+static timestamp_t expire;
+static int show_progress = -1;
+
+static int prune_tmp_file(const char *fullpath)
+{
+	struct stat st;
+	if (lstat(fullpath, &st))
+		return error("Could not stat '%s'", fullpath);
+	if (st.st_mtime > expire)
+		return 0;
+	if (show_only || verbose)
+		printf("Removing stale temporary file %s\n", fullpath);
+	if (!show_only)
+		unlink_or_warn(fullpath);
+	return 0;
+}
+
+static void perform_reachability_traversal(struct rev_info *revs)
+{
+	static int initialized;
+	struct progress *progress = NULL;
+
+	if (initialized)
+		return;
+
+	if (show_progress)
+		progress = start_delayed_progress(_("Checking connectivity"), 0);
+	mark_reachable_objects(revs, 1, expire, progress);
+	stop_progress(&progress);
+	initialized = 1;
+}
+
+static int is_object_reachable(const struct object_id *oid,
+			       struct rev_info *revs)
+{
+	struct object *obj;
+
+	perform_reachability_traversal(revs);
+
+	obj = lookup_object(the_repository, oid);
+	return obj && (obj->flags & SEEN);
+}
+
+static int prune_object(const struct object_id *oid, const char *fullpath,
+			void *data)
+{
+	struct rev_info *revs = data;
+	struct stat st;
+
+	if (is_object_reachable(oid, revs))
+		return 0;
+
+	if (lstat(fullpath, &st)) {
+		/* report errors, but do not stop pruning */
+		error("Could not stat '%s'", fullpath);
+		return 0;
+	}
+	if (st.st_mtime > expire)
+		return 0;
+	if (show_only || verbose) {
+		enum object_type type = oid_object_info(the_repository, oid,
+							NULL);
+		printf("%s %s\n", oid_to_hex(oid),
+		       (type > 0) ? type_name(type) : "unknown");
+	}
+	if (!show_only)
+		unlink_or_warn(fullpath);
+	return 0;
+}
+
+static int prune_cruft(const char *basename, const char *path, void *data)
+{
+	if (starts_with(basename, "tmp_obj_"))
+		prune_tmp_file(path);
+	else
+		fprintf(stderr, "bad sha1 file: %s\n", path);
+	return 0;
+}
+
+static int prune_subdir(unsigned int nr, const char *path, void *data)
+{
+	if (!show_only)
+		rmdir(path);
+	return 0;
+}
+
+/*
+ * Write errors (particularly out of space) can result in
+ * failed temporary packs (and more rarely indexes and other
+ * files beginning with "tmp_") accumulating in the object
+ * and the pack directories.
+ */
+static void remove_temporary_files(const char *path)
+{
+	DIR *dir;
+	struct dirent *de;
+
+	dir = opendir(path);
+	if (!dir) {
+		fprintf(stderr, "Unable to open directory %s\n", path);
+		return;
+	}
+	while ((de = readdir(dir)) != NULL)
+		if (starts_with(de->d_name, "tmp_"))
+			prune_tmp_file(mkpath("%s/%s", path, de->d_name));
+	closedir(dir);
+}
+
+int cmd_prune(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info revs;
+	int exclude_promisor_objects = 0;
+	const struct option options[] = {
+		OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
+		OPT__VERBOSE(&verbose, N_("report pruned objects")),
+		OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
+		OPT_EXPIRY_DATE(0, "expire", &expire,
+				N_("expire objects older than <time>")),
+		OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
+			 N_("limit traversal to objects outside promisor packfiles")),
+		OPT_END()
+	};
+	char *s;
+
+	expire = TIME_MAX;
+	save_commit_buffer = 0;
+	read_replace_refs = 0;
+	ref_paranoia = 1;
+	repo_init_revisions(the_repository, &revs, prefix);
+
+	argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
+
+	if (repository_format_precious_objects)
+		die(_("cannot prune in a precious-objects repo"));
+
+	while (argc--) {
+		struct object_id oid;
+		const char *name = *argv++;
+
+		if (!get_oid(name, &oid)) {
+			struct object *object = parse_object_or_die(&oid,
+								    name);
+			add_pending_object(&revs, object, "");
+		}
+		else
+			die("unrecognized argument: %s", name);
+	}
+
+	if (show_progress == -1)
+		show_progress = isatty(2);
+	if (exclude_promisor_objects) {
+		fetch_if_missing = 0;
+		revs.exclude_promisor_objects = 1;
+	}
+
+	for_each_loose_file_in_objdir(get_object_directory(), prune_object,
+				      prune_cruft, prune_subdir, &revs);
+
+	prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0);
+	remove_temporary_files(get_object_directory());
+	s = mkpathdup("%s/pack", get_object_directory());
+	remove_temporary_files(s);
+	free(s);
+
+	if (is_repository_shallow(the_repository)) {
+		perform_reachability_traversal(&revs);
+		prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);
+	}
+
+	return 0;
+}
diff --git a/builtin/pull.c b/builtin/pull.c
new file mode 100644
index 0000000000..f1eaf6e6ed
--- /dev/null
+++ b/builtin/pull.c
@@ -0,0 +1,1016 @@
+/*
+ * Builtin "git pull"
+ *
+ * Based on git-pull.sh by Junio C Hamano
+ *
+ * Fetch one or more remote refs and merge it/them into the current HEAD.
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "exec-cmd.h"
+#include "run-command.h"
+#include "sha1-array.h"
+#include "remote.h"
+#include "dir.h"
+#include "refs.h"
+#include "refspec.h"
+#include "revision.h"
+#include "submodule.h"
+#include "submodule-config.h"
+#include "tempfile.h"
+#include "lockfile.h"
+#include "wt-status.h"
+#include "commit-reach.h"
+#include "sequencer.h"
+
+enum rebase_type {
+	REBASE_INVALID = -1,
+	REBASE_FALSE = 0,
+	REBASE_TRUE,
+	REBASE_PRESERVE,
+	REBASE_MERGES,
+	REBASE_INTERACTIVE
+};
+
+/**
+ * Parses the value of --rebase. If value is a false value, returns
+ * REBASE_FALSE. If value is a true value, returns REBASE_TRUE. If value is
+ * "merges", returns REBASE_MERGES. If value is "preserve", returns
+ * REBASE_PRESERVE. If value is a invalid value, dies with a fatal error if
+ * fatal is true, otherwise returns REBASE_INVALID.
+ */
+static enum rebase_type parse_config_rebase(const char *key, const char *value,
+		int fatal)
+{
+	int v = git_parse_maybe_bool(value);
+
+	if (!v)
+		return REBASE_FALSE;
+	else if (v > 0)
+		return REBASE_TRUE;
+	else if (!strcmp(value, "preserve") || !strcmp(value, "p"))
+		return REBASE_PRESERVE;
+	else if (!strcmp(value, "merges") || !strcmp(value, "m"))
+		return REBASE_MERGES;
+	else if (!strcmp(value, "interactive") || !strcmp(value, "i"))
+		return REBASE_INTERACTIVE;
+	/*
+	 * Please update _git_config() in git-completion.bash when you
+	 * add new rebase modes.
+	 */
+
+	if (fatal)
+		die(_("Invalid value for %s: %s"), key, value);
+	else
+		error(_("Invalid value for %s: %s"), key, value);
+
+	return REBASE_INVALID;
+}
+
+/**
+ * Callback for --rebase, which parses arg with parse_config_rebase().
+ */
+static int parse_opt_rebase(const struct option *opt, const char *arg, int unset)
+{
+	enum rebase_type *value = opt->value;
+
+	if (arg)
+		*value = parse_config_rebase("--rebase", arg, 0);
+	else
+		*value = unset ? REBASE_FALSE : REBASE_TRUE;
+	return *value == REBASE_INVALID ? -1 : 0;
+}
+
+static const char * const pull_usage[] = {
+	N_("git pull [<options>] [<repository> [<refspec>...]]"),
+	NULL
+};
+
+/* Shared options */
+static int opt_verbosity;
+static char *opt_progress;
+static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+
+/* Options passed to git-merge or git-rebase */
+static enum rebase_type opt_rebase = -1;
+static char *opt_diffstat;
+static char *opt_log;
+static char *opt_signoff;
+static char *opt_squash;
+static char *opt_commit;
+static char *opt_edit;
+static char *cleanup_arg;
+static char *opt_ff;
+static char *opt_verify_signatures;
+static int opt_autostash = -1;
+static int config_autostash;
+static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
+static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
+static char *opt_gpg_sign;
+static int opt_allow_unrelated_histories;
+
+/* Options passed to git-fetch */
+static char *opt_all;
+static char *opt_append;
+static char *opt_upload_pack;
+static int opt_force;
+static char *opt_tags;
+static char *opt_prune;
+static char *max_children;
+static int opt_dry_run;
+static char *opt_keep;
+static char *opt_depth;
+static char *opt_unshallow;
+static char *opt_update_shallow;
+static char *opt_refmap;
+static char *opt_ipv4;
+static char *opt_ipv6;
+static int opt_show_forced_updates = -1;
+
+static struct option pull_options[] = {
+	/* Shared options */
+	OPT__VERBOSITY(&opt_verbosity),
+	OPT_PASSTHRU(0, "progress", &opt_progress, NULL,
+		N_("force progress reporting"),
+		PARSE_OPT_NOARG),
+	{ OPTION_CALLBACK, 0, "recurse-submodules",
+		   &recurse_submodules, N_("on-demand"),
+		   N_("control for recursive fetching of submodules"),
+		   PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },
+
+	/* Options passed to git-merge or git-rebase */
+	OPT_GROUP(N_("Options related to merging")),
+	{ OPTION_CALLBACK, 'r', "rebase", &opt_rebase,
+	  "(false|true|merges|preserve|interactive)",
+	  N_("incorporate changes by rebasing rather than merging"),
+	  PARSE_OPT_OPTARG, parse_opt_rebase },
+	OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL,
+		N_("do not show a diffstat at the end of the merge"),
+		PARSE_OPT_NOARG | PARSE_OPT_NONEG),
+	OPT_PASSTHRU(0, "stat", &opt_diffstat, NULL,
+		N_("show a diffstat at the end of the merge"),
+		PARSE_OPT_NOARG),
+	OPT_PASSTHRU(0, "summary", &opt_diffstat, NULL,
+		N_("(synonym to --stat)"),
+		PARSE_OPT_NOARG | PARSE_OPT_HIDDEN),
+	OPT_PASSTHRU(0, "log", &opt_log, N_("n"),
+		N_("add (at most <n>) entries from shortlog to merge commit message"),
+		PARSE_OPT_OPTARG),
+	OPT_PASSTHRU(0, "signoff", &opt_signoff, NULL,
+		N_("add Signed-off-by:"),
+		PARSE_OPT_OPTARG),
+	OPT_PASSTHRU(0, "squash", &opt_squash, NULL,
+		N_("create a single commit instead of doing a merge"),
+		PARSE_OPT_NOARG),
+	OPT_PASSTHRU(0, "commit", &opt_commit, NULL,
+		N_("perform a commit if the merge succeeds (default)"),
+		PARSE_OPT_NOARG),
+	OPT_PASSTHRU(0, "edit", &opt_edit, NULL,
+		N_("edit message before committing"),
+		PARSE_OPT_NOARG),
+	OPT_CLEANUP(&cleanup_arg),
+	OPT_PASSTHRU(0, "ff", &opt_ff, NULL,
+		N_("allow fast-forward"),
+		PARSE_OPT_NOARG),
+	OPT_PASSTHRU(0, "ff-only", &opt_ff, NULL,
+		N_("abort if fast-forward is not possible"),
+		PARSE_OPT_NOARG | PARSE_OPT_NONEG),
+	OPT_PASSTHRU(0, "verify-signatures", &opt_verify_signatures, NULL,
+		N_("verify that the named commit has a valid GPG signature"),
+		PARSE_OPT_NOARG),
+	OPT_BOOL(0, "autostash", &opt_autostash,
+		N_("automatically stash/stash pop before and after rebase")),
+	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
+		N_("merge strategy to use"),
+		0),
+	OPT_PASSTHRU_ARGV('X', "strategy-option", &opt_strategy_opts,
+		N_("option=value"),
+		N_("option for selected merge strategy"),
+		0),
+	OPT_PASSTHRU('S', "gpg-sign", &opt_gpg_sign, N_("key-id"),
+		N_("GPG sign commit"),
+		PARSE_OPT_OPTARG),
+	OPT_SET_INT(0, "allow-unrelated-histories",
+		    &opt_allow_unrelated_histories,
+		    N_("allow merging unrelated histories"), 1),
+
+	/* Options passed to git-fetch */
+	OPT_GROUP(N_("Options related to fetching")),
+	OPT_PASSTHRU(0, "all", &opt_all, NULL,
+		N_("fetch from all remotes"),
+		PARSE_OPT_NOARG),
+	OPT_PASSTHRU('a', "append", &opt_append, NULL,
+		N_("append to .git/FETCH_HEAD instead of overwriting"),
+		PARSE_OPT_NOARG),
+	OPT_PASSTHRU(0, "upload-pack", &opt_upload_pack, N_("path"),
+		N_("path to upload pack on remote end"),
+		0),
+	OPT__FORCE(&opt_force, N_("force overwrite of local branch"), 0),
+	OPT_PASSTHRU('t', "tags", &opt_tags, NULL,
+		N_("fetch all tags and associated objects"),
+		PARSE_OPT_NOARG),
+	OPT_PASSTHRU('p', "prune", &opt_prune, NULL,
+		N_("prune remote-tracking branches no longer on remote"),
+		PARSE_OPT_NOARG),
+	OPT_PASSTHRU('j', "jobs", &max_children, N_("n"),
+		N_("number of submodules pulled in parallel"),
+		PARSE_OPT_OPTARG),
+	OPT_BOOL(0, "dry-run", &opt_dry_run,
+		N_("dry run")),
+	OPT_PASSTHRU('k', "keep", &opt_keep, NULL,
+		N_("keep downloaded pack"),
+		PARSE_OPT_NOARG),
+	OPT_PASSTHRU(0, "depth", &opt_depth, N_("depth"),
+		N_("deepen history of shallow clone"),
+		0),
+	OPT_PASSTHRU(0, "unshallow", &opt_unshallow, NULL,
+		N_("convert to a complete repository"),
+		PARSE_OPT_NONEG | PARSE_OPT_NOARG),
+	OPT_PASSTHRU(0, "update-shallow", &opt_update_shallow, NULL,
+		N_("accept refs that update .git/shallow"),
+		PARSE_OPT_NOARG),
+	OPT_PASSTHRU(0, "refmap", &opt_refmap, N_("refmap"),
+		N_("specify fetch refmap"),
+		PARSE_OPT_NONEG),
+	OPT_PASSTHRU('4',  "ipv4", &opt_ipv4, NULL,
+		N_("use IPv4 addresses only"),
+		PARSE_OPT_NOARG),
+	OPT_PASSTHRU('6',  "ipv6", &opt_ipv6, NULL,
+		N_("use IPv6 addresses only"),
+		PARSE_OPT_NOARG),
+	OPT_BOOL(0, "show-forced-updates", &opt_show_forced_updates,
+		 N_("check for forced-updates on all updated branches")),
+
+	OPT_END()
+};
+
+/**
+ * Pushes "-q" or "-v" switches into arr to match the opt_verbosity level.
+ */
+static void argv_push_verbosity(struct argv_array *arr)
+{
+	int verbosity;
+
+	for (verbosity = opt_verbosity; verbosity > 0; verbosity--)
+		argv_array_push(arr, "-v");
+
+	for (verbosity = opt_verbosity; verbosity < 0; verbosity++)
+		argv_array_push(arr, "-q");
+}
+
+/**
+ * Pushes "-f" switches into arr to match the opt_force level.
+ */
+static void argv_push_force(struct argv_array *arr)
+{
+	int force = opt_force;
+	while (force-- > 0)
+		argv_array_push(arr, "-f");
+}
+
+/**
+ * Sets the GIT_REFLOG_ACTION environment variable to the concatenation of argv
+ */
+static void set_reflog_message(int argc, const char **argv)
+{
+	int i;
+	struct strbuf msg = STRBUF_INIT;
+
+	for (i = 0; i < argc; i++) {
+		if (i)
+			strbuf_addch(&msg, ' ');
+		strbuf_addstr(&msg, argv[i]);
+	}
+
+	setenv("GIT_REFLOG_ACTION", msg.buf, 0);
+
+	strbuf_release(&msg);
+}
+
+/**
+ * If pull.ff is unset, returns NULL. If pull.ff is "true", returns "--ff". If
+ * pull.ff is "false", returns "--no-ff". If pull.ff is "only", returns
+ * "--ff-only". Otherwise, if pull.ff is set to an invalid value, die with an
+ * error.
+ */
+static const char *config_get_ff(void)
+{
+	const char *value;
+
+	if (git_config_get_value("pull.ff", &value))
+		return NULL;
+
+	switch (git_parse_maybe_bool(value)) {
+	case 0:
+		return "--no-ff";
+	case 1:
+		return "--ff";
+	}
+
+	if (!strcmp(value, "only"))
+		return "--ff-only";
+
+	die(_("Invalid value for pull.ff: %s"), value);
+}
+
+/**
+ * Returns the default configured value for --rebase. It first looks for the
+ * value of "branch.$curr_branch.rebase", where $curr_branch is the current
+ * branch, and if HEAD is detached or the configuration key does not exist,
+ * looks for the value of "pull.rebase". If both configuration keys do not
+ * exist, returns REBASE_FALSE.
+ */
+static enum rebase_type config_get_rebase(void)
+{
+	struct branch *curr_branch = branch_get("HEAD");
+	const char *value;
+
+	if (curr_branch) {
+		char *key = xstrfmt("branch.%s.rebase", curr_branch->name);
+
+		if (!git_config_get_value(key, &value)) {
+			enum rebase_type ret = parse_config_rebase(key, value, 1);
+			free(key);
+			return ret;
+		}
+
+		free(key);
+	}
+
+	if (!git_config_get_value("pull.rebase", &value))
+		return parse_config_rebase("pull.rebase", value, 1);
+
+	return REBASE_FALSE;
+}
+
+/**
+ * Read config variables.
+ */
+static int git_pull_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "rebase.autostash")) {
+		config_autostash = git_config_bool(var, value);
+		return 0;
+	} else if (!strcmp(var, "submodule.recurse")) {
+		recurse_submodules = git_config_bool(var, value) ?
+			RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
+		return 0;
+	}
+	return git_default_config(var, value, cb);
+}
+
+/**
+ * Appends merge candidates from FETCH_HEAD that are not marked not-for-merge
+ * into merge_heads.
+ */
+static void get_merge_heads(struct oid_array *merge_heads)
+{
+	const char *filename = git_path_fetch_head(the_repository);
+	FILE *fp;
+	struct strbuf sb = STRBUF_INIT;
+	struct object_id oid;
+
+	fp = xfopen(filename, "r");
+	while (strbuf_getline_lf(&sb, fp) != EOF) {
+		const char *p;
+		if (parse_oid_hex(sb.buf, &oid, &p))
+			continue;  /* invalid line: does not start with object ID */
+		if (starts_with(p, "\tnot-for-merge\t"))
+			continue;  /* ref is not-for-merge */
+		oid_array_append(merge_heads, &oid);
+	}
+	fclose(fp);
+	strbuf_release(&sb);
+}
+
+/**
+ * Used by die_no_merge_candidates() as a for_each_remote() callback to
+ * retrieve the name of the remote if the repository only has one remote.
+ */
+static int get_only_remote(struct remote *remote, void *cb_data)
+{
+	const char **remote_name = cb_data;
+
+	if (*remote_name)
+		return -1;
+
+	*remote_name = remote->name;
+	return 0;
+}
+
+/**
+ * Dies with the appropriate reason for why there are no merge candidates:
+ *
+ * 1. We fetched from a specific remote, and a refspec was given, but it ended
+ *    up not fetching anything. This is usually because the user provided a
+ *    wildcard refspec which had no matches on the remote end.
+ *
+ * 2. We fetched from a non-default remote, but didn't specify a branch to
+ *    merge. We can't use the configured one because it applies to the default
+ *    remote, thus the user must specify the branches to merge.
+ *
+ * 3. We fetched from the branch's or repo's default remote, but:
+ *
+ *    a. We are not on a branch, so there will never be a configured branch to
+ *       merge with.
+ *
+ *    b. We are on a branch, but there is no configured branch to merge with.
+ *
+ * 4. We fetched from the branch's or repo's default remote, but the configured
+ *    branch to merge didn't get fetched. (Either it doesn't exist, or wasn't
+ *    part of the configured fetch refspec.)
+ */
+static void NORETURN die_no_merge_candidates(const char *repo, const char **refspecs)
+{
+	struct branch *curr_branch = branch_get("HEAD");
+	const char *remote = curr_branch ? curr_branch->remote_name : NULL;
+
+	if (*refspecs) {
+		if (opt_rebase)
+			fprintf_ln(stderr, _("There is no candidate for rebasing against among the refs that you just fetched."));
+		else
+			fprintf_ln(stderr, _("There are no candidates for merging among the refs that you just fetched."));
+		fprintf_ln(stderr, _("Generally this means that you provided a wildcard refspec which had no\n"
+					"matches on the remote end."));
+	} else if (repo && curr_branch && (!remote || strcmp(repo, remote))) {
+		fprintf_ln(stderr, _("You asked to pull from the remote '%s', but did not specify\n"
+			"a branch. Because this is not the default configured remote\n"
+			"for your current branch, you must specify a branch on the command line."),
+			repo);
+	} else if (!curr_branch) {
+		fprintf_ln(stderr, _("You are not currently on a branch."));
+		if (opt_rebase)
+			fprintf_ln(stderr, _("Please specify which branch you want to rebase against."));
+		else
+			fprintf_ln(stderr, _("Please specify which branch you want to merge with."));
+		fprintf_ln(stderr, _("See git-pull(1) for details."));
+		fprintf(stderr, "\n");
+		fprintf_ln(stderr, "    git pull %s %s", _("<remote>"), _("<branch>"));
+		fprintf(stderr, "\n");
+	} else if (!curr_branch->merge_nr) {
+		const char *remote_name = NULL;
+
+		if (for_each_remote(get_only_remote, &remote_name) || !remote_name)
+			remote_name = _("<remote>");
+
+		fprintf_ln(stderr, _("There is no tracking information for the current branch."));
+		if (opt_rebase)
+			fprintf_ln(stderr, _("Please specify which branch you want to rebase against."));
+		else
+			fprintf_ln(stderr, _("Please specify which branch you want to merge with."));
+		fprintf_ln(stderr, _("See git-pull(1) for details."));
+		fprintf(stderr, "\n");
+		fprintf_ln(stderr, "    git pull %s %s", _("<remote>"), _("<branch>"));
+		fprintf(stderr, "\n");
+		fprintf_ln(stderr, _("If you wish to set tracking information for this branch you can do so with:"));
+		fprintf(stderr, "\n");
+		fprintf_ln(stderr, "    git branch --set-upstream-to=%s/%s %s\n",
+				remote_name, _("<branch>"), curr_branch->name);
+	} else
+		fprintf_ln(stderr, _("Your configuration specifies to merge with the ref '%s'\n"
+			"from the remote, but no such ref was fetched."),
+			*curr_branch->merge_name);
+	exit(1);
+}
+
+/**
+ * Parses argv into [<repo> [<refspecs>...]], returning their values in `repo`
+ * as a string and `refspecs` as a null-terminated array of strings. If `repo`
+ * is not provided in argv, it is set to NULL.
+ */
+static void parse_repo_refspecs(int argc, const char **argv, const char **repo,
+		const char ***refspecs)
+{
+	if (argc > 0) {
+		*repo = *argv++;
+		argc--;
+	} else
+		*repo = NULL;
+	*refspecs = argv;
+}
+
+/**
+ * Runs git-fetch, returning its exit status. `repo` and `refspecs` are the
+ * repository and refspecs to fetch, or NULL if they are not provided.
+ */
+static int run_fetch(const char *repo, const char **refspecs)
+{
+	struct argv_array args = ARGV_ARRAY_INIT;
+	int ret;
+
+	argv_array_pushl(&args, "fetch", "--update-head-ok", NULL);
+
+	/* Shared options */
+	argv_push_verbosity(&args);
+	if (opt_progress)
+		argv_array_push(&args, opt_progress);
+
+	/* Options passed to git-fetch */
+	if (opt_all)
+		argv_array_push(&args, opt_all);
+	if (opt_append)
+		argv_array_push(&args, opt_append);
+	if (opt_upload_pack)
+		argv_array_push(&args, opt_upload_pack);
+	argv_push_force(&args);
+	if (opt_tags)
+		argv_array_push(&args, opt_tags);
+	if (opt_prune)
+		argv_array_push(&args, opt_prune);
+	if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
+		switch (recurse_submodules) {
+		case RECURSE_SUBMODULES_ON:
+			argv_array_push(&args, "--recurse-submodules=on");
+			break;
+		case RECURSE_SUBMODULES_OFF:
+			argv_array_push(&args, "--recurse-submodules=no");
+			break;
+		case RECURSE_SUBMODULES_ON_DEMAND:
+			argv_array_push(&args, "--recurse-submodules=on-demand");
+			break;
+		default:
+			BUG("submodule recursion option not understood");
+		}
+	if (max_children)
+		argv_array_push(&args, max_children);
+	if (opt_dry_run)
+		argv_array_push(&args, "--dry-run");
+	if (opt_keep)
+		argv_array_push(&args, opt_keep);
+	if (opt_depth)
+		argv_array_push(&args, opt_depth);
+	if (opt_unshallow)
+		argv_array_push(&args, opt_unshallow);
+	if (opt_update_shallow)
+		argv_array_push(&args, opt_update_shallow);
+	if (opt_refmap)
+		argv_array_push(&args, opt_refmap);
+	if (opt_ipv4)
+		argv_array_push(&args, opt_ipv4);
+	if (opt_ipv6)
+		argv_array_push(&args, opt_ipv6);
+	if (opt_show_forced_updates > 0)
+		argv_array_push(&args, "--show-forced-updates");
+	else if (opt_show_forced_updates == 0)
+		argv_array_push(&args, "--no-show-forced-updates");
+
+	if (repo) {
+		argv_array_push(&args, repo);
+		argv_array_pushv(&args, refspecs);
+	} else if (*refspecs)
+		BUG("refspecs without repo?");
+	ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
+	argv_array_clear(&args);
+	return ret;
+}
+
+/**
+ * "Pulls into void" by branching off merge_head.
+ */
+static int pull_into_void(const struct object_id *merge_head,
+		const struct object_id *curr_head)
+{
+	if (opt_verify_signatures) {
+		struct commit *commit;
+
+		commit = lookup_commit(the_repository, merge_head);
+		if (!commit)
+			die(_("unable to access commit %s"),
+			    oid_to_hex(merge_head));
+
+		verify_merge_signature(commit, opt_verbosity);
+	}
+
+	/*
+	 * Two-way merge: we treat the index as based on an empty tree,
+	 * and try to fast-forward to HEAD. This ensures we will not lose
+	 * index/worktree changes that the user already made on the unborn
+	 * branch.
+	 */
+	if (checkout_fast_forward(the_repository,
+				  the_hash_algo->empty_tree,
+				  merge_head, 0))
+		return 1;
+
+	if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
+		return 1;
+
+	return 0;
+}
+
+static int rebase_submodules(void)
+{
+	struct child_process cp = CHILD_PROCESS_INIT;
+
+	cp.git_cmd = 1;
+	cp.no_stdin = 1;
+	argv_array_pushl(&cp.args, "submodule", "update",
+				   "--recursive", "--rebase", NULL);
+	argv_push_verbosity(&cp.args);
+
+	return run_command(&cp);
+}
+
+static int update_submodules(void)
+{
+	struct child_process cp = CHILD_PROCESS_INIT;
+
+	cp.git_cmd = 1;
+	cp.no_stdin = 1;
+	argv_array_pushl(&cp.args, "submodule", "update",
+				   "--recursive", "--checkout", NULL);
+	argv_push_verbosity(&cp.args);
+
+	return run_command(&cp);
+}
+
+/**
+ * Runs git-merge, returning its exit status.
+ */
+static int run_merge(void)
+{
+	int ret;
+	struct argv_array args = ARGV_ARRAY_INIT;
+
+	argv_array_pushl(&args, "merge", NULL);
+
+	/* Shared options */
+	argv_push_verbosity(&args);
+	if (opt_progress)
+		argv_array_push(&args, opt_progress);
+
+	/* Options passed to git-merge */
+	if (opt_diffstat)
+		argv_array_push(&args, opt_diffstat);
+	if (opt_log)
+		argv_array_push(&args, opt_log);
+	if (opt_signoff)
+		argv_array_push(&args, opt_signoff);
+	if (opt_squash)
+		argv_array_push(&args, opt_squash);
+	if (opt_commit)
+		argv_array_push(&args, opt_commit);
+	if (opt_edit)
+		argv_array_push(&args, opt_edit);
+	if (cleanup_arg)
+		argv_array_pushf(&args, "--cleanup=%s", cleanup_arg);
+	if (opt_ff)
+		argv_array_push(&args, opt_ff);
+	if (opt_verify_signatures)
+		argv_array_push(&args, opt_verify_signatures);
+	argv_array_pushv(&args, opt_strategies.argv);
+	argv_array_pushv(&args, opt_strategy_opts.argv);
+	if (opt_gpg_sign)
+		argv_array_push(&args, opt_gpg_sign);
+	if (opt_allow_unrelated_histories > 0)
+		argv_array_push(&args, "--allow-unrelated-histories");
+
+	argv_array_push(&args, "FETCH_HEAD");
+	ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
+	argv_array_clear(&args);
+	return ret;
+}
+
+/**
+ * Returns remote's upstream branch for the current branch. If remote is NULL,
+ * the current branch's configured default remote is used. Returns NULL if
+ * `remote` does not name a valid remote, HEAD does not point to a branch,
+ * remote is not the branch's configured remote or the branch does not have any
+ * configured upstream branch.
+ */
+static const char *get_upstream_branch(const char *remote)
+{
+	struct remote *rm;
+	struct branch *curr_branch;
+	const char *curr_branch_remote;
+
+	rm = remote_get(remote);
+	if (!rm)
+		return NULL;
+
+	curr_branch = branch_get("HEAD");
+	if (!curr_branch)
+		return NULL;
+
+	curr_branch_remote = remote_for_branch(curr_branch, NULL);
+	assert(curr_branch_remote);
+
+	if (strcmp(curr_branch_remote, rm->name))
+		return NULL;
+
+	return branch_get_upstream(curr_branch, NULL);
+}
+
+/**
+ * Derives the remote-tracking branch from the remote and refspec.
+ *
+ * FIXME: The current implementation assumes the default mapping of
+ * refs/heads/<branch_name> to refs/remotes/<remote_name>/<branch_name>.
+ */
+static const char *get_tracking_branch(const char *remote, const char *refspec)
+{
+	struct refspec_item spec;
+	const char *spec_src;
+	const char *merge_branch;
+
+	refspec_item_init_or_die(&spec, refspec, REFSPEC_FETCH);
+	spec_src = spec.src;
+	if (!*spec_src || !strcmp(spec_src, "HEAD"))
+		spec_src = "HEAD";
+	else if (skip_prefix(spec_src, "heads/", &spec_src))
+		;
+	else if (skip_prefix(spec_src, "refs/heads/", &spec_src))
+		;
+	else if (starts_with(spec_src, "refs/") ||
+		starts_with(spec_src, "tags/") ||
+		starts_with(spec_src, "remotes/"))
+		spec_src = "";
+
+	if (*spec_src) {
+		if (!strcmp(remote, "."))
+			merge_branch = mkpath("refs/heads/%s", spec_src);
+		else
+			merge_branch = mkpath("refs/remotes/%s/%s", remote, spec_src);
+	} else
+		merge_branch = NULL;
+
+	refspec_item_clear(&spec);
+	return merge_branch;
+}
+
+/**
+ * Given the repo and refspecs, sets fork_point to the point at which the
+ * current branch forked from its remote-tracking branch. Returns 0 on success,
+ * -1 on failure.
+ */
+static int get_rebase_fork_point(struct object_id *fork_point, const char *repo,
+		const char *refspec)
+{
+	int ret;
+	struct branch *curr_branch;
+	const char *remote_branch;
+	struct child_process cp = CHILD_PROCESS_INIT;
+	struct strbuf sb = STRBUF_INIT;
+
+	curr_branch = branch_get("HEAD");
+	if (!curr_branch)
+		return -1;
+
+	if (refspec)
+		remote_branch = get_tracking_branch(repo, refspec);
+	else
+		remote_branch = get_upstream_branch(repo);
+
+	if (!remote_branch)
+		return -1;
+
+	argv_array_pushl(&cp.args, "merge-base", "--fork-point",
+			remote_branch, curr_branch->name, NULL);
+	cp.no_stdin = 1;
+	cp.no_stderr = 1;
+	cp.git_cmd = 1;
+
+	ret = capture_command(&cp, &sb, GIT_MAX_HEXSZ);
+	if (ret)
+		goto cleanup;
+
+	ret = get_oid_hex(sb.buf, fork_point);
+	if (ret)
+		goto cleanup;
+
+cleanup:
+	strbuf_release(&sb);
+	return ret ? -1 : 0;
+}
+
+/**
+ * Sets merge_base to the octopus merge base of curr_head, merge_head and
+ * fork_point. Returns 0 if a merge base is found, 1 otherwise.
+ */
+static int get_octopus_merge_base(struct object_id *merge_base,
+		const struct object_id *curr_head,
+		const struct object_id *merge_head,
+		const struct object_id *fork_point)
+{
+	struct commit_list *revs = NULL, *result;
+
+	commit_list_insert(lookup_commit_reference(the_repository, curr_head),
+			   &revs);
+	commit_list_insert(lookup_commit_reference(the_repository, merge_head),
+			   &revs);
+	if (!is_null_oid(fork_point))
+		commit_list_insert(lookup_commit_reference(the_repository, fork_point),
+				   &revs);
+
+	result = get_octopus_merge_bases(revs);
+	free_commit_list(revs);
+	reduce_heads_replace(&result);
+
+	if (!result)
+		return 1;
+
+	oidcpy(merge_base, &result->item->object.oid);
+	free_commit_list(result);
+	return 0;
+}
+
+/**
+ * Given the current HEAD oid, the merge head returned from git-fetch and the
+ * fork point calculated by get_rebase_fork_point(), runs git-rebase with the
+ * appropriate arguments and returns its exit status.
+ */
+static int run_rebase(const struct object_id *curr_head,
+		const struct object_id *merge_head,
+		const struct object_id *fork_point)
+{
+	int ret;
+	struct object_id oct_merge_base;
+	struct argv_array args = ARGV_ARRAY_INIT;
+
+	if (!get_octopus_merge_base(&oct_merge_base, curr_head, merge_head, fork_point))
+		if (!is_null_oid(fork_point) && oideq(&oct_merge_base, fork_point))
+			fork_point = NULL;
+
+	argv_array_push(&args, "rebase");
+
+	/* Shared options */
+	argv_push_verbosity(&args);
+
+	/* Options passed to git-rebase */
+	if (opt_rebase == REBASE_MERGES)
+		argv_array_push(&args, "--rebase-merges");
+	else if (opt_rebase == REBASE_PRESERVE)
+		argv_array_push(&args, "--preserve-merges");
+	else if (opt_rebase == REBASE_INTERACTIVE)
+		argv_array_push(&args, "--interactive");
+	if (opt_diffstat)
+		argv_array_push(&args, opt_diffstat);
+	argv_array_pushv(&args, opt_strategies.argv);
+	argv_array_pushv(&args, opt_strategy_opts.argv);
+	if (opt_gpg_sign)
+		argv_array_push(&args, opt_gpg_sign);
+	if (opt_autostash == 0)
+		argv_array_push(&args, "--no-autostash");
+	else if (opt_autostash == 1)
+		argv_array_push(&args, "--autostash");
+	if (opt_verify_signatures &&
+	    !strcmp(opt_verify_signatures, "--verify-signatures"))
+		warning(_("ignoring --verify-signatures for rebase"));
+
+	argv_array_push(&args, "--onto");
+	argv_array_push(&args, oid_to_hex(merge_head));
+
+	if (fork_point && !is_null_oid(fork_point))
+		argv_array_push(&args, oid_to_hex(fork_point));
+	else
+		argv_array_push(&args, oid_to_hex(merge_head));
+
+	ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
+	argv_array_clear(&args);
+	return ret;
+}
+
+int cmd_pull(int argc, const char **argv, const char *prefix)
+{
+	const char *repo, **refspecs;
+	struct oid_array merge_heads = OID_ARRAY_INIT;
+	struct object_id orig_head, curr_head;
+	struct object_id rebase_fork_point;
+	int autostash;
+
+	if (!getenv("GIT_REFLOG_ACTION"))
+		set_reflog_message(argc, argv);
+
+	git_config(git_pull_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);
+
+	if (cleanup_arg)
+		/*
+		 * this only checks the validity of cleanup_arg; we don't need
+		 * a valid value for use_editor
+		 */
+		get_cleanup_mode(cleanup_arg, 0);
+
+	parse_repo_refspecs(argc, argv, &repo, &refspecs);
+
+	if (!opt_ff)
+		opt_ff = xstrdup_or_null(config_get_ff());
+
+	if (opt_rebase < 0)
+		opt_rebase = config_get_rebase();
+
+	if (read_cache_unmerged())
+		die_resolve_conflict("pull");
+
+	if (file_exists(git_path_merge_head(the_repository)))
+		die_conclude_merge();
+
+	if (get_oid("HEAD", &orig_head))
+		oidclr(&orig_head);
+
+	if (!opt_rebase && opt_autostash != -1)
+		die(_("--[no-]autostash option is only valid with --rebase."));
+
+	autostash = config_autostash;
+	if (opt_rebase) {
+		if (opt_autostash != -1)
+			autostash = opt_autostash;
+
+		if (is_null_oid(&orig_head) && !is_cache_unborn())
+			die(_("Updating an unborn branch with changes added to the index."));
+
+		if (!autostash)
+			require_clean_work_tree(the_repository,
+				N_("pull with rebase"),
+				_("please commit or stash them."), 1, 0);
+
+		if (get_rebase_fork_point(&rebase_fork_point, repo, *refspecs))
+			oidclr(&rebase_fork_point);
+	}
+
+	if (run_fetch(repo, refspecs))
+		return 1;
+
+	if (opt_dry_run)
+		return 0;
+
+	if (get_oid("HEAD", &curr_head))
+		oidclr(&curr_head);
+
+	if (!is_null_oid(&orig_head) && !is_null_oid(&curr_head) &&
+			!oideq(&orig_head, &curr_head)) {
+		/*
+		 * The fetch involved updating the current branch.
+		 *
+		 * The working tree and the index file are still based on
+		 * orig_head commit, but we are merging into curr_head.
+		 * Update the working tree to match curr_head.
+		 */
+
+		warning(_("fetch updated the current branch head.\n"
+			"fast-forwarding your working tree from\n"
+			"commit %s."), oid_to_hex(&orig_head));
+
+		if (checkout_fast_forward(the_repository, &orig_head,
+					  &curr_head, 0))
+			die(_("Cannot fast-forward your working tree.\n"
+				"After making sure that you saved anything precious from\n"
+				"$ git diff %s\n"
+				"output, run\n"
+				"$ git reset --hard\n"
+				"to recover."), oid_to_hex(&orig_head));
+	}
+
+	get_merge_heads(&merge_heads);
+
+	if (!merge_heads.nr)
+		die_no_merge_candidates(repo, refspecs);
+
+	if (is_null_oid(&orig_head)) {
+		if (merge_heads.nr > 1)
+			die(_("Cannot merge multiple branches into empty head."));
+		return pull_into_void(merge_heads.oid, &curr_head);
+	}
+	if (opt_rebase && merge_heads.nr > 1)
+		die(_("Cannot rebase onto multiple branches."));
+
+	if (opt_rebase) {
+		int ret = 0;
+		if ((recurse_submodules == RECURSE_SUBMODULES_ON ||
+		     recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) &&
+		    submodule_touches_in_range(the_repository, &rebase_fork_point, &curr_head))
+			die(_("cannot rebase with locally recorded submodule modifications"));
+		if (!autostash) {
+			struct commit_list *list = NULL;
+			struct commit *merge_head, *head;
+
+			head = lookup_commit_reference(the_repository,
+						       &orig_head);
+			commit_list_insert(head, &list);
+			merge_head = lookup_commit_reference(the_repository,
+							     &merge_heads.oid[0]);
+			if (is_descendant_of(merge_head, list)) {
+				/* we can fast-forward this without invoking rebase */
+				opt_ff = "--ff-only";
+				ret = run_merge();
+			}
+		}
+		ret = run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point);
+
+		if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON ||
+			     recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND))
+			ret = rebase_submodules();
+
+		return ret;
+	} else {
+		int ret = run_merge();
+		if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON ||
+			     recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND))
+			ret = update_submodules();
+		return ret;
+	}
+}
diff --git a/builtin/push.c b/builtin/push.c
new file mode 100644
index 0000000000..021dd3b1e4
--- /dev/null
+++ b/builtin/push.c
@@ -0,0 +1,646 @@
+/*
+ * "git push"
+ */
+#include "cache.h"
+#include "config.h"
+#include "refs.h"
+#include "refspec.h"
+#include "run-command.h"
+#include "builtin.h"
+#include "remote.h"
+#include "transport.h"
+#include "parse-options.h"
+#include "submodule.h"
+#include "submodule-config.h"
+#include "send-pack.h"
+#include "color.h"
+
+static const char * const push_usage[] = {
+	N_("git push [<options>] [<repository> [<refspec>...]]"),
+	NULL,
+};
+
+static int push_use_color = -1;
+static char push_colors[][COLOR_MAXLEN] = {
+	GIT_COLOR_RESET,
+	GIT_COLOR_RED,	/* ERROR */
+};
+
+enum color_push {
+	PUSH_COLOR_RESET = 0,
+	PUSH_COLOR_ERROR = 1
+};
+
+static int parse_push_color_slot(const char *slot)
+{
+	if (!strcasecmp(slot, "reset"))
+		return PUSH_COLOR_RESET;
+	if (!strcasecmp(slot, "error"))
+		return PUSH_COLOR_ERROR;
+	return -1;
+}
+
+static const char *push_get_color(enum color_push ix)
+{
+	if (want_color_stderr(push_use_color))
+		return push_colors[ix];
+	return "";
+}
+
+static int thin = 1;
+static int deleterefs;
+static const char *receivepack;
+static int verbosity;
+static int progress = -1;
+static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+static enum transport_family family;
+
+static struct push_cas_option cas;
+
+static struct refspec rs = REFSPEC_INIT_PUSH;
+
+static struct string_list push_options_config = STRING_LIST_INIT_DUP;
+
+static const char *map_refspec(const char *ref,
+			       struct remote *remote, struct ref *local_refs)
+{
+	struct ref *matched = NULL;
+
+	/* Does "ref" uniquely name our ref? */
+	if (count_refspec_match(ref, local_refs, &matched) != 1)
+		return ref;
+
+	if (remote->push.nr) {
+		struct refspec_item query;
+		memset(&query, 0, sizeof(struct refspec_item));
+		query.src = matched->name;
+		if (!query_refspecs(&remote->push, &query) && query.dst) {
+			struct strbuf buf = STRBUF_INIT;
+			strbuf_addf(&buf, "%s%s:%s",
+				    query.force ? "+" : "",
+				    query.src, query.dst);
+			return strbuf_detach(&buf, NULL);
+		}
+	}
+
+	if (push_default == PUSH_DEFAULT_UPSTREAM &&
+	    starts_with(matched->name, "refs/heads/")) {
+		struct branch *branch = branch_get(matched->name + 11);
+		if (branch->merge_nr == 1 && branch->merge[0]->src) {
+			struct strbuf buf = STRBUF_INIT;
+			strbuf_addf(&buf, "%s:%s",
+				    ref, branch->merge[0]->src);
+			return strbuf_detach(&buf, NULL);
+		}
+	}
+
+	return ref;
+}
+
+static void set_refspecs(const char **refs, int nr, const char *repo)
+{
+	struct remote *remote = NULL;
+	struct ref *local_refs = NULL;
+	int i;
+
+	for (i = 0; i < nr; i++) {
+		const char *ref = refs[i];
+		if (!strcmp("tag", ref)) {
+			struct strbuf tagref = STRBUF_INIT;
+			if (nr <= ++i)
+				die(_("tag shorthand without <tag>"));
+			ref = refs[i];
+			if (deleterefs)
+				strbuf_addf(&tagref, ":refs/tags/%s", ref);
+			else
+				strbuf_addf(&tagref, "refs/tags/%s", ref);
+			ref = strbuf_detach(&tagref, NULL);
+		} else if (deleterefs) {
+			struct strbuf delref = STRBUF_INIT;
+			if (strchr(ref, ':'))
+				die(_("--delete only accepts plain target ref names"));
+			strbuf_addf(&delref, ":%s", ref);
+			ref = strbuf_detach(&delref, NULL);
+		} else if (!strchr(ref, ':')) {
+			if (!remote) {
+				/* lazily grab remote and local_refs */
+				remote = remote_get(repo);
+				local_refs = get_local_heads();
+			}
+			ref = map_refspec(ref, remote, local_refs);
+		}
+		refspec_append(&rs, ref);
+	}
+}
+
+static int push_url_of_remote(struct remote *remote, const char ***url_p)
+{
+	if (remote->pushurl_nr) {
+		*url_p = remote->pushurl;
+		return remote->pushurl_nr;
+	}
+	*url_p = remote->url;
+	return remote->url_nr;
+}
+
+static NORETURN int die_push_simple(struct branch *branch,
+				    struct remote *remote)
+{
+	/*
+	 * There's no point in using shorten_unambiguous_ref here,
+	 * as the ambiguity would be on the remote side, not what
+	 * we have locally. Plus, this is supposed to be the simple
+	 * mode. If the user is doing something crazy like setting
+	 * upstream to a non-branch, we should probably be showing
+	 * them the big ugly fully qualified ref.
+	 */
+	const char *advice_maybe = "";
+	const char *short_upstream = branch->merge[0]->src;
+
+	skip_prefix(short_upstream, "refs/heads/", &short_upstream);
+
+	/*
+	 * Don't show advice for people who explicitly set
+	 * push.default.
+	 */
+	if (push_default == PUSH_DEFAULT_UNSPECIFIED)
+		advice_maybe = _("\n"
+				 "To choose either option permanently, "
+				 "see push.default in 'git help config'.");
+	die(_("The upstream branch of your current branch does not match\n"
+	      "the name of your current branch.  To push to the upstream branch\n"
+	      "on the remote, use\n"
+	      "\n"
+	      "    git push %s HEAD:%s\n"
+	      "\n"
+	      "To push to the branch of the same name on the remote, use\n"
+	      "\n"
+	      "    git push %s HEAD\n"
+	      "%s"),
+	    remote->name, short_upstream,
+	    remote->name, advice_maybe);
+}
+
+static const char message_detached_head_die[] =
+	N_("You are not currently on a branch.\n"
+	   "To push the history leading to the current (detached HEAD)\n"
+	   "state now, use\n"
+	   "\n"
+	   "    git push %s HEAD:<name-of-remote-branch>\n");
+
+static void setup_push_upstream(struct remote *remote, struct branch *branch,
+				int triangular, int simple)
+{
+	struct strbuf refspec = STRBUF_INIT;
+
+	if (!branch)
+		die(_(message_detached_head_die), remote->name);
+	if (!branch->merge_nr || !branch->merge || !branch->remote_name)
+		die(_("The current branch %s has no upstream branch.\n"
+		    "To push the current branch and set the remote as upstream, use\n"
+		    "\n"
+		    "    git push --set-upstream %s %s\n"),
+		    branch->name,
+		    remote->name,
+		    branch->name);
+	if (branch->merge_nr != 1)
+		die(_("The current branch %s has multiple upstream branches, "
+		    "refusing to push."), branch->name);
+	if (triangular)
+		die(_("You are pushing to remote '%s', which is not the upstream of\n"
+		      "your current branch '%s', without telling me what to push\n"
+		      "to update which remote branch."),
+		    remote->name, branch->name);
+
+	if (simple) {
+		/* Additional safety */
+		if (strcmp(branch->refname, branch->merge[0]->src))
+			die_push_simple(branch, remote);
+	}
+
+	strbuf_addf(&refspec, "%s:%s", branch->refname, branch->merge[0]->src);
+	refspec_append(&rs, refspec.buf);
+}
+
+static void setup_push_current(struct remote *remote, struct branch *branch)
+{
+	struct strbuf refspec = STRBUF_INIT;
+
+	if (!branch)
+		die(_(message_detached_head_die), remote->name);
+	strbuf_addf(&refspec, "%s:%s", branch->refname, branch->refname);
+	refspec_append(&rs, refspec.buf);
+}
+
+static int is_workflow_triangular(struct remote *remote)
+{
+	struct remote *fetch_remote = remote_get(NULL);
+	return (fetch_remote && fetch_remote != remote);
+}
+
+static void setup_default_push_refspecs(struct remote *remote)
+{
+	struct branch *branch = branch_get(NULL);
+	int triangular = is_workflow_triangular(remote);
+
+	switch (push_default) {
+	default:
+	case PUSH_DEFAULT_MATCHING:
+		refspec_append(&rs, ":");
+		break;
+
+	case PUSH_DEFAULT_UNSPECIFIED:
+	case PUSH_DEFAULT_SIMPLE:
+		if (triangular)
+			setup_push_current(remote, branch);
+		else
+			setup_push_upstream(remote, branch, triangular, 1);
+		break;
+
+	case PUSH_DEFAULT_UPSTREAM:
+		setup_push_upstream(remote, branch, triangular, 0);
+		break;
+
+	case PUSH_DEFAULT_CURRENT:
+		setup_push_current(remote, branch);
+		break;
+
+	case PUSH_DEFAULT_NOTHING:
+		die(_("You didn't specify any refspecs to push, and "
+		    "push.default is \"nothing\"."));
+		break;
+	}
+}
+
+static const char message_advice_pull_before_push[] =
+	N_("Updates were rejected because the tip of your current branch is behind\n"
+	   "its remote counterpart. Integrate the remote changes (e.g.\n"
+	   "'git pull ...') before pushing again.\n"
+	   "See the 'Note about fast-forwards' in 'git push --help' for details.");
+
+static const char message_advice_checkout_pull_push[] =
+	N_("Updates were rejected because a pushed branch tip is behind its remote\n"
+	   "counterpart. Check out this branch and integrate the remote changes\n"
+	   "(e.g. 'git pull ...') before pushing again.\n"
+	   "See the 'Note about fast-forwards' in 'git push --help' for details.");
+
+static const char message_advice_ref_fetch_first[] =
+	N_("Updates were rejected because the remote contains work that you do\n"
+	   "not have locally. This is usually caused by another repository pushing\n"
+	   "to the same ref. You may want to first integrate the remote changes\n"
+	   "(e.g., 'git pull ...') before pushing again.\n"
+	   "See the 'Note about fast-forwards' in 'git push --help' for details.");
+
+static const char message_advice_ref_already_exists[] =
+	N_("Updates were rejected because the tag already exists in the remote.");
+
+static const char message_advice_ref_needs_force[] =
+	N_("You cannot update a remote ref that points at a non-commit object,\n"
+	   "or update a remote ref to make it point at a non-commit object,\n"
+	   "without using the '--force' option.\n");
+
+static void advise_pull_before_push(void)
+{
+	if (!advice_push_non_ff_current || !advice_push_update_rejected)
+		return;
+	advise(_(message_advice_pull_before_push));
+}
+
+static void advise_checkout_pull_push(void)
+{
+	if (!advice_push_non_ff_matching || !advice_push_update_rejected)
+		return;
+	advise(_(message_advice_checkout_pull_push));
+}
+
+static void advise_ref_already_exists(void)
+{
+	if (!advice_push_already_exists || !advice_push_update_rejected)
+		return;
+	advise(_(message_advice_ref_already_exists));
+}
+
+static void advise_ref_fetch_first(void)
+{
+	if (!advice_push_fetch_first || !advice_push_update_rejected)
+		return;
+	advise(_(message_advice_ref_fetch_first));
+}
+
+static void advise_ref_needs_force(void)
+{
+	if (!advice_push_needs_force || !advice_push_update_rejected)
+		return;
+	advise(_(message_advice_ref_needs_force));
+}
+
+static int push_with_options(struct transport *transport, struct refspec *rs,
+			     int flags)
+{
+	int err;
+	unsigned int reject_reasons;
+
+	transport_set_verbosity(transport, verbosity, progress);
+	transport->family = family;
+
+	if (receivepack)
+		transport_set_option(transport,
+				     TRANS_OPT_RECEIVEPACK, receivepack);
+	transport_set_option(transport, TRANS_OPT_THIN, thin ? "yes" : NULL);
+
+	if (!is_empty_cas(&cas)) {
+		if (!transport->smart_options)
+			die("underlying transport does not support --%s option",
+			    CAS_OPT_NAME);
+		transport->smart_options->cas = &cas;
+	}
+
+	if (verbosity > 0)
+		fprintf(stderr, _("Pushing to %s\n"), transport->url);
+	err = transport_push(the_repository, transport,
+			     rs, flags, &reject_reasons);
+	if (err != 0) {
+		fprintf(stderr, "%s", push_get_color(PUSH_COLOR_ERROR));
+		error(_("failed to push some refs to '%s'"), transport->url);
+		fprintf(stderr, "%s", push_get_color(PUSH_COLOR_RESET));
+	}
+
+	err |= transport_disconnect(transport);
+	if (!err)
+		return 0;
+
+	if (reject_reasons & REJECT_NON_FF_HEAD) {
+		advise_pull_before_push();
+	} else if (reject_reasons & REJECT_NON_FF_OTHER) {
+		advise_checkout_pull_push();
+	} else if (reject_reasons & REJECT_ALREADY_EXISTS) {
+		advise_ref_already_exists();
+	} else if (reject_reasons & REJECT_FETCH_FIRST) {
+		advise_ref_fetch_first();
+	} else if (reject_reasons & REJECT_NEEDS_FORCE) {
+		advise_ref_needs_force();
+	}
+
+	return 1;
+}
+
+static int do_push(const char *repo, int flags,
+		   const struct string_list *push_options)
+{
+	int i, errs;
+	struct remote *remote = pushremote_get(repo);
+	const char **url;
+	int url_nr;
+	struct refspec *push_refspec = &rs;
+
+	if (!remote) {
+		if (repo)
+			die(_("bad repository '%s'"), repo);
+		die(_("No configured push destination.\n"
+		    "Either specify the URL from the command-line or configure a remote repository using\n"
+		    "\n"
+		    "    git remote add <name> <url>\n"
+		    "\n"
+		    "and then push using the remote name\n"
+		    "\n"
+		    "    git push <name>\n"));
+	}
+
+	if (remote->mirror)
+		flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
+
+	if (push_options->nr)
+		flags |= TRANSPORT_PUSH_OPTIONS;
+
+	if (!push_refspec->nr && !(flags & TRANSPORT_PUSH_ALL)) {
+		if (remote->push.nr) {
+			push_refspec = &remote->push;
+		} else if (!(flags & TRANSPORT_PUSH_MIRROR))
+			setup_default_push_refspecs(remote);
+	}
+	errs = 0;
+	url_nr = push_url_of_remote(remote, &url);
+	if (url_nr) {
+		for (i = 0; i < url_nr; i++) {
+			struct transport *transport =
+				transport_get(remote, url[i]);
+			if (flags & TRANSPORT_PUSH_OPTIONS)
+				transport->push_options = push_options;
+			if (push_with_options(transport, push_refspec, flags))
+				errs++;
+		}
+	} else {
+		struct transport *transport =
+			transport_get(remote, NULL);
+		if (flags & TRANSPORT_PUSH_OPTIONS)
+			transport->push_options = push_options;
+		if (push_with_options(transport, push_refspec, flags))
+			errs++;
+	}
+	return !!errs;
+}
+
+static int option_parse_recurse_submodules(const struct option *opt,
+				   const char *arg, int unset)
+{
+	int *recurse_submodules = opt->value;
+
+	if (unset)
+		*recurse_submodules = RECURSE_SUBMODULES_OFF;
+	else if (arg)
+		*recurse_submodules = parse_push_recurse_submodules_arg(opt->long_name, arg);
+	else
+		die("%s missing parameter", opt->long_name);
+
+	return 0;
+}
+
+static void set_push_cert_flags(int *flags, int v)
+{
+	switch (v) {
+	case SEND_PACK_PUSH_CERT_NEVER:
+		*flags &= ~(TRANSPORT_PUSH_CERT_ALWAYS | TRANSPORT_PUSH_CERT_IF_ASKED);
+		break;
+	case SEND_PACK_PUSH_CERT_ALWAYS:
+		*flags |= TRANSPORT_PUSH_CERT_ALWAYS;
+		*flags &= ~TRANSPORT_PUSH_CERT_IF_ASKED;
+		break;
+	case SEND_PACK_PUSH_CERT_IF_ASKED:
+		*flags |= TRANSPORT_PUSH_CERT_IF_ASKED;
+		*flags &= ~TRANSPORT_PUSH_CERT_ALWAYS;
+		break;
+	}
+}
+
+
+static int git_push_config(const char *k, const char *v, void *cb)
+{
+	const char *slot_name;
+	int *flags = cb;
+	int status;
+
+	status = git_gpg_config(k, v, NULL);
+	if (status)
+		return status;
+
+	if (!strcmp(k, "push.followtags")) {
+		if (git_config_bool(k, v))
+			*flags |= TRANSPORT_PUSH_FOLLOW_TAGS;
+		else
+			*flags &= ~TRANSPORT_PUSH_FOLLOW_TAGS;
+		return 0;
+	} else if (!strcmp(k, "push.gpgsign")) {
+		const char *value;
+		if (!git_config_get_value("push.gpgsign", &value)) {
+			switch (git_parse_maybe_bool(value)) {
+			case 0:
+				set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER);
+				break;
+			case 1:
+				set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_ALWAYS);
+				break;
+			default:
+				if (value && !strcasecmp(value, "if-asked"))
+					set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED);
+				else
+					return error("Invalid value for '%s'", k);
+			}
+		}
+	} else if (!strcmp(k, "push.recursesubmodules")) {
+		const char *value;
+		if (!git_config_get_value("push.recursesubmodules", &value))
+			recurse_submodules = parse_push_recurse_submodules_arg(k, value);
+	} else if (!strcmp(k, "submodule.recurse")) {
+		int val = git_config_bool(k, v) ?
+			RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF;
+		recurse_submodules = val;
+	} else if (!strcmp(k, "push.pushoption")) {
+		if (!v)
+			return config_error_nonbool(k);
+		else
+			if (!*v)
+				string_list_clear(&push_options_config, 0);
+			else
+				string_list_append(&push_options_config, v);
+		return 0;
+	} else if (!strcmp(k, "color.push")) {
+		push_use_color = git_config_colorbool(k, v);
+		return 0;
+	} else if (skip_prefix(k, "color.push.", &slot_name)) {
+		int slot = parse_push_color_slot(slot_name);
+		if (slot < 0)
+			return 0;
+		if (!v)
+			return config_error_nonbool(k);
+		return color_parse(v, push_colors[slot]);
+	}
+
+	return git_default_config(k, v, NULL);
+}
+
+int cmd_push(int argc, const char **argv, const char *prefix)
+{
+	int flags = 0;
+	int tags = 0;
+	int push_cert = -1;
+	int rc;
+	const char *repo = NULL;	/* default repository */
+	struct string_list push_options_cmdline = STRING_LIST_INIT_DUP;
+	struct string_list *push_options;
+	const struct string_list_item *item;
+
+	struct option options[] = {
+		OPT__VERBOSITY(&verbosity),
+		OPT_STRING( 0 , "repo", &repo, N_("repository"), N_("repository")),
+		OPT_BIT( 0 , "all", &flags, N_("push all refs"), TRANSPORT_PUSH_ALL),
+		OPT_BIT( 0 , "mirror", &flags, N_("mirror all refs"),
+			    (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
+		OPT_BOOL('d', "delete", &deleterefs, N_("delete refs")),
+		OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --mirror)")),
+		OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN),
+		OPT_BIT( 0,  "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN),
+		OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE),
+		{ OPTION_CALLBACK,
+		  0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
+		  N_("require old value of ref to be at this value"),
+		  PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parseopt_push_cas_option },
+		{ OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, "(check|on-demand|no)",
+			N_("control recursive pushing of submodules"),
+			PARSE_OPT_OPTARG, option_parse_recurse_submodules },
+		OPT_BOOL_F( 0 , "thin", &thin, N_("use thin pack"), PARSE_OPT_NOCOMPLETE),
+		OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", N_("receive pack program")),
+		OPT_STRING( 0 , "exec", &receivepack, "receive-pack", N_("receive pack program")),
+		OPT_BIT('u', "set-upstream", &flags, N_("set upstream for git pull/status"),
+			TRANSPORT_PUSH_SET_UPSTREAM),
+		OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
+		OPT_BIT(0, "prune", &flags, N_("prune locally removed refs"),
+			TRANSPORT_PUSH_PRUNE),
+		OPT_BIT(0, "no-verify", &flags, N_("bypass pre-push hook"), TRANSPORT_PUSH_NO_HOOK),
+		OPT_BIT(0, "follow-tags", &flags, N_("push missing but relevant tags"),
+			TRANSPORT_PUSH_FOLLOW_TAGS),
+		{ OPTION_CALLBACK,
+		  0, "signed", &push_cert, "(yes|no|if-asked)", N_("GPG sign the push"),
+		  PARSE_OPT_OPTARG, option_parse_push_signed },
+		OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
+		OPT_STRING_LIST('o', "push-option", &push_options_cmdline, N_("server-specific"), N_("option to transmit")),
+		OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
+				TRANSPORT_FAMILY_IPV4),
+		OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
+				TRANSPORT_FAMILY_IPV6),
+		OPT_END()
+	};
+
+	packet_trace_identity("push");
+	git_config(git_push_config, &flags);
+	argc = parse_options(argc, argv, prefix, options, push_usage, 0);
+	push_options = (push_options_cmdline.nr
+		? &push_options_cmdline
+		: &push_options_config);
+	set_push_cert_flags(&flags, push_cert);
+
+	if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
+		die(_("--delete is incompatible with --all, --mirror and --tags"));
+	if (deleterefs && argc < 2)
+		die(_("--delete doesn't make sense without any refs"));
+	if (flags & TRANSPORT_PUSH_ALL) {
+		if (tags)
+			die(_("--all and --tags are incompatible"));
+		if (argc >= 2)
+			die(_("--all can't be combined with refspecs"));
+	}
+	if (flags & TRANSPORT_PUSH_MIRROR) {
+		if (tags)
+			die(_("--mirror and --tags are incompatible"));
+		if (argc >= 2)
+			die(_("--mirror can't be combined with refspecs"));
+	}
+	if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))
+		die(_("--all and --mirror are incompatible"));
+
+	if (recurse_submodules == RECURSE_SUBMODULES_CHECK)
+		flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK;
+	else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
+		flags |= TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND;
+	else if (recurse_submodules == RECURSE_SUBMODULES_ONLY)
+		flags |= TRANSPORT_RECURSE_SUBMODULES_ONLY;
+
+	if (tags)
+		refspec_append(&rs, "refs/tags/*");
+
+	if (argc > 0) {
+		repo = argv[0];
+		set_refspecs(argv + 1, argc - 1, repo);
+	}
+
+	for_each_string_list_item(item, push_options)
+		if (strchr(item->string, '\n'))
+			die(_("push options must not have new line characters"));
+
+	rc = do_push(repo, flags, push_options);
+	string_list_clear(&push_options_cmdline, 0);
+	string_list_clear(&push_options_config, 0);
+	if (rc == -1)
+		usage_with_options(push_usage, options);
+	else
+		return rc;
+}
diff --git a/builtin/range-diff.c b/builtin/range-diff.c
new file mode 100644
index 0000000000..9202e75544
--- /dev/null
+++ b/builtin/range-diff.c
@@ -0,0 +1,87 @@
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "range-diff.h"
+#include "config.h"
+
+static const char * const builtin_range_diff_usage[] = {
+N_("git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>"),
+N_("git range-diff [<options>] <old-tip>...<new-tip>"),
+N_("git range-diff [<options>] <base> <old-tip> <new-tip>"),
+NULL
+};
+
+int cmd_range_diff(int argc, const char **argv, const char *prefix)
+{
+	int creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT;
+	struct diff_options diffopt = { NULL };
+	int simple_color = -1;
+	struct option range_diff_options[] = {
+		OPT_INTEGER(0, "creation-factor", &creation_factor,
+			    N_("Percentage by which creation is weighted")),
+		OPT_BOOL(0, "no-dual-color", &simple_color,
+			    N_("use simple diff colors")),
+		OPT_END()
+	};
+	struct option *options;
+	int res = 0;
+	struct strbuf range1 = STRBUF_INIT, range2 = STRBUF_INIT;
+
+	git_config(git_diff_ui_config, NULL);
+
+	repo_diff_setup(the_repository, &diffopt);
+
+	options = parse_options_concat(range_diff_options, diffopt.parseopts);
+	argc = parse_options(argc, argv, prefix, options,
+			     builtin_range_diff_usage, 0);
+
+	diff_setup_done(&diffopt);
+
+	/* force color when --dual-color was used */
+	if (!simple_color)
+		diffopt.use_color = 1;
+
+	if (argc == 2) {
+		if (!strstr(argv[0], ".."))
+			die(_("no .. in range: '%s'"), argv[0]);
+		strbuf_addstr(&range1, argv[0]);
+
+		if (!strstr(argv[1], ".."))
+			die(_("no .. in range: '%s'"), argv[1]);
+		strbuf_addstr(&range2, argv[1]);
+	} else if (argc == 3) {
+		strbuf_addf(&range1, "%s..%s", argv[0], argv[1]);
+		strbuf_addf(&range2, "%s..%s", argv[0], argv[2]);
+	} else if (argc == 1) {
+		const char *b = strstr(argv[0], "..."), *a = argv[0];
+		int a_len;
+
+		if (!b) {
+			error(_("single arg format must be symmetric range"));
+			usage_with_options(builtin_range_diff_usage, options);
+		}
+
+		a_len = (int)(b - a);
+		if (!a_len) {
+			a = "HEAD";
+			a_len = strlen(a);
+		}
+		b += 3;
+		if (!*b)
+			b = "HEAD";
+		strbuf_addf(&range1, "%s..%.*s", b, a_len, a);
+		strbuf_addf(&range2, "%.*s..%s", a_len, a, b);
+	} else {
+		error(_("need two commit ranges"));
+		usage_with_options(builtin_range_diff_usage, options);
+	}
+	FREE_AND_NULL(options);
+
+	res = show_range_diff(range1.buf, range2.buf, creation_factor,
+			      simple_color < 1, &diffopt);
+
+	strbuf_release(&range1);
+	strbuf_release(&range2);
+
+	return res;
+}
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
new file mode 100644
index 0000000000..ca5e655d2f
--- /dev/null
+++ b/builtin/read-tree.c
@@ -0,0 +1,270 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "lockfile.h"
+#include "object.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "cache-tree.h"
+#include "unpack-trees.h"
+#include "dir.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "resolve-undo.h"
+#include "submodule.h"
+#include "submodule-config.h"
+
+static int nr_trees;
+static int read_empty;
+static struct tree *trees[MAX_UNPACK_TREES];
+
+static int list_tree(struct object_id *oid)
+{
+	struct tree *tree;
+
+	if (nr_trees >= MAX_UNPACK_TREES)
+		die("I cannot read more than %d trees", MAX_UNPACK_TREES);
+	tree = parse_tree_indirect(oid);
+	if (!tree)
+		return -1;
+	trees[nr_trees++] = tree;
+	return 0;
+}
+
+static const char * const read_tree_usage[] = {
+	N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"),
+	NULL
+};
+
+static int index_output_cb(const struct option *opt, const char *arg,
+				 int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+	set_alternate_index_output(arg);
+	return 0;
+}
+
+static int exclude_per_directory_cb(const struct option *opt, const char *arg,
+				    int unset)
+{
+	struct dir_struct *dir;
+	struct unpack_trees_options *opts;
+
+	BUG_ON_OPT_NEG(unset);
+
+	opts = (struct unpack_trees_options *)opt->value;
+
+	if (opts->dir)
+		die("more than one --exclude-per-directory given.");
+
+	dir = xcalloc(1, sizeof(*opts->dir));
+	dir->flags |= DIR_SHOW_IGNORED;
+	dir->exclude_per_dir = arg;
+	opts->dir = dir;
+	/* We do not need to nor want to do read-directory
+	 * here; we are merely interested in reusing the
+	 * per directory ignore stack mechanism.
+	 */
+	return 0;
+}
+
+static void debug_stage(const char *label, const struct cache_entry *ce,
+			struct unpack_trees_options *o)
+{
+	printf("%s ", label);
+	if (!ce)
+		printf("(missing)\n");
+	else if (ce == o->df_conflict_entry)
+		printf("(conflict)\n");
+	else
+		printf("%06o #%d %s %.8s\n",
+		       ce->ce_mode, ce_stage(ce), ce->name,
+		       oid_to_hex(&ce->oid));
+}
+
+static int debug_merge(const struct cache_entry * const *stages,
+		       struct unpack_trees_options *o)
+{
+	int i;
+
+	printf("* %d-way merge\n", o->merge_size);
+	debug_stage("index", stages[0], o);
+	for (i = 1; i <= o->merge_size; i++) {
+		char buf[24];
+		xsnprintf(buf, sizeof(buf), "ent#%d", i);
+		debug_stage(buf, stages[i], o);
+	}
+	return 0;
+}
+
+static int git_read_tree_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "submodule.recurse"))
+		return git_default_submodule_config(var, value, cb);
+
+	return git_default_config(var, value, cb);
+}
+
+int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
+{
+	int i, stage = 0;
+	struct object_id oid;
+	struct tree_desc t[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+	int prefix_set = 0;
+	struct lock_file lock_file = LOCK_INIT;
+	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_BOOL(0, "empty", &read_empty,
+			    N_("only empty the index")),
+		OPT__VERBOSE(&opts.verbose_update, N_("be verbose")),
+		OPT_GROUP(N_("Merging")),
+		OPT_BOOL('m', NULL, &opts.merge,
+			 N_("perform a merge in addition to a read")),
+		OPT_BOOL(0, "trivial", &opts.trivial_merges_only,
+			 N_("3-way merge if no file level merging required")),
+		OPT_BOOL(0, "aggressive", &opts.aggressive,
+			 N_("3-way merge in presence of adds and removes")),
+		OPT_BOOL(0, "reset", &opts.reset,
+			 N_("same as -m, but discard unmerged entries")),
+		{ OPTION_STRING, 0, "prefix", &opts.prefix, N_("<subdirectory>/"),
+		  N_("read the tree into the index under <subdirectory>/"),
+		  PARSE_OPT_NONEG },
+		OPT_BOOL('u', NULL, &opts.update,
+			 N_("update working tree with merge result")),
+		{ 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_BOOL('i', NULL, &opts.index_only,
+			 N_("don't check the working tree after merging")),
+		OPT__DRY_RUN(&opts.dry_run, N_("don't update the index or the work tree")),
+		OPT_BOOL(0, "no-sparse-checkout", &opts.skip_sparse_checkout,
+			 N_("skip applying sparse checkout filter")),
+		OPT_BOOL(0, "debug-unpack", &opts.debug_unpack,
+			 N_("debug unpack-trees")),
+		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+			    "checkout", "control recursive updating of submodules",
+			    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
+		OPT__QUIET(&opts.quiet, N_("suppress feedback messages")),
+		OPT_END()
+	};
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = -1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+
+	git_config(git_read_tree_config, NULL);
+
+	argc = parse_options(argc, argv, cmd_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_oid(arg, &oid))
+			die("Not a valid object name %s", arg);
+		if (list_tree(&oid) < 0)
+			die("failed to unpack tree object %s", arg);
+		stage++;
+	}
+	if (!nr_trees && !read_empty && !opts.merge)
+		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) {
+		switch (stage - 1) {
+		case 0:
+			die("you must specify at least one tree to merge");
+			break;
+		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_repository,
+				 the_repository->index,
+				 trees[0]);
+
+	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+		die("unable to write new index file");
+	return 0;
+}
diff --git a/builtin/rebase.c b/builtin/rebase.c
new file mode 100644
index 0000000000..670096c065
--- /dev/null
+++ b/builtin/rebase.c
@@ -0,0 +1,2169 @@
+/*
+ * "git rebase" builtin command
+ *
+ * Copyright (c) 2018 Pratik Karki
+ */
+
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "run-command.h"
+#include "exec-cmd.h"
+#include "argv-array.h"
+#include "dir.h"
+#include "packfile.h"
+#include "refs.h"
+#include "quote.h"
+#include "config.h"
+#include "cache-tree.h"
+#include "unpack-trees.h"
+#include "lockfile.h"
+#include "parse-options.h"
+#include "commit.h"
+#include "diff.h"
+#include "wt-status.h"
+#include "revision.h"
+#include "commit-reach.h"
+#include "rerere.h"
+#include "branch.h"
+#include "sequencer.h"
+#include "rebase-interactive.h"
+
+static char const * const builtin_rebase_usage[] = {
+	N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
+		"[<upstream>] [<branch>]"),
+	N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
+		"--root [<branch>]"),
+	N_("git rebase --continue | --abort | --skip | --edit-todo"),
+	NULL
+};
+
+static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
+static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
+static GIT_PATH_FUNC(apply_dir, "rebase-apply")
+static GIT_PATH_FUNC(merge_dir, "rebase-merge")
+
+enum rebase_type {
+	REBASE_UNSPECIFIED = -1,
+	REBASE_AM,
+	REBASE_MERGE,
+	REBASE_INTERACTIVE,
+	REBASE_PRESERVE_MERGES
+};
+
+struct rebase_options {
+	enum rebase_type type;
+	const char *state_dir;
+	struct commit *upstream;
+	const char *upstream_name;
+	const char *upstream_arg;
+	char *head_name;
+	struct object_id orig_head;
+	struct commit *onto;
+	const char *onto_name;
+	const char *revisions;
+	const char *switch_to;
+	int root;
+	struct object_id *squash_onto;
+	struct commit *restrict_revision;
+	int dont_finish_rebase;
+	enum {
+		REBASE_NO_QUIET = 1<<0,
+		REBASE_VERBOSE = 1<<1,
+		REBASE_DIFFSTAT = 1<<2,
+		REBASE_FORCE = 1<<3,
+		REBASE_INTERACTIVE_EXPLICIT = 1<<4,
+	} flags;
+	struct argv_array git_am_opts;
+	const char *action;
+	int signoff;
+	int allow_rerere_autoupdate;
+	int keep_empty;
+	int autosquash;
+	char *gpg_sign_opt;
+	int autostash;
+	char *cmd;
+	int allow_empty_message;
+	int rebase_merges, rebase_cousins;
+	char *strategy, *strategy_opts;
+	struct strbuf git_format_patch_opt;
+	int reschedule_failed_exec;
+	int use_legacy_rebase;
+};
+
+#define REBASE_OPTIONS_INIT {			  	\
+		.type = REBASE_UNSPECIFIED,	  	\
+		.flags = REBASE_NO_QUIET, 		\
+		.git_am_opts = ARGV_ARRAY_INIT,		\
+		.git_format_patch_opt = STRBUF_INIT	\
+	}
+
+static struct replay_opts get_replay_opts(const struct rebase_options *opts)
+{
+	struct replay_opts replay = REPLAY_OPTS_INIT;
+
+	replay.action = REPLAY_INTERACTIVE_REBASE;
+	sequencer_init_config(&replay);
+
+	replay.signoff = opts->signoff;
+	replay.allow_ff = !(opts->flags & REBASE_FORCE);
+	if (opts->allow_rerere_autoupdate)
+		replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
+	replay.allow_empty = 1;
+	replay.allow_empty_message = opts->allow_empty_message;
+	replay.verbose = opts->flags & REBASE_VERBOSE;
+	replay.reschedule_failed_exec = opts->reschedule_failed_exec;
+	replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
+	replay.strategy = opts->strategy;
+	if (opts->strategy_opts)
+		parse_strategy_opts(&replay, opts->strategy_opts);
+
+	return replay;
+}
+
+enum action {
+	ACTION_NONE = 0,
+	ACTION_CONTINUE,
+	ACTION_SKIP,
+	ACTION_ABORT,
+	ACTION_QUIT,
+	ACTION_EDIT_TODO,
+	ACTION_SHOW_CURRENT_PATCH,
+	ACTION_SHORTEN_OIDS,
+	ACTION_EXPAND_OIDS,
+	ACTION_CHECK_TODO_LIST,
+	ACTION_REARRANGE_SQUASH,
+	ACTION_ADD_EXEC
+};
+
+static const char *action_names[] = { "undefined",
+				      "continue",
+				      "skip",
+				      "abort",
+				      "quit",
+				      "edit_todo",
+				      "show_current_patch" };
+
+static int add_exec_commands(struct string_list *commands)
+{
+	const char *todo_file = rebase_path_todo();
+	struct todo_list todo_list = TODO_LIST_INIT;
+	int res;
+
+	if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+		return error_errno(_("could not read '%s'."), todo_file);
+
+	if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+					&todo_list)) {
+		todo_list_release(&todo_list);
+		return error(_("unusable todo list: '%s'"), todo_file);
+	}
+
+	todo_list_add_exec_commands(&todo_list, commands);
+	res = todo_list_write_to_file(the_repository, &todo_list,
+				      todo_file, NULL, NULL, -1, 0);
+	todo_list_release(&todo_list);
+
+	if (res)
+		return error_errno(_("could not write '%s'."), todo_file);
+	return 0;
+}
+
+static int rearrange_squash_in_todo_file(void)
+{
+	const char *todo_file = rebase_path_todo();
+	struct todo_list todo_list = TODO_LIST_INIT;
+	int res = 0;
+
+	if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+		return error_errno(_("could not read '%s'."), todo_file);
+	if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+					&todo_list)) {
+		todo_list_release(&todo_list);
+		return error(_("unusable todo list: '%s'"), todo_file);
+	}
+
+	res = todo_list_rearrange_squash(&todo_list);
+	if (!res)
+		res = todo_list_write_to_file(the_repository, &todo_list,
+					      todo_file, NULL, NULL, -1, 0);
+
+	todo_list_release(&todo_list);
+
+	if (res)
+		return error_errno(_("could not write '%s'."), todo_file);
+	return 0;
+}
+
+static int transform_todo_file(unsigned flags)
+{
+	const char *todo_file = rebase_path_todo();
+	struct todo_list todo_list = TODO_LIST_INIT;
+	int res;
+
+	if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+		return error_errno(_("could not read '%s'."), todo_file);
+
+	if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+					&todo_list)) {
+		todo_list_release(&todo_list);
+		return error(_("unusable todo list: '%s'"), todo_file);
+	}
+
+	res = todo_list_write_to_file(the_repository, &todo_list, todo_file,
+				      NULL, NULL, -1, flags);
+	todo_list_release(&todo_list);
+
+	if (res)
+		return error_errno(_("could not write '%s'."), todo_file);
+	return 0;
+}
+
+static int edit_todo_file(unsigned flags)
+{
+	const char *todo_file = rebase_path_todo();
+	struct todo_list todo_list = TODO_LIST_INIT,
+		new_todo = TODO_LIST_INIT;
+	int res = 0;
+
+	if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+		return error_errno(_("could not read '%s'."), todo_file);
+
+	strbuf_stripspace(&todo_list.buf, 1);
+	res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
+	if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
+					    NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
+		res = error_errno(_("could not write '%s'"), todo_file);
+
+	todo_list_release(&todo_list);
+	todo_list_release(&new_todo);
+
+	return res;
+}
+
+static int get_revision_ranges(struct commit *upstream, struct commit *onto,
+			       const char **head_hash,
+			       char **revisions, char **shortrevisions)
+{
+	struct commit *base_rev = upstream ? upstream : onto;
+	const char *shorthead;
+	struct object_id orig_head;
+
+	if (get_oid("HEAD", &orig_head))
+		return error(_("no HEAD?"));
+
+	*head_hash = find_unique_abbrev(&orig_head, GIT_MAX_HEXSZ);
+	*revisions = xstrfmt("%s...%s", oid_to_hex(&base_rev->object.oid),
+						   *head_hash);
+
+	shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV);
+
+	if (upstream) {
+		const char *shortrev;
+
+		shortrev = find_unique_abbrev(&base_rev->object.oid,
+					      DEFAULT_ABBREV);
+
+		*shortrevisions = xstrfmt("%s..%s", shortrev, shorthead);
+	} else
+		*shortrevisions = xstrdup(shorthead);
+
+	return 0;
+}
+
+static int init_basic_state(struct replay_opts *opts, const char *head_name,
+			    struct commit *onto, const char *orig_head)
+{
+	FILE *interactive;
+
+	if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir()))
+		return error_errno(_("could not create temporary %s"), merge_dir());
+
+	delete_reflog("REBASE_HEAD");
+
+	interactive = fopen(path_interactive(), "w");
+	if (!interactive)
+		return error_errno(_("could not mark as interactive"));
+	fclose(interactive);
+
+	return write_basic_state(opts, head_name, onto, orig_head);
+}
+
+static void split_exec_commands(const char *cmd, struct string_list *commands)
+{
+	if (cmd && *cmd) {
+		string_list_split(commands, cmd, '\n', -1);
+
+		/* rebase.c adds a new line to cmd after every command,
+		 * so here the last command is always empty */
+		string_list_remove_empty_items(commands, 0);
+	}
+}
+
+static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
+{
+	int ret;
+	const char *head_hash = NULL;
+	char *revisions = NULL, *shortrevisions = NULL;
+	struct argv_array make_script_args = ARGV_ARRAY_INIT;
+	struct todo_list todo_list = TODO_LIST_INIT;
+	struct replay_opts replay = get_replay_opts(opts);
+	struct string_list commands = STRING_LIST_INIT_DUP;
+
+	if (prepare_branch_to_be_rebased(the_repository, &replay,
+					 opts->switch_to))
+		return -1;
+
+	if (get_revision_ranges(opts->upstream, opts->onto, &head_hash,
+				&revisions, &shortrevisions))
+		return -1;
+
+	if (init_basic_state(&replay,
+			     opts->head_name ? opts->head_name : "detached HEAD",
+			     opts->onto, head_hash)) {
+		free(revisions);
+		free(shortrevisions);
+
+		return -1;
+	}
+
+	if (!opts->upstream && opts->squash_onto)
+		write_file(path_squash_onto(), "%s\n",
+			   oid_to_hex(opts->squash_onto));
+
+	argv_array_pushl(&make_script_args, "", revisions, NULL);
+	if (opts->restrict_revision)
+		argv_array_push(&make_script_args,
+				oid_to_hex(&opts->restrict_revision->object.oid));
+
+	ret = sequencer_make_script(the_repository, &todo_list.buf,
+				    make_script_args.argc, make_script_args.argv,
+				    flags);
+
+	if (ret)
+		error(_("could not generate todo list"));
+	else {
+		discard_cache();
+		if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+						&todo_list))
+			BUG("unusable todo list");
+
+		split_exec_commands(opts->cmd, &commands);
+		ret = complete_action(the_repository, &replay, flags,
+			shortrevisions, opts->onto_name, opts->onto, head_hash,
+			&commands, opts->autosquash, &todo_list);
+	}
+
+	string_list_clear(&commands, 0);
+	free(revisions);
+	free(shortrevisions);
+	todo_list_release(&todo_list);
+	argv_array_clear(&make_script_args);
+
+	return ret;
+}
+
+static int run_rebase_interactive(struct rebase_options *opts,
+				  enum action command)
+{
+	unsigned flags = 0;
+	int abbreviate_commands = 0, ret = 0;
+
+	git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
+
+	flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
+	flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
+	flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
+	flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
+	flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
+
+	switch (command) {
+	case ACTION_NONE: {
+		if (!opts->onto && !opts->upstream)
+			die(_("a base commit must be provided with --upstream or --onto"));
+
+		ret = do_interactive_rebase(opts, flags);
+		break;
+	}
+	case ACTION_SKIP: {
+		struct string_list merge_rr = STRING_LIST_INIT_DUP;
+
+		rerere_clear(the_repository, &merge_rr);
+	}
+		/* fallthrough */
+	case ACTION_CONTINUE: {
+		struct replay_opts replay_opts = get_replay_opts(opts);
+
+		ret = sequencer_continue(the_repository, &replay_opts);
+		break;
+	}
+	case ACTION_EDIT_TODO:
+		ret = edit_todo_file(flags);
+		break;
+	case ACTION_SHOW_CURRENT_PATCH: {
+		struct child_process cmd = CHILD_PROCESS_INIT;
+
+		cmd.git_cmd = 1;
+		argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL);
+		ret = run_command(&cmd);
+
+		break;
+	}
+	case ACTION_SHORTEN_OIDS:
+	case ACTION_EXPAND_OIDS:
+		ret = transform_todo_file(flags);
+		break;
+	case ACTION_CHECK_TODO_LIST:
+		ret = check_todo_list_from_file(the_repository);
+		break;
+	case ACTION_REARRANGE_SQUASH:
+		ret = rearrange_squash_in_todo_file();
+		break;
+	case ACTION_ADD_EXEC: {
+		struct string_list commands = STRING_LIST_INIT_DUP;
+
+		split_exec_commands(opts->cmd, &commands);
+		ret = add_exec_commands(&commands);
+		string_list_clear(&commands, 0);
+		break;
+	}
+	default:
+		BUG("invalid command '%d'", command);
+	}
+
+	return ret;
+}
+
+static const char * const builtin_rebase_interactive_usage[] = {
+	N_("git rebase--interactive [<options>]"),
+	NULL
+};
+
+int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
+{
+	struct rebase_options opts = REBASE_OPTIONS_INIT;
+	struct object_id squash_onto = null_oid;
+	enum action command = ACTION_NONE;
+	struct option options[] = {
+		OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
+			   REBASE_FORCE),
+		OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
+		OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
+			 N_("allow commits with empty messages")),
+		OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
+		OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins,
+			 N_("keep original branch points of cousins")),
+		OPT_BOOL(0, "autosquash", &opts.autosquash,
+			 N_("move commits that begin with squash!/fixup!")),
+		OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
+		OPT_BIT('v', "verbose", &opts.flags,
+			N_("display a diffstat of what changed upstream"),
+			REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
+		OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
+			    ACTION_CONTINUE),
+		OPT_CMDMODE(0, "skip", &command, N_("skip commit"), ACTION_SKIP),
+		OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"),
+			    ACTION_EDIT_TODO),
+		OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"),
+			    ACTION_SHOW_CURRENT_PATCH),
+		OPT_CMDMODE(0, "shorten-ids", &command,
+			N_("shorten commit ids in the todo list"), ACTION_SHORTEN_OIDS),
+		OPT_CMDMODE(0, "expand-ids", &command,
+			N_("expand commit ids in the todo list"), ACTION_EXPAND_OIDS),
+		OPT_CMDMODE(0, "check-todo-list", &command,
+			N_("check the todo list"), ACTION_CHECK_TODO_LIST),
+		OPT_CMDMODE(0, "rearrange-squash", &command,
+			N_("rearrange fixup/squash lines"), ACTION_REARRANGE_SQUASH),
+		OPT_CMDMODE(0, "add-exec-commands", &command,
+			N_("insert exec commands in todo list"), ACTION_ADD_EXEC),
+		{ OPTION_CALLBACK, 0, "onto", &opts.onto, N_("onto"), N_("onto"),
+		  PARSE_OPT_NONEG, parse_opt_commit, 0 },
+		{ OPTION_CALLBACK, 0, "restrict-revision", &opts.restrict_revision,
+		  N_("restrict-revision"), N_("restrict revision"),
+		  PARSE_OPT_NONEG, parse_opt_commit, 0 },
+		{ OPTION_CALLBACK, 0, "squash-onto", &squash_onto, N_("squash-onto"),
+		  N_("squash onto"), PARSE_OPT_NONEG, parse_opt_object_id, 0 },
+		{ OPTION_CALLBACK, 0, "upstream", &opts.upstream, N_("upstream"),
+		  N_("the upstream commit"), PARSE_OPT_NONEG, parse_opt_commit,
+		  0 },
+		OPT_STRING(0, "head-name", &opts.head_name, N_("head-name"), N_("head name")),
+		{ OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"),
+			N_("GPG-sign commits"),
+			PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
+			   N_("rebase strategy")),
+		OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"),
+			   N_("strategy options")),
+		OPT_STRING(0, "switch-to", &opts.switch_to, N_("switch-to"),
+			   N_("the branch or commit to checkout")),
+		OPT_STRING(0, "onto-name", &opts.onto_name, N_("onto-name"), N_("onto name")),
+		OPT_STRING(0, "cmd", &opts.cmd, N_("cmd"), N_("the command to run")),
+		OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_autoupdate),
+		OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec,
+			 N_("automatically re-schedule any `exec` that fails")),
+		OPT_END()
+	};
+
+	opts.rebase_cousins = -1;
+
+	if (argc == 1)
+		usage_with_options(builtin_rebase_interactive_usage, options);
+
+	argc = parse_options(argc, argv, prefix, options,
+			builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
+
+	if (!is_null_oid(&squash_onto))
+		opts.squash_onto = &squash_onto;
+
+	if (opts.rebase_cousins >= 0 && !opts.rebase_merges)
+		warning(_("--[no-]rebase-cousins has no effect without "
+			  "--rebase-merges"));
+
+	return !!run_rebase_interactive(&opts, command);
+}
+
+static int is_interactive(struct rebase_options *opts)
+{
+	return opts->type == REBASE_INTERACTIVE ||
+		opts->type == REBASE_PRESERVE_MERGES;
+}
+
+static void imply_interactive(struct rebase_options *opts, const char *option)
+{
+	switch (opts->type) {
+	case REBASE_AM:
+		die(_("%s requires an interactive rebase"), option);
+		break;
+	case REBASE_INTERACTIVE:
+	case REBASE_PRESERVE_MERGES:
+		break;
+	case REBASE_MERGE:
+		/* we now implement --merge via --interactive */
+	default:
+		opts->type = REBASE_INTERACTIVE; /* implied */
+		break;
+	}
+}
+
+/* Returns the filename prefixed by the state_dir */
+static const char *state_dir_path(const char *filename, struct rebase_options *opts)
+{
+	static struct strbuf path = STRBUF_INIT;
+	static size_t prefix_len;
+
+	if (!prefix_len) {
+		strbuf_addf(&path, "%s/", opts->state_dir);
+		prefix_len = path.len;
+	}
+
+	strbuf_setlen(&path, prefix_len);
+	strbuf_addstr(&path, filename);
+	return path.buf;
+}
+
+/* Read one file, then strip line endings */
+static int read_one(const char *path, struct strbuf *buf)
+{
+	if (strbuf_read_file(buf, path, 0) < 0)
+		return error_errno(_("could not read '%s'"), path);
+	strbuf_trim_trailing_newline(buf);
+	return 0;
+}
+
+/* Initialize the rebase options from the state directory. */
+static int read_basic_state(struct rebase_options *opts)
+{
+	struct strbuf head_name = STRBUF_INIT;
+	struct strbuf buf = STRBUF_INIT;
+	struct object_id oid;
+
+	if (read_one(state_dir_path("head-name", opts), &head_name) ||
+	    read_one(state_dir_path("onto", opts), &buf))
+		return -1;
+	opts->head_name = starts_with(head_name.buf, "refs/") ?
+		xstrdup(head_name.buf) : NULL;
+	strbuf_release(&head_name);
+	if (get_oid(buf.buf, &oid))
+		return error(_("could not get 'onto': '%s'"), buf.buf);
+	opts->onto = lookup_commit_or_die(&oid, buf.buf);
+
+	/*
+	 * We always write to orig-head, but interactive rebase used to write to
+	 * head. Fall back to reading from head to cover for the case that the
+	 * user upgraded git with an ongoing interactive rebase.
+	 */
+	strbuf_reset(&buf);
+	if (file_exists(state_dir_path("orig-head", opts))) {
+		if (read_one(state_dir_path("orig-head", opts), &buf))
+			return -1;
+	} else if (read_one(state_dir_path("head", opts), &buf))
+		return -1;
+	if (get_oid(buf.buf, &opts->orig_head))
+		return error(_("invalid orig-head: '%s'"), buf.buf);
+
+	if (file_exists(state_dir_path("quiet", opts)))
+		opts->flags &= ~REBASE_NO_QUIET;
+	else
+		opts->flags |= REBASE_NO_QUIET;
+
+	if (file_exists(state_dir_path("verbose", opts)))
+		opts->flags |= REBASE_VERBOSE;
+
+	if (file_exists(state_dir_path("signoff", opts))) {
+		opts->signoff = 1;
+		opts->flags |= REBASE_FORCE;
+	}
+
+	if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
+		strbuf_reset(&buf);
+		if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
+			    &buf))
+			return -1;
+		if (!strcmp(buf.buf, "--rerere-autoupdate"))
+			opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
+		else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
+			opts->allow_rerere_autoupdate = RERERE_NOAUTOUPDATE;
+		else
+			warning(_("ignoring invalid allow_rerere_autoupdate: "
+				  "'%s'"), buf.buf);
+	}
+
+	if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
+		strbuf_reset(&buf);
+		if (read_one(state_dir_path("gpg_sign_opt", opts),
+			    &buf))
+			return -1;
+		free(opts->gpg_sign_opt);
+		opts->gpg_sign_opt = xstrdup(buf.buf);
+	}
+
+	if (file_exists(state_dir_path("strategy", opts))) {
+		strbuf_reset(&buf);
+		if (read_one(state_dir_path("strategy", opts), &buf))
+			return -1;
+		free(opts->strategy);
+		opts->strategy = xstrdup(buf.buf);
+	}
+
+	if (file_exists(state_dir_path("strategy_opts", opts))) {
+		strbuf_reset(&buf);
+		if (read_one(state_dir_path("strategy_opts", opts), &buf))
+			return -1;
+		free(opts->strategy_opts);
+		opts->strategy_opts = xstrdup(buf.buf);
+	}
+
+	strbuf_release(&buf);
+
+	return 0;
+}
+
+static int rebase_write_basic_state(struct rebase_options *opts)
+{
+	write_file(state_dir_path("head-name", opts), "%s",
+		   opts->head_name ? opts->head_name : "detached HEAD");
+	write_file(state_dir_path("onto", opts), "%s",
+		   opts->onto ? oid_to_hex(&opts->onto->object.oid) : "");
+	write_file(state_dir_path("orig-head", opts), "%s",
+		   oid_to_hex(&opts->orig_head));
+	write_file(state_dir_path("quiet", opts), "%s",
+		   opts->flags & REBASE_NO_QUIET ? "" : "t");
+	if (opts->flags & REBASE_VERBOSE)
+		write_file(state_dir_path("verbose", opts), "%s", "");
+	if (opts->strategy)
+		write_file(state_dir_path("strategy", opts), "%s",
+			   opts->strategy);
+	if (opts->strategy_opts)
+		write_file(state_dir_path("strategy_opts", opts), "%s",
+			   opts->strategy_opts);
+	if (opts->allow_rerere_autoupdate > 0)
+		write_file(state_dir_path("allow_rerere_autoupdate", opts),
+			   "-%s-rerere-autoupdate",
+			   opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
+				"" : "-no");
+	if (opts->gpg_sign_opt)
+		write_file(state_dir_path("gpg_sign_opt", opts), "%s",
+			   opts->gpg_sign_opt);
+	if (opts->signoff)
+		write_file(state_dir_path("strategy", opts), "--signoff");
+
+	return 0;
+}
+
+static int apply_autostash(struct rebase_options *opts)
+{
+	const char *path = state_dir_path("autostash", opts);
+	struct strbuf autostash = STRBUF_INIT;
+	struct child_process stash_apply = CHILD_PROCESS_INIT;
+
+	if (!file_exists(path))
+		return 0;
+
+	if (read_one(path, &autostash))
+		return error(_("Could not read '%s'"), path);
+	/* Ensure that the hash is not mistaken for a number */
+	strbuf_addstr(&autostash, "^0");
+	argv_array_pushl(&stash_apply.args,
+			 "stash", "apply", autostash.buf, NULL);
+	stash_apply.git_cmd = 1;
+	stash_apply.no_stderr = stash_apply.no_stdout =
+		stash_apply.no_stdin = 1;
+	if (!run_command(&stash_apply))
+		printf(_("Applied autostash.\n"));
+	else {
+		struct argv_array args = ARGV_ARRAY_INIT;
+		int res = 0;
+
+		argv_array_pushl(&args,
+				 "stash", "store", "-m", "autostash", "-q",
+				 autostash.buf, NULL);
+		if (run_command_v_opt(args.argv, RUN_GIT_CMD))
+			res = error(_("Cannot store %s"), autostash.buf);
+		argv_array_clear(&args);
+		strbuf_release(&autostash);
+		if (res)
+			return res;
+
+		fprintf(stderr,
+			_("Applying autostash resulted in conflicts.\n"
+			  "Your changes are safe in the stash.\n"
+			  "You can run \"git stash pop\" or \"git stash drop\" "
+			  "at any time.\n"));
+	}
+
+	strbuf_release(&autostash);
+	return 0;
+}
+
+static int finish_rebase(struct rebase_options *opts)
+{
+	struct strbuf dir = STRBUF_INIT;
+	const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+	int ret = 0;
+
+	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+	apply_autostash(opts);
+	close_object_store(the_repository->objects);
+	/*
+	 * We ignore errors in 'gc --auto', since the
+	 * user should see them.
+	 */
+	run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+	if (opts->type == REBASE_INTERACTIVE) {
+		struct replay_opts replay = REPLAY_OPTS_INIT;
+
+		replay.action = REPLAY_INTERACTIVE_REBASE;
+		ret = sequencer_remove_state(&replay);
+	} else {
+		strbuf_addstr(&dir, opts->state_dir);
+		if (remove_dir_recursively(&dir, 0))
+			ret = error(_("could not remove '%s'"),
+				    opts->state_dir);
+		strbuf_release(&dir);
+	}
+
+	return ret;
+}
+
+static struct commit *peel_committish(const char *name)
+{
+	struct object *obj;
+	struct object_id oid;
+
+	if (get_oid(name, &oid))
+		return NULL;
+	obj = parse_object(the_repository, &oid);
+	return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
+}
+
+static void add_var(struct strbuf *buf, const char *name, const char *value)
+{
+	if (!value)
+		strbuf_addf(buf, "unset %s; ", name);
+	else {
+		strbuf_addf(buf, "%s=", name);
+		sq_quote_buf(buf, value);
+		strbuf_addstr(buf, "; ");
+	}
+}
+
+#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
+
+#define RESET_HEAD_DETACH (1<<0)
+#define RESET_HEAD_HARD (1<<1)
+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
+#define RESET_HEAD_REFS_ONLY (1<<3)
+#define RESET_ORIG_HEAD (1<<4)
+
+static int reset_head(struct object_id *oid, const char *action,
+		      const char *switch_to_branch, unsigned flags,
+		      const char *reflog_orig_head, const char *reflog_head)
+{
+	unsigned detach_head = flags & RESET_HEAD_DETACH;
+	unsigned reset_hard = flags & RESET_HEAD_HARD;
+	unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
+	unsigned refs_only = flags & RESET_HEAD_REFS_ONLY;
+	unsigned update_orig_head = flags & RESET_ORIG_HEAD;
+	struct object_id head_oid;
+	struct tree_desc desc[2] = { { NULL }, { NULL } };
+	struct lock_file lock = LOCK_INIT;
+	struct unpack_trees_options unpack_tree_opts;
+	struct tree *tree;
+	const char *reflog_action;
+	struct strbuf msg = STRBUF_INIT;
+	size_t prefix_len;
+	struct object_id *orig = NULL, oid_orig,
+		*old_orig = NULL, oid_old_orig;
+	int ret = 0, nr = 0;
+
+	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
+		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
+
+	if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {
+		ret = -1;
+		goto leave_reset_head;
+	}
+
+	if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) {
+		ret = error(_("could not determine HEAD revision"));
+		goto leave_reset_head;
+	}
+
+	if (!oid)
+		oid = &head_oid;
+
+	if (refs_only)
+		goto reset_head_refs;
+
+	memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
+	setup_unpack_trees_porcelain(&unpack_tree_opts, action);
+	unpack_tree_opts.head_idx = 1;
+	unpack_tree_opts.src_index = the_repository->index;
+	unpack_tree_opts.dst_index = the_repository->index;
+	unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
+	unpack_tree_opts.update = 1;
+	unpack_tree_opts.merge = 1;
+	if (!detach_head)
+		unpack_tree_opts.reset = 1;
+
+	if (repo_read_index_unmerged(the_repository) < 0) {
+		ret = error(_("could not read index"));
+		goto leave_reset_head;
+	}
+
+	if (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {
+		ret = error(_("failed to find tree of %s"),
+			    oid_to_hex(&head_oid));
+		goto leave_reset_head;
+	}
+
+	if (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {
+		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
+		goto leave_reset_head;
+	}
+
+	if (unpack_trees(nr, desc, &unpack_tree_opts)) {
+		ret = -1;
+		goto leave_reset_head;
+	}
+
+	tree = parse_tree_indirect(oid);
+	prime_cache_tree(the_repository, the_repository->index, tree);
+
+	if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {
+		ret = error(_("could not write index"));
+		goto leave_reset_head;
+	}
+
+reset_head_refs:
+	reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
+	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
+	prefix_len = msg.len;
+
+	if (update_orig_head) {
+		if (!get_oid("ORIG_HEAD", &oid_old_orig))
+			old_orig = &oid_old_orig;
+		if (!get_oid("HEAD", &oid_orig)) {
+			orig = &oid_orig;
+			if (!reflog_orig_head) {
+				strbuf_addstr(&msg, "updating ORIG_HEAD");
+				reflog_orig_head = msg.buf;
+			}
+			update_ref(reflog_orig_head, "ORIG_HEAD", orig,
+				   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
+		} else if (old_orig)
+			delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+	}
+
+	if (!reflog_head) {
+		strbuf_setlen(&msg, prefix_len);
+		strbuf_addstr(&msg, "updating HEAD");
+		reflog_head = msg.buf;
+	}
+	if (!switch_to_branch)
+		ret = update_ref(reflog_head, "HEAD", oid, orig,
+				 detach_head ? REF_NO_DEREF : 0,
+				 UPDATE_REFS_MSG_ON_ERR);
+	else {
+		ret = update_ref(reflog_head, switch_to_branch, oid,
+				 NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+		if (!ret)
+			ret = create_symref("HEAD", switch_to_branch,
+					    reflog_head);
+	}
+	if (run_hook)
+		run_hook_le(NULL, "post-checkout",
+			    oid_to_hex(orig ? orig : &null_oid),
+			    oid_to_hex(oid), "1", NULL);
+
+leave_reset_head:
+	strbuf_release(&msg);
+	rollback_lock_file(&lock);
+	while (nr)
+		free((void *)desc[--nr].buffer);
+	return ret;
+}
+
+static int move_to_original_branch(struct rebase_options *opts)
+{
+	struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;
+	int ret;
+
+	if (!opts->head_name)
+		return 0; /* nothing to move back to */
+
+	if (!opts->onto)
+		BUG("move_to_original_branch without onto");
+
+	strbuf_addf(&orig_head_reflog, "rebase finished: %s onto %s",
+		    opts->head_name, oid_to_hex(&opts->onto->object.oid));
+	strbuf_addf(&head_reflog, "rebase finished: returning to %s",
+		    opts->head_name);
+	ret = reset_head(NULL, "", opts->head_name, RESET_HEAD_REFS_ONLY,
+			 orig_head_reflog.buf, head_reflog.buf);
+
+	strbuf_release(&orig_head_reflog);
+	strbuf_release(&head_reflog);
+	return ret;
+}
+
+static const char *resolvemsg =
+N_("Resolve all conflicts manually, mark them as resolved with\n"
+"\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n"
+"You can instead skip this commit: run \"git rebase --skip\".\n"
+"To abort and get back to the state before \"git rebase\", run "
+"\"git rebase --abort\".");
+
+static int run_am(struct rebase_options *opts)
+{
+	struct child_process am = CHILD_PROCESS_INIT;
+	struct child_process format_patch = CHILD_PROCESS_INIT;
+	struct strbuf revisions = STRBUF_INIT;
+	int status;
+	char *rebased_patches;
+
+	am.git_cmd = 1;
+	argv_array_push(&am.args, "am");
+
+	if (opts->action && !strcmp("continue", opts->action)) {
+		argv_array_push(&am.args, "--resolved");
+		argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+		if (opts->gpg_sign_opt)
+			argv_array_push(&am.args, opts->gpg_sign_opt);
+		status = run_command(&am);
+		if (status)
+			return status;
+
+		return move_to_original_branch(opts);
+	}
+	if (opts->action && !strcmp("skip", opts->action)) {
+		argv_array_push(&am.args, "--skip");
+		argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+		status = run_command(&am);
+		if (status)
+			return status;
+
+		return move_to_original_branch(opts);
+	}
+	if (opts->action && !strcmp("show-current-patch", opts->action)) {
+		argv_array_push(&am.args, "--show-current-patch");
+		return run_command(&am);
+	}
+
+	strbuf_addf(&revisions, "%s...%s",
+		    oid_to_hex(opts->root ?
+			       /* this is now equivalent to !opts->upstream */
+			       &opts->onto->object.oid :
+			       &opts->upstream->object.oid),
+		    oid_to_hex(&opts->orig_head));
+
+	rebased_patches = xstrdup(git_path("rebased-patches"));
+	format_patch.out = open(rebased_patches,
+				O_WRONLY | O_CREAT | O_TRUNC, 0666);
+	if (format_patch.out < 0) {
+		status = error_errno(_("could not open '%s' for writing"),
+				     rebased_patches);
+		free(rebased_patches);
+		argv_array_clear(&am.args);
+		return status;
+	}
+
+	format_patch.git_cmd = 1;
+	argv_array_pushl(&format_patch.args, "format-patch", "-k", "--stdout",
+			 "--full-index", "--cherry-pick", "--right-only",
+			 "--src-prefix=a/", "--dst-prefix=b/", "--no-renames",
+			 "--no-cover-letter", "--pretty=mboxrd", "--topo-order", NULL);
+	if (opts->git_format_patch_opt.len)
+		argv_array_split(&format_patch.args,
+				 opts->git_format_patch_opt.buf);
+	argv_array_push(&format_patch.args, revisions.buf);
+	if (opts->restrict_revision)
+		argv_array_pushf(&format_patch.args, "^%s",
+				 oid_to_hex(&opts->restrict_revision->object.oid));
+
+	status = run_command(&format_patch);
+	if (status) {
+		unlink(rebased_patches);
+		free(rebased_patches);
+		argv_array_clear(&am.args);
+
+		reset_head(&opts->orig_head, "checkout", opts->head_name, 0,
+			   "HEAD", NULL);
+		error(_("\ngit encountered an error while preparing the "
+			"patches to replay\n"
+			"these revisions:\n"
+			"\n    %s\n\n"
+			"As a result, git cannot rebase them."),
+		      opts->revisions);
+
+		strbuf_release(&revisions);
+		return status;
+	}
+	strbuf_release(&revisions);
+
+	am.in = open(rebased_patches, O_RDONLY);
+	if (am.in < 0) {
+		status = error_errno(_("could not open '%s' for reading"),
+				     rebased_patches);
+		free(rebased_patches);
+		argv_array_clear(&am.args);
+		return status;
+	}
+
+	argv_array_pushv(&am.args, opts->git_am_opts.argv);
+	argv_array_push(&am.args, "--rebasing");
+	argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+	argv_array_push(&am.args, "--patch-format=mboxrd");
+	if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE)
+		argv_array_push(&am.args, "--rerere-autoupdate");
+	else if (opts->allow_rerere_autoupdate == RERERE_NOAUTOUPDATE)
+		argv_array_push(&am.args, "--no-rerere-autoupdate");
+	if (opts->gpg_sign_opt)
+		argv_array_push(&am.args, opts->gpg_sign_opt);
+	status = run_command(&am);
+	unlink(rebased_patches);
+	free(rebased_patches);
+
+	if (!status) {
+		return move_to_original_branch(opts);
+	}
+
+	if (is_directory(opts->state_dir))
+		rebase_write_basic_state(opts);
+
+	return status;
+}
+
+static int run_specific_rebase(struct rebase_options *opts, enum action action)
+{
+	const char *argv[] = { NULL, NULL };
+	struct strbuf script_snippet = STRBUF_INIT, buf = STRBUF_INIT;
+	int status;
+	const char *backend, *backend_func;
+
+	if (opts->type == REBASE_INTERACTIVE) {
+		/* Run builtin interactive rebase */
+		setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
+		if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
+			setenv("GIT_SEQUENCE_EDITOR", ":", 1);
+			opts->autosquash = 0;
+		}
+		if (opts->gpg_sign_opt) {
+			/* remove the leading "-S" */
+			char *tmp = xstrdup(opts->gpg_sign_opt + 2);
+			free(opts->gpg_sign_opt);
+			opts->gpg_sign_opt = tmp;
+		}
+
+		status = run_rebase_interactive(opts, action);
+		goto finished_rebase;
+	}
+
+	if (opts->type == REBASE_AM) {
+		status = run_am(opts);
+		goto finished_rebase;
+	}
+
+	add_var(&script_snippet, "GIT_DIR", absolute_path(get_git_dir()));
+	add_var(&script_snippet, "state_dir", opts->state_dir);
+
+	add_var(&script_snippet, "upstream_name", opts->upstream_name);
+	add_var(&script_snippet, "upstream", opts->upstream ?
+		oid_to_hex(&opts->upstream->object.oid) : NULL);
+	add_var(&script_snippet, "head_name",
+		opts->head_name ? opts->head_name : "detached HEAD");
+	add_var(&script_snippet, "orig_head", oid_to_hex(&opts->orig_head));
+	add_var(&script_snippet, "onto", opts->onto ?
+		oid_to_hex(&opts->onto->object.oid) : NULL);
+	add_var(&script_snippet, "onto_name", opts->onto_name);
+	add_var(&script_snippet, "revisions", opts->revisions);
+	add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
+		oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
+	add_var(&script_snippet, "GIT_QUIET",
+		opts->flags & REBASE_NO_QUIET ? "" : "t");
+	sq_quote_argv_pretty(&buf, opts->git_am_opts.argv);
+	add_var(&script_snippet, "git_am_opt", buf.buf);
+	strbuf_release(&buf);
+	add_var(&script_snippet, "verbose",
+		opts->flags & REBASE_VERBOSE ? "t" : "");
+	add_var(&script_snippet, "diffstat",
+		opts->flags & REBASE_DIFFSTAT ? "t" : "");
+	add_var(&script_snippet, "force_rebase",
+		opts->flags & REBASE_FORCE ? "t" : "");
+	if (opts->switch_to)
+		add_var(&script_snippet, "switch_to", opts->switch_to);
+	add_var(&script_snippet, "action", opts->action ? opts->action : "");
+	add_var(&script_snippet, "signoff", opts->signoff ? "--signoff" : "");
+	add_var(&script_snippet, "allow_rerere_autoupdate",
+		opts->allow_rerere_autoupdate ?
+			opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
+			"--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
+	add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
+	add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
+	add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
+	add_var(&script_snippet, "cmd", opts->cmd);
+	add_var(&script_snippet, "allow_empty_message",
+		opts->allow_empty_message ?  "--allow-empty-message" : "");
+	add_var(&script_snippet, "rebase_merges",
+		opts->rebase_merges ? "t" : "");
+	add_var(&script_snippet, "rebase_cousins",
+		opts->rebase_cousins ? "t" : "");
+	add_var(&script_snippet, "strategy", opts->strategy);
+	add_var(&script_snippet, "strategy_opts", opts->strategy_opts);
+	add_var(&script_snippet, "rebase_root", opts->root ? "t" : "");
+	add_var(&script_snippet, "squash_onto",
+		opts->squash_onto ? oid_to_hex(opts->squash_onto) : "");
+	add_var(&script_snippet, "git_format_patch_opt",
+		opts->git_format_patch_opt.buf);
+
+	if (is_interactive(opts) &&
+	    !(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
+		strbuf_addstr(&script_snippet,
+			      "GIT_SEQUENCE_EDITOR=:; export GIT_SEQUENCE_EDITOR; ");
+		opts->autosquash = 0;
+	}
+
+	switch (opts->type) {
+	case REBASE_PRESERVE_MERGES:
+		backend = "git-rebase--preserve-merges";
+		backend_func = "git_rebase__preserve_merges";
+		break;
+	default:
+		BUG("Unhandled rebase type %d", opts->type);
+		break;
+	}
+
+	strbuf_addf(&script_snippet,
+		    ". git-sh-setup && . %s && %s", backend, backend_func);
+	argv[0] = script_snippet.buf;
+
+	status = run_command_v_opt(argv, RUN_USING_SHELL);
+finished_rebase:
+	if (opts->dont_finish_rebase)
+		; /* do nothing */
+	else if (opts->type == REBASE_INTERACTIVE)
+		; /* interactive rebase cleans up after itself */
+	else if (status == 0) {
+		if (!file_exists(state_dir_path("stopped-sha", opts)))
+			finish_rebase(opts);
+	} else if (status == 2) {
+		struct strbuf dir = STRBUF_INIT;
+
+		apply_autostash(opts);
+		strbuf_addstr(&dir, opts->state_dir);
+		remove_dir_recursively(&dir, 0);
+		strbuf_release(&dir);
+		die("Nothing to do");
+	}
+
+	strbuf_release(&script_snippet);
+
+	return status ? -1 : 0;
+}
+
+static int rebase_config(const char *var, const char *value, void *data)
+{
+	struct rebase_options *opts = data;
+
+	if (!strcmp(var, "rebase.stat")) {
+		if (git_config_bool(var, value))
+			opts->flags |= REBASE_DIFFSTAT;
+		else
+			opts->flags &= ~REBASE_DIFFSTAT;
+		return 0;
+	}
+
+	if (!strcmp(var, "rebase.autosquash")) {
+		opts->autosquash = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (!strcmp(var, "commit.gpgsign")) {
+		free(opts->gpg_sign_opt);
+		opts->gpg_sign_opt = git_config_bool(var, value) ?
+			xstrdup("-S") : NULL;
+		return 0;
+	}
+
+	if (!strcmp(var, "rebase.autostash")) {
+		opts->autostash = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (!strcmp(var, "rebase.reschedulefailedexec")) {
+		opts->reschedule_failed_exec = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (!strcmp(var, "rebase.usebuiltin")) {
+		opts->use_legacy_rebase = !git_config_bool(var, value);
+		return 0;
+	}
+
+	return git_default_config(var, value, data);
+}
+
+/*
+ * Determines whether the commits in from..to are linear, i.e. contain
+ * no merge commits. This function *expects* `from` to be an ancestor of
+ * `to`.
+ */
+static int is_linear_history(struct commit *from, struct commit *to)
+{
+	while (to && to != from) {
+		parse_commit(to);
+		if (!to->parents)
+			return 1;
+		if (to->parents->next)
+			return 0;
+		to = to->parents->item;
+	}
+	return 1;
+}
+
+static int can_fast_forward(struct commit *onto, struct object_id *head_oid,
+			    struct object_id *merge_base)
+{
+	struct commit *head = lookup_commit(the_repository, head_oid);
+	struct commit_list *merge_bases;
+	int res;
+
+	if (!head)
+		return 0;
+
+	merge_bases = get_merge_bases(onto, head);
+	if (merge_bases && !merge_bases->next) {
+		oidcpy(merge_base, &merge_bases->item->object.oid);
+		res = oideq(merge_base, &onto->object.oid);
+	} else {
+		oidcpy(merge_base, &null_oid);
+		res = 0;
+	}
+	free_commit_list(merge_bases);
+	return res && is_linear_history(onto, head);
+}
+
+/* -i followed by -m is still -i */
+static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
+{
+	struct rebase_options *opts = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+
+	if (!is_interactive(opts))
+		opts->type = REBASE_MERGE;
+
+	return 0;
+}
+
+/* -i followed by -p is still explicitly interactive, but -p alone is not */
+static int parse_opt_interactive(const struct option *opt, const char *arg,
+				 int unset)
+{
+	struct rebase_options *opts = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+
+	opts->type = REBASE_INTERACTIVE;
+	opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
+
+	return 0;
+}
+
+static void NORETURN error_on_missing_default_upstream(void)
+{
+	struct branch *current_branch = branch_get(NULL);
+
+	printf(_("%s\n"
+		 "Please specify which branch you want to rebase against.\n"
+		 "See git-rebase(1) for details.\n"
+		 "\n"
+		 "    git rebase '<branch>'\n"
+		 "\n"),
+		current_branch ? _("There is no tracking information for "
+			"the current branch.") :
+			_("You are not currently on a branch."));
+
+	if (current_branch) {
+		const char *remote = current_branch->remote_name;
+
+		if (!remote)
+			remote = _("<remote>");
+
+		printf(_("If you wish to set tracking information for this "
+			 "branch you can do so with:\n"
+			 "\n"
+			 "    git branch --set-upstream-to=%s/<branch> %s\n"
+			 "\n"),
+		       remote, current_branch->name);
+	}
+	exit(1);
+}
+
+static void set_reflog_action(struct rebase_options *options)
+{
+	const char *env;
+	struct strbuf buf = STRBUF_INIT;
+
+	if (!is_interactive(options))
+		return;
+
+	env = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
+	if (env && strcmp("rebase", env))
+		return; /* only override it if it is "rebase" */
+
+	strbuf_addf(&buf, "rebase -i (%s)", options->action);
+	setenv(GIT_REFLOG_ACTION_ENVIRONMENT, buf.buf, 1);
+	strbuf_release(&buf);
+}
+
+static int check_exec_cmd(const char *cmd)
+{
+	if (strchr(cmd, '\n'))
+		return error(_("exec commands cannot contain newlines"));
+
+	/* Does the command consist purely of whitespace? */
+	if (!cmd[strspn(cmd, " \t\r\f\v")])
+		return error(_("empty exec command"));
+
+	return 0;
+}
+
+
+int cmd_rebase(int argc, const char **argv, const char *prefix)
+{
+	struct rebase_options options = REBASE_OPTIONS_INIT;
+	const char *branch_name;
+	int ret, flags, total_argc, in_progress = 0;
+	int ok_to_skip_pre_rebase = 0;
+	struct strbuf msg = STRBUF_INIT;
+	struct strbuf revisions = STRBUF_INIT;
+	struct strbuf buf = STRBUF_INIT;
+	struct object_id merge_base;
+	enum action action = ACTION_NONE;
+	const char *gpg_sign = NULL;
+	struct string_list exec = STRING_LIST_INIT_NODUP;
+	const char *rebase_merges = NULL;
+	int fork_point = -1;
+	struct string_list strategy_options = STRING_LIST_INIT_NODUP;
+	struct object_id squash_onto;
+	char *squash_onto_name = NULL;
+	int reschedule_failed_exec = -1;
+	struct option builtin_rebase_options[] = {
+		OPT_STRING(0, "onto", &options.onto_name,
+			   N_("revision"),
+			   N_("rebase onto given branch instead of upstream")),
+		OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase,
+			 N_("allow pre-rebase hook to run")),
+		OPT_NEGBIT('q', "quiet", &options.flags,
+			   N_("be quiet. implies --no-stat"),
+			   REBASE_NO_QUIET| REBASE_VERBOSE | REBASE_DIFFSTAT),
+		OPT_BIT('v', "verbose", &options.flags,
+			N_("display a diffstat of what changed upstream"),
+			REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
+		{OPTION_NEGBIT, 'n', "no-stat", &options.flags, NULL,
+			N_("do not show diffstat of what changed upstream"),
+			PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
+		OPT_BOOL(0, "signoff", &options.signoff,
+			 N_("add a Signed-off-by: line to each commit")),
+		OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &options.git_am_opts,
+				  NULL, N_("passed to 'git am'"),
+				  PARSE_OPT_NOARG),
+		OPT_PASSTHRU_ARGV(0, "committer-date-is-author-date",
+				  &options.git_am_opts, NULL,
+				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
+		OPT_PASSTHRU_ARGV(0, "ignore-date", &options.git_am_opts, NULL,
+				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
+		OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
+				  N_("passed to 'git apply'"), 0),
+		OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts,
+				  N_("action"), N_("passed to 'git apply'"), 0),
+		OPT_BIT('f', "force-rebase", &options.flags,
+			N_("cherry-pick all commits, even if unchanged"),
+			REBASE_FORCE),
+		OPT_BIT(0, "no-ff", &options.flags,
+			N_("cherry-pick all commits, even if unchanged"),
+			REBASE_FORCE),
+		OPT_CMDMODE(0, "continue", &action, N_("continue"),
+			    ACTION_CONTINUE),
+		OPT_CMDMODE(0, "skip", &action,
+			    N_("skip current patch and continue"), ACTION_SKIP),
+		OPT_CMDMODE(0, "abort", &action,
+			    N_("abort and check out the original branch"),
+			    ACTION_ABORT),
+		OPT_CMDMODE(0, "quit", &action,
+			    N_("abort but keep HEAD where it is"), ACTION_QUIT),
+		OPT_CMDMODE(0, "edit-todo", &action, N_("edit the todo list "
+			    "during an interactive rebase"), ACTION_EDIT_TODO),
+		OPT_CMDMODE(0, "show-current-patch", &action,
+			    N_("show the patch file being applied or merged"),
+			    ACTION_SHOW_CURRENT_PATCH),
+		{ OPTION_CALLBACK, 'm', "merge", &options, NULL,
+			N_("use merging strategies to rebase"),
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+			parse_opt_merge },
+		{ OPTION_CALLBACK, 'i', "interactive", &options, NULL,
+			N_("let the user edit the list of commits to rebase"),
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+			parse_opt_interactive },
+		OPT_SET_INT('p', "preserve-merges", &options.type,
+			    N_("(DEPRECATED) try to recreate merges instead of "
+			       "ignoring them"), REBASE_PRESERVE_MERGES),
+		OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
+		OPT_BOOL('k', "keep-empty", &options.keep_empty,
+			 N_("preserve empty commits during rebase")),
+		OPT_BOOL(0, "autosquash", &options.autosquash,
+			 N_("move commits that begin with "
+			    "squash!/fixup! under -i")),
+		{ OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
+			N_("GPG-sign commits"),
+			PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_BOOL(0, "autostash", &options.autostash,
+			 N_("automatically stash/stash pop before and after")),
+		OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
+				N_("add exec lines after each commit of the "
+				   "editable list")),
+		OPT_BOOL(0, "allow-empty-message",
+			 &options.allow_empty_message,
+			 N_("allow rebasing commits with empty messages")),
+		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
+			N_("mode"),
+			N_("try to rebase merges instead of skipping them"),
+			PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+		OPT_BOOL(0, "fork-point", &fork_point,
+			 N_("use 'merge-base --fork-point' to refine upstream")),
+		OPT_STRING('s', "strategy", &options.strategy,
+			   N_("strategy"), N_("use the given merge strategy")),
+		OPT_STRING_LIST('X', "strategy-option", &strategy_options,
+				N_("option"),
+				N_("pass the argument through to the merge "
+				   "strategy")),
+		OPT_BOOL(0, "root", &options.root,
+			 N_("rebase all reachable commits up to the root(s)")),
+		OPT_BOOL(0, "reschedule-failed-exec",
+			 &reschedule_failed_exec,
+			 N_("automatically re-schedule any `exec` that fails")),
+		OPT_END(),
+	};
+	int i;
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(builtin_rebase_usage,
+				   builtin_rebase_options);
+
+	options.allow_empty_message = 1;
+	git_config(rebase_config, &options);
+
+	if (options.use_legacy_rebase ||
+	    !git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1))
+		warning(_("the rebase.useBuiltin support has been removed!\n"
+			  "See its entry in 'git help config' for details."));
+
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "%s/applying", apply_dir());
+	if(file_exists(buf.buf))
+		die(_("It looks like 'git am' is in progress. Cannot rebase."));
+
+	if (is_directory(apply_dir())) {
+		options.type = REBASE_AM;
+		options.state_dir = apply_dir();
+	} else if (is_directory(merge_dir())) {
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "%s/rewritten", merge_dir());
+		if (is_directory(buf.buf)) {
+			options.type = REBASE_PRESERVE_MERGES;
+			options.flags |= REBASE_INTERACTIVE_EXPLICIT;
+		} else {
+			strbuf_reset(&buf);
+			strbuf_addf(&buf, "%s/interactive", merge_dir());
+			if(file_exists(buf.buf)) {
+				options.type = REBASE_INTERACTIVE;
+				options.flags |= REBASE_INTERACTIVE_EXPLICIT;
+			} else
+				options.type = REBASE_MERGE;
+		}
+		options.state_dir = merge_dir();
+	}
+
+	if (options.type != REBASE_UNSPECIFIED)
+		in_progress = 1;
+
+	total_argc = argc;
+	argc = parse_options(argc, argv, prefix,
+			     builtin_rebase_options,
+			     builtin_rebase_usage, 0);
+
+	if (action != ACTION_NONE && total_argc != 2) {
+		usage_with_options(builtin_rebase_usage,
+				   builtin_rebase_options);
+	}
+
+	if (argc > 2)
+		usage_with_options(builtin_rebase_usage,
+				   builtin_rebase_options);
+
+	if (options.type == REBASE_PRESERVE_MERGES)
+		warning(_("git rebase --preserve-merges is deprecated. "
+			  "Use --rebase-merges instead."));
+
+	if (action != ACTION_NONE && !in_progress)
+		die(_("No rebase in progress?"));
+	setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
+
+	if (action == ACTION_EDIT_TODO && !is_interactive(&options))
+		die(_("The --edit-todo action can only be used during "
+		      "interactive rebase."));
+
+	if (trace2_is_enabled()) {
+		if (is_interactive(&options))
+			trace2_cmd_mode("interactive");
+		else if (exec.nr)
+			trace2_cmd_mode("interactive-exec");
+		else
+			trace2_cmd_mode(action_names[action]);
+	}
+
+	switch (action) {
+	case ACTION_CONTINUE: {
+		struct object_id head;
+		struct lock_file lock_file = LOCK_INIT;
+		int fd;
+
+		options.action = "continue";
+		set_reflog_action(&options);
+
+		/* Sanity check */
+		if (get_oid("HEAD", &head))
+			die(_("Cannot read HEAD"));
+
+		fd = hold_locked_index(&lock_file, 0);
+		if (repo_read_index(the_repository) < 0)
+			die(_("could not read index"));
+		refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL,
+			      NULL);
+		if (0 <= fd)
+			repo_update_index_if_able(the_repository, &lock_file);
+		rollback_lock_file(&lock_file);
+
+		if (has_unstaged_changes(the_repository, 1)) {
+			puts(_("You must edit all merge conflicts and then\n"
+			       "mark them as resolved using git add"));
+			exit(1);
+		}
+		if (read_basic_state(&options))
+			exit(1);
+		goto run_rebase;
+	}
+	case ACTION_SKIP: {
+		struct string_list merge_rr = STRING_LIST_INIT_DUP;
+
+		options.action = "skip";
+		set_reflog_action(&options);
+
+		rerere_clear(the_repository, &merge_rr);
+		string_list_clear(&merge_rr, 1);
+
+		if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
+			       NULL, NULL) < 0)
+			die(_("could not discard worktree changes"));
+		remove_branch_state(the_repository, 0);
+		if (read_basic_state(&options))
+			exit(1);
+		goto run_rebase;
+	}
+	case ACTION_ABORT: {
+		struct string_list merge_rr = STRING_LIST_INIT_DUP;
+		options.action = "abort";
+		set_reflog_action(&options);
+
+		rerere_clear(the_repository, &merge_rr);
+		string_list_clear(&merge_rr, 1);
+
+		if (read_basic_state(&options))
+			exit(1);
+		if (reset_head(&options.orig_head, "reset",
+			       options.head_name, RESET_HEAD_HARD,
+			       NULL, NULL) < 0)
+			die(_("could not move back to %s"),
+			    oid_to_hex(&options.orig_head));
+		remove_branch_state(the_repository, 0);
+		ret = !!finish_rebase(&options);
+		goto cleanup;
+	}
+	case ACTION_QUIT: {
+		if (options.type == REBASE_INTERACTIVE) {
+			struct replay_opts replay = REPLAY_OPTS_INIT;
+
+			replay.action = REPLAY_INTERACTIVE_REBASE;
+			ret = !!sequencer_remove_state(&replay);
+		} else {
+			strbuf_reset(&buf);
+			strbuf_addstr(&buf, options.state_dir);
+			ret = !!remove_dir_recursively(&buf, 0);
+			if (ret)
+				error(_("could not remove '%s'"),
+				       options.state_dir);
+		}
+		goto cleanup;
+	}
+	case ACTION_EDIT_TODO:
+		options.action = "edit-todo";
+		options.dont_finish_rebase = 1;
+		goto run_rebase;
+	case ACTION_SHOW_CURRENT_PATCH:
+		options.action = "show-current-patch";
+		options.dont_finish_rebase = 1;
+		goto run_rebase;
+	case ACTION_NONE:
+		break;
+	default:
+		BUG("action: %d", action);
+	}
+
+	/* Make sure no rebase is in progress */
+	if (in_progress) {
+		const char *last_slash = strrchr(options.state_dir, '/');
+		const char *state_dir_base =
+			last_slash ? last_slash + 1 : options.state_dir;
+		const char *cmd_live_rebase =
+			"git rebase (--continue | --abort | --skip)";
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "rm -fr \"%s\"", options.state_dir);
+		die(_("It seems that there is already a %s directory, and\n"
+		      "I wonder if you are in the middle of another rebase.  "
+		      "If that is the\n"
+		      "case, please try\n\t%s\n"
+		      "If that is not the case, please\n\t%s\n"
+		      "and run me again.  I am stopping in case you still "
+		      "have something\n"
+		      "valuable there.\n"),
+		    state_dir_base, cmd_live_rebase, buf.buf);
+	}
+
+	for (i = 0; i < options.git_am_opts.argc; i++) {
+		const char *option = options.git_am_opts.argv[i], *p;
+		if (!strcmp(option, "--committer-date-is-author-date") ||
+		    !strcmp(option, "--ignore-date") ||
+		    !strcmp(option, "--whitespace=fix") ||
+		    !strcmp(option, "--whitespace=strip"))
+			options.flags |= REBASE_FORCE;
+		else if (skip_prefix(option, "-C", &p)) {
+			while (*p)
+				if (!isdigit(*(p++)))
+					die(_("switch `C' expects a "
+					      "numerical value"));
+		} else if (skip_prefix(option, "--whitespace=", &p)) {
+			if (*p && strcmp(p, "warn") && strcmp(p, "nowarn") &&
+			    strcmp(p, "error") && strcmp(p, "error-all"))
+				die("Invalid whitespace option: '%s'", p);
+		}
+	}
+
+	for (i = 0; i < exec.nr; i++)
+		if (check_exec_cmd(exec.items[i].string))
+			exit(1);
+
+	if (!(options.flags & REBASE_NO_QUIET))
+		argv_array_push(&options.git_am_opts, "-q");
+
+	if (options.keep_empty)
+		imply_interactive(&options, "--keep-empty");
+
+	if (gpg_sign) {
+		free(options.gpg_sign_opt);
+		options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
+	}
+
+	if (exec.nr) {
+		int i;
+
+		imply_interactive(&options, "--exec");
+
+		strbuf_reset(&buf);
+		for (i = 0; i < exec.nr; i++)
+			strbuf_addf(&buf, "exec %s\n", exec.items[i].string);
+		options.cmd = xstrdup(buf.buf);
+	}
+
+	if (rebase_merges) {
+		if (!*rebase_merges)
+			; /* default mode; do nothing */
+		else if (!strcmp("rebase-cousins", rebase_merges))
+			options.rebase_cousins = 1;
+		else if (strcmp("no-rebase-cousins", rebase_merges))
+			die(_("Unknown mode: %s"), rebase_merges);
+		options.rebase_merges = 1;
+		imply_interactive(&options, "--rebase-merges");
+	}
+
+	if (strategy_options.nr) {
+		int i;
+
+		if (!options.strategy)
+			options.strategy = "recursive";
+
+		strbuf_reset(&buf);
+		for (i = 0; i < strategy_options.nr; i++)
+			strbuf_addf(&buf, " --%s",
+				    strategy_options.items[i].string);
+		options.strategy_opts = xstrdup(buf.buf);
+	}
+
+	if (options.strategy) {
+		options.strategy = xstrdup(options.strategy);
+		switch (options.type) {
+		case REBASE_AM:
+			die(_("--strategy requires --merge or --interactive"));
+		case REBASE_MERGE:
+		case REBASE_INTERACTIVE:
+		case REBASE_PRESERVE_MERGES:
+			/* compatible */
+			break;
+		case REBASE_UNSPECIFIED:
+			options.type = REBASE_MERGE;
+			break;
+		default:
+			BUG("unhandled rebase type (%d)", options.type);
+		}
+	}
+
+	if (options.type == REBASE_MERGE)
+		imply_interactive(&options, "--merge");
+
+	if (options.root && !options.onto_name)
+		imply_interactive(&options, "--root without --onto");
+
+	if (isatty(2) && options.flags & REBASE_NO_QUIET)
+		strbuf_addstr(&options.git_format_patch_opt, " --progress");
+
+	switch (options.type) {
+	case REBASE_MERGE:
+	case REBASE_INTERACTIVE:
+	case REBASE_PRESERVE_MERGES:
+		options.state_dir = merge_dir();
+		break;
+	case REBASE_AM:
+		options.state_dir = apply_dir();
+		break;
+	default:
+		/* the default rebase backend is `--am` */
+		options.type = REBASE_AM;
+		options.state_dir = apply_dir();
+		break;
+	}
+
+	if (reschedule_failed_exec > 0 && !is_interactive(&options))
+		die(_("--reschedule-failed-exec requires "
+		      "--exec or --interactive"));
+	if (reschedule_failed_exec >= 0)
+		options.reschedule_failed_exec = reschedule_failed_exec;
+
+	if (options.git_am_opts.argc) {
+		/* all am options except -q are compatible only with --am */
+		for (i = options.git_am_opts.argc - 1; i >= 0; i--)
+			if (strcmp(options.git_am_opts.argv[i], "-q"))
+				break;
+
+		if (is_interactive(&options) && i >= 0)
+			die(_("cannot combine am options with either "
+			      "interactive or merge options"));
+	}
+
+	if (options.signoff) {
+		if (options.type == REBASE_PRESERVE_MERGES)
+			die("cannot combine '--signoff' with "
+			    "'--preserve-merges'");
+		argv_array_push(&options.git_am_opts, "--signoff");
+		options.flags |= REBASE_FORCE;
+	}
+
+	if (options.type == REBASE_PRESERVE_MERGES) {
+		/*
+		 * Note: incompatibility with --signoff handled in signoff block above
+		 * Note: incompatibility with --interactive is just a strong warning;
+		 *       git-rebase.txt caveats with "unless you know what you are doing"
+		 */
+		if (options.rebase_merges)
+			die(_("cannot combine '--preserve-merges' with "
+			      "'--rebase-merges'"));
+
+		if (options.reschedule_failed_exec)
+			die(_("error: cannot combine '--preserve-merges' with "
+			      "'--reschedule-failed-exec'"));
+	}
+
+	if (options.rebase_merges) {
+		if (strategy_options.nr)
+			die(_("cannot combine '--rebase-merges' with "
+			      "'--strategy-option'"));
+		if (options.strategy)
+			die(_("cannot combine '--rebase-merges' with "
+			      "'--strategy'"));
+	}
+
+	if (!options.root) {
+		if (argc < 1) {
+			struct branch *branch;
+
+			branch = branch_get(NULL);
+			options.upstream_name = branch_get_upstream(branch,
+								    NULL);
+			if (!options.upstream_name)
+				error_on_missing_default_upstream();
+			if (fork_point < 0)
+				fork_point = 1;
+		} else {
+			options.upstream_name = argv[0];
+			argc--;
+			argv++;
+			if (!strcmp(options.upstream_name, "-"))
+				options.upstream_name = "@{-1}";
+		}
+		options.upstream = peel_committish(options.upstream_name);
+		if (!options.upstream)
+			die(_("invalid upstream '%s'"), options.upstream_name);
+		options.upstream_arg = options.upstream_name;
+	} else {
+		if (!options.onto_name) {
+			if (commit_tree("", 0, the_hash_algo->empty_tree, NULL,
+					&squash_onto, NULL, NULL) < 0)
+				die(_("Could not create new root commit"));
+			options.squash_onto = &squash_onto;
+			options.onto_name = squash_onto_name =
+				xstrdup(oid_to_hex(&squash_onto));
+		}
+		options.upstream_name = NULL;
+		options.upstream = NULL;
+		if (argc > 1)
+			usage_with_options(builtin_rebase_usage,
+					   builtin_rebase_options);
+		options.upstream_arg = "--root";
+	}
+
+	/* Make sure the branch to rebase onto is valid. */
+	if (!options.onto_name)
+		options.onto_name = options.upstream_name;
+	if (strstr(options.onto_name, "...")) {
+		if (get_oid_mb(options.onto_name, &merge_base) < 0)
+			die(_("'%s': need exactly one merge base"),
+			    options.onto_name);
+		options.onto = lookup_commit_or_die(&merge_base,
+						    options.onto_name);
+	} else {
+		options.onto = peel_committish(options.onto_name);
+		if (!options.onto)
+			die(_("Does not point to a valid commit '%s'"),
+				options.onto_name);
+	}
+
+	/*
+	 * If the branch to rebase is given, that is the branch we will rebase
+	 * branch_name -- branch/commit being rebased, or
+	 * 		  HEAD (already detached)
+	 * orig_head -- commit object name of tip of the branch before rebasing
+	 * head_name -- refs/heads/<that-branch> or NULL (detached HEAD)
+	 */
+	if (argc == 1) {
+		/* Is it "rebase other branchname" or "rebase other commit"? */
+		branch_name = argv[0];
+		options.switch_to = argv[0];
+
+		/* Is it a local branch? */
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "refs/heads/%s", branch_name);
+		if (!read_ref(buf.buf, &options.orig_head))
+			options.head_name = xstrdup(buf.buf);
+		/* If not is it a valid ref (branch or commit)? */
+		else if (!get_oid(branch_name, &options.orig_head))
+			options.head_name = NULL;
+		else
+			die(_("fatal: no such branch/commit '%s'"),
+			    branch_name);
+	} else if (argc == 0) {
+		/* Do not need to switch branches, we are already on it. */
+		options.head_name =
+			xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL,
+					 &flags));
+		if (!options.head_name)
+			die(_("No such ref: %s"), "HEAD");
+		if (flags & REF_ISSYMREF) {
+			if (!skip_prefix(options.head_name,
+					 "refs/heads/", &branch_name))
+				branch_name = options.head_name;
+
+		} else {
+			FREE_AND_NULL(options.head_name);
+			branch_name = "HEAD";
+		}
+		if (get_oid("HEAD", &options.orig_head))
+			die(_("Could not resolve HEAD to a revision"));
+	} else
+		BUG("unexpected number of arguments left to parse");
+
+	if (fork_point > 0) {
+		struct commit *head =
+			lookup_commit_reference(the_repository,
+						&options.orig_head);
+		options.restrict_revision =
+			get_fork_point(options.upstream_name, head);
+	}
+
+	if (repo_read_index(the_repository) < 0)
+		die(_("could not read index"));
+
+	if (options.autostash) {
+		struct lock_file lock_file = LOCK_INIT;
+		int fd;
+
+		fd = hold_locked_index(&lock_file, 0);
+		refresh_cache(REFRESH_QUIET);
+		if (0 <= fd)
+			repo_update_index_if_able(the_repository, &lock_file);
+		rollback_lock_file(&lock_file);
+
+		if (has_unstaged_changes(the_repository, 1) ||
+		    has_uncommitted_changes(the_repository, 1)) {
+			const char *autostash =
+				state_dir_path("autostash", &options);
+			struct child_process stash = CHILD_PROCESS_INIT;
+			struct object_id oid;
+			struct commit *head =
+				lookup_commit_reference(the_repository,
+							&options.orig_head);
+
+			argv_array_pushl(&stash.args,
+					 "stash", "create", "autostash", NULL);
+			stash.git_cmd = 1;
+			stash.no_stdin = 1;
+			strbuf_reset(&buf);
+			if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
+				die(_("Cannot autostash"));
+			strbuf_trim_trailing_newline(&buf);
+			if (get_oid(buf.buf, &oid))
+				die(_("Unexpected stash response: '%s'"),
+				    buf.buf);
+			strbuf_reset(&buf);
+			strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
+
+			if (safe_create_leading_directories_const(autostash))
+				die(_("Could not create directory for '%s'"),
+				    options.state_dir);
+			write_file(autostash, "%s", oid_to_hex(&oid));
+			printf(_("Created autostash: %s\n"), buf.buf);
+			if (reset_head(&head->object.oid, "reset --hard",
+				       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)
+				die(_("could not reset --hard"));
+			printf(_("HEAD is now at %s"),
+			       find_unique_abbrev(&head->object.oid,
+						  DEFAULT_ABBREV));
+			strbuf_reset(&buf);
+			pp_commit_easy(CMIT_FMT_ONELINE, head, &buf);
+			if (buf.len > 0)
+				printf(" %s", buf.buf);
+			putchar('\n');
+
+			if (discard_index(the_repository->index) < 0 ||
+				repo_read_index(the_repository) < 0)
+				die(_("could not read index"));
+		}
+	}
+
+	if (require_clean_work_tree(the_repository, "rebase",
+				    _("Please commit or stash them."), 1, 1)) {
+		ret = 1;
+		goto cleanup;
+	}
+
+	/*
+	 * Now we are rebasing commits upstream..orig_head (or with --root,
+	 * everything leading up to orig_head) on top of onto.
+	 */
+
+	/*
+	 * Check if we are already based on onto with linear history,
+	 * but this should be done only when upstream and onto are the same
+	 * and if this is not an interactive rebase.
+	 */
+	if (can_fast_forward(options.onto, &options.orig_head, &merge_base) &&
+	    !is_interactive(&options) && !options.restrict_revision &&
+	    options.upstream &&
+	    !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) {
+		int flag;
+
+		if (!(options.flags & REBASE_FORCE)) {
+			/* Lazily switch to the target branch if needed... */
+			if (options.switch_to) {
+				struct object_id oid;
+
+				if (get_oid(options.switch_to, &oid) < 0) {
+					ret = !!error(_("could not parse '%s'"),
+						      options.switch_to);
+					goto cleanup;
+				}
+
+				strbuf_reset(&buf);
+				strbuf_addf(&buf, "%s: checkout %s",
+					    getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
+					    options.switch_to);
+				if (reset_head(&oid, "checkout",
+					       options.head_name,
+					       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
+					       NULL, buf.buf) < 0) {
+					ret = !!error(_("could not switch to "
+							"%s"),
+						      options.switch_to);
+					goto cleanup;
+				}
+			}
+
+			if (!(options.flags & REBASE_NO_QUIET))
+				; /* be quiet */
+			else if (!strcmp(branch_name, "HEAD") &&
+				 resolve_ref_unsafe("HEAD", 0, NULL, &flag))
+				puts(_("HEAD is up to date."));
+			else
+				printf(_("Current branch %s is up to date.\n"),
+				       branch_name);
+			ret = !!finish_rebase(&options);
+			goto cleanup;
+		} else if (!(options.flags & REBASE_NO_QUIET))
+			; /* be quiet */
+		else if (!strcmp(branch_name, "HEAD") &&
+			 resolve_ref_unsafe("HEAD", 0, NULL, &flag))
+			puts(_("HEAD is up to date, rebase forced."));
+		else
+			printf(_("Current branch %s is up to date, rebase "
+				 "forced.\n"), branch_name);
+	}
+
+	/* If a hook exists, give it a chance to interrupt*/
+	if (!ok_to_skip_pre_rebase &&
+	    run_hook_le(NULL, "pre-rebase", options.upstream_arg,
+			argc ? argv[0] : NULL, NULL))
+		die(_("The pre-rebase hook refused to rebase."));
+
+	if (options.flags & REBASE_DIFFSTAT) {
+		struct diff_options opts;
+
+		if (options.flags & REBASE_VERBOSE) {
+			if (is_null_oid(&merge_base))
+				printf(_("Changes to %s:\n"),
+				       oid_to_hex(&options.onto->object.oid));
+			else
+				printf(_("Changes from %s to %s:\n"),
+				       oid_to_hex(&merge_base),
+				       oid_to_hex(&options.onto->object.oid));
+		}
+
+		/* We want color (if set), but no pager */
+		diff_setup(&opts);
+		opts.stat_width = -1; /* use full terminal width */
+		opts.stat_graph_width = -1; /* respect statGraphWidth config */
+		opts.output_format |=
+			DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+		opts.detect_rename = DIFF_DETECT_RENAME;
+		diff_setup_done(&opts);
+		diff_tree_oid(is_null_oid(&merge_base) ?
+			      the_hash_algo->empty_tree : &merge_base,
+			      &options.onto->object.oid, "", &opts);
+		diffcore_std(&opts);
+		diff_flush(&opts);
+	}
+
+	if (is_interactive(&options))
+		goto run_rebase;
+
+	/* Detach HEAD and reset the tree */
+	if (options.flags & REBASE_NO_QUIET)
+		printf(_("First, rewinding head to replay your work on top of "
+			 "it...\n"));
+
+	strbuf_addf(&msg, "%s: checkout %s",
+		    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
+	if (reset_head(&options.onto->object.oid, "checkout", NULL,
+		       RESET_HEAD_DETACH | RESET_ORIG_HEAD |
+		       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
+		       NULL, msg.buf))
+		die(_("Could not detach HEAD"));
+	strbuf_release(&msg);
+
+	/*
+	 * If the onto is a proper descendant of the tip of the branch, then
+	 * we just fast-forwarded.
+	 */
+	strbuf_reset(&msg);
+	if (oideq(&merge_base, &options.orig_head)) {
+		printf(_("Fast-forwarded %s to %s.\n"),
+			branch_name, options.onto_name);
+		strbuf_addf(&msg, "rebase finished: %s onto %s",
+			options.head_name ? options.head_name : "detached HEAD",
+			oid_to_hex(&options.onto->object.oid));
+		reset_head(NULL, "Fast-forwarded", options.head_name,
+			   RESET_HEAD_REFS_ONLY, "HEAD", msg.buf);
+		strbuf_release(&msg);
+		ret = !!finish_rebase(&options);
+		goto cleanup;
+	}
+
+	strbuf_addf(&revisions, "%s..%s",
+		    options.root ? oid_to_hex(&options.onto->object.oid) :
+		    (options.restrict_revision ?
+		     oid_to_hex(&options.restrict_revision->object.oid) :
+		     oid_to_hex(&options.upstream->object.oid)),
+		    oid_to_hex(&options.orig_head));
+
+	options.revisions = revisions.buf;
+
+run_rebase:
+	ret = !!run_specific_rebase(&options, action);
+
+cleanup:
+	strbuf_release(&buf);
+	strbuf_release(&revisions);
+	free(options.head_name);
+	free(options.gpg_sign_opt);
+	free(options.cmd);
+	free(squash_onto_name);
+	return ret;
+}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
new file mode 100644
index 0000000000..dcf385511f
--- /dev/null
+++ b/builtin/receive-pack.c
@@ -0,0 +1,2061 @@
+#include "builtin.h"
+#include "repository.h"
+#include "config.h"
+#include "lockfile.h"
+#include "pack.h"
+#include "refs.h"
+#include "pkt-line.h"
+#include "sideband.h"
+#include "run-command.h"
+#include "exec-cmd.h"
+#include "commit.h"
+#include "object.h"
+#include "remote.h"
+#include "connect.h"
+#include "string-list.h"
+#include "sha1-array.h"
+#include "connected.h"
+#include "argv-array.h"
+#include "version.h"
+#include "tag.h"
+#include "gpg-interface.h"
+#include "sigchain.h"
+#include "fsck.h"
+#include "tmp-objdir.h"
+#include "oidset.h"
+#include "packfile.h"
+#include "object-store.h"
+#include "protocol.h"
+#include "commit-reach.h"
+
+static const char * const receive_pack_usage[] = {
+	N_("git receive-pack <git-dir>"),
+	NULL
+};
+
+enum deny_action {
+	DENY_UNCONFIGURED,
+	DENY_IGNORE,
+	DENY_WARN,
+	DENY_REFUSE,
+	DENY_UPDATE_INSTEAD
+};
+
+static int deny_deletes;
+static int deny_non_fast_forwards;
+static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
+static enum deny_action deny_delete_current = DENY_UNCONFIGURED;
+static int receive_fsck_objects = -1;
+static int transfer_fsck_objects = -1;
+static struct strbuf fsck_msg_types = STRBUF_INIT;
+static int receive_unpack_limit = -1;
+static int transfer_unpack_limit = -1;
+static int advertise_atomic_push = 1;
+static int advertise_push_options;
+static int unpack_limit = 100;
+static off_t max_input_size;
+static int report_status;
+static int use_sideband;
+static int use_atomic;
+static int use_push_options;
+static int quiet;
+static int prefer_ofs_delta = 1;
+static int auto_update_server_info;
+static int auto_gc = 1;
+static int reject_thin;
+static int stateless_rpc;
+static const char *service_dir;
+static const char *head_name;
+static void *head_name_to_free;
+static int sent_capabilities;
+static int shallow_update;
+static const char *alt_shallow_file;
+static struct strbuf push_cert = STRBUF_INIT;
+static struct object_id push_cert_oid;
+static struct signature_check sigcheck;
+static const char *push_cert_nonce;
+static const char *cert_nonce_seed;
+
+static const char *NONCE_UNSOLICITED = "UNSOLICITED";
+static const char *NONCE_BAD = "BAD";
+static const char *NONCE_MISSING = "MISSING";
+static const char *NONCE_OK = "OK";
+static const char *NONCE_SLOP = "SLOP";
+static const char *nonce_status;
+static long nonce_stamp_slop;
+static timestamp_t nonce_stamp_slop_limit;
+static struct ref_transaction *transaction;
+
+static enum {
+	KEEPALIVE_NEVER = 0,
+	KEEPALIVE_AFTER_NUL,
+	KEEPALIVE_ALWAYS
+} use_keepalive;
+static int keepalive_in_sec = 5;
+
+static struct tmp_objdir *tmp_objdir;
+
+static enum deny_action parse_deny_action(const char *var, const char *value)
+{
+	if (value) {
+		if (!strcasecmp(value, "ignore"))
+			return DENY_IGNORE;
+		if (!strcasecmp(value, "warn"))
+			return DENY_WARN;
+		if (!strcasecmp(value, "refuse"))
+			return DENY_REFUSE;
+		if (!strcasecmp(value, "updateinstead"))
+			return DENY_UPDATE_INSTEAD;
+	}
+	if (git_config_bool(var, value))
+		return DENY_REFUSE;
+	return DENY_IGNORE;
+}
+
+static int receive_pack_config(const char *var, const char *value, void *cb)
+{
+	int status = parse_hide_refs_config(var, value, "receive");
+
+	if (status)
+		return status;
+
+	if (strcmp(var, "receive.denydeletes") == 0) {
+		deny_deletes = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.denynonfastforwards") == 0) {
+		deny_non_fast_forwards = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.unpacklimit") == 0) {
+		receive_unpack_limit = git_config_int(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "transfer.unpacklimit") == 0) {
+		transfer_unpack_limit = git_config_int(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.fsck.skiplist") == 0) {
+		const char *path;
+
+		if (git_config_pathname(&path, var, value))
+			return 1;
+		strbuf_addf(&fsck_msg_types, "%cskiplist=%s",
+			fsck_msg_types.len ? ',' : '=', path);
+		free((char *)path);
+		return 0;
+	}
+
+	if (skip_prefix(var, "receive.fsck.", &var)) {
+		if (is_valid_msg_type(var, value))
+			strbuf_addf(&fsck_msg_types, "%c%s=%s",
+				fsck_msg_types.len ? ',' : '=', var, value);
+		else
+			warning("Skipping unknown msg id '%s'", var);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.fsckobjects") == 0) {
+		receive_fsck_objects = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "transfer.fsckobjects") == 0) {
+		transfer_fsck_objects = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (!strcmp(var, "receive.denycurrentbranch")) {
+		deny_current_branch = parse_deny_action(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.denydeletecurrent") == 0) {
+		deny_delete_current = parse_deny_action(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
+		prefer_ofs_delta = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.updateserverinfo") == 0) {
+		auto_update_server_info = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.autogc") == 0) {
+		auto_gc = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.shallowupdate") == 0) {
+		shallow_update = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.certnonceseed") == 0)
+		return git_config_string(&cert_nonce_seed, var, value);
+
+	if (strcmp(var, "receive.certnonceslop") == 0) {
+		nonce_stamp_slop_limit = git_config_ulong(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.advertiseatomic") == 0) {
+		advertise_atomic_push = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.advertisepushoptions") == 0) {
+		advertise_push_options = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.keepalive") == 0) {
+		keepalive_in_sec = git_config_int(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.maxinputsize") == 0) {
+		max_input_size = git_config_int64(var, value);
+		return 0;
+	}
+
+	return git_default_config(var, value, cb);
+}
+
+static void show_ref(const char *path, const struct object_id *oid)
+{
+	if (sent_capabilities) {
+		packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), path);
+	} else {
+		struct strbuf cap = STRBUF_INIT;
+
+		strbuf_addstr(&cap,
+			      "report-status delete-refs side-band-64k quiet");
+		if (advertise_atomic_push)
+			strbuf_addstr(&cap, " atomic");
+		if (prefer_ofs_delta)
+			strbuf_addstr(&cap, " ofs-delta");
+		if (push_cert_nonce)
+			strbuf_addf(&cap, " push-cert=%s", push_cert_nonce);
+		if (advertise_push_options)
+			strbuf_addstr(&cap, " push-options");
+		strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized());
+		packet_write_fmt(1, "%s %s%c%s\n",
+			     oid_to_hex(oid), path, 0, cap.buf);
+		strbuf_release(&cap);
+		sent_capabilities = 1;
+	}
+}
+
+static int show_ref_cb(const char *path_full, const struct object_id *oid,
+		       int flag, void *data)
+{
+	struct oidset *seen = data;
+	const char *path = strip_namespace(path_full);
+
+	if (ref_is_hidden(path, path_full))
+		return 0;
+
+	/*
+	 * Advertise refs outside our current namespace as ".have"
+	 * refs, so that the client can use them to minimize data
+	 * transfer but will otherwise ignore them.
+	 */
+	if (!path) {
+		if (oidset_insert(seen, oid))
+			return 0;
+		path = ".have";
+	} else {
+		oidset_insert(seen, oid);
+	}
+	show_ref(path, oid);
+	return 0;
+}
+
+static void show_one_alternate_ref(const struct object_id *oid,
+				   void *data)
+{
+	struct oidset *seen = data;
+
+	if (oidset_insert(seen, oid))
+		return;
+
+	show_ref(".have", oid);
+}
+
+static void write_head_info(void)
+{
+	static struct oidset seen = OIDSET_INIT;
+
+	for_each_ref(show_ref_cb, &seen);
+	for_each_alternate_ref(show_one_alternate_ref, &seen);
+	oidset_clear(&seen);
+	if (!sent_capabilities)
+		show_ref("capabilities^{}", &null_oid);
+
+	advertise_shallow_grafts(1);
+
+	/* EOF */
+	packet_flush(1);
+}
+
+struct command {
+	struct command *next;
+	const char *error_string;
+	unsigned int skip_update:1,
+		     did_not_exist:1;
+	int index;
+	struct object_id old_oid;
+	struct object_id new_oid;
+	char ref_name[FLEX_ARRAY]; /* more */
+};
+
+static void rp_error(const char *err, ...) __attribute__((format (printf, 1, 2)));
+static void rp_warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
+
+static void report_message(const char *prefix, const char *err, va_list params)
+{
+	int sz;
+	char msg[4096];
+
+	sz = xsnprintf(msg, sizeof(msg), "%s", prefix);
+	sz += vsnprintf(msg + sz, sizeof(msg) - sz, err, params);
+	if (sz > (sizeof(msg) - 1))
+		sz = sizeof(msg) - 1;
+	msg[sz++] = '\n';
+
+	if (use_sideband)
+		send_sideband(1, 2, msg, sz, use_sideband);
+	else
+		xwrite(2, msg, sz);
+}
+
+static void rp_warning(const char *err, ...)
+{
+	va_list params;
+	va_start(params, err);
+	report_message("warning: ", err, params);
+	va_end(params);
+}
+
+static void rp_error(const char *err, ...)
+{
+	va_list params;
+	va_start(params, err);
+	report_message("error: ", err, params);
+	va_end(params);
+}
+
+static int copy_to_sideband(int in, int out, void *arg)
+{
+	char data[128];
+	int keepalive_active = 0;
+
+	if (keepalive_in_sec <= 0)
+		use_keepalive = KEEPALIVE_NEVER;
+	if (use_keepalive == KEEPALIVE_ALWAYS)
+		keepalive_active = 1;
+
+	while (1) {
+		ssize_t sz;
+
+		if (keepalive_active) {
+			struct pollfd pfd;
+			int ret;
+
+			pfd.fd = in;
+			pfd.events = POLLIN;
+			ret = poll(&pfd, 1, 1000 * keepalive_in_sec);
+
+			if (ret < 0) {
+				if (errno == EINTR)
+					continue;
+				else
+					break;
+			} else if (ret == 0) {
+				/* no data; send a keepalive packet */
+				static const char buf[] = "0005\1";
+				write_or_die(1, buf, sizeof(buf) - 1);
+				continue;
+			} /* else there is actual data to read */
+		}
+
+		sz = xread(in, data, sizeof(data));
+		if (sz <= 0)
+			break;
+
+		if (use_keepalive == KEEPALIVE_AFTER_NUL && !keepalive_active) {
+			const char *p = memchr(data, '\0', sz);
+			if (p) {
+				/*
+				 * The NUL tells us to start sending keepalives. Make
+				 * sure we send any other data we read along
+				 * with it.
+				 */
+				keepalive_active = 1;
+				send_sideband(1, 2, data, p - data, use_sideband);
+				send_sideband(1, 2, p + 1, sz - (p - data + 1), use_sideband);
+				continue;
+			}
+		}
+
+		/*
+		 * Either we're not looking for a NUL signal, or we didn't see
+		 * it yet; just pass along the data.
+		 */
+		send_sideband(1, 2, data, sz, use_sideband);
+	}
+	close(in);
+	return 0;
+}
+
+#define HMAC_BLOCK_SIZE 64
+
+static void hmac_sha1(unsigned char *out,
+		      const char *key_in, size_t key_len,
+		      const char *text, size_t text_len)
+{
+	unsigned char key[HMAC_BLOCK_SIZE];
+	unsigned char k_ipad[HMAC_BLOCK_SIZE];
+	unsigned char k_opad[HMAC_BLOCK_SIZE];
+	int i;
+	git_SHA_CTX ctx;
+
+	/* RFC 2104 2. (1) */
+	memset(key, '\0', HMAC_BLOCK_SIZE);
+	if (HMAC_BLOCK_SIZE < key_len) {
+		git_SHA1_Init(&ctx);
+		git_SHA1_Update(&ctx, key_in, key_len);
+		git_SHA1_Final(key, &ctx);
+	} else {
+		memcpy(key, key_in, key_len);
+	}
+
+	/* RFC 2104 2. (2) & (5) */
+	for (i = 0; i < sizeof(key); i++) {
+		k_ipad[i] = key[i] ^ 0x36;
+		k_opad[i] = key[i] ^ 0x5c;
+	}
+
+	/* RFC 2104 2. (3) & (4) */
+	git_SHA1_Init(&ctx);
+	git_SHA1_Update(&ctx, k_ipad, sizeof(k_ipad));
+	git_SHA1_Update(&ctx, text, text_len);
+	git_SHA1_Final(out, &ctx);
+
+	/* RFC 2104 2. (6) & (7) */
+	git_SHA1_Init(&ctx);
+	git_SHA1_Update(&ctx, k_opad, sizeof(k_opad));
+	git_SHA1_Update(&ctx, out, GIT_SHA1_RAWSZ);
+	git_SHA1_Final(out, &ctx);
+}
+
+static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
+{
+	struct strbuf buf = STRBUF_INIT;
+	unsigned char sha1[GIT_SHA1_RAWSZ];
+
+	strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
+	hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));
+	strbuf_release(&buf);
+
+	/* RFC 2104 5. HMAC-SHA1-80 */
+	strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, GIT_SHA1_HEXSZ, sha1_to_hex(sha1));
+	return strbuf_detach(&buf, NULL);
+}
+
+/*
+ * NEEDSWORK: reuse find_commit_header() from jk/commit-author-parsing
+ * after dropping "_commit" from its name and possibly moving it out
+ * of commit.c
+ */
+static char *find_header(const char *msg, size_t len, const char *key,
+			 const char **next_line)
+{
+	int key_len = strlen(key);
+	const char *line = msg;
+
+	while (line && line < msg + len) {
+		const char *eol = strchrnul(line, '\n');
+
+		if ((msg + len <= eol) || line == eol)
+			return NULL;
+		if (line + key_len < eol &&
+		    !memcmp(line, key, key_len) && line[key_len] == ' ') {
+			int offset = key_len + 1;
+			if (next_line)
+				*next_line = *eol ? eol + 1 : eol;
+			return xmemdupz(line + offset, (eol - line) - offset);
+		}
+		line = *eol ? eol + 1 : NULL;
+	}
+	return NULL;
+}
+
+static const char *check_nonce(const char *buf, size_t len)
+{
+	char *nonce = find_header(buf, len, "nonce", NULL);
+	timestamp_t stamp, ostamp;
+	char *bohmac, *expect = NULL;
+	const char *retval = NONCE_BAD;
+
+	if (!nonce) {
+		retval = NONCE_MISSING;
+		goto leave;
+	} else if (!push_cert_nonce) {
+		retval = NONCE_UNSOLICITED;
+		goto leave;
+	} else if (!strcmp(push_cert_nonce, nonce)) {
+		retval = NONCE_OK;
+		goto leave;
+	}
+
+	if (!stateless_rpc) {
+		/* returned nonce MUST match what we gave out earlier */
+		retval = NONCE_BAD;
+		goto leave;
+	}
+
+	/*
+	 * In stateless mode, we may be receiving a nonce issued by
+	 * another instance of the server that serving the same
+	 * repository, and the timestamps may not match, but the
+	 * nonce-seed and dir should match, so we can recompute and
+	 * report the time slop.
+	 *
+	 * In addition, when a nonce issued by another instance has
+	 * timestamp within receive.certnonceslop seconds, we pretend
+	 * as if we issued that nonce when reporting to the hook.
+	 */
+
+	/* nonce is concat(<seconds-since-epoch>, "-", <hmac>) */
+	if (*nonce <= '0' || '9' < *nonce) {
+		retval = NONCE_BAD;
+		goto leave;
+	}
+	stamp = parse_timestamp(nonce, &bohmac, 10);
+	if (bohmac == nonce || bohmac[0] != '-') {
+		retval = NONCE_BAD;
+		goto leave;
+	}
+
+	expect = prepare_push_cert_nonce(service_dir, stamp);
+	if (strcmp(expect, nonce)) {
+		/* Not what we would have signed earlier */
+		retval = NONCE_BAD;
+		goto leave;
+	}
+
+	/*
+	 * By how many seconds is this nonce stale?  Negative value
+	 * would mean it was issued by another server with its clock
+	 * skewed in the future.
+	 */
+	ostamp = parse_timestamp(push_cert_nonce, NULL, 10);
+	nonce_stamp_slop = (long)ostamp - (long)stamp;
+
+	if (nonce_stamp_slop_limit &&
+	    labs(nonce_stamp_slop) <= nonce_stamp_slop_limit) {
+		/*
+		 * Pretend as if the received nonce (which passes the
+		 * HMAC check, so it is not a forged by third-party)
+		 * is what we issued.
+		 */
+		free((void *)push_cert_nonce);
+		push_cert_nonce = xstrdup(nonce);
+		retval = NONCE_OK;
+	} else {
+		retval = NONCE_SLOP;
+	}
+
+leave:
+	free(nonce);
+	free(expect);
+	return retval;
+}
+
+/*
+ * Return 1 if there is no push_cert or if the push options in push_cert are
+ * the same as those in the argument; 0 otherwise.
+ */
+static int check_cert_push_options(const struct string_list *push_options)
+{
+	const char *buf = push_cert.buf;
+	int len = push_cert.len;
+
+	char *option;
+	const char *next_line;
+	int options_seen = 0;
+
+	int retval = 1;
+
+	if (!len)
+		return 1;
+
+	while ((option = find_header(buf, len, "push-option", &next_line))) {
+		len -= (next_line - buf);
+		buf = next_line;
+		options_seen++;
+		if (options_seen > push_options->nr
+		    || strcmp(option,
+			      push_options->items[options_seen - 1].string)) {
+			retval = 0;
+			goto leave;
+		}
+		free(option);
+	}
+
+	if (options_seen != push_options->nr)
+		retval = 0;
+
+leave:
+	free(option);
+	return retval;
+}
+
+static void prepare_push_cert_sha1(struct child_process *proc)
+{
+	static int already_done;
+
+	if (!push_cert.len)
+		return;
+
+	if (!already_done) {
+		int bogs /* beginning_of_gpg_sig */;
+
+		already_done = 1;
+		if (write_object_file(push_cert.buf, push_cert.len, "blob",
+				      &push_cert_oid))
+			oidclr(&push_cert_oid);
+
+		memset(&sigcheck, '\0', sizeof(sigcheck));
+
+		bogs = parse_signature(push_cert.buf, push_cert.len);
+		check_signature(push_cert.buf, bogs, push_cert.buf + bogs,
+				push_cert.len - bogs, &sigcheck);
+
+		nonce_status = check_nonce(push_cert.buf, bogs);
+	}
+	if (!is_null_oid(&push_cert_oid)) {
+		argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT=%s",
+				 oid_to_hex(&push_cert_oid));
+		argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_SIGNER=%s",
+				 sigcheck.signer ? sigcheck.signer : "");
+		argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_KEY=%s",
+				 sigcheck.key ? sigcheck.key : "");
+		argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_STATUS=%c",
+				 sigcheck.result);
+		if (push_cert_nonce) {
+			argv_array_pushf(&proc->env_array,
+					 "GIT_PUSH_CERT_NONCE=%s",
+					 push_cert_nonce);
+			argv_array_pushf(&proc->env_array,
+					 "GIT_PUSH_CERT_NONCE_STATUS=%s",
+					 nonce_status);
+			if (nonce_status == NONCE_SLOP)
+				argv_array_pushf(&proc->env_array,
+						 "GIT_PUSH_CERT_NONCE_SLOP=%ld",
+						 nonce_stamp_slop);
+		}
+	}
+}
+
+struct receive_hook_feed_state {
+	struct command *cmd;
+	int skip_broken;
+	struct strbuf buf;
+	const struct string_list *push_options;
+};
+
+typedef int (*feed_fn)(void *, const char **, size_t *);
+static int run_and_feed_hook(const char *hook_name, feed_fn feed,
+			     struct receive_hook_feed_state *feed_state)
+{
+	struct child_process proc = CHILD_PROCESS_INIT;
+	struct async muxer;
+	const char *argv[2];
+	int code;
+
+	argv[0] = find_hook(hook_name);
+	if (!argv[0])
+		return 0;
+
+	argv[1] = NULL;
+
+	proc.argv = argv;
+	proc.in = -1;
+	proc.stdout_to_stderr = 1;
+	proc.trace2_hook_name = hook_name;
+
+	if (feed_state->push_options) {
+		int i;
+		for (i = 0; i < feed_state->push_options->nr; i++)
+			argv_array_pushf(&proc.env_array,
+				"GIT_PUSH_OPTION_%d=%s", i,
+				feed_state->push_options->items[i].string);
+		argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT=%d",
+				 feed_state->push_options->nr);
+	} else
+		argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT");
+
+	if (tmp_objdir)
+		argv_array_pushv(&proc.env_array, tmp_objdir_env(tmp_objdir));
+
+	if (use_sideband) {
+		memset(&muxer, 0, sizeof(muxer));
+		muxer.proc = copy_to_sideband;
+		muxer.in = -1;
+		code = start_async(&muxer);
+		if (code)
+			return code;
+		proc.err = muxer.in;
+	}
+
+	prepare_push_cert_sha1(&proc);
+
+	code = start_command(&proc);
+	if (code) {
+		if (use_sideband)
+			finish_async(&muxer);
+		return code;
+	}
+
+	sigchain_push(SIGPIPE, SIG_IGN);
+
+	while (1) {
+		const char *buf;
+		size_t n;
+		if (feed(feed_state, &buf, &n))
+			break;
+		if (write_in_full(proc.in, buf, n) < 0)
+			break;
+	}
+	close(proc.in);
+	if (use_sideband)
+		finish_async(&muxer);
+
+	sigchain_pop(SIGPIPE);
+
+	return finish_command(&proc);
+}
+
+static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
+{
+	struct receive_hook_feed_state *state = state_;
+	struct command *cmd = state->cmd;
+
+	while (cmd &&
+	       state->skip_broken && (cmd->error_string || cmd->did_not_exist))
+		cmd = cmd->next;
+	if (!cmd)
+		return -1; /* EOF */
+	strbuf_reset(&state->buf);
+	strbuf_addf(&state->buf, "%s %s %s\n",
+		    oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid),
+		    cmd->ref_name);
+	state->cmd = cmd->next;
+	if (bufp) {
+		*bufp = state->buf.buf;
+		*sizep = state->buf.len;
+	}
+	return 0;
+}
+
+static int run_receive_hook(struct command *commands,
+			    const char *hook_name,
+			    int skip_broken,
+			    const struct string_list *push_options)
+{
+	struct receive_hook_feed_state state;
+	int status;
+
+	strbuf_init(&state.buf, 0);
+	state.cmd = commands;
+	state.skip_broken = skip_broken;
+	if (feed_receive_hook(&state, NULL, NULL))
+		return 0;
+	state.cmd = commands;
+	state.push_options = push_options;
+	status = run_and_feed_hook(hook_name, feed_receive_hook, &state);
+	strbuf_release(&state.buf);
+	return status;
+}
+
+static int run_update_hook(struct command *cmd)
+{
+	const char *argv[5];
+	struct child_process proc = CHILD_PROCESS_INIT;
+	int code;
+
+	argv[0] = find_hook("update");
+	if (!argv[0])
+		return 0;
+
+	argv[1] = cmd->ref_name;
+	argv[2] = oid_to_hex(&cmd->old_oid);
+	argv[3] = oid_to_hex(&cmd->new_oid);
+	argv[4] = NULL;
+
+	proc.no_stdin = 1;
+	proc.stdout_to_stderr = 1;
+	proc.err = use_sideband ? -1 : 0;
+	proc.argv = argv;
+	proc.trace2_hook_name = "update";
+
+	code = start_command(&proc);
+	if (code)
+		return code;
+	if (use_sideband)
+		copy_to_sideband(proc.err, -1, NULL);
+	return finish_command(&proc);
+}
+
+static int is_ref_checked_out(const char *ref)
+{
+	if (is_bare_repository())
+		return 0;
+
+	if (!head_name)
+		return 0;
+	return !strcmp(head_name, ref);
+}
+
+static char *refuse_unconfigured_deny_msg =
+	N_("By default, updating the current branch in a non-bare repository\n"
+	   "is denied, because it will make the index and work tree inconsistent\n"
+	   "with what you pushed, and will require 'git reset --hard' to match\n"
+	   "the work tree to HEAD.\n"
+	   "\n"
+	   "You can set the 'receive.denyCurrentBranch' configuration variable\n"
+	   "to 'ignore' or 'warn' in the remote repository to allow pushing into\n"
+	   "its current branch; however, this is not recommended unless you\n"
+	   "arranged to update its work tree to match what you pushed in some\n"
+	   "other way.\n"
+	   "\n"
+	   "To squelch this message and still keep the default behaviour, set\n"
+	   "'receive.denyCurrentBranch' configuration variable to 'refuse'.");
+
+static void refuse_unconfigured_deny(void)
+{
+	rp_error("%s", _(refuse_unconfigured_deny_msg));
+}
+
+static char *refuse_unconfigured_deny_delete_current_msg =
+	N_("By default, deleting the current branch is denied, because the next\n"
+	   "'git clone' won't result in any file checked out, causing confusion.\n"
+	   "\n"
+	   "You can set 'receive.denyDeleteCurrent' configuration variable to\n"
+	   "'warn' or 'ignore' in the remote repository to allow deleting the\n"
+	   "current branch, with or without a warning message.\n"
+	   "\n"
+	   "To squelch this message, you can set it to 'refuse'.");
+
+static void refuse_unconfigured_deny_delete_current(void)
+{
+	rp_error("%s", _(refuse_unconfigured_deny_delete_current_msg));
+}
+
+static int command_singleton_iterator(void *cb_data, struct object_id *oid);
+static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
+{
+	struct lock_file shallow_lock = LOCK_INIT;
+	struct oid_array extra = OID_ARRAY_INIT;
+	struct check_connected_options opt = CHECK_CONNECTED_INIT;
+	uint32_t mask = 1 << (cmd->index % 32);
+	int i;
+
+	trace_printf_key(&trace_shallow,
+			 "shallow: update_shallow_ref %s\n", cmd->ref_name);
+	for (i = 0; i < si->shallow->nr; i++)
+		if (si->used_shallow[i] &&
+		    (si->used_shallow[i][cmd->index / 32] & mask) &&
+		    !delayed_reachability_test(si, i))
+			oid_array_append(&extra, &si->shallow->oid[i]);
+
+	opt.env = tmp_objdir_env(tmp_objdir);
+	setup_alternate_shallow(&shallow_lock, &opt.shallow_file, &extra);
+	if (check_connected(command_singleton_iterator, cmd, &opt)) {
+		rollback_lock_file(&shallow_lock);
+		oid_array_clear(&extra);
+		return -1;
+	}
+
+	commit_lock_file(&shallow_lock);
+
+	/*
+	 * Make sure setup_alternate_shallow() for the next ref does
+	 * not lose these new roots..
+	 */
+	for (i = 0; i < extra.nr; i++)
+		register_shallow(the_repository, &extra.oid[i]);
+
+	si->shallow_ref[cmd->index] = 0;
+	oid_array_clear(&extra);
+	return 0;
+}
+
+/*
+ * NEEDSWORK: we should consolidate various implementions of "are we
+ * on an unborn branch?" test into one, and make the unified one more
+ * robust. !get_sha1() based check used here and elsewhere would not
+ * allow us to tell an unborn branch from corrupt ref, for example.
+ * For the purpose of fixing "deploy-to-update does not work when
+ * pushing into an empty repository" issue, this should suffice for
+ * now.
+ */
+static int head_has_history(void)
+{
+	struct object_id oid;
+
+	return !get_oid("HEAD", &oid);
+}
+
+static const char *push_to_deploy(unsigned char *sha1,
+				  struct argv_array *env,
+				  const char *work_tree)
+{
+	const char *update_refresh[] = {
+		"update-index", "-q", "--ignore-submodules", "--refresh", NULL
+	};
+	const char *diff_files[] = {
+		"diff-files", "--quiet", "--ignore-submodules", "--", NULL
+	};
+	const char *diff_index[] = {
+		"diff-index", "--quiet", "--cached", "--ignore-submodules",
+		NULL, "--", NULL
+	};
+	const char *read_tree[] = {
+		"read-tree", "-u", "-m", NULL, NULL
+	};
+	struct child_process child = CHILD_PROCESS_INIT;
+
+	child.argv = update_refresh;
+	child.env = env->argv;
+	child.dir = work_tree;
+	child.no_stdin = 1;
+	child.stdout_to_stderr = 1;
+	child.git_cmd = 1;
+	if (run_command(&child))
+		return "Up-to-date check failed";
+
+	/* run_command() does not clean up completely; reinitialize */
+	child_process_init(&child);
+	child.argv = diff_files;
+	child.env = env->argv;
+	child.dir = work_tree;
+	child.no_stdin = 1;
+	child.stdout_to_stderr = 1;
+	child.git_cmd = 1;
+	if (run_command(&child))
+		return "Working directory has unstaged changes";
+
+	/* diff-index with either HEAD or an empty tree */
+	diff_index[4] = head_has_history() ? "HEAD" : empty_tree_oid_hex();
+
+	child_process_init(&child);
+	child.argv = diff_index;
+	child.env = env->argv;
+	child.no_stdin = 1;
+	child.no_stdout = 1;
+	child.stdout_to_stderr = 0;
+	child.git_cmd = 1;
+	if (run_command(&child))
+		return "Working directory has staged changes";
+
+	read_tree[3] = sha1_to_hex(sha1);
+	child_process_init(&child);
+	child.argv = read_tree;
+	child.env = env->argv;
+	child.dir = work_tree;
+	child.no_stdin = 1;
+	child.no_stdout = 1;
+	child.stdout_to_stderr = 0;
+	child.git_cmd = 1;
+	if (run_command(&child))
+		return "Could not update working tree to new HEAD";
+
+	return NULL;
+}
+
+static const char *push_to_checkout_hook = "push-to-checkout";
+
+static const char *push_to_checkout(unsigned char *sha1,
+				    struct argv_array *env,
+				    const char *work_tree)
+{
+	argv_array_pushf(env, "GIT_WORK_TREE=%s", absolute_path(work_tree));
+	if (run_hook_le(env->argv, push_to_checkout_hook,
+			sha1_to_hex(sha1), NULL))
+		return "push-to-checkout hook declined";
+	else
+		return NULL;
+}
+
+static const char *update_worktree(unsigned char *sha1)
+{
+	const char *retval;
+	const char *work_tree = git_work_tree_cfg ? git_work_tree_cfg : "..";
+	struct argv_array env = ARGV_ARRAY_INIT;
+
+	if (is_bare_repository())
+		return "denyCurrentBranch = updateInstead needs a worktree";
+
+	argv_array_pushf(&env, "GIT_DIR=%s", absolute_path(get_git_dir()));
+
+	if (!find_hook(push_to_checkout_hook))
+		retval = push_to_deploy(sha1, &env, work_tree);
+	else
+		retval = push_to_checkout(sha1, &env, work_tree);
+
+	argv_array_clear(&env);
+	return retval;
+}
+
+static const char *update(struct command *cmd, struct shallow_info *si)
+{
+	const char *name = cmd->ref_name;
+	struct strbuf namespaced_name_buf = STRBUF_INIT;
+	static char *namespaced_name;
+	const char *ret;
+	struct object_id *old_oid = &cmd->old_oid;
+	struct object_id *new_oid = &cmd->new_oid;
+	int do_update_worktree = 0;
+
+	/* only refs/... are allowed */
+	if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
+		rp_error("refusing to create funny ref '%s' remotely", name);
+		return "funny refname";
+	}
+
+	strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name);
+	free(namespaced_name);
+	namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
+
+	if (is_ref_checked_out(namespaced_name)) {
+		switch (deny_current_branch) {
+		case DENY_IGNORE:
+			break;
+		case DENY_WARN:
+			rp_warning("updating the current branch");
+			break;
+		case DENY_REFUSE:
+		case DENY_UNCONFIGURED:
+			rp_error("refusing to update checked out branch: %s", name);
+			if (deny_current_branch == DENY_UNCONFIGURED)
+				refuse_unconfigured_deny();
+			return "branch is currently checked out";
+		case DENY_UPDATE_INSTEAD:
+			/* pass -- let other checks intervene first */
+			do_update_worktree = 1;
+			break;
+		}
+	}
+
+	if (!is_null_oid(new_oid) && !has_object_file(new_oid)) {
+		error("unpack should have generated %s, "
+		      "but I can't find it!", oid_to_hex(new_oid));
+		return "bad pack";
+	}
+
+	if (!is_null_oid(old_oid) && is_null_oid(new_oid)) {
+		if (deny_deletes && starts_with(name, "refs/heads/")) {
+			rp_error("denying ref deletion for %s", name);
+			return "deletion prohibited";
+		}
+
+		if (head_name && !strcmp(namespaced_name, head_name)) {
+			switch (deny_delete_current) {
+			case DENY_IGNORE:
+				break;
+			case DENY_WARN:
+				rp_warning("deleting the current branch");
+				break;
+			case DENY_REFUSE:
+			case DENY_UNCONFIGURED:
+			case DENY_UPDATE_INSTEAD:
+				if (deny_delete_current == DENY_UNCONFIGURED)
+					refuse_unconfigured_deny_delete_current();
+				rp_error("refusing to delete the current branch: %s", name);
+				return "deletion of the current branch prohibited";
+			default:
+				return "Invalid denyDeleteCurrent setting";
+			}
+		}
+	}
+
+	if (deny_non_fast_forwards && !is_null_oid(new_oid) &&
+	    !is_null_oid(old_oid) &&
+	    starts_with(name, "refs/heads/")) {
+		struct object *old_object, *new_object;
+		struct commit *old_commit, *new_commit;
+
+		old_object = parse_object(the_repository, old_oid);
+		new_object = parse_object(the_repository, new_oid);
+
+		if (!old_object || !new_object ||
+		    old_object->type != OBJ_COMMIT ||
+		    new_object->type != OBJ_COMMIT) {
+			error("bad sha1 objects for %s", name);
+			return "bad ref";
+		}
+		old_commit = (struct commit *)old_object;
+		new_commit = (struct commit *)new_object;
+		if (!in_merge_bases(old_commit, new_commit)) {
+			rp_error("denying non-fast-forward %s"
+				 " (you should pull first)", name);
+			return "non-fast-forward";
+		}
+	}
+	if (run_update_hook(cmd)) {
+		rp_error("hook declined to update %s", name);
+		return "hook declined";
+	}
+
+	if (do_update_worktree) {
+		ret = update_worktree(new_oid->hash);
+		if (ret)
+			return ret;
+	}
+
+	if (is_null_oid(new_oid)) {
+		struct strbuf err = STRBUF_INIT;
+		if (!parse_object(the_repository, old_oid)) {
+			old_oid = NULL;
+			if (ref_exists(name)) {
+				rp_warning("Allowing deletion of corrupt ref.");
+			} else {
+				rp_warning("Deleting a non-existent ref.");
+				cmd->did_not_exist = 1;
+			}
+		}
+		if (ref_transaction_delete(transaction,
+					   namespaced_name,
+					   old_oid,
+					   0, "push", &err)) {
+			rp_error("%s", err.buf);
+			strbuf_release(&err);
+			return "failed to delete";
+		}
+		strbuf_release(&err);
+		return NULL; /* good */
+	}
+	else {
+		struct strbuf err = STRBUF_INIT;
+		if (shallow_update && si->shallow_ref[cmd->index] &&
+		    update_shallow_ref(cmd, si))
+			return "shallow error";
+
+		if (ref_transaction_update(transaction,
+					   namespaced_name,
+					   new_oid, old_oid,
+					   0, "push",
+					   &err)) {
+			rp_error("%s", err.buf);
+			strbuf_release(&err);
+
+			return "failed to update ref";
+		}
+		strbuf_release(&err);
+
+		return NULL; /* good */
+	}
+}
+
+static void run_update_post_hook(struct command *commands)
+{
+	struct command *cmd;
+	struct child_process proc = CHILD_PROCESS_INIT;
+	const char *hook;
+
+	hook = find_hook("post-update");
+	if (!hook)
+		return;
+
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (cmd->error_string || cmd->did_not_exist)
+			continue;
+		if (!proc.args.argc)
+			argv_array_push(&proc.args, hook);
+		argv_array_push(&proc.args, cmd->ref_name);
+	}
+	if (!proc.args.argc)
+		return;
+
+	proc.no_stdin = 1;
+	proc.stdout_to_stderr = 1;
+	proc.err = use_sideband ? -1 : 0;
+	proc.trace2_hook_name = "post-update";
+
+	if (!start_command(&proc)) {
+		if (use_sideband)
+			copy_to_sideband(proc.err, -1, NULL);
+		finish_command(&proc);
+	}
+}
+
+static void check_aliased_update_internal(struct command *cmd,
+					  struct string_list *list,
+					  const char *dst_name, int flag)
+{
+	struct string_list_item *item;
+	struct command *dst_cmd;
+
+	if (!(flag & REF_ISSYMREF))
+		return;
+
+	if (!dst_name) {
+		rp_error("refusing update to broken symref '%s'", cmd->ref_name);
+		cmd->skip_update = 1;
+		cmd->error_string = "broken symref";
+		return;
+	}
+	dst_name = strip_namespace(dst_name);
+
+	if ((item = string_list_lookup(list, dst_name)) == NULL)
+		return;
+
+	cmd->skip_update = 1;
+
+	dst_cmd = (struct command *) item->util;
+
+	if (oideq(&cmd->old_oid, &dst_cmd->old_oid) &&
+	    oideq(&cmd->new_oid, &dst_cmd->new_oid))
+		return;
+
+	dst_cmd->skip_update = 1;
+
+	rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
+		 " its target '%s' (%s..%s)",
+		 cmd->ref_name,
+		 find_unique_abbrev(&cmd->old_oid, DEFAULT_ABBREV),
+		 find_unique_abbrev(&cmd->new_oid, DEFAULT_ABBREV),
+		 dst_cmd->ref_name,
+		 find_unique_abbrev(&dst_cmd->old_oid, DEFAULT_ABBREV),
+		 find_unique_abbrev(&dst_cmd->new_oid, DEFAULT_ABBREV));
+
+	cmd->error_string = dst_cmd->error_string =
+		"inconsistent aliased update";
+}
+
+static void check_aliased_update(struct command *cmd, struct string_list *list)
+{
+	struct strbuf buf = STRBUF_INIT;
+	const char *dst_name;
+	int flag;
+
+	strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
+	dst_name = resolve_ref_unsafe(buf.buf, 0, NULL, &flag);
+	check_aliased_update_internal(cmd, list, dst_name, flag);
+	strbuf_release(&buf);
+}
+
+static void check_aliased_updates(struct command *commands)
+{
+	struct command *cmd;
+	struct string_list ref_list = STRING_LIST_INIT_NODUP;
+
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		struct string_list_item *item =
+			string_list_append(&ref_list, cmd->ref_name);
+		item->util = (void *)cmd;
+	}
+	string_list_sort(&ref_list);
+
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (!cmd->error_string)
+			check_aliased_update(cmd, &ref_list);
+	}
+
+	string_list_clear(&ref_list, 0);
+}
+
+static int command_singleton_iterator(void *cb_data, struct object_id *oid)
+{
+	struct command **cmd_list = cb_data;
+	struct command *cmd = *cmd_list;
+
+	if (!cmd || is_null_oid(&cmd->new_oid))
+		return -1; /* end of list */
+	*cmd_list = NULL; /* this returns only one */
+	oidcpy(oid, &cmd->new_oid);
+	return 0;
+}
+
+static void set_connectivity_errors(struct command *commands,
+				    struct shallow_info *si)
+{
+	struct command *cmd;
+
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		struct command *singleton = cmd;
+		struct check_connected_options opt = CHECK_CONNECTED_INIT;
+
+		if (shallow_update && si->shallow_ref[cmd->index])
+			/* to be checked in update_shallow_ref() */
+			continue;
+
+		opt.env = tmp_objdir_env(tmp_objdir);
+		if (!check_connected(command_singleton_iterator, &singleton,
+				     &opt))
+			continue;
+
+		cmd->error_string = "missing necessary objects";
+	}
+}
+
+struct iterate_data {
+	struct command *cmds;
+	struct shallow_info *si;
+};
+
+static int iterate_receive_command_list(void *cb_data, struct object_id *oid)
+{
+	struct iterate_data *data = cb_data;
+	struct command **cmd_list = &data->cmds;
+	struct command *cmd = *cmd_list;
+
+	for (; cmd; cmd = cmd->next) {
+		if (shallow_update && data->si->shallow_ref[cmd->index])
+			/* to be checked in update_shallow_ref() */
+			continue;
+		if (!is_null_oid(&cmd->new_oid) && !cmd->skip_update) {
+			oidcpy(oid, &cmd->new_oid);
+			*cmd_list = cmd->next;
+			return 0;
+		}
+	}
+	*cmd_list = NULL;
+	return -1; /* end of list */
+}
+
+static void reject_updates_to_hidden(struct command *commands)
+{
+	struct strbuf refname_full = STRBUF_INIT;
+	size_t prefix_len;
+	struct command *cmd;
+
+	strbuf_addstr(&refname_full, get_git_namespace());
+	prefix_len = refname_full.len;
+
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (cmd->error_string)
+			continue;
+
+		strbuf_setlen(&refname_full, prefix_len);
+		strbuf_addstr(&refname_full, cmd->ref_name);
+
+		if (!ref_is_hidden(cmd->ref_name, refname_full.buf))
+			continue;
+		if (is_null_oid(&cmd->new_oid))
+			cmd->error_string = "deny deleting a hidden ref";
+		else
+			cmd->error_string = "deny updating a hidden ref";
+	}
+
+	strbuf_release(&refname_full);
+}
+
+static int should_process_cmd(struct command *cmd)
+{
+	return !cmd->error_string && !cmd->skip_update;
+}
+
+static void warn_if_skipped_connectivity_check(struct command *commands,
+					       struct shallow_info *si)
+{
+	struct command *cmd;
+	int checked_connectivity = 1;
+
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (should_process_cmd(cmd) && si->shallow_ref[cmd->index]) {
+			error("BUG: connectivity check has not been run on ref %s",
+			      cmd->ref_name);
+			checked_connectivity = 0;
+		}
+	}
+	if (!checked_connectivity)
+		BUG("connectivity check skipped???");
+}
+
+static void execute_commands_non_atomic(struct command *commands,
+					struct shallow_info *si)
+{
+	struct command *cmd;
+	struct strbuf err = STRBUF_INIT;
+
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (!should_process_cmd(cmd))
+			continue;
+
+		transaction = ref_transaction_begin(&err);
+		if (!transaction) {
+			rp_error("%s", err.buf);
+			strbuf_reset(&err);
+			cmd->error_string = "transaction failed to start";
+			continue;
+		}
+
+		cmd->error_string = update(cmd, si);
+
+		if (!cmd->error_string
+		    && ref_transaction_commit(transaction, &err)) {
+			rp_error("%s", err.buf);
+			strbuf_reset(&err);
+			cmd->error_string = "failed to update ref";
+		}
+		ref_transaction_free(transaction);
+	}
+	strbuf_release(&err);
+}
+
+static void execute_commands_atomic(struct command *commands,
+					struct shallow_info *si)
+{
+	struct command *cmd;
+	struct strbuf err = STRBUF_INIT;
+	const char *reported_error = "atomic push failure";
+
+	transaction = ref_transaction_begin(&err);
+	if (!transaction) {
+		rp_error("%s", err.buf);
+		strbuf_reset(&err);
+		reported_error = "transaction failed to start";
+		goto failure;
+	}
+
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (!should_process_cmd(cmd))
+			continue;
+
+		cmd->error_string = update(cmd, si);
+
+		if (cmd->error_string)
+			goto failure;
+	}
+
+	if (ref_transaction_commit(transaction, &err)) {
+		rp_error("%s", err.buf);
+		reported_error = "atomic transaction failed";
+		goto failure;
+	}
+	goto cleanup;
+
+failure:
+	for (cmd = commands; cmd; cmd = cmd->next)
+		if (!cmd->error_string)
+			cmd->error_string = reported_error;
+
+cleanup:
+	ref_transaction_free(transaction);
+	strbuf_release(&err);
+}
+
+static void execute_commands(struct command *commands,
+			     const char *unpacker_error,
+			     struct shallow_info *si,
+			     const struct string_list *push_options)
+{
+	struct check_connected_options opt = CHECK_CONNECTED_INIT;
+	struct command *cmd;
+	struct iterate_data data;
+	struct async muxer;
+	int err_fd = 0;
+
+	if (unpacker_error) {
+		for (cmd = commands; cmd; cmd = cmd->next)
+			cmd->error_string = "unpacker error";
+		return;
+	}
+
+	if (use_sideband) {
+		memset(&muxer, 0, sizeof(muxer));
+		muxer.proc = copy_to_sideband;
+		muxer.in = -1;
+		if (!start_async(&muxer))
+			err_fd = muxer.in;
+		/* ...else, continue without relaying sideband */
+	}
+
+	data.cmds = commands;
+	data.si = si;
+	opt.err_fd = err_fd;
+	opt.progress = err_fd && !quiet;
+	opt.env = tmp_objdir_env(tmp_objdir);
+	if (check_connected(iterate_receive_command_list, &data, &opt))
+		set_connectivity_errors(commands, si);
+
+	if (use_sideband)
+		finish_async(&muxer);
+
+	reject_updates_to_hidden(commands);
+
+	if (run_receive_hook(commands, "pre-receive", 0, push_options)) {
+		for (cmd = commands; cmd; cmd = cmd->next) {
+			if (!cmd->error_string)
+				cmd->error_string = "pre-receive hook declined";
+		}
+		return;
+	}
+
+	/*
+	 * Now we'll start writing out refs, which means the objects need
+	 * to be in their final positions so that other processes can see them.
+	 */
+	if (tmp_objdir_migrate(tmp_objdir) < 0) {
+		for (cmd = commands; cmd; cmd = cmd->next) {
+			if (!cmd->error_string)
+				cmd->error_string = "unable to migrate objects to permanent storage";
+		}
+		return;
+	}
+	tmp_objdir = NULL;
+
+	check_aliased_updates(commands);
+
+	free(head_name_to_free);
+	head_name = head_name_to_free = resolve_refdup("HEAD", 0, NULL, NULL);
+
+	if (use_atomic)
+		execute_commands_atomic(commands, si);
+	else
+		execute_commands_non_atomic(commands, si);
+
+	if (shallow_update)
+		warn_if_skipped_connectivity_check(commands, si);
+}
+
+static struct command **queue_command(struct command **tail,
+				      const char *line,
+				      int linelen)
+{
+	struct object_id old_oid, new_oid;
+	struct command *cmd;
+	const char *refname;
+	int reflen;
+	const char *p;
+
+	if (parse_oid_hex(line, &old_oid, &p) ||
+	    *p++ != ' ' ||
+	    parse_oid_hex(p, &new_oid, &p) ||
+	    *p++ != ' ')
+		die("protocol error: expected old/new/ref, got '%s'", line);
+
+	refname = p;
+	reflen = linelen - (p - line);
+	FLEX_ALLOC_MEM(cmd, ref_name, refname, reflen);
+	oidcpy(&cmd->old_oid, &old_oid);
+	oidcpy(&cmd->new_oid, &new_oid);
+	*tail = cmd;
+	return &cmd->next;
+}
+
+static void queue_commands_from_cert(struct command **tail,
+				     struct strbuf *push_cert)
+{
+	const char *boc, *eoc;
+
+	if (*tail)
+		die("protocol error: got both push certificate and unsigned commands");
+
+	boc = strstr(push_cert->buf, "\n\n");
+	if (!boc)
+		die("malformed push certificate %.*s", 100, push_cert->buf);
+	else
+		boc += 2;
+	eoc = push_cert->buf + parse_signature(push_cert->buf, push_cert->len);
+
+	while (boc < eoc) {
+		const char *eol = memchr(boc, '\n', eoc - boc);
+		tail = queue_command(tail, boc, eol ? eol - boc : eoc - boc);
+		boc = eol ? eol + 1 : eoc;
+	}
+}
+
+static struct command *read_head_info(struct packet_reader *reader,
+				      struct oid_array *shallow)
+{
+	struct command *commands = NULL;
+	struct command **p = &commands;
+	for (;;) {
+		int linelen;
+
+		if (packet_reader_read(reader) != PACKET_READ_NORMAL)
+			break;
+
+		if (reader->pktlen > 8 && starts_with(reader->line, "shallow ")) {
+			struct object_id oid;
+			if (get_oid_hex(reader->line + 8, &oid))
+				die("protocol error: expected shallow sha, got '%s'",
+				    reader->line + 8);
+			oid_array_append(shallow, &oid);
+			continue;
+		}
+
+		linelen = strlen(reader->line);
+		if (linelen < reader->pktlen) {
+			const char *feature_list = reader->line + linelen + 1;
+			if (parse_feature_request(feature_list, "report-status"))
+				report_status = 1;
+			if (parse_feature_request(feature_list, "side-band-64k"))
+				use_sideband = LARGE_PACKET_MAX;
+			if (parse_feature_request(feature_list, "quiet"))
+				quiet = 1;
+			if (advertise_atomic_push
+			    && parse_feature_request(feature_list, "atomic"))
+				use_atomic = 1;
+			if (advertise_push_options
+			    && parse_feature_request(feature_list, "push-options"))
+				use_push_options = 1;
+		}
+
+		if (!strcmp(reader->line, "push-cert")) {
+			int true_flush = 0;
+			int saved_options = reader->options;
+			reader->options &= ~PACKET_READ_CHOMP_NEWLINE;
+
+			for (;;) {
+				packet_reader_read(reader);
+				if (reader->status == PACKET_READ_FLUSH) {
+					true_flush = 1;
+					break;
+				}
+				if (reader->status != PACKET_READ_NORMAL) {
+					die("protocol error: got an unexpected packet");
+				}
+				if (!strcmp(reader->line, "push-cert-end\n"))
+					break; /* end of cert */
+				strbuf_addstr(&push_cert, reader->line);
+			}
+			reader->options = saved_options;
+
+			if (true_flush)
+				break;
+			continue;
+		}
+
+		p = queue_command(p, reader->line, linelen);
+	}
+
+	if (push_cert.len)
+		queue_commands_from_cert(p, &push_cert);
+
+	return commands;
+}
+
+static void read_push_options(struct packet_reader *reader,
+			      struct string_list *options)
+{
+	while (1) {
+		if (packet_reader_read(reader) != PACKET_READ_NORMAL)
+			break;
+
+		string_list_append(options, reader->line);
+	}
+}
+
+static const char *parse_pack_header(struct pack_header *hdr)
+{
+	switch (read_pack_header(0, hdr)) {
+	case PH_ERROR_EOF:
+		return "eof before pack header was fully read";
+
+	case PH_ERROR_PACK_SIGNATURE:
+		return "protocol error (pack signature mismatch detected)";
+
+	case PH_ERROR_PROTOCOL:
+		return "protocol error (pack version unsupported)";
+
+	default:
+		return "unknown error in parse_pack_header";
+
+	case 0:
+		return NULL;
+	}
+}
+
+static const char *pack_lockfile;
+
+static void push_header_arg(struct argv_array *args, struct pack_header *hdr)
+{
+	argv_array_pushf(args, "--pack_header=%"PRIu32",%"PRIu32,
+			ntohl(hdr->hdr_version), ntohl(hdr->hdr_entries));
+}
+
+static const char *unpack(int err_fd, struct shallow_info *si)
+{
+	struct pack_header hdr;
+	const char *hdr_err;
+	int status;
+	struct child_process child = CHILD_PROCESS_INIT;
+	int fsck_objects = (receive_fsck_objects >= 0
+			    ? receive_fsck_objects
+			    : transfer_fsck_objects >= 0
+			    ? transfer_fsck_objects
+			    : 0);
+
+	hdr_err = parse_pack_header(&hdr);
+	if (hdr_err) {
+		if (err_fd > 0)
+			close(err_fd);
+		return hdr_err;
+	}
+
+	if (si->nr_ours || si->nr_theirs) {
+		alt_shallow_file = setup_temporary_shallow(si->shallow);
+		argv_array_push(&child.args, "--shallow-file");
+		argv_array_push(&child.args, alt_shallow_file);
+	}
+
+	tmp_objdir = tmp_objdir_create();
+	if (!tmp_objdir) {
+		if (err_fd > 0)
+			close(err_fd);
+		return "unable to create temporary object directory";
+	}
+	child.env = tmp_objdir_env(tmp_objdir);
+
+	/*
+	 * Normally we just pass the tmp_objdir environment to the child
+	 * processes that do the heavy lifting, but we may need to see these
+	 * objects ourselves to set up shallow information.
+	 */
+	tmp_objdir_add_as_alternate(tmp_objdir);
+
+	if (ntohl(hdr.hdr_entries) < unpack_limit) {
+		argv_array_push(&child.args, "unpack-objects");
+		push_header_arg(&child.args, &hdr);
+		if (quiet)
+			argv_array_push(&child.args, "-q");
+		if (fsck_objects)
+			argv_array_pushf(&child.args, "--strict%s",
+				fsck_msg_types.buf);
+		if (max_input_size)
+			argv_array_pushf(&child.args, "--max-input-size=%"PRIuMAX,
+				(uintmax_t)max_input_size);
+		child.no_stdout = 1;
+		child.err = err_fd;
+		child.git_cmd = 1;
+		status = run_command(&child);
+		if (status)
+			return "unpack-objects abnormal exit";
+	} else {
+		char hostname[HOST_NAME_MAX + 1];
+
+		argv_array_pushl(&child.args, "index-pack", "--stdin", NULL);
+		push_header_arg(&child.args, &hdr);
+
+		if (xgethostname(hostname, sizeof(hostname)))
+			xsnprintf(hostname, sizeof(hostname), "localhost");
+		argv_array_pushf(&child.args,
+				 "--keep=receive-pack %"PRIuMAX" on %s",
+				 (uintmax_t)getpid(),
+				 hostname);
+
+		if (!quiet && err_fd)
+			argv_array_push(&child.args, "--show-resolving-progress");
+		if (use_sideband)
+			argv_array_push(&child.args, "--report-end-of-input");
+		if (fsck_objects)
+			argv_array_pushf(&child.args, "--strict%s",
+				fsck_msg_types.buf);
+		if (!reject_thin)
+			argv_array_push(&child.args, "--fix-thin");
+		if (max_input_size)
+			argv_array_pushf(&child.args, "--max-input-size=%"PRIuMAX,
+				(uintmax_t)max_input_size);
+		child.out = -1;
+		child.err = err_fd;
+		child.git_cmd = 1;
+		status = start_command(&child);
+		if (status)
+			return "index-pack fork failed";
+		pack_lockfile = index_pack_lockfile(child.out);
+		close(child.out);
+		status = finish_command(&child);
+		if (status)
+			return "index-pack abnormal exit";
+		reprepare_packed_git(the_repository);
+	}
+	return NULL;
+}
+
+static const char *unpack_with_sideband(struct shallow_info *si)
+{
+	struct async muxer;
+	const char *ret;
+
+	if (!use_sideband)
+		return unpack(0, si);
+
+	use_keepalive = KEEPALIVE_AFTER_NUL;
+	memset(&muxer, 0, sizeof(muxer));
+	muxer.proc = copy_to_sideband;
+	muxer.in = -1;
+	if (start_async(&muxer))
+		return NULL;
+
+	ret = unpack(muxer.in, si);
+
+	finish_async(&muxer);
+	return ret;
+}
+
+static void prepare_shallow_update(struct shallow_info *si)
+{
+	int i, j, k, bitmap_size = DIV_ROUND_UP(si->ref->nr, 32);
+
+	ALLOC_ARRAY(si->used_shallow, si->shallow->nr);
+	assign_shallow_commits_to_refs(si, si->used_shallow, NULL);
+
+	si->need_reachability_test =
+		xcalloc(si->shallow->nr, sizeof(*si->need_reachability_test));
+	si->reachable =
+		xcalloc(si->shallow->nr, sizeof(*si->reachable));
+	si->shallow_ref = xcalloc(si->ref->nr, sizeof(*si->shallow_ref));
+
+	for (i = 0; i < si->nr_ours; i++)
+		si->need_reachability_test[si->ours[i]] = 1;
+
+	for (i = 0; i < si->shallow->nr; i++) {
+		if (!si->used_shallow[i])
+			continue;
+		for (j = 0; j < bitmap_size; j++) {
+			if (!si->used_shallow[i][j])
+				continue;
+			si->need_reachability_test[i]++;
+			for (k = 0; k < 32; k++)
+				if (si->used_shallow[i][j] & (1U << k))
+					si->shallow_ref[j * 32 + k]++;
+		}
+
+		/*
+		 * true for those associated with some refs and belong
+		 * in "ours" list aka "step 7 not done yet"
+		 */
+		si->need_reachability_test[i] =
+			si->need_reachability_test[i] > 1;
+	}
+
+	/*
+	 * keep hooks happy by forcing a temporary shallow file via
+	 * env variable because we can't add --shallow-file to every
+	 * command. check_connected() will be done with
+	 * true .git/shallow though.
+	 */
+	setenv(GIT_SHALLOW_FILE_ENVIRONMENT, alt_shallow_file, 1);
+}
+
+static void update_shallow_info(struct command *commands,
+				struct shallow_info *si,
+				struct oid_array *ref)
+{
+	struct command *cmd;
+	int *ref_status;
+	remove_nonexistent_theirs_shallow(si);
+	if (!si->nr_ours && !si->nr_theirs) {
+		shallow_update = 0;
+		return;
+	}
+
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (is_null_oid(&cmd->new_oid))
+			continue;
+		oid_array_append(ref, &cmd->new_oid);
+		cmd->index = ref->nr - 1;
+	}
+	si->ref = ref;
+
+	if (shallow_update) {
+		prepare_shallow_update(si);
+		return;
+	}
+
+	ALLOC_ARRAY(ref_status, ref->nr);
+	assign_shallow_commits_to_refs(si, NULL, ref_status);
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (is_null_oid(&cmd->new_oid))
+			continue;
+		if (ref_status[cmd->index]) {
+			cmd->error_string = "shallow update not allowed";
+			cmd->skip_update = 1;
+		}
+	}
+	free(ref_status);
+}
+
+static void report(struct command *commands, const char *unpack_status)
+{
+	struct command *cmd;
+	struct strbuf buf = STRBUF_INIT;
+
+	packet_buf_write(&buf, "unpack %s\n",
+			 unpack_status ? unpack_status : "ok");
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (!cmd->error_string)
+			packet_buf_write(&buf, "ok %s\n",
+					 cmd->ref_name);
+		else
+			packet_buf_write(&buf, "ng %s %s\n",
+					 cmd->ref_name, cmd->error_string);
+	}
+	packet_buf_flush(&buf);
+
+	if (use_sideband)
+		send_sideband(1, 1, buf.buf, buf.len, use_sideband);
+	else
+		write_or_die(1, buf.buf, buf.len);
+	strbuf_release(&buf);
+}
+
+static int delete_only(struct command *commands)
+{
+	struct command *cmd;
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (!is_null_oid(&cmd->new_oid))
+			return 0;
+	}
+	return 1;
+}
+
+int cmd_receive_pack(int argc, const char **argv, const char *prefix)
+{
+	int advertise_refs = 0;
+	struct command *commands;
+	struct oid_array shallow = OID_ARRAY_INIT;
+	struct oid_array ref = OID_ARRAY_INIT;
+	struct shallow_info si;
+	struct packet_reader reader;
+
+	struct option options[] = {
+		OPT__QUIET(&quiet, N_("quiet")),
+		OPT_HIDDEN_BOOL(0, "stateless-rpc", &stateless_rpc, NULL),
+		OPT_HIDDEN_BOOL(0, "advertise-refs", &advertise_refs, NULL),
+		OPT_HIDDEN_BOOL(0, "reject-thin-pack-for-testing", &reject_thin, NULL),
+		OPT_END()
+	};
+
+	packet_trace_identity("receive-pack");
+
+	argc = parse_options(argc, argv, prefix, options, receive_pack_usage, 0);
+
+	if (argc > 1)
+		usage_msg_opt(_("Too many arguments."), receive_pack_usage, options);
+	if (argc == 0)
+		usage_msg_opt(_("You must specify a directory."), receive_pack_usage, options);
+
+	service_dir = argv[0];
+
+	setup_path();
+
+	if (!enter_repo(service_dir, 0))
+		die("'%s' does not appear to be a git repository", service_dir);
+
+	git_config(receive_pack_config, NULL);
+	if (cert_nonce_seed)
+		push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL));
+
+	if (0 <= transfer_unpack_limit)
+		unpack_limit = transfer_unpack_limit;
+	else if (0 <= receive_unpack_limit)
+		unpack_limit = receive_unpack_limit;
+
+	switch (determine_protocol_version_server()) {
+	case protocol_v2:
+		/*
+		 * push support for protocol v2 has not been implemented yet,
+		 * so ignore the request to use v2 and fallback to using v0.
+		 */
+		break;
+	case protocol_v1:
+		/*
+		 * v1 is just the original protocol with a version string,
+		 * so just fall through after writing the version string.
+		 */
+		if (advertise_refs || !stateless_rpc)
+			packet_write_fmt(1, "version 1\n");
+
+		/* fallthrough */
+	case protocol_v0:
+		break;
+	case protocol_unknown_version:
+		BUG("unknown protocol version");
+	}
+
+	if (advertise_refs || !stateless_rpc) {
+		write_head_info();
+	}
+	if (advertise_refs)
+		return 0;
+
+	packet_reader_init(&reader, 0, NULL, 0,
+			   PACKET_READ_CHOMP_NEWLINE |
+			   PACKET_READ_DIE_ON_ERR_PACKET);
+
+	if ((commands = read_head_info(&reader, &shallow)) != NULL) {
+		const char *unpack_status = NULL;
+		struct string_list push_options = STRING_LIST_INIT_DUP;
+
+		if (use_push_options)
+			read_push_options(&reader, &push_options);
+		if (!check_cert_push_options(&push_options)) {
+			struct command *cmd;
+			for (cmd = commands; cmd; cmd = cmd->next)
+				cmd->error_string = "inconsistent push options";
+		}
+
+		prepare_shallow_info(&si, &shallow);
+		if (!si.nr_ours && !si.nr_theirs)
+			shallow_update = 0;
+		if (!delete_only(commands)) {
+			unpack_status = unpack_with_sideband(&si);
+			update_shallow_info(commands, &si, &ref);
+		}
+		use_keepalive = KEEPALIVE_ALWAYS;
+		execute_commands(commands, unpack_status, &si,
+				 &push_options);
+		if (pack_lockfile)
+			unlink_or_warn(pack_lockfile);
+		if (report_status)
+			report(commands, unpack_status);
+		run_receive_hook(commands, "post-receive", 1,
+				 &push_options);
+		run_update_post_hook(commands);
+		string_list_clear(&push_options, 0);
+		if (auto_gc) {
+			const char *argv_gc_auto[] = {
+				"gc", "--auto", "--quiet", NULL,
+			};
+			struct child_process proc = CHILD_PROCESS_INIT;
+
+			proc.no_stdin = 1;
+			proc.stdout_to_stderr = 1;
+			proc.err = use_sideband ? -1 : 0;
+			proc.git_cmd = 1;
+			proc.argv = argv_gc_auto;
+
+			close_object_store(the_repository->objects);
+			if (!start_command(&proc)) {
+				if (use_sideband)
+					copy_to_sideband(proc.err, -1, NULL);
+				finish_command(&proc);
+			}
+		}
+		if (auto_update_server_info)
+			update_server_info(0);
+		clear_shallow_info(&si);
+	}
+	if (use_sideband)
+		packet_flush(1);
+	oid_array_clear(&shallow);
+	oid_array_clear(&ref);
+	free((void *)push_cert_nonce);
+	return 0;
+}
diff --git a/builtin/reflog.c b/builtin/reflog.c
new file mode 100644
index 0000000000..4d3430900d
--- /dev/null
+++ b/builtin/reflog.c
@@ -0,0 +1,788 @@
+#include "builtin.h"
+#include "config.h"
+#include "lockfile.h"
+#include "object-store.h"
+#include "repository.h"
+#include "commit.h"
+#include "refs.h"
+#include "dir.h"
+#include "tree-walk.h"
+#include "diff.h"
+#include "revision.h"
+#include "reachable.h"
+#include "worktree.h"
+
+/* NEEDSWORK: switch to using parse_options */
+static const char reflog_expire_usage[] =
+N_("git reflog expire [--expire=<time>] "
+   "[--expire-unreachable=<time>] "
+   "[--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] "
+   "[--verbose] [--all] <refs>...");
+static const char reflog_delete_usage[] =
+N_("git reflog delete [--rewrite] [--updateref] "
+   "[--dry-run | -n] [--verbose] <refs>...");
+static const char reflog_exists_usage[] =
+N_("git reflog exists <ref>");
+
+static timestamp_t default_reflog_expire;
+static timestamp_t default_reflog_expire_unreachable;
+
+struct cmd_reflog_expire_cb {
+	struct rev_info revs;
+	int stalefix;
+	timestamp_t expire_total;
+	timestamp_t expire_unreachable;
+	int recno;
+};
+
+struct expire_reflog_policy_cb {
+	enum {
+		UE_NORMAL,
+		UE_ALWAYS,
+		UE_HEAD
+	} unreachable_expire_kind;
+	struct commit_list *mark_list;
+	unsigned long mark_limit;
+	struct cmd_reflog_expire_cb cmd;
+	struct commit *tip_commit;
+	struct commit_list *tips;
+};
+
+struct collected_reflog {
+	struct object_id oid;
+	char reflog[FLEX_ARRAY];
+};
+
+struct collect_reflog_cb {
+	struct collected_reflog **e;
+	int alloc;
+	int nr;
+	struct worktree *wt;
+};
+
+/* Remember to update object flag allocation in object.h */
+#define INCOMPLETE	(1u<<10)
+#define STUDYING	(1u<<11)
+#define REACHABLE	(1u<<12)
+
+static int tree_is_complete(const struct object_id *oid)
+{
+	struct tree_desc desc;
+	struct name_entry entry;
+	int complete;
+	struct tree *tree;
+
+	tree = lookup_tree(the_repository, oid);
+	if (!tree)
+		return 0;
+	if (tree->object.flags & SEEN)
+		return 1;
+	if (tree->object.flags & INCOMPLETE)
+		return 0;
+
+	if (!tree->buffer) {
+		enum object_type type;
+		unsigned long size;
+		void *data = read_object_file(oid, &type, &size);
+		if (!data) {
+			tree->object.flags |= INCOMPLETE;
+			return 0;
+		}
+		tree->buffer = data;
+		tree->size = size;
+	}
+	init_tree_desc(&desc, tree->buffer, tree->size);
+	complete = 1;
+	while (tree_entry(&desc, &entry)) {
+		if (!has_object_file(&entry.oid) ||
+		    (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
+			tree->object.flags |= INCOMPLETE;
+			complete = 0;
+		}
+	}
+	free_tree_buffer(tree);
+
+	if (complete)
+		tree->object.flags |= SEEN;
+	return complete;
+}
+
+static int commit_is_complete(struct commit *commit)
+{
+	struct object_array study;
+	struct object_array found;
+	int is_incomplete = 0;
+	int i;
+
+	/* early return */
+	if (commit->object.flags & SEEN)
+		return 1;
+	if (commit->object.flags & INCOMPLETE)
+		return 0;
+	/*
+	 * Find all commits that are reachable and are not marked as
+	 * SEEN.  Then make sure the trees and blobs contained are
+	 * complete.  After that, mark these commits also as SEEN.
+	 * If some of the objects that are needed to complete this
+	 * commit are missing, mark this commit as INCOMPLETE.
+	 */
+	memset(&study, 0, sizeof(study));
+	memset(&found, 0, sizeof(found));
+	add_object_array(&commit->object, NULL, &study);
+	add_object_array(&commit->object, NULL, &found);
+	commit->object.flags |= STUDYING;
+	while (study.nr) {
+		struct commit *c;
+		struct commit_list *parent;
+
+		c = (struct commit *)object_array_pop(&study);
+		if (!c->object.parsed && !parse_object(the_repository, &c->object.oid))
+			c->object.flags |= INCOMPLETE;
+
+		if (c->object.flags & INCOMPLETE) {
+			is_incomplete = 1;
+			break;
+		}
+		else if (c->object.flags & SEEN)
+			continue;
+		for (parent = c->parents; parent; parent = parent->next) {
+			struct commit *p = parent->item;
+			if (p->object.flags & STUDYING)
+				continue;
+			p->object.flags |= STUDYING;
+			add_object_array(&p->object, NULL, &study);
+			add_object_array(&p->object, NULL, &found);
+		}
+	}
+	if (!is_incomplete) {
+		/*
+		 * make sure all commits in "found" array have all the
+		 * necessary objects.
+		 */
+		for (i = 0; i < found.nr; i++) {
+			struct commit *c =
+				(struct commit *)found.objects[i].item;
+			if (!tree_is_complete(get_commit_tree_oid(c))) {
+				is_incomplete = 1;
+				c->object.flags |= INCOMPLETE;
+			}
+		}
+		if (!is_incomplete) {
+			/* mark all found commits as complete, iow SEEN */
+			for (i = 0; i < found.nr; i++)
+				found.objects[i].item->flags |= SEEN;
+		}
+	}
+	/* clear flags from the objects we traversed */
+	for (i = 0; i < found.nr; i++)
+		found.objects[i].item->flags &= ~STUDYING;
+	if (is_incomplete)
+		commit->object.flags |= INCOMPLETE;
+	else {
+		/*
+		 * If we come here, we have (1) traversed the ancestry chain
+		 * from the "commit" until we reach SEEN commits (which are
+		 * known to be complete), and (2) made sure that the commits
+		 * encountered during the above traversal refer to trees that
+		 * are complete.  Which means that we know *all* the commits
+		 * we have seen during this process are complete.
+		 */
+		for (i = 0; i < found.nr; i++)
+			found.objects[i].item->flags |= SEEN;
+	}
+	/* free object arrays */
+	object_array_clear(&study);
+	object_array_clear(&found);
+	return !is_incomplete;
+}
+
+static int keep_entry(struct commit **it, struct object_id *oid)
+{
+	struct commit *commit;
+
+	if (is_null_oid(oid))
+		return 1;
+	commit = lookup_commit_reference_gently(the_repository, oid, 1);
+	if (!commit)
+		return 0;
+
+	/*
+	 * Make sure everything in this commit exists.
+	 *
+	 * We have walked all the objects reachable from the refs
+	 * and cache earlier.  The commits reachable by this commit
+	 * must meet SEEN commits -- and then we should mark them as
+	 * SEEN as well.
+	 */
+	if (!commit_is_complete(commit))
+		return 0;
+	*it = commit;
+	return 1;
+}
+
+/*
+ * Starting from commits in the cb->mark_list, mark commits that are
+ * reachable from them.  Stop the traversal at commits older than
+ * the expire_limit and queue them back, so that the caller can call
+ * us again to restart the traversal with longer expire_limit.
+ */
+static void mark_reachable(struct expire_reflog_policy_cb *cb)
+{
+	struct commit_list *pending;
+	timestamp_t expire_limit = cb->mark_limit;
+	struct commit_list *leftover = NULL;
+
+	for (pending = cb->mark_list; pending; pending = pending->next)
+		pending->item->object.flags &= ~REACHABLE;
+
+	pending = cb->mark_list;
+	while (pending) {
+		struct commit_list *parent;
+		struct commit *commit = pop_commit(&pending);
+		if (commit->object.flags & REACHABLE)
+			continue;
+		if (parse_commit(commit))
+			continue;
+		commit->object.flags |= REACHABLE;
+		if (commit->date < expire_limit) {
+			commit_list_insert(commit, &leftover);
+			continue;
+		}
+		commit->object.flags |= REACHABLE;
+		parent = commit->parents;
+		while (parent) {
+			commit = parent->item;
+			parent = parent->next;
+			if (commit->object.flags & REACHABLE)
+				continue;
+			commit_list_insert(commit, &pending);
+		}
+	}
+	cb->mark_list = leftover;
+}
+
+static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, struct object_id *oid)
+{
+	/*
+	 * We may or may not have the commit yet - if not, look it
+	 * up using the supplied sha1.
+	 */
+	if (!commit) {
+		if (is_null_oid(oid))
+			return 0;
+
+		commit = lookup_commit_reference_gently(the_repository, oid,
+							1);
+
+		/* Not a commit -- keep it */
+		if (!commit)
+			return 0;
+	}
+
+	/* Reachable from the current ref?  Don't prune. */
+	if (commit->object.flags & REACHABLE)
+		return 0;
+
+	if (cb->mark_list && cb->mark_limit) {
+		cb->mark_limit = 0; /* dig down to the root */
+		mark_reachable(cb);
+	}
+
+	return !(commit->object.flags & REACHABLE);
+}
+
+/*
+ * Return true iff the specified reflog entry should be expired.
+ */
+static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
+				    const char *email, timestamp_t timestamp, int tz,
+				    const char *message, void *cb_data)
+{
+	struct expire_reflog_policy_cb *cb = cb_data;
+	struct commit *old_commit, *new_commit;
+
+	if (timestamp < cb->cmd.expire_total)
+		return 1;
+
+	old_commit = new_commit = NULL;
+	if (cb->cmd.stalefix &&
+	    (!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid)))
+		return 1;
+
+	if (timestamp < cb->cmd.expire_unreachable) {
+		if (cb->unreachable_expire_kind == UE_ALWAYS)
+			return 1;
+		if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
+			return 1;
+	}
+
+	if (cb->cmd.recno && --(cb->cmd.recno) == 0)
+		return 1;
+
+	return 0;
+}
+
+static int push_tip_to_list(const char *refname, const struct object_id *oid,
+			    int flags, void *cb_data)
+{
+	struct commit_list **list = cb_data;
+	struct commit *tip_commit;
+	if (flags & REF_ISSYMREF)
+		return 0;
+	tip_commit = lookup_commit_reference_gently(the_repository, oid, 1);
+	if (!tip_commit)
+		return 0;
+	commit_list_insert(tip_commit, list);
+	return 0;
+}
+
+static int is_head(const char *refname)
+{
+	switch (ref_type(refname)) {
+	case REF_TYPE_OTHER_PSEUDOREF:
+	case REF_TYPE_MAIN_PSEUDOREF:
+		if (parse_worktree_ref(refname, NULL, NULL, &refname))
+			BUG("not a worktree ref: %s", refname);
+		break;
+	default:
+		break;
+	}
+	return !strcmp(refname, "HEAD");
+}
+
+static void reflog_expiry_prepare(const char *refname,
+				  const struct object_id *oid,
+				  void *cb_data)
+{
+	struct expire_reflog_policy_cb *cb = cb_data;
+
+	if (!cb->cmd.expire_unreachable || is_head(refname)) {
+		cb->tip_commit = NULL;
+		cb->unreachable_expire_kind = UE_HEAD;
+	} else {
+		cb->tip_commit = lookup_commit_reference_gently(the_repository,
+								oid, 1);
+		if (!cb->tip_commit)
+			cb->unreachable_expire_kind = UE_ALWAYS;
+		else
+			cb->unreachable_expire_kind = UE_NORMAL;
+	}
+
+	if (cb->cmd.expire_unreachable <= cb->cmd.expire_total)
+		cb->unreachable_expire_kind = UE_ALWAYS;
+
+	cb->mark_list = NULL;
+	cb->tips = NULL;
+	if (cb->unreachable_expire_kind != UE_ALWAYS) {
+		if (cb->unreachable_expire_kind == UE_HEAD) {
+			struct commit_list *elem;
+
+			for_each_ref(push_tip_to_list, &cb->tips);
+			for (elem = cb->tips; elem; elem = elem->next)
+				commit_list_insert(elem->item, &cb->mark_list);
+		} else {
+			commit_list_insert(cb->tip_commit, &cb->mark_list);
+		}
+		cb->mark_limit = cb->cmd.expire_total;
+		mark_reachable(cb);
+	}
+}
+
+static void reflog_expiry_cleanup(void *cb_data)
+{
+	struct expire_reflog_policy_cb *cb = cb_data;
+
+	if (cb->unreachable_expire_kind != UE_ALWAYS) {
+		if (cb->unreachable_expire_kind == UE_HEAD) {
+			struct commit_list *elem;
+			for (elem = cb->tips; elem; elem = elem->next)
+				clear_commit_marks(elem->item, REACHABLE);
+			free_commit_list(cb->tips);
+		} else {
+			clear_commit_marks(cb->tip_commit, REACHABLE);
+		}
+	}
+}
+
+static int collect_reflog(const char *ref, const struct object_id *oid, int unused, void *cb_data)
+{
+	struct collected_reflog *e;
+	struct collect_reflog_cb *cb = cb_data;
+	struct strbuf newref = STRBUF_INIT;
+
+	/*
+	 * Avoid collecting the same shared ref multiple times because
+	 * they are available via all worktrees.
+	 */
+	if (!cb->wt->is_current && ref_type(ref) == REF_TYPE_NORMAL)
+		return 0;
+
+	strbuf_worktree_ref(cb->wt, &newref, ref);
+	FLEX_ALLOC_STR(e, reflog, newref.buf);
+	strbuf_release(&newref);
+
+	oidcpy(&e->oid, oid);
+	ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
+	cb->e[cb->nr++] = e;
+	return 0;
+}
+
+static struct reflog_expire_cfg {
+	struct reflog_expire_cfg *next;
+	timestamp_t expire_total;
+	timestamp_t expire_unreachable;
+	char pattern[FLEX_ARRAY];
+} *reflog_expire_cfg, **reflog_expire_cfg_tail;
+
+static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
+{
+	struct reflog_expire_cfg *ent;
+
+	if (!reflog_expire_cfg_tail)
+		reflog_expire_cfg_tail = &reflog_expire_cfg;
+
+	for (ent = reflog_expire_cfg; ent; ent = ent->next)
+		if (!strncmp(ent->pattern, pattern, len) &&
+		    ent->pattern[len] == '\0')
+			return ent;
+
+	FLEX_ALLOC_MEM(ent, pattern, pattern, len);
+	*reflog_expire_cfg_tail = ent;
+	reflog_expire_cfg_tail = &(ent->next);
+	return ent;
+}
+
+/* expiry timer slot */
+#define EXPIRE_TOTAL   01
+#define EXPIRE_UNREACH 02
+
+static int reflog_expire_config(const char *var, const char *value, void *cb)
+{
+	const char *pattern, *key;
+	int pattern_len;
+	timestamp_t expire;
+	int slot;
+	struct reflog_expire_cfg *ent;
+
+	if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
+		return git_default_config(var, value, cb);
+
+	if (!strcmp(key, "reflogexpire")) {
+		slot = EXPIRE_TOTAL;
+		if (git_config_expiry_date(&expire, var, value))
+			return -1;
+	} else if (!strcmp(key, "reflogexpireunreachable")) {
+		slot = EXPIRE_UNREACH;
+		if (git_config_expiry_date(&expire, var, value))
+			return -1;
+	} else
+		return git_default_config(var, value, cb);
+
+	if (!pattern) {
+		switch (slot) {
+		case EXPIRE_TOTAL:
+			default_reflog_expire = expire;
+			break;
+		case EXPIRE_UNREACH:
+			default_reflog_expire_unreachable = expire;
+			break;
+		}
+		return 0;
+	}
+
+	ent = find_cfg_ent(pattern, pattern_len);
+	if (!ent)
+		return -1;
+	switch (slot) {
+	case EXPIRE_TOTAL:
+		ent->expire_total = expire;
+		break;
+	case EXPIRE_UNREACH:
+		ent->expire_unreachable = expire;
+		break;
+	}
+	return 0;
+}
+
+static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, const char *ref)
+{
+	struct reflog_expire_cfg *ent;
+
+	if (slot == (EXPIRE_TOTAL|EXPIRE_UNREACH))
+		return; /* both given explicitly -- nothing to tweak */
+
+	for (ent = reflog_expire_cfg; ent; ent = ent->next) {
+		if (!wildmatch(ent->pattern, ref, 0)) {
+			if (!(slot & EXPIRE_TOTAL))
+				cb->expire_total = ent->expire_total;
+			if (!(slot & EXPIRE_UNREACH))
+				cb->expire_unreachable = ent->expire_unreachable;
+			return;
+		}
+	}
+
+	/*
+	 * If unconfigured, make stash never expire
+	 */
+	if (!strcmp(ref, "refs/stash")) {
+		if (!(slot & EXPIRE_TOTAL))
+			cb->expire_total = 0;
+		if (!(slot & EXPIRE_UNREACH))
+			cb->expire_unreachable = 0;
+		return;
+	}
+
+	/* Nothing matched -- use the default value */
+	if (!(slot & EXPIRE_TOTAL))
+		cb->expire_total = default_reflog_expire;
+	if (!(slot & EXPIRE_UNREACH))
+		cb->expire_unreachable = default_reflog_expire_unreachable;
+}
+
+static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
+{
+	struct expire_reflog_policy_cb cb;
+	timestamp_t now = time(NULL);
+	int i, status, do_all, all_worktrees = 1;
+	int explicit_expiry = 0;
+	unsigned int flags = 0;
+
+	default_reflog_expire_unreachable = now - 30 * 24 * 3600;
+	default_reflog_expire = now - 90 * 24 * 3600;
+	git_config(reflog_expire_config, NULL);
+
+	save_commit_buffer = 0;
+	do_all = status = 0;
+	memset(&cb, 0, sizeof(cb));
+
+	cb.cmd.expire_total = default_reflog_expire;
+	cb.cmd.expire_unreachable = default_reflog_expire_unreachable;
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
+			flags |= EXPIRE_REFLOGS_DRY_RUN;
+		else if (starts_with(arg, "--expire=")) {
+			if (parse_expiry_date(arg + 9, &cb.cmd.expire_total))
+				die(_("'%s' is not a valid timestamp"), arg);
+			explicit_expiry |= EXPIRE_TOTAL;
+		}
+		else if (starts_with(arg, "--expire-unreachable=")) {
+			if (parse_expiry_date(arg + 21, &cb.cmd.expire_unreachable))
+				die(_("'%s' is not a valid timestamp"), arg);
+			explicit_expiry |= EXPIRE_UNREACH;
+		}
+		else if (!strcmp(arg, "--stale-fix"))
+			cb.cmd.stalefix = 1;
+		else if (!strcmp(arg, "--rewrite"))
+			flags |= EXPIRE_REFLOGS_REWRITE;
+		else if (!strcmp(arg, "--updateref"))
+			flags |= EXPIRE_REFLOGS_UPDATE_REF;
+		else if (!strcmp(arg, "--all"))
+			do_all = 1;
+		else if (!strcmp(arg, "--single-worktree"))
+			all_worktrees = 0;
+		else if (!strcmp(arg, "--verbose"))
+			flags |= EXPIRE_REFLOGS_VERBOSE;
+		else if (!strcmp(arg, "--")) {
+			i++;
+			break;
+		}
+		else if (arg[0] == '-')
+			usage(_(reflog_expire_usage));
+		else
+			break;
+	}
+
+	/*
+	 * We can trust the commits and objects reachable from refs
+	 * even in older repository.  We cannot trust what's reachable
+	 * from reflog if the repository was pruned with older git.
+	 */
+	if (cb.cmd.stalefix) {
+		repo_init_revisions(the_repository, &cb.cmd.revs, prefix);
+		if (flags & EXPIRE_REFLOGS_VERBOSE)
+			printf(_("Marking reachable objects..."));
+		mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL);
+		if (flags & EXPIRE_REFLOGS_VERBOSE)
+			putchar('\n');
+	}
+
+	if (do_all) {
+		struct collect_reflog_cb collected;
+		struct worktree **worktrees, **p;
+		int i;
+
+		memset(&collected, 0, sizeof(collected));
+		worktrees = get_worktrees(0);
+		for (p = worktrees; *p; p++) {
+			if (!all_worktrees && !(*p)->is_current)
+				continue;
+			collected.wt = *p;
+			refs_for_each_reflog(get_worktree_ref_store(*p),
+					     collect_reflog, &collected);
+		}
+		free_worktrees(worktrees);
+		for (i = 0; i < collected.nr; i++) {
+			struct collected_reflog *e = collected.e[i];
+			set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);
+			status |= reflog_expire(e->reflog, &e->oid, flags,
+						reflog_expiry_prepare,
+						should_expire_reflog_ent,
+						reflog_expiry_cleanup,
+						&cb);
+			free(e);
+		}
+		free(collected.e);
+	}
+
+	for (; i < argc; i++) {
+		char *ref;
+		struct object_id oid;
+		if (!dwim_log(argv[i], strlen(argv[i]), &oid, &ref)) {
+			status |= error(_("%s points nowhere!"), argv[i]);
+			continue;
+		}
+		set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref);
+		status |= reflog_expire(ref, &oid, flags,
+					reflog_expiry_prepare,
+					should_expire_reflog_ent,
+					reflog_expiry_cleanup,
+					&cb);
+	}
+	return status;
+}
+
+static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
+		const char *email, timestamp_t timestamp, int tz,
+		const char *message, void *cb_data)
+{
+	struct expire_reflog_policy_cb *cb = cb_data;
+	if (!cb->cmd.expire_total || timestamp < cb->cmd.expire_total)
+		cb->cmd.recno++;
+	return 0;
+}
+
+static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
+{
+	struct expire_reflog_policy_cb cb;
+	int i, status = 0;
+	unsigned int flags = 0;
+
+	memset(&cb, 0, sizeof(cb));
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
+			flags |= EXPIRE_REFLOGS_DRY_RUN;
+		else if (!strcmp(arg, "--rewrite"))
+			flags |= EXPIRE_REFLOGS_REWRITE;
+		else if (!strcmp(arg, "--updateref"))
+			flags |= EXPIRE_REFLOGS_UPDATE_REF;
+		else if (!strcmp(arg, "--verbose"))
+			flags |= EXPIRE_REFLOGS_VERBOSE;
+		else if (!strcmp(arg, "--")) {
+			i++;
+			break;
+		}
+		else if (arg[0] == '-')
+			usage(_(reflog_delete_usage));
+		else
+			break;
+	}
+
+	if (argc - i < 1)
+		return error(_("no reflog specified to delete"));
+
+	for ( ; i < argc; i++) {
+		const char *spec = strstr(argv[i], "@{");
+		struct object_id oid;
+		char *ep, *ref;
+		int recno;
+
+		if (!spec) {
+			status |= error(_("not a reflog: %s"), argv[i]);
+			continue;
+		}
+
+		if (!dwim_log(argv[i], spec - argv[i], &oid, &ref)) {
+			status |= error(_("no reflog for '%s'"), argv[i]);
+			continue;
+		}
+
+		recno = strtoul(spec + 2, &ep, 10);
+		if (*ep == '}') {
+			cb.cmd.recno = -recno;
+			for_each_reflog_ent(ref, count_reflog_ent, &cb);
+		} else {
+			cb.cmd.expire_total = approxidate(spec + 2);
+			for_each_reflog_ent(ref, count_reflog_ent, &cb);
+			cb.cmd.expire_total = 0;
+		}
+
+		status |= reflog_expire(ref, &oid, flags,
+					reflog_expiry_prepare,
+					should_expire_reflog_ent,
+					reflog_expiry_cleanup,
+					&cb);
+		free(ref);
+	}
+	return status;
+}
+
+static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)
+{
+	int i, start = 0;
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "--")) {
+			i++;
+			break;
+		}
+		else if (arg[0] == '-')
+			usage(_(reflog_exists_usage));
+		else
+			break;
+	}
+
+	start = i;
+
+	if (argc - start != 1)
+		usage(_(reflog_exists_usage));
+
+	if (check_refname_format(argv[start], REFNAME_ALLOW_ONELEVEL))
+		die(_("invalid ref format: %s"), argv[start]);
+	return !reflog_exists(argv[start]);
+}
+
+/*
+ * main "reflog"
+ */
+
+static const char reflog_usage[] =
+N_("git reflog [ show | expire | delete | exists ]");
+
+int cmd_reflog(int argc, const char **argv, const char *prefix)
+{
+	if (argc > 1 && !strcmp(argv[1], "-h"))
+		usage(_(reflog_usage));
+
+	/* With no command, we default to showing it. */
+	if (argc < 2 || *argv[1] == '-')
+		return cmd_log_reflog(argc, argv, prefix);
+
+	if (!strcmp(argv[1], "show"))
+		return cmd_log_reflog(argc - 1, argv + 1, prefix);
+
+	if (!strcmp(argv[1], "expire"))
+		return cmd_reflog_expire(argc - 1, argv + 1, prefix);
+
+	if (!strcmp(argv[1], "delete"))
+		return cmd_reflog_delete(argc - 1, argv + 1, prefix);
+
+	if (!strcmp(argv[1], "exists"))
+		return cmd_reflog_exists(argc - 1, argv + 1, prefix);
+
+	return cmd_log_reflog(argc, argv, prefix);
+}
diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c
new file mode 100644
index 0000000000..6a9127a33c
--- /dev/null
+++ b/builtin/remote-ext.c
@@ -0,0 +1,202 @@
+#include "builtin.h"
+#include "transport.h"
+#include "run-command.h"
+#include "pkt-line.h"
+
+static const char usage_msg[] =
+	"git remote-ext <remote> <url>";
+
+/*
+ * URL syntax:
+ *	'command [arg1 [arg2 [...]]]'	Invoke command with given arguments.
+ *	Special characters:
+ *	'% ': Literal space in argument.
+ *	'%%': Literal percent sign.
+ *	'%S': Name of service (git-upload-pack/git-upload-archive/
+ *		git-receive-pack.
+ *	'%s': Same as \s, but with possible git- prefix stripped.
+ *	'%G': Only allowed as first 'character' of argument. Do not pass this
+ *		Argument to command, instead send this as name of repository
+ *		in in-line git://-style request (also activates sending this
+ *		style of request).
+ *	'%V': Only allowed as first 'character' of argument. Used in
+ *		conjunction with '%G': Do not pass this argument to command,
+ *		instead send this as vhost in git://-style request (note: does
+ *		not activate sending git:// style request).
+ */
+
+static char *git_req;
+static char *git_req_vhost;
+
+static char *strip_escapes(const char *str, const char *service,
+	const char **next)
+{
+	size_t rpos = 0;
+	int escape = 0;
+	char special = 0;
+	const char *service_noprefix = service;
+	struct strbuf ret = STRBUF_INIT;
+
+	skip_prefix(service_noprefix, "git-", &service_noprefix);
+
+	/* Pass the service to command. */
+	setenv("GIT_EXT_SERVICE", service, 1);
+	setenv("GIT_EXT_SERVICE_NOPREFIX", service_noprefix, 1);
+
+	/* Scan the length of argument. */
+	while (str[rpos] && (escape || str[rpos] != ' ')) {
+		if (escape) {
+			switch (str[rpos]) {
+			case ' ':
+			case '%':
+			case 's':
+			case 'S':
+				break;
+			case 'G':
+			case 'V':
+				special = str[rpos];
+				if (rpos == 1)
+					break;
+				/* fallthrough */
+			default:
+				die("Bad remote-ext placeholder '%%%c'.",
+					str[rpos]);
+			}
+			escape = 0;
+		} else
+			escape = (str[rpos] == '%');
+		rpos++;
+	}
+	if (escape && !str[rpos])
+		die("remote-ext command has incomplete placeholder");
+	*next = str + rpos;
+	if (**next == ' ')
+		++*next;	/* Skip over space */
+
+	/*
+	 * Do the actual placeholder substitution. The string will be short
+	 * enough not to overflow integers.
+	 */
+	rpos = special ? 2 : 0;		/* Skip first 2 bytes in specials. */
+	escape = 0;
+	while (str[rpos] && (escape || str[rpos] != ' ')) {
+		if (escape) {
+			switch (str[rpos]) {
+			case ' ':
+			case '%':
+				strbuf_addch(&ret, str[rpos]);
+				break;
+			case 's':
+				strbuf_addstr(&ret, service_noprefix);
+				break;
+			case 'S':
+				strbuf_addstr(&ret, service);
+				break;
+			}
+			escape = 0;
+		} else
+			switch (str[rpos]) {
+			case '%':
+				escape = 1;
+				break;
+			default:
+				strbuf_addch(&ret, str[rpos]);
+				break;
+			}
+		rpos++;
+	}
+	switch (special) {
+	case 'G':
+		git_req = strbuf_detach(&ret, NULL);
+		return NULL;
+	case 'V':
+		git_req_vhost = strbuf_detach(&ret, NULL);
+		return NULL;
+	default:
+		return strbuf_detach(&ret, NULL);
+	}
+}
+
+static void parse_argv(struct argv_array *out, const char *arg, const char *service)
+{
+	while (*arg) {
+		char *expanded = strip_escapes(arg, service, &arg);
+		if (expanded)
+			argv_array_push(out, expanded);
+		free(expanded);
+	}
+}
+
+static void send_git_request(int stdin_fd, const char *serv, const char *repo,
+	const char *vhost)
+{
+	if (!vhost)
+		packet_write_fmt(stdin_fd, "%s %s%c", serv, repo, 0);
+	else
+		packet_write_fmt(stdin_fd, "%s %s%chost=%s%c", serv, repo, 0,
+			     vhost, 0);
+}
+
+static int run_child(const char *arg, const char *service)
+{
+	int r;
+	struct child_process child = CHILD_PROCESS_INIT;
+
+	child.in = -1;
+	child.out = -1;
+	child.err = 0;
+	parse_argv(&child.args, arg, service);
+
+	if (start_command(&child) < 0)
+		die("Can't run specified command");
+
+	if (git_req)
+		send_git_request(child.in, service, git_req, git_req_vhost);
+
+	r = bidirectional_transfer_loop(child.out, child.in);
+	if (!r)
+		r = finish_command(&child);
+	else
+		finish_command(&child);
+	return r;
+}
+
+#define MAXCOMMAND 4096
+
+static int command_loop(const char *child)
+{
+	char buffer[MAXCOMMAND];
+
+	while (1) {
+		size_t i;
+		if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
+			if (ferror(stdin))
+				die("Command input error");
+			exit(0);
+		}
+		/* Strip end of line characters. */
+		i = strlen(buffer);
+		while (i > 0 && isspace(buffer[i - 1]))
+			buffer[--i] = 0;
+
+		if (!strcmp(buffer, "capabilities")) {
+			printf("*connect\n\n");
+			fflush(stdout);
+		} else if (!strncmp(buffer, "connect ", 8)) {
+			printf("\n");
+			fflush(stdout);
+			return run_child(child, buffer + 8);
+		} else {
+			fprintf(stderr, "Bad command");
+			return 1;
+		}
+	}
+}
+
+int cmd_remote_ext(int argc, const char **argv, const char *prefix)
+{
+	if (argc != 3)
+		usage(usage_msg);
+
+	return command_loop(argv[2]);
+}
diff --git a/builtin/remote-fd.c b/builtin/remote-fd.c
new file mode 100644
index 0000000000..91dfe07e06
--- /dev/null
+++ b/builtin/remote-fd.c
@@ -0,0 +1,82 @@
+#include "builtin.h"
+#include "transport.h"
+
+static const char usage_msg[] =
+	"git remote-fd <remote> <url>";
+
+/*
+ * URL syntax:
+ *	'fd::<inoutfd>[/<anything>]'		Read/write socket pair
+ *						<inoutfd>.
+ *	'fd::<infd>,<outfd>[/<anything>]'	Read pipe <infd> and write
+ *						pipe <outfd>.
+ *	[foo] indicates 'foo' is optional. <anything> is any string.
+ *
+ * The data output to <outfd>/<inoutfd> should be passed unmolested to
+ * git-receive-pack/git-upload-pack/git-upload-archive and output of
+ * git-receive-pack/git-upload-pack/git-upload-archive should be passed
+ * unmolested to <infd>/<inoutfd>.
+ *
+ */
+
+#define MAXCOMMAND 4096
+
+static void command_loop(int input_fd, int output_fd)
+{
+	char buffer[MAXCOMMAND];
+
+	while (1) {
+		size_t i;
+		if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
+			if (ferror(stdin))
+				die("Input error");
+			return;
+		}
+		/* Strip end of line characters. */
+		i = strlen(buffer);
+		while (i > 0 && isspace(buffer[i - 1]))
+			buffer[--i] = 0;
+
+		if (!strcmp(buffer, "capabilities")) {
+			printf("*connect\n\n");
+			fflush(stdout);
+		} else if (!strncmp(buffer, "connect ", 8)) {
+			printf("\n");
+			fflush(stdout);
+			if (bidirectional_transfer_loop(input_fd,
+				output_fd))
+				die("Copying data between file descriptors failed");
+			return;
+		} else {
+			die("Bad command: %s", buffer);
+		}
+	}
+}
+
+int cmd_remote_fd(int argc, const char **argv, const char *prefix)
+{
+	int input_fd = -1;
+	int output_fd = -1;
+	char *end;
+
+	if (argc != 3)
+		usage(usage_msg);
+
+	input_fd = (int)strtoul(argv[2], &end, 10);
+
+	if ((end == argv[2]) || (*end != ',' && *end != '/' && *end))
+		die("Bad URL syntax");
+
+	if (*end == '/' || !*end) {
+		output_fd = input_fd;
+	} else {
+		char *end2;
+		output_fd = (int)strtoul(end + 1, &end2, 10);
+
+		if ((end2 == end + 1) || (*end2 != '/' && *end2))
+			die("Bad URL syntax");
+	}
+
+	command_loop(input_fd, output_fd);
+	return 0;
+}
diff --git a/builtin/remote.c b/builtin/remote.c
new file mode 100644
index 0000000000..5591cef775
--- /dev/null
+++ b/builtin/remote.c
@@ -0,0 +1,1646 @@
+#include "builtin.h"
+#include "config.h"
+#include "parse-options.h"
+#include "transport.h"
+#include "remote.h"
+#include "string-list.h"
+#include "strbuf.h"
+#include "run-command.h"
+#include "refs.h"
+#include "refspec.h"
+#include "object-store.h"
+#include "argv-array.h"
+#include "commit-reach.h"
+
+static const char * const builtin_remote_usage[] = {
+	N_("git remote [-v | --verbose]"),
+	N_("git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>"),
+	N_("git remote rename <old> <new>"),
+	N_("git remote remove <name>"),
+	N_("git remote set-head <name> (-a | --auto | -d | --delete | <branch>)"),
+	N_("git remote [-v | --verbose] show [-n] <name>"),
+	N_("git remote prune [-n | --dry-run] <name>"),
+	N_("git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]"),
+	N_("git remote set-branches [--add] <name> <branch>..."),
+	N_("git remote get-url [--push] [--all] <name>"),
+	N_("git remote set-url [--push] <name> <newurl> [<oldurl>]"),
+	N_("git remote set-url --add <name> <newurl>"),
+	N_("git remote set-url --delete <name> <url>"),
+	NULL
+};
+
+static const char * const builtin_remote_add_usage[] = {
+	N_("git remote add [<options>] <name> <url>"),
+	NULL
+};
+
+static const char * const builtin_remote_rename_usage[] = {
+	N_("git remote rename <old> <new>"),
+	NULL
+};
+
+static const char * const builtin_remote_rm_usage[] = {
+	N_("git remote remove <name>"),
+	NULL
+};
+
+static const char * const builtin_remote_sethead_usage[] = {
+	N_("git remote set-head <name> (-a | --auto | -d | --delete | <branch>)"),
+	NULL
+};
+
+static const char * const builtin_remote_setbranches_usage[] = {
+	N_("git remote set-branches <name> <branch>..."),
+	N_("git remote set-branches --add <name> <branch>..."),
+	NULL
+};
+
+static const char * const builtin_remote_show_usage[] = {
+	N_("git remote show [<options>] <name>"),
+	NULL
+};
+
+static const char * const builtin_remote_prune_usage[] = {
+	N_("git remote prune [<options>] <name>"),
+	NULL
+};
+
+static const char * const builtin_remote_update_usage[] = {
+	N_("git remote update [<options>] [<group> | <remote>]..."),
+	NULL
+};
+
+static const char * const builtin_remote_geturl_usage[] = {
+	N_("git remote get-url [--push] [--all] <name>"),
+	NULL
+};
+
+static const char * const builtin_remote_seturl_usage[] = {
+	N_("git remote set-url [--push] <name> <newurl> [<oldurl>]"),
+	N_("git remote set-url --add <name> <newurl>"),
+	N_("git remote set-url --delete <name> <url>"),
+	NULL
+};
+
+#define GET_REF_STATES (1<<0)
+#define GET_HEAD_NAMES (1<<1)
+#define GET_PUSH_REF_STATES (1<<2)
+
+static int verbose;
+
+static int fetch_remote(const char *name)
+{
+	const char *argv[] = { "fetch", name, NULL, NULL };
+	if (verbose) {
+		argv[1] = "-v";
+		argv[2] = name;
+	}
+	printf_ln(_("Updating %s"), name);
+	if (run_command_v_opt(argv, RUN_GIT_CMD))
+		return error(_("Could not fetch %s"), name);
+	return 0;
+}
+
+enum {
+	TAGS_UNSET = 0,
+	TAGS_DEFAULT = 1,
+	TAGS_SET = 2
+};
+
+#define MIRROR_NONE 0
+#define MIRROR_FETCH 1
+#define MIRROR_PUSH 2
+#define MIRROR_BOTH (MIRROR_FETCH|MIRROR_PUSH)
+
+static void add_branch(const char *key, const char *branchname,
+		       const char *remotename, int mirror, struct strbuf *tmp)
+{
+	strbuf_reset(tmp);
+	strbuf_addch(tmp, '+');
+	if (mirror)
+		strbuf_addf(tmp, "refs/%s:refs/%s",
+				branchname, branchname);
+	else
+		strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s",
+				branchname, remotename, branchname);
+	git_config_set_multivar(key, tmp->buf, "^$", 0);
+}
+
+static const char mirror_advice[] =
+N_("--mirror is dangerous and deprecated; please\n"
+   "\t use --mirror=fetch or --mirror=push instead");
+
+static int parse_mirror_opt(const struct option *opt, const char *arg, int not)
+{
+	unsigned *mirror = opt->value;
+	if (not)
+		*mirror = MIRROR_NONE;
+	else if (!arg) {
+		warning("%s", _(mirror_advice));
+		*mirror = MIRROR_BOTH;
+	}
+	else if (!strcmp(arg, "fetch"))
+		*mirror = MIRROR_FETCH;
+	else if (!strcmp(arg, "push"))
+		*mirror = MIRROR_PUSH;
+	else
+		return error(_("unknown mirror argument: %s"), arg);
+	return 0;
+}
+
+static int add(int argc, const char **argv)
+{
+	int fetch = 0, fetch_tags = TAGS_DEFAULT;
+	unsigned mirror = MIRROR_NONE;
+	struct string_list track = STRING_LIST_INIT_NODUP;
+	const char *master = NULL;
+	struct remote *remote;
+	struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
+	const char *name, *url;
+	int i;
+
+	struct option options[] = {
+		OPT_BOOL('f', "fetch", &fetch, N_("fetch the remote branches")),
+		OPT_SET_INT(0, "tags", &fetch_tags,
+			    N_("import all tags and associated objects when fetching"),
+			    TAGS_SET),
+		OPT_SET_INT(0, NULL, &fetch_tags,
+			    N_("or do not fetch any tag at all (--no-tags)"), TAGS_UNSET),
+		OPT_STRING_LIST('t', "track", &track, N_("branch"),
+				N_("branch(es) to track")),
+		OPT_STRING('m', "master", &master, N_("branch"), N_("master branch")),
+		{ OPTION_CALLBACK, 0, "mirror", &mirror, "(push|fetch)",
+			N_("set up remote as a mirror to push to or fetch from"),
+			PARSE_OPT_OPTARG | PARSE_OPT_COMP_ARG, parse_mirror_opt },
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, NULL, options, builtin_remote_add_usage,
+			     0);
+
+	if (argc != 2)
+		usage_with_options(builtin_remote_add_usage, options);
+
+	if (mirror && master)
+		die(_("specifying a master branch makes no sense with --mirror"));
+	if (mirror && !(mirror & MIRROR_FETCH) && track.nr)
+		die(_("specifying branches to track makes sense only with fetch mirrors"));
+
+	name = argv[0];
+	url = argv[1];
+
+	remote = remote_get(name);
+	if (remote_is_configured(remote, 1))
+		die(_("remote %s already exists."), name);
+
+	strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name);
+	if (!valid_fetch_refspec(buf2.buf))
+		die(_("'%s' is not a valid remote name"), name);
+
+	strbuf_addf(&buf, "remote.%s.url", name);
+	git_config_set(buf.buf, url);
+
+	if (!mirror || mirror & MIRROR_FETCH) {
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "remote.%s.fetch", name);
+		if (track.nr == 0)
+			string_list_append(&track, "*");
+		for (i = 0; i < track.nr; i++) {
+			add_branch(buf.buf, track.items[i].string,
+				   name, mirror, &buf2);
+		}
+	}
+
+	if (mirror & MIRROR_PUSH) {
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "remote.%s.mirror", name);
+		git_config_set(buf.buf, "true");
+	}
+
+	if (fetch_tags != TAGS_DEFAULT) {
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "remote.%s.tagopt", name);
+		git_config_set(buf.buf,
+			       fetch_tags == TAGS_SET ? "--tags" : "--no-tags");
+	}
+
+	if (fetch && fetch_remote(name))
+		return 1;
+
+	if (master) {
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "refs/remotes/%s/HEAD", name);
+
+		strbuf_reset(&buf2);
+		strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
+
+		if (create_symref(buf.buf, buf2.buf, "remote add"))
+			return error(_("Could not setup master '%s'"), master);
+	}
+
+	strbuf_release(&buf);
+	strbuf_release(&buf2);
+	string_list_clear(&track, 0);
+
+	return 0;
+}
+
+struct branch_info {
+	char *remote_name;
+	struct string_list merge;
+	enum {
+		NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE, REBASE_MERGES
+	} rebase;
+};
+
+static struct string_list branch_list = STRING_LIST_INIT_NODUP;
+
+static const char *abbrev_ref(const char *name, const char *prefix)
+{
+	skip_prefix(name, prefix, &name);
+	return name;
+}
+#define abbrev_branch(name) abbrev_ref((name), "refs/heads/")
+
+static int config_read_branches(const char *key, const char *value, void *cb)
+{
+	if (starts_with(key, "branch.")) {
+		const char *orig_key = key;
+		char *name;
+		struct string_list_item *item;
+		struct branch_info *info;
+		enum { REMOTE, MERGE, REBASE } type;
+		size_t key_len;
+
+		key += 7;
+		if (strip_suffix(key, ".remote", &key_len)) {
+			name = xmemdupz(key, key_len);
+			type = REMOTE;
+		} else if (strip_suffix(key, ".merge", &key_len)) {
+			name = xmemdupz(key, key_len);
+			type = MERGE;
+		} else if (strip_suffix(key, ".rebase", &key_len)) {
+			name = xmemdupz(key, key_len);
+			type = REBASE;
+		} else
+			return 0;
+
+		item = string_list_insert(&branch_list, name);
+
+		if (!item->util)
+			item->util = xcalloc(1, sizeof(struct branch_info));
+		info = item->util;
+		if (type == REMOTE) {
+			if (info->remote_name)
+				warning(_("more than one %s"), orig_key);
+			info->remote_name = xstrdup(value);
+		} else if (type == MERGE) {
+			char *space = strchr(value, ' ');
+			value = abbrev_branch(value);
+			while (space) {
+				char *merge;
+				merge = xstrndup(value, space - value);
+				string_list_append(&info->merge, merge);
+				value = abbrev_branch(space + 1);
+				space = strchr(value, ' ');
+			}
+			string_list_append(&info->merge, xstrdup(value));
+		} else {
+			int v = git_parse_maybe_bool(value);
+			if (v >= 0)
+				info->rebase = v;
+			else if (!strcmp(value, "preserve"))
+				info->rebase = NORMAL_REBASE;
+			else if (!strcmp(value, "merges"))
+				info->rebase = REBASE_MERGES;
+			else if (!strcmp(value, "interactive"))
+				info->rebase = INTERACTIVE_REBASE;
+		}
+	}
+	return 0;
+}
+
+static void read_branches(void)
+{
+	if (branch_list.nr)
+		return;
+	git_config(config_read_branches, NULL);
+}
+
+struct ref_states {
+	struct remote *remote;
+	struct string_list new_refs, stale, tracked, heads, push;
+	int queried;
+};
+
+static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
+{
+	struct ref *fetch_map = NULL, **tail = &fetch_map;
+	struct ref *ref, *stale_refs;
+	int i;
+
+	for (i = 0; i < states->remote->fetch.nr; i++)
+		if (get_fetch_map(remote_refs, &states->remote->fetch.items[i], &tail, 1))
+			die(_("Could not get fetch map for refspec %s"),
+				states->remote->fetch.raw[i]);
+
+	states->new_refs.strdup_strings = 1;
+	states->tracked.strdup_strings = 1;
+	states->stale.strdup_strings = 1;
+	for (ref = fetch_map; ref; ref = ref->next) {
+		if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
+			string_list_append(&states->new_refs, abbrev_branch(ref->name));
+		else
+			string_list_append(&states->tracked, abbrev_branch(ref->name));
+	}
+	stale_refs = get_stale_heads(&states->remote->fetch, fetch_map);
+	for (ref = stale_refs; ref; ref = ref->next) {
+		struct string_list_item *item =
+			string_list_append(&states->stale, abbrev_branch(ref->name));
+		item->util = xstrdup(ref->name);
+	}
+	free_refs(stale_refs);
+	free_refs(fetch_map);
+
+	string_list_sort(&states->new_refs);
+	string_list_sort(&states->tracked);
+	string_list_sort(&states->stale);
+
+	return 0;
+}
+
+struct push_info {
+	char *dest;
+	int forced;
+	enum {
+		PUSH_STATUS_CREATE = 0,
+		PUSH_STATUS_DELETE,
+		PUSH_STATUS_UPTODATE,
+		PUSH_STATUS_FASTFORWARD,
+		PUSH_STATUS_OUTOFDATE,
+		PUSH_STATUS_NOTQUERIED
+	} status;
+};
+
+static int get_push_ref_states(const struct ref *remote_refs,
+	struct ref_states *states)
+{
+	struct remote *remote = states->remote;
+	struct ref *ref, *local_refs, *push_map;
+	if (remote->mirror)
+		return 0;
+
+	local_refs = get_local_heads();
+	push_map = copy_ref_list(remote_refs);
+
+	match_push_refs(local_refs, &push_map, &remote->push, MATCH_REFS_NONE);
+
+	states->push.strdup_strings = 1;
+	for (ref = push_map; ref; ref = ref->next) {
+		struct string_list_item *item;
+		struct push_info *info;
+
+		if (!ref->peer_ref)
+			continue;
+		oidcpy(&ref->new_oid, &ref->peer_ref->new_oid);
+
+		item = string_list_append(&states->push,
+					  abbrev_branch(ref->peer_ref->name));
+		item->util = xcalloc(1, sizeof(struct push_info));
+		info = item->util;
+		info->forced = ref->force;
+		info->dest = xstrdup(abbrev_branch(ref->name));
+
+		if (is_null_oid(&ref->new_oid)) {
+			info->status = PUSH_STATUS_DELETE;
+		} else if (oideq(&ref->old_oid, &ref->new_oid))
+			info->status = PUSH_STATUS_UPTODATE;
+		else if (is_null_oid(&ref->old_oid))
+			info->status = PUSH_STATUS_CREATE;
+		else if (has_object_file(&ref->old_oid) &&
+			 ref_newer(&ref->new_oid, &ref->old_oid))
+			info->status = PUSH_STATUS_FASTFORWARD;
+		else
+			info->status = PUSH_STATUS_OUTOFDATE;
+	}
+	free_refs(local_refs);
+	free_refs(push_map);
+	return 0;
+}
+
+static int get_push_ref_states_noquery(struct ref_states *states)
+{
+	int i;
+	struct remote *remote = states->remote;
+	struct string_list_item *item;
+	struct push_info *info;
+
+	if (remote->mirror)
+		return 0;
+
+	states->push.strdup_strings = 1;
+	if (!remote->push.nr) {
+		item = string_list_append(&states->push, _("(matching)"));
+		info = item->util = xcalloc(1, sizeof(struct push_info));
+		info->status = PUSH_STATUS_NOTQUERIED;
+		info->dest = xstrdup(item->string);
+	}
+	for (i = 0; i < remote->push.nr; i++) {
+		const struct refspec_item *spec = &remote->push.items[i];
+		if (spec->matching)
+			item = string_list_append(&states->push, _("(matching)"));
+		else if (strlen(spec->src))
+			item = string_list_append(&states->push, spec->src);
+		else
+			item = string_list_append(&states->push, _("(delete)"));
+
+		info = item->util = xcalloc(1, sizeof(struct push_info));
+		info->forced = spec->force;
+		info->status = PUSH_STATUS_NOTQUERIED;
+		info->dest = xstrdup(spec->dst ? spec->dst : item->string);
+	}
+	return 0;
+}
+
+static int get_head_names(const struct ref *remote_refs, struct ref_states *states)
+{
+	struct ref *ref, *matches;
+	struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
+	struct refspec_item refspec;
+
+	refspec.force = 0;
+	refspec.pattern = 1;
+	refspec.src = refspec.dst = "refs/heads/*";
+	states->heads.strdup_strings = 1;
+	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
+	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
+				    fetch_map, 1);
+	for (ref = matches; ref; ref = ref->next)
+		string_list_append(&states->heads, abbrev_branch(ref->name));
+
+	free_refs(fetch_map);
+	free_refs(matches);
+
+	return 0;
+}
+
+struct known_remote {
+	struct known_remote *next;
+	struct remote *remote;
+};
+
+struct known_remotes {
+	struct remote *to_delete;
+	struct known_remote *list;
+};
+
+static int add_known_remote(struct remote *remote, void *cb_data)
+{
+	struct known_remotes *all = cb_data;
+	struct known_remote *r;
+
+	if (!strcmp(all->to_delete->name, remote->name))
+		return 0;
+
+	r = xmalloc(sizeof(*r));
+	r->remote = remote;
+	r->next = all->list;
+	all->list = r;
+	return 0;
+}
+
+struct branches_for_remote {
+	struct remote *remote;
+	struct string_list *branches, *skipped;
+	struct known_remotes *keep;
+};
+
+static int add_branch_for_removal(const char *refname,
+	const struct object_id *oid, int flags, void *cb_data)
+{
+	struct branches_for_remote *branches = cb_data;
+	struct refspec_item refspec;
+	struct known_remote *kr;
+
+	memset(&refspec, 0, sizeof(refspec));
+	refspec.dst = (char *)refname;
+	if (remote_find_tracking(branches->remote, &refspec))
+		return 0;
+
+	/* don't delete a branch if another remote also uses it */
+	for (kr = branches->keep->list; kr; kr = kr->next) {
+		memset(&refspec, 0, sizeof(refspec));
+		refspec.dst = (char *)refname;
+		if (!remote_find_tracking(kr->remote, &refspec))
+			return 0;
+	}
+
+	/* don't delete non-remote-tracking refs */
+	if (!starts_with(refname, "refs/remotes/")) {
+		/* advise user how to delete local branches */
+		if (starts_with(refname, "refs/heads/"))
+			string_list_append(branches->skipped,
+					   abbrev_branch(refname));
+		/* silently skip over other non-remote refs */
+		return 0;
+	}
+
+	string_list_append(branches->branches, refname);
+
+	return 0;
+}
+
+struct rename_info {
+	const char *old_name;
+	const char *new_name;
+	struct string_list *remote_branches;
+};
+
+static int read_remote_branches(const char *refname,
+	const struct object_id *oid, int flags, void *cb_data)
+{
+	struct rename_info *rename = cb_data;
+	struct strbuf buf = STRBUF_INIT;
+	struct string_list_item *item;
+	int flag;
+	const char *symref;
+
+	strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name);
+	if (starts_with(refname, buf.buf)) {
+		item = string_list_append(rename->remote_branches, refname);
+		symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
+					    NULL, &flag);
+		if (symref && (flag & REF_ISSYMREF))
+			item->util = xstrdup(symref);
+		else
+			item->util = NULL;
+	}
+	strbuf_release(&buf);
+
+	return 0;
+}
+
+static int migrate_file(struct remote *remote)
+{
+	struct strbuf buf = STRBUF_INIT;
+	int i;
+
+	strbuf_addf(&buf, "remote.%s.url", remote->name);
+	for (i = 0; i < remote->url_nr; i++)
+		git_config_set_multivar(buf.buf, remote->url[i], "^$", 0);
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "remote.%s.push", remote->name);
+	for (i = 0; i < remote->push.raw_nr; i++)
+		git_config_set_multivar(buf.buf, remote->push.raw[i], "^$", 0);
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "remote.%s.fetch", remote->name);
+	for (i = 0; i < remote->fetch.raw_nr; i++)
+		git_config_set_multivar(buf.buf, remote->fetch.raw[i], "^$", 0);
+	if (remote->origin == REMOTE_REMOTES)
+		unlink_or_warn(git_path("remotes/%s", remote->name));
+	else if (remote->origin == REMOTE_BRANCHES)
+		unlink_or_warn(git_path("branches/%s", remote->name));
+	strbuf_release(&buf);
+
+	return 0;
+}
+
+static int mv(int argc, const char **argv)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+	struct remote *oldremote, *newremote;
+	struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT,
+		old_remote_context = STRBUF_INIT;
+	struct string_list remote_branches = STRING_LIST_INIT_DUP;
+	struct rename_info rename;
+	int i, refspec_updated = 0;
+
+	if (argc != 3)
+		usage_with_options(builtin_remote_rename_usage, options);
+
+	rename.old_name = argv[1];
+	rename.new_name = argv[2];
+	rename.remote_branches = &remote_branches;
+
+	oldremote = remote_get(rename.old_name);
+	if (!remote_is_configured(oldremote, 1))
+		die(_("No such remote: '%s'"), rename.old_name);
+
+	if (!strcmp(rename.old_name, rename.new_name) && oldremote->origin != REMOTE_CONFIG)
+		return migrate_file(oldremote);
+
+	newremote = remote_get(rename.new_name);
+	if (remote_is_configured(newremote, 1))
+		die(_("remote %s already exists."), rename.new_name);
+
+	strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new_name);
+	if (!valid_fetch_refspec(buf.buf))
+		die(_("'%s' is not a valid remote name"), rename.new_name);
+
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "remote.%s", rename.old_name);
+	strbuf_addf(&buf2, "remote.%s", rename.new_name);
+	if (git_config_rename_section(buf.buf, buf2.buf) < 1)
+		return error(_("Could not rename config section '%s' to '%s'"),
+				buf.buf, buf2.buf);
+
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "remote.%s.fetch", rename.new_name);
+	git_config_set_multivar(buf.buf, NULL, NULL, 1);
+	strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name);
+	for (i = 0; i < oldremote->fetch.raw_nr; i++) {
+		char *ptr;
+
+		strbuf_reset(&buf2);
+		strbuf_addstr(&buf2, oldremote->fetch.raw[i]);
+		ptr = strstr(buf2.buf, old_remote_context.buf);
+		if (ptr) {
+			refspec_updated = 1;
+			strbuf_splice(&buf2,
+				      ptr-buf2.buf + strlen(":refs/remotes/"),
+				      strlen(rename.old_name), rename.new_name,
+				      strlen(rename.new_name));
+		} else
+			warning(_("Not updating non-default fetch refspec\n"
+				  "\t%s\n"
+				  "\tPlease update the configuration manually if necessary."),
+				buf2.buf);
+
+		git_config_set_multivar(buf.buf, buf2.buf, "^$", 0);
+	}
+
+	read_branches();
+	for (i = 0; i < branch_list.nr; i++) {
+		struct string_list_item *item = branch_list.items + i;
+		struct branch_info *info = item->util;
+		if (info->remote_name && !strcmp(info->remote_name, rename.old_name)) {
+			strbuf_reset(&buf);
+			strbuf_addf(&buf, "branch.%s.remote", item->string);
+			git_config_set(buf.buf, rename.new_name);
+		}
+	}
+
+	if (!refspec_updated)
+		return 0;
+
+	/*
+	 * First remove symrefs, then rename the rest, finally create
+	 * the new symrefs.
+	 */
+	for_each_ref(read_remote_branches, &rename);
+	for (i = 0; i < remote_branches.nr; i++) {
+		struct string_list_item *item = remote_branches.items + i;
+		int flag = 0;
+		struct object_id oid;
+
+		read_ref_full(item->string, RESOLVE_REF_READING, &oid, &flag);
+		if (!(flag & REF_ISSYMREF))
+			continue;
+		if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
+			die(_("deleting '%s' failed"), item->string);
+	}
+	for (i = 0; i < remote_branches.nr; i++) {
+		struct string_list_item *item = remote_branches.items + i;
+
+		if (item->util)
+			continue;
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, item->string);
+		strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name),
+				rename.new_name, strlen(rename.new_name));
+		strbuf_reset(&buf2);
+		strbuf_addf(&buf2, "remote: renamed %s to %s",
+				item->string, buf.buf);
+		if (rename_ref(item->string, buf.buf, buf2.buf))
+			die(_("renaming '%s' failed"), item->string);
+	}
+	for (i = 0; i < remote_branches.nr; i++) {
+		struct string_list_item *item = remote_branches.items + i;
+
+		if (!item->util)
+			continue;
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, item->string);
+		strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name),
+				rename.new_name, strlen(rename.new_name));
+		strbuf_reset(&buf2);
+		strbuf_addstr(&buf2, item->util);
+		strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old_name),
+				rename.new_name, strlen(rename.new_name));
+		strbuf_reset(&buf3);
+		strbuf_addf(&buf3, "remote: renamed %s to %s",
+				item->string, buf.buf);
+		if (create_symref(buf.buf, buf2.buf, buf3.buf))
+			die(_("creating '%s' failed"), buf.buf);
+	}
+	string_list_clear(&remote_branches, 1);
+	return 0;
+}
+
+static int rm(int argc, const char **argv)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+	struct remote *remote;
+	struct strbuf buf = STRBUF_INIT;
+	struct known_remotes known_remotes = { NULL, NULL };
+	struct string_list branches = STRING_LIST_INIT_DUP;
+	struct string_list skipped = STRING_LIST_INIT_DUP;
+	struct branches_for_remote cb_data;
+	int i, result;
+
+	memset(&cb_data, 0, sizeof(cb_data));
+	cb_data.branches = &branches;
+	cb_data.skipped = &skipped;
+	cb_data.keep = &known_remotes;
+
+	if (argc != 2)
+		usage_with_options(builtin_remote_rm_usage, options);
+
+	remote = remote_get(argv[1]);
+	if (!remote_is_configured(remote, 1))
+		die(_("No such remote: '%s'"), argv[1]);
+
+	known_remotes.to_delete = remote;
+	for_each_remote(add_known_remote, &known_remotes);
+
+	read_branches();
+	for (i = 0; i < branch_list.nr; i++) {
+		struct string_list_item *item = branch_list.items + i;
+		struct branch_info *info = item->util;
+		if (info->remote_name && !strcmp(info->remote_name, remote->name)) {
+			const char *keys[] = { "remote", "merge", NULL }, **k;
+			for (k = keys; *k; k++) {
+				strbuf_reset(&buf);
+				strbuf_addf(&buf, "branch.%s.%s",
+						item->string, *k);
+				result = git_config_set_gently(buf.buf, NULL);
+				if (result && result != CONFIG_NOTHING_SET)
+					die(_("could not unset '%s'"), buf.buf);
+			}
+		}
+	}
+
+	/*
+	 * We cannot just pass a function to for_each_ref() which deletes
+	 * the branches one by one, since for_each_ref() relies on cached
+	 * refs, which are invalidated when deleting a branch.
+	 */
+	cb_data.remote = remote;
+	result = for_each_ref(add_branch_for_removal, &cb_data);
+	strbuf_release(&buf);
+
+	if (!result)
+		result = delete_refs("remote: remove", &branches, REF_NO_DEREF);
+	string_list_clear(&branches, 0);
+
+	if (skipped.nr) {
+		fprintf_ln(stderr,
+			   Q_("Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
+			      "to delete it, use:",
+			      "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
+			      "to delete them, use:",
+			      skipped.nr));
+		for (i = 0; i < skipped.nr; i++)
+			fprintf(stderr, "  git branch -d %s\n",
+				skipped.items[i].string);
+	}
+	string_list_clear(&skipped, 0);
+
+	if (!result) {
+		strbuf_addf(&buf, "remote.%s", remote->name);
+		if (git_config_rename_section(buf.buf, NULL) < 1)
+			return error(_("Could not remove config section '%s'"), buf.buf);
+	}
+
+	return result;
+}
+
+static void clear_push_info(void *util, const char *string)
+{
+	struct push_info *info = util;
+	free(info->dest);
+	free(info);
+}
+
+static void free_remote_ref_states(struct ref_states *states)
+{
+	string_list_clear(&states->new_refs, 0);
+	string_list_clear(&states->stale, 1);
+	string_list_clear(&states->tracked, 0);
+	string_list_clear(&states->heads, 0);
+	string_list_clear_func(&states->push, clear_push_info);
+}
+
+static int append_ref_to_tracked_list(const char *refname,
+	const struct object_id *oid, int flags, void *cb_data)
+{
+	struct ref_states *states = cb_data;
+	struct refspec_item refspec;
+
+	if (flags & REF_ISSYMREF)
+		return 0;
+
+	memset(&refspec, 0, sizeof(refspec));
+	refspec.dst = (char *)refname;
+	if (!remote_find_tracking(states->remote, &refspec))
+		string_list_append(&states->tracked, abbrev_branch(refspec.src));
+
+	return 0;
+}
+
+static int get_remote_ref_states(const char *name,
+				 struct ref_states *states,
+				 int query)
+{
+	struct transport *transport;
+	const struct ref *remote_refs;
+
+	states->remote = remote_get(name);
+	if (!states->remote)
+		return error(_("No such remote: '%s'"), name);
+
+	read_branches();
+
+	if (query) {
+		transport = transport_get(states->remote, states->remote->url_nr > 0 ?
+			states->remote->url[0] : NULL);
+		remote_refs = transport_get_remote_refs(transport, NULL);
+		transport_disconnect(transport);
+
+		states->queried = 1;
+		if (query & GET_REF_STATES)
+			get_ref_states(remote_refs, states);
+		if (query & GET_HEAD_NAMES)
+			get_head_names(remote_refs, states);
+		if (query & GET_PUSH_REF_STATES)
+			get_push_ref_states(remote_refs, states);
+	} else {
+		for_each_ref(append_ref_to_tracked_list, states);
+		string_list_sort(&states->tracked);
+		get_push_ref_states_noquery(states);
+	}
+
+	return 0;
+}
+
+struct show_info {
+	struct string_list *list;
+	struct ref_states *states;
+	int width, width2;
+	int any_rebase;
+};
+
+static int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
+{
+	struct show_info *info = cb_data;
+	int n = strlen(item->string);
+	if (n > info->width)
+		info->width = n;
+	string_list_insert(info->list, item->string);
+	return 0;
+}
+
+static int show_remote_info_item(struct string_list_item *item, void *cb_data)
+{
+	struct show_info *info = cb_data;
+	struct ref_states *states = info->states;
+	const char *name = item->string;
+
+	if (states->queried) {
+		const char *fmt = "%s";
+		const char *arg = "";
+		if (string_list_has_string(&states->new_refs, name)) {
+			fmt = _(" new (next fetch will store in remotes/%s)");
+			arg = states->remote->name;
+		} else if (string_list_has_string(&states->tracked, name))
+			arg = _(" tracked");
+		else if (string_list_has_string(&states->stale, name))
+			arg = _(" stale (use 'git remote prune' to remove)");
+		else
+			arg = _(" ???");
+		printf("    %-*s", info->width, name);
+		printf(fmt, arg);
+		printf("\n");
+	} else
+		printf("    %s\n", name);
+
+	return 0;
+}
+
+static int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
+{
+	struct show_info *show_info = cb_data;
+	struct ref_states *states = show_info->states;
+	struct branch_info *branch_info = branch_item->util;
+	struct string_list_item *item;
+	int n;
+
+	if (!branch_info->merge.nr || !branch_info->remote_name ||
+	    strcmp(states->remote->name, branch_info->remote_name))
+		return 0;
+	if ((n = strlen(branch_item->string)) > show_info->width)
+		show_info->width = n;
+	if (branch_info->rebase)
+		show_info->any_rebase = 1;
+
+	item = string_list_insert(show_info->list, branch_item->string);
+	item->util = branch_info;
+
+	return 0;
+}
+
+static int show_local_info_item(struct string_list_item *item, void *cb_data)
+{
+	struct show_info *show_info = cb_data;
+	struct branch_info *branch_info = item->util;
+	struct string_list *merge = &branch_info->merge;
+	int width = show_info->width + 4;
+	int i;
+
+	if (branch_info->rebase && branch_info->merge.nr > 1) {
+		error(_("invalid branch.%s.merge; cannot rebase onto > 1 branch"),
+			item->string);
+		return 0;
+	}
+
+	printf("    %-*s ", show_info->width, item->string);
+	if (branch_info->rebase) {
+		const char *msg;
+		if (branch_info->rebase == INTERACTIVE_REBASE)
+			msg = _("rebases interactively onto remote %s");
+		else if (branch_info->rebase == REBASE_MERGES)
+			msg = _("rebases interactively (with merges) onto "
+				"remote %s");
+		else
+			msg = _("rebases onto remote %s");
+		printf_ln(msg, merge->items[0].string);
+		return 0;
+	} else if (show_info->any_rebase) {
+		printf_ln(_(" merges with remote %s"), merge->items[0].string);
+		width++;
+	} else {
+		printf_ln(_("merges with remote %s"), merge->items[0].string);
+	}
+	for (i = 1; i < merge->nr; i++)
+		printf(_("%-*s    and with remote %s\n"), width, "",
+		       merge->items[i].string);
+
+	return 0;
+}
+
+static int add_push_to_show_info(struct string_list_item *push_item, void *cb_data)
+{
+	struct show_info *show_info = cb_data;
+	struct push_info *push_info = push_item->util;
+	struct string_list_item *item;
+	int n;
+	if ((n = strlen(push_item->string)) > show_info->width)
+		show_info->width = n;
+	if ((n = strlen(push_info->dest)) > show_info->width2)
+		show_info->width2 = n;
+	item = string_list_append(show_info->list, push_item->string);
+	item->util = push_item->util;
+	return 0;
+}
+
+/*
+ * Sorting comparison for a string list that has push_info
+ * structs in its util field
+ */
+static int cmp_string_with_push(const void *va, const void *vb)
+{
+	const struct string_list_item *a = va;
+	const struct string_list_item *b = vb;
+	const struct push_info *a_push = a->util;
+	const struct push_info *b_push = b->util;
+	int cmp = strcmp(a->string, b->string);
+	return cmp ? cmp : strcmp(a_push->dest, b_push->dest);
+}
+
+static int show_push_info_item(struct string_list_item *item, void *cb_data)
+{
+	struct show_info *show_info = cb_data;
+	struct push_info *push_info = item->util;
+	const char *src = item->string, *status = NULL;
+
+	switch (push_info->status) {
+	case PUSH_STATUS_CREATE:
+		status = _("create");
+		break;
+	case PUSH_STATUS_DELETE:
+		status = _("delete");
+		src = _("(none)");
+		break;
+	case PUSH_STATUS_UPTODATE:
+		status = _("up to date");
+		break;
+	case PUSH_STATUS_FASTFORWARD:
+		status = _("fast-forwardable");
+		break;
+	case PUSH_STATUS_OUTOFDATE:
+		status = _("local out of date");
+		break;
+	case PUSH_STATUS_NOTQUERIED:
+		break;
+	}
+	if (status) {
+		if (push_info->forced)
+			printf_ln(_("    %-*s forces to %-*s (%s)"), show_info->width, src,
+			       show_info->width2, push_info->dest, status);
+		else
+			printf_ln(_("    %-*s pushes to %-*s (%s)"), show_info->width, src,
+			       show_info->width2, push_info->dest, status);
+	} else {
+		if (push_info->forced)
+			printf_ln(_("    %-*s forces to %s"), show_info->width, src,
+			       push_info->dest);
+		else
+			printf_ln(_("    %-*s pushes to %s"), show_info->width, src,
+			       push_info->dest);
+	}
+	return 0;
+}
+
+static int get_one_entry(struct remote *remote, void *priv)
+{
+	struct string_list *list = priv;
+	struct strbuf url_buf = STRBUF_INIT;
+	const char **url;
+	int i, url_nr;
+
+	if (remote->url_nr > 0) {
+		strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
+		string_list_append(list, remote->name)->util =
+				strbuf_detach(&url_buf, NULL);
+	} else
+		string_list_append(list, remote->name)->util = NULL;
+	if (remote->pushurl_nr) {
+		url = remote->pushurl;
+		url_nr = remote->pushurl_nr;
+	} else {
+		url = remote->url;
+		url_nr = remote->url_nr;
+	}
+	for (i = 0; i < url_nr; i++)
+	{
+		strbuf_addf(&url_buf, "%s (push)", url[i]);
+		string_list_append(list, remote->name)->util =
+				strbuf_detach(&url_buf, NULL);
+	}
+
+	return 0;
+}
+
+static int show_all(void)
+{
+	struct string_list list = STRING_LIST_INIT_NODUP;
+	int result;
+
+	list.strdup_strings = 1;
+	result = for_each_remote(get_one_entry, &list);
+
+	if (!result) {
+		int i;
+
+		string_list_sort(&list);
+		for (i = 0; i < list.nr; i++) {
+			struct string_list_item *item = list.items + i;
+			if (verbose)
+				printf("%s\t%s\n", item->string,
+					item->util ? (const char *)item->util : "");
+			else {
+				if (i && !strcmp((item - 1)->string, item->string))
+					continue;
+				printf("%s\n", item->string);
+			}
+		}
+	}
+	string_list_clear(&list, 1);
+	return result;
+}
+
+static int show(int argc, const char **argv)
+{
+	int no_query = 0, result = 0, query_flag = 0;
+	struct option options[] = {
+		OPT_BOOL('n', NULL, &no_query, N_("do not query remotes")),
+		OPT_END()
+	};
+	struct ref_states states;
+	struct string_list info_list = STRING_LIST_INIT_NODUP;
+	struct show_info info;
+
+	argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage,
+			     0);
+
+	if (argc < 1)
+		return show_all();
+
+	if (!no_query)
+		query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES);
+
+	memset(&states, 0, sizeof(states));
+	memset(&info, 0, sizeof(info));
+	info.states = &states;
+	info.list = &info_list;
+	for (; argc; argc--, argv++) {
+		int i;
+		const char **url;
+		int url_nr;
+
+		get_remote_ref_states(*argv, &states, query_flag);
+
+		printf_ln(_("* remote %s"), *argv);
+		printf_ln(_("  Fetch URL: %s"), states.remote->url_nr > 0 ?
+		       states.remote->url[0] : _("(no URL)"));
+		if (states.remote->pushurl_nr) {
+			url = states.remote->pushurl;
+			url_nr = states.remote->pushurl_nr;
+		} else {
+			url = states.remote->url;
+			url_nr = states.remote->url_nr;
+		}
+		for (i = 0; i < url_nr; i++)
+			/*
+			 * TRANSLATORS: the colon ':' should align
+			 * with the one in " Fetch URL: %s"
+			 * translation.
+			 */
+			printf_ln(_("  Push  URL: %s"), url[i]);
+		if (!i)
+			printf_ln(_("  Push  URL: %s"), _("(no URL)"));
+		if (no_query)
+			printf_ln(_("  HEAD branch: %s"), _("(not queried)"));
+		else if (!states.heads.nr)
+			printf_ln(_("  HEAD branch: %s"), _("(unknown)"));
+		else if (states.heads.nr == 1)
+			printf_ln(_("  HEAD branch: %s"), states.heads.items[0].string);
+		else {
+			printf(_("  HEAD branch (remote HEAD is ambiguous,"
+				 " may be one of the following):\n"));
+			for (i = 0; i < states.heads.nr; i++)
+				printf("    %s\n", states.heads.items[i].string);
+		}
+
+		/* remote branch info */
+		info.width = 0;
+		for_each_string_list(&states.new_refs, add_remote_to_show_info, &info);
+		for_each_string_list(&states.tracked, add_remote_to_show_info, &info);
+		for_each_string_list(&states.stale, add_remote_to_show_info, &info);
+		if (info.list->nr)
+			printf_ln(Q_("  Remote branch:%s",
+				     "  Remote branches:%s",
+				     info.list->nr),
+				  no_query ? _(" (status not queried)") : "");
+		for_each_string_list(info.list, show_remote_info_item, &info);
+		string_list_clear(info.list, 0);
+
+		/* git pull info */
+		info.width = 0;
+		info.any_rebase = 0;
+		for_each_string_list(&branch_list, add_local_to_show_info, &info);
+		if (info.list->nr)
+			printf_ln(Q_("  Local branch configured for 'git pull':",
+				     "  Local branches configured for 'git pull':",
+				     info.list->nr));
+		for_each_string_list(info.list, show_local_info_item, &info);
+		string_list_clear(info.list, 0);
+
+		/* git push info */
+		if (states.remote->mirror)
+			printf_ln(_("  Local refs will be mirrored by 'git push'"));
+
+		info.width = info.width2 = 0;
+		for_each_string_list(&states.push, add_push_to_show_info, &info);
+		QSORT(info.list->items, info.list->nr, cmp_string_with_push);
+		if (info.list->nr)
+			printf_ln(Q_("  Local ref configured for 'git push'%s:",
+				     "  Local refs configured for 'git push'%s:",
+				     info.list->nr),
+				  no_query ? _(" (status not queried)") : "");
+		for_each_string_list(info.list, show_push_info_item, &info);
+		string_list_clear(info.list, 0);
+
+		free_remote_ref_states(&states);
+	}
+
+	return result;
+}
+
+static int set_head(int argc, const char **argv)
+{
+	int i, opt_a = 0, opt_d = 0, result = 0;
+	struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
+	char *head_name = NULL;
+
+	struct option options[] = {
+		OPT_BOOL('a', "auto", &opt_a,
+			 N_("set refs/remotes/<name>/HEAD according to remote")),
+		OPT_BOOL('d', "delete", &opt_d,
+			 N_("delete refs/remotes/<name>/HEAD")),
+		OPT_END()
+	};
+	argc = parse_options(argc, argv, NULL, options, builtin_remote_sethead_usage,
+			     0);
+	if (argc)
+		strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
+
+	if (!opt_a && !opt_d && argc == 2) {
+		head_name = xstrdup(argv[1]);
+	} else if (opt_a && !opt_d && argc == 1) {
+		struct ref_states states;
+		memset(&states, 0, sizeof(states));
+		get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES);
+		if (!states.heads.nr)
+			result |= error(_("Cannot determine remote HEAD"));
+		else if (states.heads.nr > 1) {
+			result |= error(_("Multiple remote HEAD branches. "
+					  "Please choose one explicitly with:"));
+			for (i = 0; i < states.heads.nr; i++)
+				fprintf(stderr, "  git remote set-head %s %s\n",
+					argv[0], states.heads.items[i].string);
+		} else
+			head_name = xstrdup(states.heads.items[0].string);
+		free_remote_ref_states(&states);
+	} else if (opt_d && !opt_a && argc == 1) {
+		if (delete_ref(NULL, buf.buf, NULL, REF_NO_DEREF))
+			result |= error(_("Could not delete %s"), buf.buf);
+	} else
+		usage_with_options(builtin_remote_sethead_usage, options);
+
+	if (head_name) {
+		strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
+		/* make sure it's valid */
+		if (!ref_exists(buf2.buf))
+			result |= error(_("Not a valid ref: %s"), buf2.buf);
+		else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
+			result |= error(_("Could not setup %s"), buf.buf);
+		if (opt_a)
+			printf("%s/HEAD set to %s\n", argv[0], head_name);
+		free(head_name);
+	}
+
+	strbuf_release(&buf);
+	strbuf_release(&buf2);
+	return result;
+}
+
+static int prune_remote(const char *remote, int dry_run)
+{
+	int result = 0;
+	struct ref_states states;
+	struct string_list refs_to_prune = STRING_LIST_INIT_NODUP;
+	struct string_list_item *item;
+	const char *dangling_msg = dry_run
+		? _(" %s will become dangling!")
+		: _(" %s has become dangling!");
+
+	memset(&states, 0, sizeof(states));
+	get_remote_ref_states(remote, &states, GET_REF_STATES);
+
+	if (!states.stale.nr) {
+		free_remote_ref_states(&states);
+		return 0;
+	}
+
+	printf_ln(_("Pruning %s"), remote);
+	printf_ln(_("URL: %s"),
+		  states.remote->url_nr
+		  ? states.remote->url[0]
+		  : _("(no URL)"));
+
+	for_each_string_list_item(item, &states.stale)
+		string_list_append(&refs_to_prune, item->util);
+	string_list_sort(&refs_to_prune);
+
+	if (!dry_run)
+		result |= delete_refs("remote: prune", &refs_to_prune, 0);
+
+	for_each_string_list_item(item, &states.stale) {
+		const char *refname = item->util;
+
+		if (dry_run)
+			printf_ln(_(" * [would prune] %s"),
+			       abbrev_ref(refname, "refs/remotes/"));
+		else
+			printf_ln(_(" * [pruned] %s"),
+			       abbrev_ref(refname, "refs/remotes/"));
+	}
+
+	warn_dangling_symrefs(stdout, dangling_msg, &refs_to_prune);
+
+	string_list_clear(&refs_to_prune, 0);
+	free_remote_ref_states(&states);
+	return result;
+}
+
+static int prune(int argc, const char **argv)
+{
+	int dry_run = 0, result = 0;
+	struct option options[] = {
+		OPT__DRY_RUN(&dry_run, N_("dry run")),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage,
+			     0);
+
+	if (argc < 1)
+		usage_with_options(builtin_remote_prune_usage, options);
+
+	for (; argc; argc--, argv++)
+		result |= prune_remote(*argv, dry_run);
+
+	return result;
+}
+
+static int get_remote_default(const char *key, const char *value, void *priv)
+{
+	if (strcmp(key, "remotes.default") == 0) {
+		int *found = priv;
+		*found = 1;
+	}
+	return 0;
+}
+
+static int update(int argc, const char **argv)
+{
+	int i, prune = -1;
+	struct option options[] = {
+		OPT_BOOL('p', "prune", &prune,
+			 N_("prune remotes after fetching")),
+		OPT_END()
+	};
+	struct argv_array fetch_argv = ARGV_ARRAY_INIT;
+	int default_defined = 0;
+	int retval;
+
+	argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,
+			     PARSE_OPT_KEEP_ARGV0);
+
+	argv_array_push(&fetch_argv, "fetch");
+
+	if (prune != -1)
+		argv_array_push(&fetch_argv, prune ? "--prune" : "--no-prune");
+	if (verbose)
+		argv_array_push(&fetch_argv, "-v");
+	argv_array_push(&fetch_argv, "--multiple");
+	if (argc < 2)
+		argv_array_push(&fetch_argv, "default");
+	for (i = 1; i < argc; i++)
+		argv_array_push(&fetch_argv, argv[i]);
+
+	if (strcmp(fetch_argv.argv[fetch_argv.argc-1], "default") == 0) {
+		git_config(get_remote_default, &default_defined);
+		if (!default_defined) {
+			argv_array_pop(&fetch_argv);
+			argv_array_push(&fetch_argv, "--all");
+		}
+	}
+
+	retval = run_command_v_opt(fetch_argv.argv, RUN_GIT_CMD);
+	argv_array_clear(&fetch_argv);
+	return retval;
+}
+
+static int remove_all_fetch_refspecs(const char *key)
+{
+	return git_config_set_multivar_gently(key, NULL, NULL, 1);
+}
+
+static void add_branches(struct remote *remote, const char **branches,
+			 const char *key)
+{
+	const char *remotename = remote->name;
+	int mirror = remote->mirror;
+	struct strbuf refspec = STRBUF_INIT;
+
+	for (; *branches; branches++)
+		add_branch(key, *branches, remotename, mirror, &refspec);
+
+	strbuf_release(&refspec);
+}
+
+static int set_remote_branches(const char *remotename, const char **branches,
+				int add_mode)
+{
+	struct strbuf key = STRBUF_INIT;
+	struct remote *remote;
+
+	strbuf_addf(&key, "remote.%s.fetch", remotename);
+
+	remote = remote_get(remotename);
+	if (!remote_is_configured(remote, 1))
+		die(_("No such remote '%s'"), remotename);
+
+	if (!add_mode && remove_all_fetch_refspecs(key.buf)) {
+		strbuf_release(&key);
+		return 1;
+	}
+	add_branches(remote, branches, key.buf);
+
+	strbuf_release(&key);
+	return 0;
+}
+
+static int set_branches(int argc, const char **argv)
+{
+	int add_mode = 0;
+	struct option options[] = {
+		OPT_BOOL('\0', "add", &add_mode, N_("add branch")),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, NULL, options,
+			     builtin_remote_setbranches_usage, 0);
+	if (argc == 0) {
+		error(_("no remote specified"));
+		usage_with_options(builtin_remote_setbranches_usage, options);
+	}
+	argv[argc] = NULL;
+
+	return set_remote_branches(argv[0], argv + 1, add_mode);
+}
+
+static int get_url(int argc, const char **argv)
+{
+	int i, push_mode = 0, all_mode = 0;
+	const char *remotename = NULL;
+	struct remote *remote;
+	const char **url;
+	int url_nr;
+	struct option options[] = {
+		OPT_BOOL('\0', "push", &push_mode,
+			 N_("query push URLs rather than fetch URLs")),
+		OPT_BOOL('\0', "all", &all_mode,
+			 N_("return all URLs")),
+		OPT_END()
+	};
+	argc = parse_options(argc, argv, NULL, options, builtin_remote_geturl_usage, 0);
+
+	if (argc != 1)
+		usage_with_options(builtin_remote_geturl_usage, options);
+
+	remotename = argv[0];
+
+	remote = remote_get(remotename);
+	if (!remote_is_configured(remote, 1))
+		die(_("No such remote '%s'"), remotename);
+
+	url_nr = 0;
+	if (push_mode) {
+		url = remote->pushurl;
+		url_nr = remote->pushurl_nr;
+	}
+	/* else fetch mode */
+
+	/* Use the fetch URL when no push URLs were found or requested. */
+	if (!url_nr) {
+		url = remote->url;
+		url_nr = remote->url_nr;
+	}
+
+	if (!url_nr)
+		die(_("no URLs configured for remote '%s'"), remotename);
+
+	if (all_mode) {
+		for (i = 0; i < url_nr; i++)
+			printf_ln("%s", url[i]);
+	} else {
+		printf_ln("%s", *url);
+	}
+
+	return 0;
+}
+
+static int set_url(int argc, const char **argv)
+{
+	int i, push_mode = 0, add_mode = 0, delete_mode = 0;
+	int matches = 0, negative_matches = 0;
+	const char *remotename = NULL;
+	const char *newurl = NULL;
+	const char *oldurl = NULL;
+	struct remote *remote;
+	regex_t old_regex;
+	const char **urlset;
+	int urlset_nr;
+	struct strbuf name_buf = STRBUF_INIT;
+	struct option options[] = {
+		OPT_BOOL('\0', "push", &push_mode,
+			 N_("manipulate push URLs")),
+		OPT_BOOL('\0', "add", &add_mode,
+			 N_("add URL")),
+		OPT_BOOL('\0', "delete", &delete_mode,
+			    N_("delete URLs")),
+		OPT_END()
+	};
+	argc = parse_options(argc, argv, NULL, options, builtin_remote_seturl_usage,
+			     PARSE_OPT_KEEP_ARGV0);
+
+	if (add_mode && delete_mode)
+		die(_("--add --delete doesn't make sense"));
+
+	if (argc < 3 || argc > 4 || ((add_mode || delete_mode) && argc != 3))
+		usage_with_options(builtin_remote_seturl_usage, options);
+
+	remotename = argv[1];
+	newurl = argv[2];
+	if (argc > 3)
+		oldurl = argv[3];
+
+	if (delete_mode)
+		oldurl = newurl;
+
+	remote = remote_get(remotename);
+	if (!remote_is_configured(remote, 1))
+		die(_("No such remote '%s'"), remotename);
+
+	if (push_mode) {
+		strbuf_addf(&name_buf, "remote.%s.pushurl", remotename);
+		urlset = remote->pushurl;
+		urlset_nr = remote->pushurl_nr;
+	} else {
+		strbuf_addf(&name_buf, "remote.%s.url", remotename);
+		urlset = remote->url;
+		urlset_nr = remote->url_nr;
+	}
+
+	/* Special cases that add new entry. */
+	if ((!oldurl && !delete_mode) || add_mode) {
+		if (add_mode)
+			git_config_set_multivar(name_buf.buf, newurl,
+						       "^$", 0);
+		else
+			git_config_set(name_buf.buf, newurl);
+		goto out;
+	}
+
+	/* Old URL specified. Demand that one matches. */
+	if (regcomp(&old_regex, oldurl, REG_EXTENDED))
+		die(_("Invalid old URL pattern: %s"), oldurl);
+
+	for (i = 0; i < urlset_nr; i++)
+		if (!regexec(&old_regex, urlset[i], 0, NULL, 0))
+			matches++;
+		else
+			negative_matches++;
+	if (!delete_mode && !matches)
+		die(_("No such URL found: %s"), oldurl);
+	if (delete_mode && !negative_matches && !push_mode)
+		die(_("Will not delete all non-push URLs"));
+
+	regfree(&old_regex);
+
+	if (!delete_mode)
+		git_config_set_multivar(name_buf.buf, newurl, oldurl, 0);
+	else
+		git_config_set_multivar(name_buf.buf, NULL, oldurl, 1);
+out:
+	strbuf_release(&name_buf);
+	return 0;
+}
+
+int cmd_remote(int argc, const char **argv, const char *prefix)
+{
+	struct option options[] = {
+		OPT__VERBOSE(&verbose, N_("be verbose; must be placed before a subcommand")),
+		OPT_END()
+	};
+	int result;
+
+	argc = parse_options(argc, argv, prefix, options, builtin_remote_usage,
+		PARSE_OPT_STOP_AT_NON_OPTION);
+
+	if (argc < 1)
+		result = show_all();
+	else if (!strcmp(argv[0], "add"))
+		result = add(argc, argv);
+	else if (!strcmp(argv[0], "rename"))
+		result = mv(argc, argv);
+	else if (!strcmp(argv[0], "rm") || !strcmp(argv[0], "remove"))
+		result = rm(argc, argv);
+	else if (!strcmp(argv[0], "set-head"))
+		result = set_head(argc, argv);
+	else if (!strcmp(argv[0], "set-branches"))
+		result = set_branches(argc, argv);
+	else if (!strcmp(argv[0], "get-url"))
+		result = get_url(argc, argv);
+	else if (!strcmp(argv[0], "set-url"))
+		result = set_url(argc, argv);
+	else if (!strcmp(argv[0], "show"))
+		result = show(argc, argv);
+	else if (!strcmp(argv[0], "prune"))
+		result = prune(argc, argv);
+	else if (!strcmp(argv[0], "update"))
+		result = update(argc, argv);
+	else {
+		error(_("Unknown subcommand: %s"), argv[0]);
+		usage_with_options(builtin_remote_usage, options);
+	}
+
+	return result ? 1 : 0;
+}
diff --git a/builtin/repack.c b/builtin/repack.c
new file mode 100644
index 0000000000..632c0c0a79
--- /dev/null
+++ b/builtin/repack.c
@@ -0,0 +1,572 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "dir.h"
+#include "parse-options.h"
+#include "run-command.h"
+#include "sigchain.h"
+#include "strbuf.h"
+#include "string-list.h"
+#include "argv-array.h"
+#include "midx.h"
+#include "packfile.h"
+#include "object-store.h"
+
+static int delta_base_offset = 1;
+static int pack_kept_objects = -1;
+static int write_bitmaps = -1;
+static int use_delta_islands;
+static char *packdir, *packtmp;
+
+static const char *const git_repack_usage[] = {
+	N_("git repack [<options>]"),
+	NULL
+};
+
+static const char incremental_bitmap_conflict_error[] = N_(
+"Incremental repacks are incompatible with bitmap indexes.  Use\n"
+"--no-write-bitmap-index or disable the pack.writebitmaps configuration."
+);
+
+
+static int repack_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "repack.usedeltabaseoffset")) {
+		delta_base_offset = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "repack.packkeptobjects")) {
+		pack_kept_objects = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "repack.writebitmaps") ||
+	    !strcmp(var, "pack.writebitmaps")) {
+		write_bitmaps = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "repack.usedeltaislands")) {
+		use_delta_islands = git_config_bool(var, value);
+		return 0;
+	}
+	return git_default_config(var, value, cb);
+}
+
+/*
+ * Remove temporary $GIT_OBJECT_DIRECTORY/pack/.tmp-$$-pack-* files.
+ */
+static void remove_temporary_files(void)
+{
+	struct strbuf buf = STRBUF_INIT;
+	size_t dirlen, prefixlen;
+	DIR *dir;
+	struct dirent *e;
+
+	dir = opendir(packdir);
+	if (!dir)
+		return;
+
+	/* Point at the slash at the end of ".../objects/pack/" */
+	dirlen = strlen(packdir) + 1;
+	strbuf_addstr(&buf, packtmp);
+	/* Hold the length of  ".tmp-%d-pack-" */
+	prefixlen = buf.len - dirlen;
+
+	while ((e = readdir(dir))) {
+		if (strncmp(e->d_name, buf.buf + dirlen, prefixlen))
+			continue;
+		strbuf_setlen(&buf, dirlen);
+		strbuf_addstr(&buf, e->d_name);
+		unlink(buf.buf);
+	}
+	closedir(dir);
+	strbuf_release(&buf);
+}
+
+static void remove_pack_on_signal(int signo)
+{
+	remove_temporary_files();
+	sigchain_pop(signo);
+	raise(signo);
+}
+
+/*
+ * Adds all packs hex strings to the fname list, which do not
+ * have a corresponding .keep file. These packs are not to
+ * be kept if we are going to pack everything into one file.
+ */
+static void get_non_kept_pack_filenames(struct string_list *fname_list,
+					const struct string_list *extra_keep)
+{
+	DIR *dir;
+	struct dirent *e;
+	char *fname;
+
+	if (!(dir = opendir(packdir)))
+		return;
+
+	while ((e = readdir(dir)) != NULL) {
+		size_t len;
+		int i;
+
+		for (i = 0; i < extra_keep->nr; i++)
+			if (!fspathcmp(e->d_name, extra_keep->items[i].string))
+				break;
+		if (extra_keep->nr > 0 && i < extra_keep->nr)
+			continue;
+
+		if (!strip_suffix(e->d_name, ".pack", &len))
+			continue;
+
+		fname = xmemdupz(e->d_name, len);
+
+		if (!file_exists(mkpath("%s/%s.keep", packdir, fname)))
+			string_list_append_nodup(fname_list, fname);
+		else
+			free(fname);
+	}
+	closedir(dir);
+}
+
+static void remove_redundant_pack(const char *dir_name, const char *base_name)
+{
+	struct strbuf buf = STRBUF_INIT;
+	strbuf_addf(&buf, "%s/%s.pack", dir_name, base_name);
+	unlink_pack_path(buf.buf, 1);
+	strbuf_release(&buf);
+}
+
+struct pack_objects_args {
+	const char *window;
+	const char *window_memory;
+	const char *depth;
+	const char *threads;
+	const char *max_pack_size;
+	int no_reuse_delta;
+	int no_reuse_object;
+	int quiet;
+	int local;
+};
+
+static void prepare_pack_objects(struct child_process *cmd,
+				 const struct pack_objects_args *args)
+{
+	argv_array_push(&cmd->args, "pack-objects");
+	if (args->window)
+		argv_array_pushf(&cmd->args, "--window=%s", args->window);
+	if (args->window_memory)
+		argv_array_pushf(&cmd->args, "--window-memory=%s", args->window_memory);
+	if (args->depth)
+		argv_array_pushf(&cmd->args, "--depth=%s", args->depth);
+	if (args->threads)
+		argv_array_pushf(&cmd->args, "--threads=%s", args->threads);
+	if (args->max_pack_size)
+		argv_array_pushf(&cmd->args, "--max-pack-size=%s", args->max_pack_size);
+	if (args->no_reuse_delta)
+		argv_array_pushf(&cmd->args, "--no-reuse-delta");
+	if (args->no_reuse_object)
+		argv_array_pushf(&cmd->args, "--no-reuse-object");
+	if (args->local)
+		argv_array_push(&cmd->args,  "--local");
+	if (args->quiet)
+		argv_array_push(&cmd->args,  "--quiet");
+	if (delta_base_offset)
+		argv_array_push(&cmd->args,  "--delta-base-offset");
+	argv_array_push(&cmd->args, packtmp);
+	cmd->git_cmd = 1;
+	cmd->out = -1;
+}
+
+/*
+ * Write oid to the given struct child_process's stdin, starting it first if
+ * necessary.
+ */
+static int write_oid(const struct object_id *oid, struct packed_git *pack,
+		     uint32_t pos, void *data)
+{
+	struct child_process *cmd = data;
+
+	if (cmd->in == -1) {
+		if (start_command(cmd))
+			die(_("could not start pack-objects to repack promisor objects"));
+	}
+
+	xwrite(cmd->in, oid_to_hex(oid), GIT_SHA1_HEXSZ);
+	xwrite(cmd->in, "\n", 1);
+	return 0;
+}
+
+static void repack_promisor_objects(const struct pack_objects_args *args,
+				    struct string_list *names)
+{
+	struct child_process cmd = CHILD_PROCESS_INIT;
+	FILE *out;
+	struct strbuf line = STRBUF_INIT;
+
+	prepare_pack_objects(&cmd, args);
+	cmd.in = -1;
+
+	/*
+	 * NEEDSWORK: Giving pack-objects only the OIDs without any ordering
+	 * hints may result in suboptimal deltas in the resulting pack. See if
+	 * the OIDs can be sent with fake paths such that pack-objects can use a
+	 * {type -> existing pack order} ordering when computing deltas instead
+	 * of a {type -> size} ordering, which may produce better deltas.
+	 */
+	for_each_packed_object(write_oid, &cmd,
+			       FOR_EACH_OBJECT_PROMISOR_ONLY);
+
+	if (cmd.in == -1)
+		/* No packed objects; cmd was never started */
+		return;
+
+	close(cmd.in);
+
+	out = xfdopen(cmd.out, "r");
+	while (strbuf_getline_lf(&line, out) != EOF) {
+		char *promisor_name;
+		int fd;
+		if (line.len != the_hash_algo->hexsz)
+			die(_("repack: Expecting full hex object ID lines only from pack-objects."));
+		string_list_append(names, line.buf);
+
+		/*
+		 * pack-objects creates the .pack and .idx files, but not the
+		 * .promisor file. Create the .promisor file, which is empty.
+		 */
+		promisor_name = mkpathdup("%s-%s.promisor", packtmp,
+					  line.buf);
+		fd = open(promisor_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
+		if (fd < 0)
+			die_errno(_("unable to create '%s'"), promisor_name);
+		close(fd);
+		free(promisor_name);
+	}
+	fclose(out);
+	if (finish_command(&cmd))
+		die(_("could not finish pack-objects to repack promisor objects"));
+}
+
+#define ALL_INTO_ONE 1
+#define LOOSEN_UNREACHABLE 2
+
+int cmd_repack(int argc, const char **argv, const char *prefix)
+{
+	struct {
+		const char *name;
+		unsigned optional:1;
+	} exts[] = {
+		{".pack"},
+		{".idx"},
+		{".bitmap", 1},
+		{".promisor", 1},
+	};
+	struct child_process cmd = CHILD_PROCESS_INIT;
+	struct string_list_item *item;
+	struct string_list names = STRING_LIST_INIT_DUP;
+	struct string_list rollback = STRING_LIST_INIT_NODUP;
+	struct string_list existing_packs = STRING_LIST_INIT_DUP;
+	struct strbuf line = STRBUF_INIT;
+	int i, ext, ret, failed;
+	FILE *out;
+
+	/* variables to be filled by option parsing */
+	int pack_everything = 0;
+	int delete_redundant = 0;
+	const char *unpack_unreachable = NULL;
+	int keep_unreachable = 0;
+	struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
+	int no_update_server_info = 0;
+	int midx_cleared = 0;
+	struct pack_objects_args po_args = {NULL};
+
+	struct option builtin_repack_options[] = {
+		OPT_BIT('a', NULL, &pack_everything,
+				N_("pack everything in a single pack"), ALL_INTO_ONE),
+		OPT_BIT('A', NULL, &pack_everything,
+				N_("same as -a, and turn unreachable objects loose"),
+				   LOOSEN_UNREACHABLE | ALL_INTO_ONE),
+		OPT_BOOL('d', NULL, &delete_redundant,
+				N_("remove redundant packs, and run git-prune-packed")),
+		OPT_BOOL('f', NULL, &po_args.no_reuse_delta,
+				N_("pass --no-reuse-delta to git-pack-objects")),
+		OPT_BOOL('F', NULL, &po_args.no_reuse_object,
+				N_("pass --no-reuse-object to git-pack-objects")),
+		OPT_BOOL('n', NULL, &no_update_server_info,
+				N_("do not run git-update-server-info")),
+		OPT__QUIET(&po_args.quiet, N_("be quiet")),
+		OPT_BOOL('l', "local", &po_args.local,
+				N_("pass --local to git-pack-objects")),
+		OPT_BOOL('b', "write-bitmap-index", &write_bitmaps,
+				N_("write bitmap index")),
+		OPT_BOOL('i', "delta-islands", &use_delta_islands,
+				N_("pass --delta-islands to git-pack-objects")),
+		OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"),
+				N_("with -A, do not loosen objects older than this")),
+		OPT_BOOL('k', "keep-unreachable", &keep_unreachable,
+				N_("with -a, repack unreachable objects")),
+		OPT_STRING(0, "window", &po_args.window, N_("n"),
+				N_("size of the window used for delta compression")),
+		OPT_STRING(0, "window-memory", &po_args.window_memory, N_("bytes"),
+				N_("same as the above, but limit memory size instead of entries count")),
+		OPT_STRING(0, "depth", &po_args.depth, N_("n"),
+				N_("limits the maximum delta depth")),
+		OPT_STRING(0, "threads", &po_args.threads, N_("n"),
+				N_("limits the maximum number of threads")),
+		OPT_STRING(0, "max-pack-size", &po_args.max_pack_size, N_("bytes"),
+				N_("maximum size of each packfile")),
+		OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects,
+				N_("repack objects in packs marked with .keep")),
+		OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"),
+				N_("do not repack this pack")),
+		OPT_END()
+	};
+
+	git_config(repack_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, builtin_repack_options,
+				git_repack_usage, 0);
+
+	if (delete_redundant && repository_format_precious_objects)
+		die(_("cannot delete packs in a precious-objects repo"));
+
+	if (keep_unreachable &&
+	    (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
+		die(_("--keep-unreachable and -A are incompatible"));
+
+	if (write_bitmaps < 0) {
+		if (!(pack_everything & ALL_INTO_ONE) ||
+		    !is_bare_repository())
+			write_bitmaps = 0;
+	}
+	if (pack_kept_objects < 0)
+		pack_kept_objects = write_bitmaps > 0;
+
+	if (write_bitmaps && !(pack_everything & ALL_INTO_ONE))
+		die(_(incremental_bitmap_conflict_error));
+
+	packdir = mkpathdup("%s/pack", get_object_directory());
+	packtmp = mkpathdup("%s/.tmp-%d-pack", packdir, (int)getpid());
+
+	sigchain_push_common(remove_pack_on_signal);
+
+	prepare_pack_objects(&cmd, &po_args);
+
+	argv_array_push(&cmd.args, "--keep-true-parents");
+	if (!pack_kept_objects)
+		argv_array_push(&cmd.args, "--honor-pack-keep");
+	for (i = 0; i < keep_pack_list.nr; i++)
+		argv_array_pushf(&cmd.args, "--keep-pack=%s",
+				 keep_pack_list.items[i].string);
+	argv_array_push(&cmd.args, "--non-empty");
+	argv_array_push(&cmd.args, "--all");
+	argv_array_push(&cmd.args, "--reflog");
+	argv_array_push(&cmd.args, "--indexed-objects");
+	if (repository_format_partial_clone)
+		argv_array_push(&cmd.args, "--exclude-promisor-objects");
+	if (write_bitmaps > 0)
+		argv_array_push(&cmd.args, "--write-bitmap-index");
+	else if (write_bitmaps < 0)
+		argv_array_push(&cmd.args, "--write-bitmap-index-quiet");
+	if (use_delta_islands)
+		argv_array_push(&cmd.args, "--delta-islands");
+
+	if (pack_everything & ALL_INTO_ONE) {
+		get_non_kept_pack_filenames(&existing_packs, &keep_pack_list);
+
+		repack_promisor_objects(&po_args, &names);
+
+		if (existing_packs.nr && delete_redundant) {
+			if (unpack_unreachable) {
+				argv_array_pushf(&cmd.args,
+						"--unpack-unreachable=%s",
+						unpack_unreachable);
+				argv_array_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
+			} else if (pack_everything & LOOSEN_UNREACHABLE) {
+				argv_array_push(&cmd.args,
+						"--unpack-unreachable");
+			} else if (keep_unreachable) {
+				argv_array_push(&cmd.args, "--keep-unreachable");
+				argv_array_push(&cmd.args, "--pack-loose-unreachable");
+			} else {
+				argv_array_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
+			}
+		}
+	} else {
+		argv_array_push(&cmd.args, "--unpacked");
+		argv_array_push(&cmd.args, "--incremental");
+	}
+
+	cmd.no_stdin = 1;
+
+	ret = start_command(&cmd);
+	if (ret)
+		return ret;
+
+	out = xfdopen(cmd.out, "r");
+	while (strbuf_getline_lf(&line, out) != EOF) {
+		if (line.len != the_hash_algo->hexsz)
+			die(_("repack: Expecting full hex object ID lines only from pack-objects."));
+		string_list_append(&names, line.buf);
+	}
+	fclose(out);
+	ret = finish_command(&cmd);
+	if (ret)
+		return ret;
+
+	if (!names.nr && !po_args.quiet)
+		printf_ln(_("Nothing new to pack."));
+
+	close_object_store(the_repository->objects);
+
+	/*
+	 * Ok we have prepared all new packfiles.
+	 * First see if there are packs of the same name and if so
+	 * if we can move them out of the way (this can happen if we
+	 * repacked immediately after packing fully.
+	 */
+	failed = 0;
+	for_each_string_list_item(item, &names) {
+		for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
+			char *fname, *fname_old;
+
+			if (!midx_cleared) {
+				clear_midx_file(the_repository);
+				midx_cleared = 1;
+			}
+
+			fname = mkpathdup("%s/pack-%s%s", packdir,
+						item->string, exts[ext].name);
+			if (!file_exists(fname)) {
+				free(fname);
+				continue;
+			}
+
+			fname_old = mkpathdup("%s/old-%s%s", packdir,
+						item->string, exts[ext].name);
+			if (file_exists(fname_old))
+				if (unlink(fname_old))
+					failed = 1;
+
+			if (!failed && rename(fname, fname_old)) {
+				free(fname);
+				free(fname_old);
+				failed = 1;
+				break;
+			} else {
+				string_list_append(&rollback, fname);
+				free(fname_old);
+			}
+		}
+		if (failed)
+			break;
+	}
+	if (failed) {
+		struct string_list rollback_failure = STRING_LIST_INIT_DUP;
+		for_each_string_list_item(item, &rollback) {
+			char *fname, *fname_old;
+			fname = mkpathdup("%s/%s", packdir, item->string);
+			fname_old = mkpathdup("%s/old-%s", packdir, item->string);
+			if (rename(fname_old, fname))
+				string_list_append(&rollback_failure, fname);
+			free(fname);
+			free(fname_old);
+		}
+
+		if (rollback_failure.nr) {
+			int i;
+			fprintf(stderr,
+				_("WARNING: Some packs in use have been renamed by\n"
+				  "WARNING: prefixing old- to their name, in order to\n"
+				  "WARNING: replace them with the new version of the\n"
+				  "WARNING: file.  But the operation failed, and the\n"
+				  "WARNING: attempt to rename them back to their\n"
+				  "WARNING: original names also failed.\n"
+				  "WARNING: Please rename them in %s manually:\n"), packdir);
+			for (i = 0; i < rollback_failure.nr; i++)
+				fprintf(stderr, "WARNING:   old-%s -> %s\n",
+					rollback_failure.items[i].string,
+					rollback_failure.items[i].string);
+		}
+		exit(1);
+	}
+
+	/* Now the ones with the same name are out of the way... */
+	for_each_string_list_item(item, &names) {
+		for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
+			char *fname, *fname_old;
+			struct stat statbuffer;
+			int exists = 0;
+			fname = mkpathdup("%s/pack-%s%s",
+					packdir, item->string, exts[ext].name);
+			fname_old = mkpathdup("%s-%s%s",
+					packtmp, item->string, exts[ext].name);
+			if (!stat(fname_old, &statbuffer)) {
+				statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+				chmod(fname_old, statbuffer.st_mode);
+				exists = 1;
+			}
+			if (exists || !exts[ext].optional) {
+				if (rename(fname_old, fname))
+					die_errno(_("renaming '%s' failed"), fname_old);
+			}
+			free(fname);
+			free(fname_old);
+		}
+	}
+
+	/* Remove the "old-" files */
+	for_each_string_list_item(item, &names) {
+		for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
+			char *fname;
+			fname = mkpathdup("%s/old-%s%s",
+					  packdir,
+					  item->string,
+					  exts[ext].name);
+			if (remove_path(fname))
+				warning(_("failed to remove '%s'"), fname);
+			free(fname);
+		}
+	}
+
+	/* End of pack replacement. */
+
+	reprepare_packed_git(the_repository);
+
+	if (delete_redundant) {
+		const int hexsz = the_hash_algo->hexsz;
+		int opts = 0;
+		string_list_sort(&names);
+		for_each_string_list_item(item, &existing_packs) {
+			char *sha1;
+			size_t len = strlen(item->string);
+			if (len < hexsz)
+				continue;
+			sha1 = item->string + len - hexsz;
+			if (!string_list_has_string(&names, sha1))
+				remove_redundant_pack(packdir, item->string);
+		}
+		if (!po_args.quiet && isatty(2))
+			opts |= PRUNE_PACKED_VERBOSE;
+		prune_packed_objects(opts);
+
+		if (!keep_unreachable &&
+		    (!(pack_everything & LOOSEN_UNREACHABLE) ||
+		     unpack_unreachable) &&
+		    is_repository_shallow(the_repository))
+			prune_shallow(PRUNE_QUICK);
+	}
+
+	if (!no_update_server_info)
+		update_server_info(0);
+	remove_temporary_files();
+
+	if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0))
+		write_midx_file(get_object_directory());
+
+	string_list_clear(&names, 0);
+	string_list_clear(&rollback, 0);
+	string_list_clear(&existing_packs, 0);
+	strbuf_release(&line);
+
+	return 0;
+}
diff --git a/builtin/replace.c b/builtin/replace.c
new file mode 100644
index 0000000000..644b21ca8d
--- /dev/null
+++ b/builtin/replace.c
@@ -0,0 +1,622 @@
+/*
+ * Builtin "git replace"
+ *
+ * Copyright (c) 2008 Christian Couder <chriscool@tuxfamily.org>
+ *
+ * Based on builtin/tag.c by Kristian Høgsberg <krh@redhat.com>
+ * and Carlos Rica <jasampler@gmail.com> that was itself based on
+ * git-tag.sh and mktag.c by Linus Torvalds.
+ */
+
+#include "cache.h"
+#include "config.h"
+#include "builtin.h"
+#include "refs.h"
+#include "parse-options.h"
+#include "run-command.h"
+#include "object-store.h"
+#include "repository.h"
+#include "tag.h"
+
+static const char * const git_replace_usage[] = {
+	N_("git replace [-f] <object> <replacement>"),
+	N_("git replace [-f] --edit <object>"),
+	N_("git replace [-f] --graft <commit> [<parent>...]"),
+	N_("git replace [-f] --convert-graft-file"),
+	N_("git replace -d <object>..."),
+	N_("git replace [--format=<format>] [-l [<pattern>]]"),
+	NULL
+};
+
+enum replace_format {
+	REPLACE_FORMAT_SHORT,
+	REPLACE_FORMAT_MEDIUM,
+	REPLACE_FORMAT_LONG
+};
+
+struct show_data {
+	const char *pattern;
+	enum replace_format format;
+};
+
+static int show_reference(struct repository *r, const char *refname,
+			  const struct object_id *oid,
+			  int flag, void *cb_data)
+{
+	struct show_data *data = cb_data;
+
+	if (!wildmatch(data->pattern, refname, 0)) {
+		if (data->format == REPLACE_FORMAT_SHORT)
+			printf("%s\n", refname);
+		else if (data->format == REPLACE_FORMAT_MEDIUM)
+			printf("%s -> %s\n", refname, oid_to_hex(oid));
+		else { /* data->format == REPLACE_FORMAT_LONG */
+			struct object_id object;
+			enum object_type obj_type, repl_type;
+
+			if (get_oid(refname, &object))
+				return error(_("failed to resolve '%s' as a valid ref"), refname);
+
+			obj_type = oid_object_info(r, &object, NULL);
+			repl_type = oid_object_info(r, oid, NULL);
+
+			printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
+			       oid_to_hex(oid), type_name(repl_type));
+		}
+	}
+
+	return 0;
+}
+
+static int list_replace_refs(const char *pattern, const char *format)
+{
+	struct show_data data;
+
+	if (pattern == NULL)
+		pattern = "*";
+	data.pattern = pattern;
+
+	if (format == NULL || *format == '\0' || !strcmp(format, "short"))
+		data.format = REPLACE_FORMAT_SHORT;
+	else if (!strcmp(format, "medium"))
+		data.format = REPLACE_FORMAT_MEDIUM;
+	else if (!strcmp(format, "long"))
+		data.format = REPLACE_FORMAT_LONG;
+	/*
+	 * Please update _git_replace() in git-completion.bash when
+	 * you add new format
+	 */
+	else
+		return error(_("invalid replace format '%s'\n"
+			       "valid formats are 'short', 'medium' and 'long'"),
+			     format);
+
+	for_each_replace_ref(the_repository, show_reference, (void *)&data);
+
+	return 0;
+}
+
+typedef int (*each_replace_name_fn)(const char *name, const char *ref,
+				    const struct object_id *oid);
+
+static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
+{
+	const char **p, *full_hex;
+	struct strbuf ref = STRBUF_INIT;
+	size_t base_len;
+	int had_error = 0;
+	struct object_id oid;
+
+	strbuf_addstr(&ref, git_replace_ref_base);
+	base_len = ref.len;
+
+	for (p = argv; *p; p++) {
+		if (get_oid(*p, &oid)) {
+			error("failed to resolve '%s' as a valid ref", *p);
+			had_error = 1;
+			continue;
+		}
+
+		strbuf_setlen(&ref, base_len);
+		strbuf_addstr(&ref, oid_to_hex(&oid));
+		full_hex = ref.buf + base_len;
+
+		if (read_ref(ref.buf, &oid)) {
+			error(_("replace ref '%s' not found"), full_hex);
+			had_error = 1;
+			continue;
+		}
+		if (fn(full_hex, ref.buf, &oid))
+			had_error = 1;
+	}
+	strbuf_release(&ref);
+	return had_error;
+}
+
+static int delete_replace_ref(const char *name, const char *ref,
+			      const struct object_id *oid)
+{
+	if (delete_ref(NULL, ref, oid, 0))
+		return 1;
+	printf_ln(_("Deleted replace ref '%s'"), name);
+	return 0;
+}
+
+static int check_ref_valid(struct object_id *object,
+			    struct object_id *prev,
+			    struct strbuf *ref,
+			    int force)
+{
+	strbuf_reset(ref);
+	strbuf_addf(ref, "%s%s", git_replace_ref_base, oid_to_hex(object));
+	if (check_refname_format(ref->buf, 0))
+		return error(_("'%s' is not a valid ref name"), ref->buf);
+
+	if (read_ref(ref->buf, prev))
+		oidclr(prev);
+	else if (!force)
+		return error(_("replace ref '%s' already exists"), ref->buf);
+	return 0;
+}
+
+static int replace_object_oid(const char *object_ref,
+			       struct object_id *object,
+			       const char *replace_ref,
+			       struct object_id *repl,
+			       int force)
+{
+	struct object_id prev;
+	enum object_type obj_type, repl_type;
+	struct strbuf ref = STRBUF_INIT;
+	struct ref_transaction *transaction;
+	struct strbuf err = STRBUF_INIT;
+	int res = 0;
+
+	obj_type = oid_object_info(the_repository, object, NULL);
+	repl_type = oid_object_info(the_repository, repl, NULL);
+	if (!force && obj_type != repl_type)
+		return error(_("Objects must be of the same type.\n"
+			       "'%s' points to a replaced object of type '%s'\n"
+			       "while '%s' points to a replacement object of "
+			       "type '%s'."),
+			     object_ref, type_name(obj_type),
+			     replace_ref, type_name(repl_type));
+
+	if (check_ref_valid(object, &prev, &ref, force)) {
+		strbuf_release(&ref);
+		return -1;
+	}
+
+	transaction = ref_transaction_begin(&err);
+	if (!transaction ||
+	    ref_transaction_update(transaction, ref.buf, repl, &prev,
+				   0, NULL, &err) ||
+	    ref_transaction_commit(transaction, &err))
+		res = error("%s", err.buf);
+
+	ref_transaction_free(transaction);
+	strbuf_release(&ref);
+	return res;
+}
+
+static int replace_object(const char *object_ref, const char *replace_ref, int force)
+{
+	struct object_id object, repl;
+
+	if (get_oid(object_ref, &object))
+		return error(_("failed to resolve '%s' as a valid ref"),
+			     object_ref);
+	if (get_oid(replace_ref, &repl))
+		return error(_("failed to resolve '%s' as a valid ref"),
+			     replace_ref);
+
+	return replace_object_oid(object_ref, &object, replace_ref, &repl, force);
+}
+
+/*
+ * Write the contents of the object named by "sha1" to the file "filename".
+ * If "raw" is true, then the object's raw contents are printed according to
+ * "type". Otherwise, we pretty-print the contents for human editing.
+ */
+static int export_object(const struct object_id *oid, enum object_type type,
+			  int raw, const char *filename)
+{
+	struct child_process cmd = CHILD_PROCESS_INIT;
+	int fd;
+
+	fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+	if (fd < 0)
+		return error_errno(_("unable to open %s for writing"), filename);
+
+	argv_array_push(&cmd.args, "--no-replace-objects");
+	argv_array_push(&cmd.args, "cat-file");
+	if (raw)
+		argv_array_push(&cmd.args, type_name(type));
+	else
+		argv_array_push(&cmd.args, "-p");
+	argv_array_push(&cmd.args, oid_to_hex(oid));
+	cmd.git_cmd = 1;
+	cmd.out = fd;
+
+	if (run_command(&cmd))
+		return error(_("cat-file reported failure"));
+	return 0;
+}
+
+/*
+ * Read a previously-exported (and possibly edited) object back from "filename",
+ * interpreting it as "type", and writing the result to the object database.
+ * The sha1 of the written object is returned via sha1.
+ */
+static int import_object(struct object_id *oid, enum object_type type,
+			  int raw, const char *filename)
+{
+	int fd;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return error_errno(_("unable to open %s for reading"), filename);
+
+	if (!raw && type == OBJ_TREE) {
+		const char *argv[] = { "mktree", NULL };
+		struct child_process cmd = CHILD_PROCESS_INIT;
+		struct strbuf result = STRBUF_INIT;
+
+		cmd.argv = argv;
+		cmd.git_cmd = 1;
+		cmd.in = fd;
+		cmd.out = -1;
+
+		if (start_command(&cmd)) {
+			close(fd);
+			return error(_("unable to spawn mktree"));
+		}
+
+		if (strbuf_read(&result, cmd.out, 41) < 0) {
+			error_errno(_("unable to read from mktree"));
+			close(fd);
+			close(cmd.out);
+			return -1;
+		}
+		close(cmd.out);
+
+		if (finish_command(&cmd)) {
+			strbuf_release(&result);
+			return error(_("mktree reported failure"));
+		}
+		if (get_oid_hex(result.buf, oid) < 0) {
+			strbuf_release(&result);
+			return error(_("mktree did not return an object name"));
+		}
+
+		strbuf_release(&result);
+	} else {
+		struct stat st;
+		int flags = HASH_FORMAT_CHECK | HASH_WRITE_OBJECT;
+
+		if (fstat(fd, &st) < 0) {
+			error_errno(_("unable to fstat %s"), filename);
+			close(fd);
+			return -1;
+		}
+		if (index_fd(the_repository->index, oid, fd, &st, type, NULL, flags) < 0)
+			return error(_("unable to write object to database"));
+		/* index_fd close()s fd for us */
+	}
+
+	/*
+	 * No need to close(fd) here; both run-command and index-fd
+	 * will have done it for us.
+	 */
+	return 0;
+}
+
+static int edit_and_replace(const char *object_ref, int force, int raw)
+{
+	char *tmpfile;
+	enum object_type type;
+	struct object_id old_oid, new_oid, prev;
+	struct strbuf ref = STRBUF_INIT;
+
+	if (get_oid(object_ref, &old_oid) < 0)
+		return error(_("not a valid object name: '%s'"), object_ref);
+
+	type = oid_object_info(the_repository, &old_oid, NULL);
+	if (type < 0)
+		return error(_("unable to get object type for %s"),
+			     oid_to_hex(&old_oid));
+
+	if (check_ref_valid(&old_oid, &prev, &ref, force)) {
+		strbuf_release(&ref);
+		return -1;
+	}
+	strbuf_release(&ref);
+
+	tmpfile = git_pathdup("REPLACE_EDITOBJ");
+	if (export_object(&old_oid, type, raw, tmpfile)) {
+		free(tmpfile);
+		return -1;
+	}
+	if (launch_editor(tmpfile, NULL, NULL) < 0) {
+		free(tmpfile);
+		return error(_("editing object file failed"));
+	}
+	if (import_object(&new_oid, type, raw, tmpfile)) {
+		free(tmpfile);
+		return -1;
+	}
+	free(tmpfile);
+
+	if (oideq(&old_oid, &new_oid))
+		return error(_("new object is the same as the old one: '%s'"), oid_to_hex(&old_oid));
+
+	return replace_object_oid(object_ref, &old_oid, "replacement", &new_oid, force);
+}
+
+static int replace_parents(struct strbuf *buf, int argc, const char **argv)
+{
+	struct strbuf new_parents = STRBUF_INIT;
+	const char *parent_start, *parent_end;
+	int i;
+
+	/* find existing parents */
+	parent_start = buf->buf;
+	parent_start += GIT_SHA1_HEXSZ + 6; /* "tree " + "hex sha1" + "\n" */
+	parent_end = parent_start;
+
+	while (starts_with(parent_end, "parent "))
+		parent_end += 48; /* "parent " + "hex sha1" + "\n" */
+
+	/* prepare new parents */
+	for (i = 0; i < argc; i++) {
+		struct object_id oid;
+		struct commit *commit;
+
+		if (get_oid(argv[i], &oid) < 0) {
+			strbuf_release(&new_parents);
+			return error(_("not a valid object name: '%s'"),
+				     argv[i]);
+		}
+		commit = lookup_commit_reference(the_repository, &oid);
+		if (!commit) {
+			strbuf_release(&new_parents);
+			return error(_("could not parse %s as a commit"), argv[i]);
+		}
+		strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&commit->object.oid));
+	}
+
+	/* replace existing parents with new ones */
+	strbuf_splice(buf, parent_start - buf->buf, parent_end - parent_start,
+		      new_parents.buf, new_parents.len);
+
+	strbuf_release(&new_parents);
+	return 0;
+}
+
+struct check_mergetag_data {
+	int argc;
+	const char **argv;
+};
+
+static int check_one_mergetag(struct commit *commit,
+			       struct commit_extra_header *extra,
+			       void *data)
+{
+	struct check_mergetag_data *mergetag_data = (struct check_mergetag_data *)data;
+	const char *ref = mergetag_data->argv[0];
+	struct object_id tag_oid;
+	struct tag *tag;
+	int i;
+
+	hash_object_file(extra->value, extra->len, type_name(OBJ_TAG), &tag_oid);
+	tag = lookup_tag(the_repository, &tag_oid);
+	if (!tag)
+		return error(_("bad mergetag in commit '%s'"), ref);
+	if (parse_tag_buffer(the_repository, tag, extra->value, extra->len))
+		return error(_("malformed mergetag in commit '%s'"), ref);
+
+	/* iterate over new parents */
+	for (i = 1; i < mergetag_data->argc; i++) {
+		struct object_id oid;
+		if (get_oid(mergetag_data->argv[i], &oid) < 0)
+			return error(_("not a valid object name: '%s'"),
+				     mergetag_data->argv[i]);
+		if (oideq(&tag->tagged->oid, &oid))
+			return 0; /* found */
+	}
+
+	return error(_("original commit '%s' contains mergetag '%s' that is "
+		       "discarded; use --edit instead of --graft"), ref,
+		     oid_to_hex(&tag_oid));
+}
+
+static int check_mergetags(struct commit *commit, int argc, const char **argv)
+{
+	struct check_mergetag_data mergetag_data;
+
+	mergetag_data.argc = argc;
+	mergetag_data.argv = argv;
+	return for_each_mergetag(check_one_mergetag, commit, &mergetag_data);
+}
+
+static int create_graft(int argc, const char **argv, int force, int gentle)
+{
+	struct object_id old_oid, new_oid;
+	const char *old_ref = argv[0];
+	struct commit *commit;
+	struct strbuf buf = STRBUF_INIT;
+	const char *buffer;
+	unsigned long size;
+
+	if (get_oid(old_ref, &old_oid) < 0)
+		return error(_("not a valid object name: '%s'"), old_ref);
+	commit = lookup_commit_reference(the_repository, &old_oid);
+	if (!commit)
+		return error(_("could not parse %s"), old_ref);
+
+	buffer = get_commit_buffer(commit, &size);
+	strbuf_add(&buf, buffer, size);
+	unuse_commit_buffer(commit, buffer);
+
+	if (replace_parents(&buf, argc - 1, &argv[1]) < 0) {
+		strbuf_release(&buf);
+		return -1;
+	}
+
+	if (remove_signature(&buf)) {
+		warning(_("the original commit '%s' has a gpg signature"), old_ref);
+		warning(_("the signature will be removed in the replacement commit!"));
+	}
+
+	if (check_mergetags(commit, argc, argv)) {
+		strbuf_release(&buf);
+		return -1;
+	}
+
+	if (write_object_file(buf.buf, buf.len, commit_type, &new_oid)) {
+		strbuf_release(&buf);
+		return error(_("could not write replacement commit for: '%s'"),
+			     old_ref);
+	}
+
+	strbuf_release(&buf);
+
+	if (oideq(&commit->object.oid, &new_oid)) {
+		if (gentle) {
+			warning(_("graft for '%s' unnecessary"),
+				oid_to_hex(&commit->object.oid));
+			return 0;
+		}
+		return error(_("new commit is the same as the old one: '%s'"),
+			     oid_to_hex(&commit->object.oid));
+	}
+
+	return replace_object_oid(old_ref, &commit->object.oid,
+				  "replacement", &new_oid, force);
+}
+
+static int convert_graft_file(int force)
+{
+	const char *graft_file = get_graft_file(the_repository);
+	FILE *fp = fopen_or_warn(graft_file, "r");
+	struct strbuf buf = STRBUF_INIT, err = STRBUF_INIT;
+	struct argv_array args = ARGV_ARRAY_INIT;
+
+	if (!fp)
+		return -1;
+
+	advice_graft_file_deprecated = 0;
+	while (strbuf_getline(&buf, fp) != EOF) {
+		if (*buf.buf == '#')
+			continue;
+
+		argv_array_split(&args, buf.buf);
+		if (args.argc && create_graft(args.argc, args.argv, force, 1))
+			strbuf_addf(&err, "\n\t%s", buf.buf);
+		argv_array_clear(&args);
+	}
+	fclose(fp);
+
+	strbuf_release(&buf);
+
+	if (!err.len)
+		return unlink_or_warn(graft_file);
+
+	warning(_("could not convert the following graft(s):\n%s"), err.buf);
+	strbuf_release(&err);
+
+	return -1;
+}
+
+int cmd_replace(int argc, const char **argv, const char *prefix)
+{
+	int force = 0;
+	int raw = 0;
+	const char *format = NULL;
+	enum {
+		MODE_UNSPECIFIED = 0,
+		MODE_LIST,
+		MODE_DELETE,
+		MODE_EDIT,
+		MODE_GRAFT,
+		MODE_CONVERT_GRAFT_FILE,
+		MODE_REPLACE
+	} cmdmode = MODE_UNSPECIFIED;
+	struct option options[] = {
+		OPT_CMDMODE('l', "list", &cmdmode, N_("list replace refs"), MODE_LIST),
+		OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE),
+		OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT),
+		OPT_CMDMODE('g', "graft", &cmdmode, N_("change a commit's parents"), MODE_GRAFT),
+		OPT_CMDMODE(0, "convert-graft-file", &cmdmode, N_("convert existing graft file"), MODE_CONVERT_GRAFT_FILE),
+		OPT_BOOL_F('f', "force", &force, N_("replace the ref if it exists"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL(0, "raw", &raw, N_("do not pretty-print contents for --edit")),
+		OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
+		OPT_END()
+	};
+
+	read_replace_refs = 0;
+	git_config(git_default_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
+
+	if (!cmdmode)
+		cmdmode = argc ? MODE_REPLACE : MODE_LIST;
+
+	if (format && cmdmode != MODE_LIST)
+		usage_msg_opt(_("--format cannot be used when not listing"),
+			      git_replace_usage, options);
+
+	if (force &&
+	    cmdmode != MODE_REPLACE &&
+	    cmdmode != MODE_EDIT &&
+	    cmdmode != MODE_GRAFT &&
+	    cmdmode != MODE_CONVERT_GRAFT_FILE)
+		usage_msg_opt(_("-f only makes sense when writing a replacement"),
+			      git_replace_usage, options);
+
+	if (raw && cmdmode != MODE_EDIT)
+		usage_msg_opt(_("--raw only makes sense with --edit"),
+			      git_replace_usage, options);
+
+	switch (cmdmode) {
+	case MODE_DELETE:
+		if (argc < 1)
+			usage_msg_opt(_("-d needs at least one argument"),
+				      git_replace_usage, options);
+		return for_each_replace_name(argv, delete_replace_ref);
+
+	case MODE_REPLACE:
+		if (argc != 2)
+			usage_msg_opt(_("bad number of arguments"),
+				      git_replace_usage, options);
+		return replace_object(argv[0], argv[1], force);
+
+	case MODE_EDIT:
+		if (argc != 1)
+			usage_msg_opt(_("-e needs exactly one argument"),
+				      git_replace_usage, options);
+		return edit_and_replace(argv[0], force, raw);
+
+	case MODE_GRAFT:
+		if (argc < 1)
+			usage_msg_opt(_("-g needs at least one argument"),
+				      git_replace_usage, options);
+		return create_graft(argc, argv, force, 0);
+
+	case MODE_CONVERT_GRAFT_FILE:
+		if (argc != 0)
+			usage_msg_opt(_("--convert-graft-file takes no argument"),
+				      git_replace_usage, options);
+		return !!convert_graft_file(force);
+
+	case MODE_LIST:
+		if (argc > 1)
+			usage_msg_opt(_("only one pattern can be given with -l"),
+				      git_replace_usage, options);
+		return list_replace_refs(argv[0], format);
+
+	default:
+		BUG("invalid cmdmode %d", (int)cmdmode);
+	}
+}
diff --git a/builtin/rerere.c b/builtin/rerere.c
new file mode 100644
index 0000000000..fd3be17b97
--- /dev/null
+++ b/builtin/rerere.c
@@ -0,0 +1,120 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "dir.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "rerere.h"
+#include "xdiff/xdiff.h"
+#include "xdiff-interface.h"
+#include "pathspec.h"
+
+static const char * const rerere_usage[] = {
+	N_("git rerere [clear | forget <path>... | status | remaining | diff | gc]"),
+	NULL,
+};
+
+static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
+{
+	int i;
+	for (i = 0; i < nbuf; i++)
+		if (write_in_full(1, ptr[i].ptr, ptr[i].size) < 0)
+			return -1;
+	return 0;
+}
+
+static int diff_two(const char *file1, const char *label1,
+		const char *file2, const char *label2)
+{
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
+	xdemitcb_t ecb;
+	mmfile_t minus, plus;
+	int ret;
+
+	if (read_mmfile(&minus, file1) || read_mmfile(&plus, file2))
+		return -1;
+
+	printf("--- a/%s\n+++ b/%s\n", label1, label2);
+	fflush(stdout);
+	memset(&xpp, 0, sizeof(xpp));
+	xpp.flags = 0;
+	memset(&xecfg, 0, sizeof(xecfg));
+	xecfg.ctxlen = 3;
+	ecb.out_hunk = NULL;
+	ecb.out_line = outf;
+	ret = xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
+
+	free(minus.ptr);
+	free(plus.ptr);
+	return ret;
+}
+
+int cmd_rerere(int argc, const char **argv, const char *prefix)
+{
+	struct string_list merge_rr = STRING_LIST_INIT_DUP;
+	int i, autoupdate = -1, flags = 0;
+
+	struct option options[] = {
+		OPT_SET_INT(0, "rerere-autoupdate", &autoupdate,
+			N_("register clean resolutions in index"), 1),
+		OPT_END(),
+	};
+
+	argc = parse_options(argc, argv, prefix, options, rerere_usage, 0);
+
+	git_config(git_xmerge_config, NULL);
+
+	if (autoupdate == 1)
+		flags = RERERE_AUTOUPDATE;
+	if (autoupdate == 0)
+		flags = RERERE_NOAUTOUPDATE;
+
+	if (argc < 1)
+		return repo_rerere(the_repository, flags);
+
+	if (!strcmp(argv[0], "forget")) {
+		struct pathspec pathspec;
+		if (argc < 2)
+			warning(_("'git rerere forget' without paths is deprecated"));
+		parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD,
+			       prefix, argv + 1);
+		return rerere_forget(the_repository, &pathspec);
+	}
+
+	if (!strcmp(argv[0], "clear")) {
+		rerere_clear(the_repository, &merge_rr);
+	} else if (!strcmp(argv[0], "gc"))
+		rerere_gc(the_repository, &merge_rr);
+	else if (!strcmp(argv[0], "status")) {
+		if (setup_rerere(the_repository, &merge_rr,
+				 flags | RERERE_READONLY) < 0)
+			return 0;
+		for (i = 0; i < merge_rr.nr; i++)
+			printf("%s\n", merge_rr.items[i].string);
+	} else if (!strcmp(argv[0], "remaining")) {
+		rerere_remaining(the_repository, &merge_rr);
+		for (i = 0; i < merge_rr.nr; i++) {
+			if (merge_rr.items[i].util != RERERE_RESOLVED)
+				printf("%s\n", merge_rr.items[i].string);
+			else
+				/* prepare for later call to
+				 * string_list_clear() */
+				merge_rr.items[i].util = NULL;
+		}
+	} else if (!strcmp(argv[0], "diff")) {
+		if (setup_rerere(the_repository, &merge_rr,
+				 flags | RERERE_READONLY) < 0)
+			return 0;
+		for (i = 0; i < merge_rr.nr; i++) {
+			const char *path = merge_rr.items[i].string;
+			const struct rerere_id *id = merge_rr.items[i].util;
+			if (diff_two(rerere_path(id, "preimage"), path, path, path))
+				die(_("unable to generate diff for '%s'"), rerere_path(id, NULL));
+		}
+	} else
+		usage_with_options(rerere_usage, options);
+
+	string_list_clear(&merge_rr, 1);
+	return 0;
+}
diff --git a/builtin/reset.c b/builtin/reset.c
new file mode 100644
index 0000000000..fdd572168b
--- /dev/null
+++ b/builtin/reset.c
@@ -0,0 +1,427 @@
+/*
+ * "git reset" builtin command
+ *
+ * Copyright (c) 2007 Carlos Rica
+ *
+ * Based on git-reset.sh, which is
+ *
+ * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "config.h"
+#include "lockfile.h"
+#include "tag.h"
+#include "object.h"
+#include "pretty.h"
+#include "run-command.h"
+#include "refs.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "tree.h"
+#include "branch.h"
+#include "parse-options.h"
+#include "unpack-trees.h"
+#include "cache-tree.h"
+#include "submodule.h"
+#include "submodule-config.h"
+
+#define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
+
+static const char * const git_reset_usage[] = {
+	N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
+	N_("git reset [-q] [<tree-ish>] [--] <paths>..."),
+	N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
+	NULL
+};
+
+enum reset_type { MIXED, SOFT, HARD, MERGE, KEEP, NONE };
+static const char *reset_type_names[] = {
+	N_("mixed"), N_("soft"), N_("hard"), N_("merge"), N_("keep"), NULL
+};
+
+static inline int is_merge(void)
+{
+	return !access(git_path_merge_head(the_repository), F_OK);
+}
+
+static int reset_index(const struct object_id *oid, int reset_type, int quiet)
+{
+	int i, nr = 0;
+	struct tree_desc desc[2];
+	struct tree *tree;
+	struct unpack_trees_options opts;
+	int ret = -1;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.fn = oneway_merge;
+	opts.merge = 1;
+	if (!quiet)
+		opts.verbose_update = 1;
+	switch (reset_type) {
+	case KEEP:
+	case MERGE:
+		opts.update = 1;
+		break;
+	case HARD:
+		opts.update = 1;
+		/* fallthrough */
+	default:
+		opts.reset = 1;
+	}
+
+	read_cache_unmerged();
+
+	if (reset_type == KEEP) {
+		struct object_id head_oid;
+		if (get_oid("HEAD", &head_oid))
+			return error(_("You do not have a valid HEAD."));
+		if (!fill_tree_descriptor(the_repository, desc + nr, &head_oid))
+			return error(_("Failed to find tree of HEAD."));
+		nr++;
+		opts.fn = twoway_merge;
+	}
+
+	if (!fill_tree_descriptor(the_repository, desc + nr, oid)) {
+		error(_("Failed to find tree of %s."), oid_to_hex(oid));
+		goto out;
+	}
+	nr++;
+
+	if (unpack_trees(nr, desc, &opts))
+		goto out;
+
+	if (reset_type == MIXED || reset_type == HARD) {
+		tree = parse_tree_indirect(oid);
+		prime_cache_tree(the_repository, the_repository->index, tree);
+	}
+
+	ret = 0;
+
+out:
+	for (i = 0; i < nr; i++)
+		free((void *)desc[i].buffer);
+	return ret;
+}
+
+static void print_new_head_line(struct commit *commit)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	printf(_("HEAD is now at %s"),
+		find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+
+	pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
+	if (buf.len > 0)
+		printf(" %s", buf.buf);
+	putchar('\n');
+	strbuf_release(&buf);
+}
+
+static void update_index_from_diff(struct diff_queue_struct *q,
+		struct diff_options *opt, void *data)
+{
+	int i;
+	int intent_to_add = *(int *)data;
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filespec *one = q->queue[i]->one;
+		int is_missing = !(one->mode && !is_null_oid(&one->oid));
+		struct cache_entry *ce;
+
+		if (is_missing && !intent_to_add) {
+			remove_file_from_cache(one->path);
+			continue;
+		}
+
+		ce = make_cache_entry(&the_index, one->mode, &one->oid, one->path,
+				      0, 0);
+		if (!ce)
+			die(_("make_cache_entry failed for path '%s'"),
+			    one->path);
+		if (is_missing) {
+			ce->ce_flags |= CE_INTENT_TO_ADD;
+			set_object_name_for_intent_to_add_entry(ce);
+		}
+		add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
+	}
+}
+
+static int read_from_tree(const struct pathspec *pathspec,
+			  struct object_id *tree_oid,
+			  int intent_to_add)
+{
+	struct diff_options opt;
+
+	memset(&opt, 0, sizeof(opt));
+	copy_pathspec(&opt.pathspec, pathspec);
+	opt.output_format = DIFF_FORMAT_CALLBACK;
+	opt.format_callback = update_index_from_diff;
+	opt.format_callback_data = &intent_to_add;
+	opt.flags.override_submodule_config = 1;
+	opt.repo = the_repository;
+
+	if (do_diff_cache(tree_oid, &opt))
+		return 1;
+	diffcore_std(&opt);
+	diff_flush(&opt);
+	clear_pathspec(&opt.pathspec);
+
+	return 0;
+}
+
+static void set_reflog_message(struct strbuf *sb, const char *action,
+			       const char *rev)
+{
+	const char *rla = getenv("GIT_REFLOG_ACTION");
+
+	strbuf_reset(sb);
+	if (rla)
+		strbuf_addf(sb, "%s: %s", rla, action);
+	else if (rev)
+		strbuf_addf(sb, "reset: moving to %s", rev);
+	else
+		strbuf_addf(sb, "reset: %s", action);
+}
+
+static void die_if_unmerged_cache(int reset_type)
+{
+	if (is_merge() || unmerged_cache())
+		die(_("Cannot do a %s reset in the middle of a merge."),
+		    _(reset_type_names[reset_type]));
+
+}
+
+static void parse_args(struct pathspec *pathspec,
+		       const char **argv, const char *prefix,
+		       int patch_mode,
+		       const char **rev_ret)
+{
+	const char *rev = "HEAD";
+	struct object_id unused;
+	/*
+	 * Possible arguments are:
+	 *
+	 * git reset [-opts] [<rev>]
+	 * git reset [-opts] <tree> [<paths>...]
+	 * git reset [-opts] <tree> -- [<paths>...]
+	 * git reset [-opts] -- [<paths>...]
+	 * git reset [-opts] <paths>...
+	 *
+	 * At this point, argv points immediately after [-opts].
+	 */
+
+	if (argv[0]) {
+		if (!strcmp(argv[0], "--")) {
+			argv++; /* reset to HEAD, possibly with paths */
+		} else if (argv[1] && !strcmp(argv[1], "--")) {
+			rev = argv[0];
+			argv += 2;
+		}
+		/*
+		 * Otherwise, argv[0] could be either <rev> or <paths> and
+		 * has to be unambiguous. If there is a single argument, it
+		 * can not be a tree
+		 */
+		else if ((!argv[1] && !get_oid_committish(argv[0], &unused)) ||
+			 (argv[1] && !get_oid_treeish(argv[0], &unused))) {
+			/*
+			 * Ok, argv[0] looks like a commit/tree; it should not
+			 * be a filename.
+			 */
+			verify_non_filename(prefix, argv[0]);
+			rev = *argv++;
+		} else {
+			/* Otherwise we treat this as a filename */
+			verify_filename(prefix, argv[0], 1);
+		}
+	}
+	*rev_ret = rev;
+
+	if (read_cache() < 0)
+		die(_("index file corrupt"));
+
+	parse_pathspec(pathspec, 0,
+		       PATHSPEC_PREFER_FULL |
+		       (patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0),
+		       prefix, argv);
+}
+
+static int reset_refs(const char *rev, const struct object_id *oid)
+{
+	int update_ref_status;
+	struct strbuf msg = STRBUF_INIT;
+	struct object_id *orig = NULL, oid_orig,
+		*old_orig = NULL, oid_old_orig;
+
+	if (!get_oid("ORIG_HEAD", &oid_old_orig))
+		old_orig = &oid_old_orig;
+	if (!get_oid("HEAD", &oid_orig)) {
+		orig = &oid_orig;
+		set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
+		update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
+			   UPDATE_REFS_MSG_ON_ERR);
+	} else if (old_orig)
+		delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+	set_reflog_message(&msg, "updating HEAD", rev);
+	update_ref_status = update_ref(msg.buf, "HEAD", oid, orig, 0,
+				       UPDATE_REFS_MSG_ON_ERR);
+	strbuf_release(&msg);
+	return update_ref_status;
+}
+
+static int git_reset_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "submodule.recurse"))
+		return git_default_submodule_config(var, value, cb);
+
+	return git_default_config(var, value, cb);
+}
+
+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, unborn;
+	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_END()
+	};
+
+	git_config(git_reset_config, NULL);
+	git_config_get_bool("reset.quiet", &quiet);
+
+	argc = parse_options(argc, argv, prefix, options, git_reset_usage,
+						PARSE_OPT_KEEP_DASHDASH);
+	parse_args(&pathspec, argv, prefix, patch_mode, &rev);
+
+	unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
+	if (unborn) {
+		/* reset on unborn branch: treat as reset to empty tree */
+		oidcpy(&oid, the_hash_algo->empty_tree);
+	} 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(the_repository, &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}"));
+		trace2_cmd_mode("patch-interactive");
+		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 (pathspec.nr)
+		trace2_cmd_mode("path");
+	else
+		trace2_cmd_mode(reset_type_names[reset_type]);
+
+	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;
+			the_index.updated_skipworktree = 1;
+			if (!quiet && get_git_work_tree()) {
+				uint64_t t_begin, t_delta_in_ms;
+
+				t_begin = getnanotime();
+				refresh_index(&the_index, flags, NULL, NULL,
+					      _("Unstaged changes after reset:"));
+				t_delta_in_ms = (getnanotime() - t_begin) / 1000000;
+				if (advice_reset_quiet_warning && t_delta_in_ms > REFRESH_INDEX_DELAY_WARNING_IN_MS) {
+					printf(_("\nIt took %.2f seconds to enumerate unstaged changes after reset.  You can\n"
+						"use '--quiet' to avoid this.  Set the config setting reset.quiet to true\n"
+						"to make this the default.\n"), t_delta_in_ms / 1000.0);
+				}
+			}
+		} 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(the_repository, &oid));
+	}
+	if (!pathspec.nr)
+		remove_branch_state(the_repository, 0);
+
+	return update_ref_status;
+}
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
new file mode 100644
index 0000000000..301ccb970b
--- /dev/null
+++ b/builtin/rev-list.c
@@ -0,0 +1,619 @@
+#include "cache.h"
+#include "config.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "list-objects.h"
+#include "list-objects-filter.h"
+#include "list-objects-filter-options.h"
+#include "object.h"
+#include "object-store.h"
+#include "pack.h"
+#include "pack-bitmap.h"
+#include "builtin.h"
+#include "log-tree.h"
+#include "graph.h"
+#include "bisect.h"
+#include "progress.h"
+#include "reflog-walk.h"
+#include "oidset.h"
+#include "packfile.h"
+#include "object-store.h"
+
+static const char rev_list_usage[] =
+"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
+"  limiting output:\n"
+"    --max-count=<n>\n"
+"    --max-age=<epoch>\n"
+"    --min-age=<epoch>\n"
+"    --sparse\n"
+"    --no-merges\n"
+"    --min-parents=<n>\n"
+"    --no-min-parents\n"
+"    --max-parents=<n>\n"
+"    --no-max-parents\n"
+"    --remove-empty\n"
+"    --all\n"
+"    --branches\n"
+"    --tags\n"
+"    --remotes\n"
+"    --stdin\n"
+"    --quiet\n"
+"  ordering output:\n"
+"    --topo-order\n"
+"    --date-order\n"
+"    --reverse\n"
+"  formatting output:\n"
+"    --parents\n"
+"    --children\n"
+"    --objects | --objects-edge\n"
+"    --unpacked\n"
+"    --header | --pretty\n"
+"    --[no-]object-names\n"
+"    --abbrev=<n> | --no-abbrev\n"
+"    --abbrev-commit\n"
+"    --left-right\n"
+"    --count\n"
+"  special purpose:\n"
+"    --bisect\n"
+"    --bisect-vars\n"
+"    --bisect-all"
+;
+
+static struct progress *progress;
+static unsigned progress_counter;
+
+static struct list_objects_filter_options filter_options;
+static struct oidset omitted_objects;
+static int arg_print_omitted; /* print objects omitted by filter */
+
+static struct oidset missing_objects;
+enum missing_action {
+	MA_ERROR = 0,    /* fail if any missing objects are encountered */
+	MA_ALLOW_ANY,    /* silently allow ALL missing objects */
+	MA_PRINT,        /* print ALL missing objects in special section */
+	MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */
+};
+static enum missing_action arg_missing_action;
+
+/* display only the oid of each object encountered */
+static int arg_show_object_names = 1;
+
+#define DEFAULT_OIDSET_SIZE     (16*1024)
+
+static void finish_commit(struct commit *commit);
+static void show_commit(struct commit *commit, void *data)
+{
+	struct rev_list_info *info = data;
+	struct rev_info *revs = info->revs;
+
+	display_progress(progress, ++progress_counter);
+
+	if (info->flags & REV_LIST_QUIET) {
+		finish_commit(commit);
+		return;
+	}
+
+	graph_show_commit(revs->graph);
+
+	if (revs->count) {
+		if (commit->object.flags & PATCHSAME)
+			revs->count_same++;
+		else if (commit->object.flags & SYMMETRIC_LEFT)
+			revs->count_left++;
+		else
+			revs->count_right++;
+		finish_commit(commit);
+		return;
+	}
+
+	if (info->show_timestamp)
+		printf("%"PRItime" ", commit->date);
+	if (info->header_prefix)
+		fputs(info->header_prefix, stdout);
+
+	if (!revs->graph)
+		fputs(get_revision_mark(revs, commit), stdout);
+	if (revs->abbrev_commit && revs->abbrev)
+		fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev),
+		      stdout);
+	else
+		fputs(oid_to_hex(&commit->object.oid), stdout);
+	if (revs->print_parents) {
+		struct commit_list *parents = commit->parents;
+		while (parents) {
+			printf(" %s", oid_to_hex(&parents->item->object.oid));
+			parents = parents->next;
+		}
+	}
+	if (revs->children.name) {
+		struct commit_list *children;
+
+		children = lookup_decoration(&revs->children, &commit->object);
+		while (children) {
+			printf(" %s", oid_to_hex(&children->item->object.oid));
+			children = children->next;
+		}
+	}
+	show_decorations(revs, commit);
+	if (revs->commit_format == CMIT_FMT_ONELINE)
+		putchar(' ');
+	else
+		putchar('\n');
+
+	if (revs->verbose_header) {
+		struct strbuf buf = STRBUF_INIT;
+		struct pretty_print_context ctx = {0};
+		ctx.abbrev = revs->abbrev;
+		ctx.date_mode = revs->date_mode;
+		ctx.date_mode_explicit = revs->date_mode_explicit;
+		ctx.fmt = revs->commit_format;
+		ctx.output_encoding = get_log_output_encoding();
+		ctx.color = revs->diffopt.use_color;
+		pretty_print_commit(&ctx, commit, &buf);
+		if (buf.len) {
+			if (revs->commit_format != CMIT_FMT_ONELINE)
+				graph_show_oneline(revs->graph);
+
+			graph_show_commit_msg(revs->graph, stdout, &buf);
+
+			/*
+			 * Add a newline after the commit message.
+			 *
+			 * Usually, this newline produces a blank
+			 * padding line between entries, in which case
+			 * we need to add graph padding on this line.
+			 *
+			 * However, the commit message may not end in a
+			 * newline.  In this case the newline simply
+			 * ends the last line of the commit message,
+			 * and we don't need any graph output.  (This
+			 * always happens with CMIT_FMT_ONELINE, and it
+			 * happens with CMIT_FMT_USERFORMAT when the
+			 * format doesn't explicitly end in a newline.)
+			 */
+			if (buf.len && buf.buf[buf.len - 1] == '\n')
+				graph_show_padding(revs->graph);
+			putchar(info->hdr_termination);
+		} else {
+			/*
+			 * If the message buffer is empty, just show
+			 * the rest of the graph output for this
+			 * commit.
+			 */
+			if (graph_show_remainder(revs->graph))
+				putchar('\n');
+			if (revs->commit_format == CMIT_FMT_ONELINE)
+				putchar('\n');
+		}
+		strbuf_release(&buf);
+	} else {
+		if (graph_show_remainder(revs->graph))
+			putchar('\n');
+	}
+	maybe_flush_or_die(stdout, "stdout");
+	finish_commit(commit);
+}
+
+static void finish_commit(struct commit *commit)
+{
+	if (commit->parents) {
+		free_commit_list(commit->parents);
+		commit->parents = NULL;
+	}
+	free_commit_buffer(the_repository->parsed_objects,
+			   commit);
+}
+
+static inline void finish_object__ma(struct object *obj)
+{
+	/*
+	 * Whether or not we try to dynamically fetch missing objects
+	 * from the server, we currently DO NOT have the object.  We
+	 * can either print, allow (ignore), or conditionally allow
+	 * (ignore) them.
+	 */
+	switch (arg_missing_action) {
+	case MA_ERROR:
+		die("missing %s object '%s'",
+		    type_name(obj->type), oid_to_hex(&obj->oid));
+		return;
+
+	case MA_ALLOW_ANY:
+		return;
+
+	case MA_PRINT:
+		oidset_insert(&missing_objects, &obj->oid);
+		return;
+
+	case MA_ALLOW_PROMISOR:
+		if (is_promisor_object(&obj->oid))
+			return;
+		die("unexpected missing %s object '%s'",
+		    type_name(obj->type), oid_to_hex(&obj->oid));
+		return;
+
+	default:
+		BUG("unhandled missing_action");
+		return;
+	}
+}
+
+static int finish_object(struct object *obj, const char *name, void *cb_data)
+{
+	struct rev_list_info *info = cb_data;
+	if (oid_object_info_extended(the_repository, &obj->oid, NULL, 0) < 0) {
+		finish_object__ma(obj);
+		return 1;
+	}
+	if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
+		parse_object(the_repository, &obj->oid);
+	return 0;
+}
+
+static void show_object(struct object *obj, const char *name, void *cb_data)
+{
+	struct rev_list_info *info = cb_data;
+	if (finish_object(obj, name, cb_data))
+		return;
+	display_progress(progress, ++progress_counter);
+	if (info->flags & REV_LIST_QUIET)
+		return;
+	if (arg_show_object_names)
+		show_object_with_name(stdout, obj, name);
+	else
+		printf("%s\n", oid_to_hex(&obj->oid));
+}
+
+static void show_edge(struct commit *commit)
+{
+	printf("-%s\n", oid_to_hex(&commit->object.oid));
+}
+
+static void print_var_str(const char *var, const char *val)
+{
+	printf("%s='%s'\n", var, val);
+}
+
+static void print_var_int(const char *var, int val)
+{
+	printf("%s=%d\n", var, val);
+}
+
+static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
+{
+	int cnt, flags = info->flags;
+	char hex[GIT_MAX_HEXSZ + 1] = "";
+	struct commit_list *tried;
+	struct rev_info *revs = info->revs;
+
+	if (!revs->commits)
+		return 1;
+
+	revs->commits = filter_skipped(revs->commits, &tried,
+				       flags & BISECT_SHOW_ALL,
+				       NULL, NULL);
+
+	/*
+	 * revs->commits can reach "reaches" commits among
+	 * "all" commits.  If it is good, then there are
+	 * (all-reaches) commits left to be bisected.
+	 * On the other hand, if it is bad, then the set
+	 * to bisect is "reaches".
+	 * A bisect set of size N has (N-1) commits further
+	 * to test, as we already know one bad one.
+	 */
+	cnt = all - reaches;
+	if (cnt < reaches)
+		cnt = reaches;
+
+	if (revs->commits)
+		oid_to_hex_r(hex, &revs->commits->item->object.oid);
+
+	if (flags & BISECT_SHOW_ALL) {
+		traverse_commit_list(revs, show_commit, show_object, info);
+		printf("------\n");
+	}
+
+	print_var_str("bisect_rev", hex);
+	print_var_int("bisect_nr", cnt - 1);
+	print_var_int("bisect_good", all - reaches - 1);
+	print_var_int("bisect_bad", reaches - 1);
+	print_var_int("bisect_all", all);
+	print_var_int("bisect_steps", estimate_bisect_steps(all));
+
+	return 0;
+}
+
+static int show_object_fast(
+	const struct object_id *oid,
+	enum object_type type,
+	int exclude,
+	uint32_t name_hash,
+	struct packed_git *found_pack,
+	off_t found_offset)
+{
+	fprintf(stdout, "%s\n", oid_to_hex(oid));
+	return 1;
+}
+
+static inline int parse_missing_action_value(const char *value)
+{
+	if (!strcmp(value, "error")) {
+		arg_missing_action = MA_ERROR;
+		return 1;
+	}
+
+	if (!strcmp(value, "allow-any")) {
+		arg_missing_action = MA_ALLOW_ANY;
+		fetch_if_missing = 0;
+		return 1;
+	}
+
+	if (!strcmp(value, "print")) {
+		arg_missing_action = MA_PRINT;
+		fetch_if_missing = 0;
+		return 1;
+	}
+
+	if (!strcmp(value, "allow-promisor")) {
+		arg_missing_action = MA_ALLOW_PROMISOR;
+		fetch_if_missing = 0;
+		return 1;
+	}
+
+	return 0;
+}
+
+int cmd_rev_list(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info revs;
+	struct rev_list_info info;
+	struct setup_revision_opt s_r_opt = {
+		.allow_exclude_promisor_objects = 1,
+	};
+	int i;
+	int bisect_list = 0;
+	int bisect_show_vars = 0;
+	int bisect_find_all = 0;
+	int use_bitmap_index = 0;
+	const char *show_progress = NULL;
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(rev_list_usage);
+
+	git_config(git_default_config, NULL);
+	repo_init_revisions(the_repository, &revs, prefix);
+	revs.abbrev = DEFAULT_ABBREV;
+	revs.commit_format = CMIT_FMT_UNSPECIFIED;
+
+	/*
+	 * Scan the argument list before invoking setup_revisions(), so that we
+	 * know if fetch_if_missing needs to be set to 0.
+	 *
+	 * "--exclude-promisor-objects" acts as a pre-filter on missing objects
+	 * by not crossing the boundary from realized objects to promisor
+	 * objects.
+	 *
+	 * Let "--missing" to conditionally set fetch_if_missing.
+	 */
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "--exclude-promisor-objects")) {
+			fetch_if_missing = 0;
+			revs.exclude_promisor_objects = 1;
+			break;
+		}
+	}
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (skip_prefix(arg, "--missing=", &arg)) {
+			if (revs.exclude_promisor_objects)
+				die(_("cannot combine --exclude-promisor-objects and --missing"));
+			if (parse_missing_action_value(arg))
+				break;
+		}
+	}
+
+	if (arg_missing_action)
+		revs.do_not_die_on_missing_tree = 1;
+
+	argc = setup_revisions(argc, argv, &revs, &s_r_opt);
+
+	memset(&info, 0, sizeof(info));
+	info.revs = &revs;
+	if (revs.bisect)
+		bisect_list = 1;
+
+	if (revs.diffopt.flags.quick)
+		info.flags |= REV_LIST_QUIET;
+	for (i = 1 ; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (!strcmp(arg, "--header")) {
+			revs.verbose_header = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--timestamp")) {
+			info.show_timestamp = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--bisect")) {
+			bisect_list = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--bisect-all")) {
+			bisect_list = 1;
+			bisect_find_all = 1;
+			info.flags |= BISECT_SHOW_ALL;
+			revs.show_decorations = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--bisect-vars")) {
+			bisect_list = 1;
+			bisect_show_vars = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--use-bitmap-index")) {
+			use_bitmap_index = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--test-bitmap")) {
+			test_bitmap_walk(&revs);
+			return 0;
+		}
+		if (skip_prefix(arg, "--progress=", &arg)) {
+			show_progress = arg;
+			continue;
+		}
+
+		if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) {
+			parse_list_objects_filter(&filter_options, arg);
+			if (filter_options.choice && !revs.blob_objects)
+				die(_("object filtering requires --objects"));
+			if (filter_options.choice == LOFC_SPARSE_OID &&
+			    !filter_options.sparse_oid_value)
+				die(_("invalid sparse value '%s'"),
+				    filter_options.filter_spec);
+			continue;
+		}
+		if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) {
+			list_objects_filter_set_no_filter(&filter_options);
+			continue;
+		}
+		if (!strcmp(arg, "--filter-print-omitted")) {
+			arg_print_omitted = 1;
+			continue;
+		}
+
+		if (!strcmp(arg, "--exclude-promisor-objects"))
+			continue; /* already handled above */
+		if (skip_prefix(arg, "--missing=", &arg))
+			continue; /* already handled above */
+
+		if (!strcmp(arg, ("--no-object-names"))) {
+			arg_show_object_names = 0;
+			continue;
+		}
+
+		if (!strcmp(arg, ("--object-names"))) {
+			arg_show_object_names = 1;
+			continue;
+		}
+
+		usage(rev_list_usage);
+
+	}
+	if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
+		/* The command line has a --pretty  */
+		info.hdr_termination = '\n';
+		if (revs.commit_format == CMIT_FMT_ONELINE)
+			info.header_prefix = "";
+		else
+			info.header_prefix = "commit ";
+	}
+	else if (revs.verbose_header)
+		/* Only --header was specified */
+		revs.commit_format = CMIT_FMT_RAW;
+
+	if ((!revs.commits && reflog_walk_empty(revs.reflog_info) &&
+	     (!(revs.tag_objects || revs.tree_objects || revs.blob_objects) &&
+	      !revs.pending.nr) &&
+	     !revs.rev_input_given && !revs.read_from_stdin) ||
+	    revs.diff)
+		usage(rev_list_usage);
+
+	if (revs.show_notes)
+		die(_("rev-list does not support display of notes"));
+
+	if (filter_options.choice && use_bitmap_index)
+		die(_("cannot combine --use-bitmap-index with object filtering"));
+
+	save_commit_buffer = (revs.verbose_header ||
+			      revs.grep_filter.pattern_list ||
+			      revs.grep_filter.header_list);
+	if (bisect_list)
+		revs.limited = 1;
+
+	if (show_progress)
+		progress = start_delayed_progress(show_progress, 0);
+
+	if (use_bitmap_index && !revs.prune) {
+		if (revs.count && !revs.left_right && !revs.cherry_mark) {
+			uint32_t commit_count;
+			int max_count = revs.max_count;
+			struct bitmap_index *bitmap_git;
+			if ((bitmap_git = prepare_bitmap_walk(&revs))) {
+				count_bitmap_commit_list(bitmap_git, &commit_count, NULL, NULL, NULL);
+				if (max_count >= 0 && max_count < commit_count)
+					commit_count = max_count;
+				printf("%d\n", commit_count);
+				free_bitmap_index(bitmap_git);
+				return 0;
+			}
+		} else if (revs.max_count < 0 &&
+			   revs.tag_objects && revs.tree_objects && revs.blob_objects) {
+			struct bitmap_index *bitmap_git;
+			if ((bitmap_git = prepare_bitmap_walk(&revs))) {
+				traverse_bitmap_commit_list(bitmap_git, &show_object_fast);
+				free_bitmap_index(bitmap_git);
+				return 0;
+			}
+		}
+	}
+
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+	if (revs.tree_objects)
+		mark_edges_uninteresting(&revs, show_edge, 0);
+
+	if (bisect_list) {
+		int reaches, all;
+
+		find_bisection(&revs.commits, &reaches, &all, bisect_find_all);
+
+		if (bisect_show_vars)
+			return show_bisect_vars(&info, reaches, all);
+	}
+
+	if (arg_print_omitted)
+		oidset_init(&omitted_objects, DEFAULT_OIDSET_SIZE);
+	if (arg_missing_action == MA_PRINT)
+		oidset_init(&missing_objects, DEFAULT_OIDSET_SIZE);
+
+	traverse_commit_list_filtered(
+		&filter_options, &revs, show_commit, show_object, &info,
+		(arg_print_omitted ? &omitted_objects : NULL));
+
+	if (arg_print_omitted) {
+		struct oidset_iter iter;
+		struct object_id *oid;
+		oidset_iter_init(&omitted_objects, &iter);
+		while ((oid = oidset_iter_next(&iter)))
+			printf("~%s\n", oid_to_hex(oid));
+		oidset_clear(&omitted_objects);
+	}
+	if (arg_missing_action == MA_PRINT) {
+		struct oidset_iter iter;
+		struct object_id *oid;
+		oidset_iter_init(&missing_objects, &iter);
+		while ((oid = oidset_iter_next(&iter)))
+			printf("?%s\n", oid_to_hex(oid));
+		oidset_clear(&missing_objects);
+	}
+
+	stop_progress(&progress);
+
+	if (revs.count) {
+		if (revs.left_right && revs.cherry_mark)
+			printf("%d\t%d\t%d\n", revs.count_left, revs.count_right, revs.count_same);
+		else if (revs.left_right)
+			printf("%d\t%d\n", revs.count_left, revs.count_right);
+		else if (revs.cherry_mark)
+			printf("%d\t%d\n", revs.count_left + revs.count_right, revs.count_same);
+		else
+			printf("%d\n", revs.count_left + revs.count_right);
+	}
+
+	return 0;
+}
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
new file mode 100644
index 0000000000..f8bbe6d47e
--- /dev/null
+++ b/builtin/rev-parse.c
@@ -0,0 +1,965 @@
+/*
+ * rev-parse.c
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "commit.h"
+#include "refs.h"
+#include "quote.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "diff.h"
+#include "revision.h"
+#include "split-index.h"
+#include "submodule.h"
+#include "commit-reach.h"
+
+#define DO_REVS		1
+#define DO_NOREV	2
+#define DO_FLAGS	4
+#define DO_NONFLAGS	8
+static int filter = ~0;
+
+static const char *def;
+
+#define NORMAL 0
+#define REVERSED 1
+static int show_type = NORMAL;
+
+#define SHOW_SYMBOLIC_ASIS 1
+#define SHOW_SYMBOLIC_FULL 2
+static int symbolic;
+static int abbrev;
+static int abbrev_ref;
+static int abbrev_ref_strict;
+static int output_sq;
+
+static int stuck_long;
+static struct string_list *ref_excludes;
+
+/*
+ * Some arguments are relevant "revision" arguments,
+ * others are about output format or other details.
+ * This sorts it all out.
+ */
+static int is_rev_argument(const char *arg)
+{
+	static const char *rev_args[] = {
+		"--all",
+		"--bisect",
+		"--dense",
+		"--branches=",
+		"--branches",
+		"--header",
+		"--ignore-missing",
+		"--max-age=",
+		"--max-count=",
+		"--min-age=",
+		"--no-merges",
+		"--min-parents=",
+		"--no-min-parents",
+		"--max-parents=",
+		"--no-max-parents",
+		"--objects",
+		"--objects-edge",
+		"--parents",
+		"--pretty",
+		"--remotes=",
+		"--remotes",
+		"--glob=",
+		"--sparse",
+		"--tags=",
+		"--tags",
+		"--topo-order",
+		"--date-order",
+		"--unpacked",
+		NULL
+	};
+	const char **p = rev_args;
+
+	/* accept -<digit>, like traditional "head" */
+	if ((*arg == '-') && isdigit(arg[1]))
+		return 1;
+
+	for (;;) {
+		const char *str = *p++;
+		int len;
+		if (!str)
+			return 0;
+		len = strlen(str);
+		if (!strcmp(arg, str) ||
+		    (str[len-1] == '=' && !strncmp(arg, str, len)))
+			return 1;
+	}
+}
+
+/* Output argument as a string, either SQ or normal */
+static void show(const char *arg)
+{
+	if (output_sq) {
+		int sq = '\'', ch;
+
+		putchar(sq);
+		while ((ch = *arg++)) {
+			if (ch == sq)
+				fputs("'\\'", stdout);
+			putchar(ch);
+		}
+		putchar(sq);
+		putchar(' ');
+	}
+	else
+		puts(arg);
+}
+
+/* Like show(), but with a negation prefix according to type */
+static void show_with_type(int type, const char *arg)
+{
+	if (type != show_type)
+		putchar('^');
+	show(arg);
+}
+
+/* Output a revision, only if filter allows it */
+static void show_rev(int type, const struct object_id *oid, const char *name)
+{
+	if (!(filter & DO_REVS))
+		return;
+	def = NULL;
+
+	if ((symbolic || abbrev_ref) && name) {
+		if (symbolic == SHOW_SYMBOLIC_FULL || abbrev_ref) {
+			struct object_id discard;
+			char *full;
+
+			switch (dwim_ref(name, strlen(name), &discard, &full)) {
+			case 0:
+				/*
+				 * Not found -- not a ref.  We could
+				 * emit "name" here, but symbolic-full
+				 * users are interested in finding the
+				 * refs spelled in full, and they would
+				 * need to filter non-refs if we did so.
+				 */
+				break;
+			case 1: /* happy */
+				if (abbrev_ref)
+					full = shorten_unambiguous_ref(full,
+						abbrev_ref_strict);
+				show_with_type(type, full);
+				break;
+			default: /* ambiguous */
+				error("refname '%s' is ambiguous", name);
+				break;
+			}
+			free(full);
+		} else {
+			show_with_type(type, name);
+		}
+	}
+	else if (abbrev)
+		show_with_type(type, find_unique_abbrev(oid, abbrev));
+	else
+		show_with_type(type, oid_to_hex(oid));
+}
+
+/* Output a flag, only if filter allows it. */
+static int show_flag(const char *arg)
+{
+	if (!(filter & DO_FLAGS))
+		return 0;
+	if (filter & (is_rev_argument(arg) ? DO_REVS : DO_NOREV)) {
+		show(arg);
+		return 1;
+	}
+	return 0;
+}
+
+static int show_default(void)
+{
+	const char *s = def;
+
+	if (s) {
+		struct object_id oid;
+
+		def = NULL;
+		if (!get_oid(s, &oid)) {
+			show_rev(NORMAL, &oid, s);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int show_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data)
+{
+	if (ref_excluded(ref_excludes, refname))
+		return 0;
+	show_rev(NORMAL, oid, refname);
+	return 0;
+}
+
+static int anti_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data)
+{
+	show_rev(REVERSED, oid, refname);
+	return 0;
+}
+
+static int show_abbrev(const struct object_id *oid, void *cb_data)
+{
+	show_rev(NORMAL, oid, NULL);
+	return 0;
+}
+
+static void show_datestring(const char *flag, const char *datestr)
+{
+	char *buffer;
+
+	/* date handling requires both flags and revs */
+	if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
+		return;
+	buffer = xstrfmt("%s%"PRItime, flag, approxidate(datestr));
+	show(buffer);
+	free(buffer);
+}
+
+static int show_file(const char *arg, int output_prefix)
+{
+	show_default();
+	if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
+		if (output_prefix) {
+			const char *prefix = startup_info->prefix;
+			char *fname = prefix_filename(prefix, arg);
+			show(fname);
+			free(fname);
+		} else
+			show(arg);
+		return 1;
+	}
+	return 0;
+}
+
+static int try_difference(const char *arg)
+{
+	char *dotdot;
+	struct object_id start_oid;
+	struct object_id end_oid;
+	const char *end;
+	const char *start;
+	int symmetric;
+	static const char head_by_default[] = "HEAD";
+
+	if (!(dotdot = strstr(arg, "..")))
+		return 0;
+	end = dotdot + 2;
+	start = arg;
+	symmetric = (*end == '.');
+
+	*dotdot = 0;
+	end += symmetric;
+
+	if (!*end)
+		end = head_by_default;
+	if (dotdot == arg)
+		start = head_by_default;
+
+	if (start == head_by_default && end == head_by_default &&
+	    !symmetric) {
+		/*
+		 * Just ".."?  That is not a range but the
+		 * pathspec for the parent directory.
+		 */
+		*dotdot = '.';
+		return 0;
+	}
+
+	if (!get_oid_committish(start, &start_oid) && !get_oid_committish(end, &end_oid)) {
+		show_rev(NORMAL, &end_oid, end);
+		show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start);
+		if (symmetric) {
+			struct commit_list *exclude;
+			struct commit *a, *b;
+			a = lookup_commit_reference(the_repository, &start_oid);
+			b = lookup_commit_reference(the_repository, &end_oid);
+			if (!a || !b) {
+				*dotdot = '.';
+				return 0;
+			}
+			exclude = get_merge_bases(a, b);
+			while (exclude) {
+				struct commit *commit = pop_commit(&exclude);
+				show_rev(REVERSED, &commit->object.oid, NULL);
+			}
+		}
+		*dotdot = '.';
+		return 1;
+	}
+	*dotdot = '.';
+	return 0;
+}
+
+static int try_parent_shorthands(const char *arg)
+{
+	char *dotdot;
+	struct object_id oid;
+	struct commit *commit;
+	struct commit_list *parents;
+	int parent_number;
+	int include_rev = 0;
+	int include_parents = 0;
+	int exclude_parent = 0;
+
+	if ((dotdot = strstr(arg, "^!"))) {
+		include_rev = 1;
+		if (dotdot[2])
+			return 0;
+	} else if ((dotdot = strstr(arg, "^@"))) {
+		include_parents = 1;
+		if (dotdot[2])
+			return 0;
+	} else if ((dotdot = strstr(arg, "^-"))) {
+		include_rev = 1;
+		exclude_parent = 1;
+
+		if (dotdot[2]) {
+			char *end;
+			exclude_parent = strtoul(dotdot + 2, &end, 10);
+			if (*end != '\0' || !exclude_parent)
+				return 0;
+		}
+	} else
+		return 0;
+
+	*dotdot = 0;
+	if (get_oid_committish(arg, &oid) ||
+	    !(commit = lookup_commit_reference(the_repository, &oid))) {
+		*dotdot = '^';
+		return 0;
+	}
+
+	if (exclude_parent &&
+	    exclude_parent > commit_list_count(commit->parents)) {
+		*dotdot = '^';
+		return 0;
+	}
+
+	if (include_rev)
+		show_rev(NORMAL, &oid, arg);
+	for (parents = commit->parents, parent_number = 1;
+	     parents;
+	     parents = parents->next, parent_number++) {
+		char *name = NULL;
+
+		if (exclude_parent && parent_number != exclude_parent)
+			continue;
+
+		if (symbolic)
+			name = xstrfmt("%s^%d", arg, parent_number);
+		show_rev(include_parents ? NORMAL : REVERSED,
+			 &parents->item->object.oid, name);
+		free(name);
+	}
+
+	*dotdot = '^';
+	return 1;
+}
+
+static int parseopt_dump(const struct option *o, const char *arg, int unset)
+{
+	struct strbuf *parsed = o->value;
+	if (unset)
+		strbuf_addf(parsed, " --no-%s", o->long_name);
+	else if (o->short_name && (o->long_name == NULL || !stuck_long))
+		strbuf_addf(parsed, " -%c", o->short_name);
+	else
+		strbuf_addf(parsed, " --%s", o->long_name);
+	if (arg) {
+		if (!stuck_long)
+			strbuf_addch(parsed, ' ');
+		else if (o->long_name)
+			strbuf_addch(parsed, '=');
+		sq_quote_buf(parsed, arg);
+	}
+	return 0;
+}
+
+static const char *skipspaces(const char *s)
+{
+	while (isspace(*s))
+		s++;
+	return s;
+}
+
+static char *findspace(const char *s)
+{
+	for (; *s; s++)
+		if (isspace(*s))
+			return (char*)s;
+	return NULL;
+}
+
+static int cmd_parseopt(int argc, const char **argv, const char *prefix)
+{
+	static int keep_dashdash = 0, stop_at_non_option = 0;
+	static char const * const parseopt_usage[] = {
+		N_("git rev-parse --parseopt [<options>] -- [<args>...]"),
+		NULL
+	};
+	static struct option parseopt_opts[] = {
+		OPT_BOOL(0, "keep-dashdash", &keep_dashdash,
+					N_("keep the `--` passed as an arg")),
+		OPT_BOOL(0, "stop-at-non-option", &stop_at_non_option,
+					N_("stop parsing after the "
+					   "first non-option argument")),
+		OPT_BOOL(0, "stuck-long", &stuck_long,
+					N_("output in stuck long form")),
+		OPT_END(),
+	};
+	static const char * const flag_chars = "*=?!";
+
+	struct strbuf sb = STRBUF_INIT, parsed = STRBUF_INIT;
+	const char **usage = NULL;
+	struct option *opts = NULL;
+	int onb = 0, osz = 0, unb = 0, usz = 0;
+
+	strbuf_addstr(&parsed, "set --");
+	argc = parse_options(argc, argv, prefix, parseopt_opts, parseopt_usage,
+	                     PARSE_OPT_KEEP_DASHDASH);
+	if (argc < 1 || strcmp(argv[0], "--"))
+		usage_with_options(parseopt_usage, parseopt_opts);
+
+	/* get the usage up to the first line with a -- on it */
+	for (;;) {
+		if (strbuf_getline(&sb, stdin) == EOF)
+			die("premature end of input");
+		ALLOC_GROW(usage, unb + 1, usz);
+		if (!strcmp("--", sb.buf)) {
+			if (unb < 1)
+				die("no usage string given before the `--' separator");
+			usage[unb] = NULL;
+			break;
+		}
+		usage[unb++] = strbuf_detach(&sb, NULL);
+	}
+
+	/* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */
+	while (strbuf_getline(&sb, stdin) != EOF) {
+		const char *s;
+		char *help;
+		struct option *o;
+
+		if (!sb.len)
+			continue;
+
+		ALLOC_GROW(opts, onb + 1, osz);
+		memset(opts + onb, 0, sizeof(opts[onb]));
+
+		o = &opts[onb++];
+		help = findspace(sb.buf);
+		if (!help || sb.buf == help) {
+			o->type = OPTION_GROUP;
+			o->help = xstrdup(skipspaces(sb.buf));
+			continue;
+		}
+
+		*help = '\0';
+
+		o->type = OPTION_CALLBACK;
+		o->help = xstrdup(skipspaces(help+1));
+		o->value = &parsed;
+		o->flags = PARSE_OPT_NOARG;
+		o->callback = &parseopt_dump;
+
+		/* name(s) */
+		s = strpbrk(sb.buf, flag_chars);
+		if (s == NULL)
+			s = help;
+
+		if (s - sb.buf == 1) /* short option only */
+			o->short_name = *sb.buf;
+		else if (sb.buf[1] != ',') /* long option only */
+			o->long_name = xmemdupz(sb.buf, s - sb.buf);
+		else {
+			o->short_name = *sb.buf;
+			o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2);
+		}
+
+		/* flags */
+		while (s < help) {
+			switch (*s++) {
+			case '=':
+				o->flags &= ~PARSE_OPT_NOARG;
+				continue;
+			case '?':
+				o->flags &= ~PARSE_OPT_NOARG;
+				o->flags |= PARSE_OPT_OPTARG;
+				continue;
+			case '!':
+				o->flags |= PARSE_OPT_NONEG;
+				continue;
+			case '*':
+				o->flags |= PARSE_OPT_HIDDEN;
+				continue;
+			}
+			s--;
+			break;
+		}
+
+		if (s < help)
+			o->argh = xmemdupz(s, help - s);
+	}
+	strbuf_release(&sb);
+
+	/* put an OPT_END() */
+	ALLOC_GROW(opts, onb + 1, osz);
+	memset(opts + onb, 0, sizeof(opts[onb]));
+	argc = parse_options(argc, argv, prefix, opts, usage,
+			(keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0) |
+			(stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0) |
+			PARSE_OPT_SHELL_EVAL);
+
+	strbuf_addstr(&parsed, " --");
+	sq_quote_argv(&parsed, argv);
+	puts(parsed.buf);
+	return 0;
+}
+
+static int cmd_sq_quote(int argc, const char **argv)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	if (argc)
+		sq_quote_argv(&buf, argv);
+	printf("%s\n", buf.buf);
+	strbuf_release(&buf);
+
+	return 0;
+}
+
+static void die_no_single_rev(int quiet)
+{
+	if (quiet)
+		exit(1);
+	else
+		die("Needed a single revision");
+}
+
+static const char builtin_rev_parse_usage[] =
+N_("git rev-parse --parseopt [<options>] -- [<args>...]\n"
+   "   or: git rev-parse --sq-quote [<arg>...]\n"
+   "   or: git rev-parse [<options>] [<arg>...]\n"
+   "\n"
+   "Run \"git rev-parse --parseopt -h\" for more information on the first usage.");
+
+/*
+ * Parse "opt" or "opt=<value>", setting value respectively to either
+ * NULL or the string after "=".
+ */
+static int opt_with_value(const char *arg, const char *opt, const char **value)
+{
+	if (skip_prefix(arg, opt, &arg)) {
+		if (!*arg) {
+			*value = NULL;
+			return 1;
+		}
+		if (*arg++ == '=') {
+			*value = arg;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static void handle_ref_opt(const char *pattern, const char *prefix)
+{
+	if (pattern)
+		for_each_glob_ref_in(show_reference, pattern, prefix, NULL);
+	else
+		for_each_ref_in(prefix, show_reference, NULL);
+	clear_ref_exclusion(&ref_excludes);
+}
+
+int cmd_rev_parse(int argc, const char **argv, const char *prefix)
+{
+	int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+	int did_repo_setup = 0;
+	int has_dashdash = 0;
+	int output_prefix = 0;
+	struct object_id oid;
+	unsigned int flags = 0;
+	const char *name = NULL;
+	struct object_context unused;
+	struct strbuf buf = STRBUF_INIT;
+
+	if (argc > 1 && !strcmp("--parseopt", argv[1]))
+		return cmd_parseopt(argc - 1, argv + 1, prefix);
+
+	if (argc > 1 && !strcmp("--sq-quote", argv[1]))
+		return cmd_sq_quote(argc - 2, argv + 2);
+
+	if (argc > 1 && !strcmp("-h", argv[1]))
+		usage(builtin_rev_parse_usage);
+
+	for (i = 1; i < argc; i++) {
+		if (!strcmp(argv[i], "--")) {
+			has_dashdash = 1;
+			break;
+		}
+	}
+
+	/* No options; just report on whether we're in a git repo or not. */
+	if (argc == 1) {
+		setup_git_directory();
+		git_config(git_default_config, NULL);
+		return 0;
+	}
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (!strcmp(arg, "--local-env-vars")) {
+			int i;
+			for (i = 0; local_repo_env[i]; i++)
+				printf("%s\n", local_repo_env[i]);
+			continue;
+		}
+		if (!strcmp(arg, "--resolve-git-dir")) {
+			const char *gitdir = argv[++i];
+			if (!gitdir)
+				die("--resolve-git-dir requires an argument");
+			gitdir = resolve_gitdir(gitdir);
+			if (!gitdir)
+				die("not a gitdir '%s'", argv[i]);
+			puts(gitdir);
+			continue;
+		}
+
+		/* The rest of the options require a git repository. */
+		if (!did_repo_setup) {
+			prefix = setup_git_directory();
+			git_config(git_default_config, NULL);
+			did_repo_setup = 1;
+		}
+
+		if (!strcmp(arg, "--git-path")) {
+			if (!argv[i + 1])
+				die("--git-path requires an argument");
+			strbuf_reset(&buf);
+			puts(relative_path(git_path("%s", argv[i + 1]),
+					   prefix, &buf));
+			i++;
+			continue;
+		}
+		if (as_is) {
+			if (show_file(arg, output_prefix) && as_is < 2)
+				verify_filename(prefix, arg, 0);
+			continue;
+		}
+		if (!strcmp(arg,"-n")) {
+			if (++i >= argc)
+				die("-n requires an argument");
+			if ((filter & DO_FLAGS) && (filter & DO_REVS)) {
+				show(arg);
+				show(argv[i]);
+			}
+			continue;
+		}
+		if (starts_with(arg, "-n")) {
+			if ((filter & DO_FLAGS) && (filter & DO_REVS))
+				show(arg);
+			continue;
+		}
+
+		if (*arg == '-') {
+			if (!strcmp(arg, "--")) {
+				as_is = 2;
+				/* Pass on the "--" if we show anything but files.. */
+				if (filter & (DO_FLAGS | DO_REVS))
+					show_file(arg, 0);
+				continue;
+			}
+			if (!strcmp(arg, "--default")) {
+				def = argv[++i];
+				if (!def)
+					die("--default requires an argument");
+				continue;
+			}
+			if (!strcmp(arg, "--prefix")) {
+				prefix = argv[++i];
+				if (!prefix)
+					die("--prefix requires an argument");
+				startup_info->prefix = prefix;
+				output_prefix = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--revs-only")) {
+				filter &= ~DO_NOREV;
+				continue;
+			}
+			if (!strcmp(arg, "--no-revs")) {
+				filter &= ~DO_REVS;
+				continue;
+			}
+			if (!strcmp(arg, "--flags")) {
+				filter &= ~DO_NONFLAGS;
+				continue;
+			}
+			if (!strcmp(arg, "--no-flags")) {
+				filter &= ~DO_FLAGS;
+				continue;
+			}
+			if (!strcmp(arg, "--verify")) {
+				filter &= ~(DO_FLAGS|DO_NOREV);
+				verify = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
+				quiet = 1;
+				flags |= GET_OID_QUIETLY;
+				continue;
+			}
+			if (opt_with_value(arg, "--short", &arg)) {
+				filter &= ~(DO_FLAGS|DO_NOREV);
+				verify = 1;
+				abbrev = DEFAULT_ABBREV;
+				if (!arg)
+					continue;
+				abbrev = strtoul(arg, NULL, 10);
+				if (abbrev < MINIMUM_ABBREV)
+					abbrev = MINIMUM_ABBREV;
+				else if (40 <= abbrev)
+					abbrev = 40;
+				continue;
+			}
+			if (!strcmp(arg, "--sq")) {
+				output_sq = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--not")) {
+				show_type ^= REVERSED;
+				continue;
+			}
+			if (!strcmp(arg, "--symbolic")) {
+				symbolic = SHOW_SYMBOLIC_ASIS;
+				continue;
+			}
+			if (!strcmp(arg, "--symbolic-full-name")) {
+				symbolic = SHOW_SYMBOLIC_FULL;
+				continue;
+			}
+			if (opt_with_value(arg, "--abbrev-ref", &arg)) {
+				abbrev_ref = 1;
+				abbrev_ref_strict = warn_ambiguous_refs;
+				if (arg) {
+					if (!strcmp(arg, "strict"))
+						abbrev_ref_strict = 1;
+					else if (!strcmp(arg, "loose"))
+						abbrev_ref_strict = 0;
+					else
+						die("unknown mode for --abbrev-ref: %s",
+						    arg);
+				}
+				continue;
+			}
+			if (!strcmp(arg, "--all")) {
+				for_each_ref(show_reference, NULL);
+				clear_ref_exclusion(&ref_excludes);
+				continue;
+			}
+			if (skip_prefix(arg, "--disambiguate=", &arg)) {
+				for_each_abbrev(arg, show_abbrev, NULL);
+				continue;
+			}
+			if (!strcmp(arg, "--bisect")) {
+				for_each_fullref_in("refs/bisect/bad", show_reference, NULL, 0);
+				for_each_fullref_in("refs/bisect/good", anti_reference, NULL, 0);
+				continue;
+			}
+			if (opt_with_value(arg, "--branches", &arg)) {
+				handle_ref_opt(arg, "refs/heads/");
+				continue;
+			}
+			if (opt_with_value(arg, "--tags", &arg)) {
+				handle_ref_opt(arg, "refs/tags/");
+				continue;
+			}
+			if (skip_prefix(arg, "--glob=", &arg)) {
+				handle_ref_opt(arg, NULL);
+				continue;
+			}
+			if (opt_with_value(arg, "--remotes", &arg)) {
+				handle_ref_opt(arg, "refs/remotes/");
+				continue;
+			}
+			if (skip_prefix(arg, "--exclude=", &arg)) {
+				add_ref_exclusion(&ref_excludes, arg);
+				continue;
+			}
+			if (!strcmp(arg, "--show-toplevel")) {
+				const char *work_tree = get_git_work_tree();
+				if (work_tree)
+					puts(work_tree);
+				continue;
+			}
+			if (!strcmp(arg, "--show-superproject-working-tree")) {
+				const char *superproject = get_superproject_working_tree();
+				if (superproject)
+					puts(superproject);
+				continue;
+			}
+			if (!strcmp(arg, "--show-prefix")) {
+				if (prefix)
+					puts(prefix);
+				else
+					putchar('\n');
+				continue;
+			}
+			if (!strcmp(arg, "--show-cdup")) {
+				const char *pfx = prefix;
+				if (!is_inside_work_tree()) {
+					const char *work_tree =
+						get_git_work_tree();
+					if (work_tree)
+						printf("%s\n", work_tree);
+					continue;
+				}
+				while (pfx) {
+					pfx = strchr(pfx, '/');
+					if (pfx) {
+						pfx++;
+						printf("../");
+					}
+				}
+				putchar('\n');
+				continue;
+			}
+			if (!strcmp(arg, "--git-dir") ||
+			    !strcmp(arg, "--absolute-git-dir")) {
+				const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
+				char *cwd;
+				int len;
+				if (arg[2] == 'g') {	/* --git-dir */
+					if (gitdir) {
+						puts(gitdir);
+						continue;
+					}
+					if (!prefix) {
+						puts(".git");
+						continue;
+					}
+				} else {		/* --absolute-git-dir */
+					if (!gitdir && !prefix)
+						gitdir = ".git";
+					if (gitdir) {
+						puts(real_path(gitdir));
+						continue;
+					}
+				}
+				cwd = xgetcwd();
+				len = strlen(cwd);
+				printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
+				free(cwd);
+				continue;
+			}
+			if (!strcmp(arg, "--git-common-dir")) {
+				strbuf_reset(&buf);
+				puts(relative_path(get_git_common_dir(),
+						   prefix, &buf));
+				continue;
+			}
+			if (!strcmp(arg, "--is-inside-git-dir")) {
+				printf("%s\n", is_inside_git_dir() ? "true"
+						: "false");
+				continue;
+			}
+			if (!strcmp(arg, "--is-inside-work-tree")) {
+				printf("%s\n", is_inside_work_tree() ? "true"
+						: "false");
+				continue;
+			}
+			if (!strcmp(arg, "--is-bare-repository")) {
+				printf("%s\n", is_bare_repository() ? "true"
+						: "false");
+				continue;
+			}
+			if (!strcmp(arg, "--is-shallow-repository")) {
+				printf("%s\n",
+						is_repository_shallow(the_repository) ? "true"
+						: "false");
+				continue;
+			}
+			if (!strcmp(arg, "--shared-index-path")) {
+				if (read_cache() < 0)
+					die(_("Could not read the index"));
+				if (the_index.split_index) {
+					const struct object_id *oid = &the_index.split_index->base_oid;
+					const char *path = git_path("sharedindex.%s", oid_to_hex(oid));
+					strbuf_reset(&buf);
+					puts(relative_path(path, prefix, &buf));
+				}
+				continue;
+			}
+			if (skip_prefix(arg, "--since=", &arg)) {
+				show_datestring("--max-age=", arg);
+				continue;
+			}
+			if (skip_prefix(arg, "--after=", &arg)) {
+				show_datestring("--max-age=", arg);
+				continue;
+			}
+			if (skip_prefix(arg, "--before=", &arg)) {
+				show_datestring("--min-age=", arg);
+				continue;
+			}
+			if (skip_prefix(arg, "--until=", &arg)) {
+				show_datestring("--min-age=", arg);
+				continue;
+			}
+			if (show_flag(arg) && verify)
+				die_no_single_rev(quiet);
+			continue;
+		}
+
+		/* Not a flag argument */
+		if (try_difference(arg))
+			continue;
+		if (try_parent_shorthands(arg))
+			continue;
+		name = arg;
+		type = NORMAL;
+		if (*arg == '^') {
+			name++;
+			type = REVERSED;
+		}
+		if (!get_oid_with_context(the_repository, name,
+					  flags, &oid, &unused)) {
+			if (verify)
+				revs_count++;
+			else
+				show_rev(type, &oid, name);
+			continue;
+		}
+		if (verify)
+			die_no_single_rev(quiet);
+		if (has_dashdash)
+			die("bad revision '%s'", arg);
+		as_is = 1;
+		if (!show_file(arg, output_prefix))
+			continue;
+		verify_filename(prefix, arg, 1);
+	}
+	strbuf_release(&buf);
+	if (verify) {
+		if (revs_count == 1) {
+			show_rev(type, &oid, name);
+			return 0;
+		} else if (revs_count == 0 && show_default())
+			return 0;
+		die_no_single_rev(quiet);
+	} else
+		show_default();
+	return 0;
+}
diff --git a/builtin/revert.c b/builtin/revert.c
new file mode 100644
index 0000000000..f61cc5d82c
--- /dev/null
+++ b/builtin/revert.c
@@ -0,0 +1,247 @@
+#include "cache.h"
+#include "config.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "diff.h"
+#include "revision.h"
+#include "rerere.h"
+#include "dir.h"
+#include "sequencer.h"
+#include "branch.h"
+
+/*
+ * This implements the builtins revert and cherry-pick.
+ *
+ * Copyright (c) 2007 Johannes E. Schindelin
+ *
+ * Based on git-revert.sh, which is
+ *
+ * Copyright (c) 2005 Linus Torvalds
+ * Copyright (c) 2005 Junio C Hamano
+ */
+
+static const char * const revert_usage[] = {
+	N_("git revert [<options>] <commit-ish>..."),
+	N_("git revert <subcommand>"),
+	NULL
+};
+
+static const char * const cherry_pick_usage[] = {
+	N_("git cherry-pick [<options>] <commit-ish>..."),
+	N_("git cherry-pick <subcommand>"),
+	NULL
+};
+
+static const char *action_name(const struct replay_opts *opts)
+{
+	return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
+}
+
+static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
+{
+	return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
+}
+
+static int option_parse_x(const struct option *opt,
+			  const char *arg, int unset)
+{
+	struct replay_opts **opts_ptr = opt->value;
+	struct replay_opts *opts = *opts_ptr;
+
+	if (unset)
+		return 0;
+
+	ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
+	opts->xopts[opts->xopts_nr++] = xstrdup(arg);
+	return 0;
+}
+
+static int option_parse_m(const struct option *opt,
+			  const char *arg, int unset)
+{
+	struct replay_opts *replay = opt->value;
+	char *end;
+
+	if (unset) {
+		replay->mainline = 0;
+		return 0;
+	}
+
+	replay->mainline = strtol(arg, &end, 10);
+	if (*end || replay->mainline <= 0)
+		return error(_("option `%s' expects a number greater than zero"),
+			     opt->long_name);
+
+	return 0;
+}
+
+LAST_ARG_MUST_BE_NULL
+static void verify_opt_compatible(const char *me, const char *base_opt, ...)
+{
+	const char *this_opt;
+	va_list ap;
+
+	va_start(ap, base_opt);
+	while ((this_opt = va_arg(ap, const char *))) {
+		if (va_arg(ap, int))
+			break;
+	}
+	va_end(ap);
+
+	if (this_opt)
+		die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
+}
+
+static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
+{
+	const char * const * usage_str = revert_or_cherry_pick_usage(opts);
+	const char *me = action_name(opts);
+	const char *cleanup_arg = NULL;
+	int cmd = 0;
+	struct option base_options[] = {
+		OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
+		OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'),
+		OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'),
+		OPT_CMDMODE(0, "skip", &cmd, N_("skip current commit and continue"), 's'),
+		OPT_CLEANUP(&cleanup_arg),
+		OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
+		OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")),
+		OPT_NOOP_NOARG('r', NULL),
+		OPT_BOOL('s', "signoff", &opts->signoff, N_("add Signed-off-by:")),
+		OPT_CALLBACK('m', "mainline", opts, N_("parent-number"),
+			     N_("select mainline parent"), option_parse_m),
+		OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
+		OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")),
+		OPT_CALLBACK('X', "strategy-option", &opts, N_("option"),
+			N_("option for merge strategy"), option_parse_x),
+		{ OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key-id"),
+		  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_END()
+	};
+	struct option *options = base_options;
+
+	if (opts->action == REPLAY_PICK) {
+		struct option cp_extra[] = {
+			OPT_BOOL('x', NULL, &opts->record_origin, N_("append commit name")),
+			OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
+			OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
+			OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
+			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
+			OPT_END(),
+		};
+		options = parse_options_concat(options, cp_extra);
+	}
+
+	argc = parse_options(argc, argv, NULL, options, usage_str,
+			PARSE_OPT_KEEP_ARGV0 |
+			PARSE_OPT_KEEP_UNKNOWN);
+
+	/* implies allow_empty */
+	if (opts->keep_redundant_commits)
+		opts->allow_empty = 1;
+
+	if (cleanup_arg) {
+		opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);
+		opts->explicit_cleanup = 1;
+	}
+
+	/* Check for incompatible command line arguments */
+	if (cmd) {
+		char *this_operation;
+		if (cmd == 'q')
+			this_operation = "--quit";
+		else if (cmd == 'c')
+			this_operation = "--continue";
+		else if (cmd == 's')
+			this_operation = "--skip";
+		else {
+			assert(cmd == 'a');
+			this_operation = "--abort";
+		}
+
+		verify_opt_compatible(me, this_operation,
+				"--no-commit", opts->no_commit,
+				"--signoff", opts->signoff,
+				"--mainline", opts->mainline,
+				"--strategy", opts->strategy ? 1 : 0,
+				"--strategy-option", opts->xopts ? 1 : 0,
+				"-x", opts->record_origin,
+				"--ff", opts->allow_ff,
+				"--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
+				"--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
+				NULL);
+	}
+
+	if (opts->allow_ff)
+		verify_opt_compatible(me, "--ff",
+				"--signoff", opts->signoff,
+				"--no-commit", opts->no_commit,
+				"-x", opts->record_origin,
+				"--edit", opts->edit,
+				NULL);
+
+	if (cmd) {
+		opts->revs = NULL;
+	} else {
+		struct setup_revision_opt s_r_opt;
+		opts->revs = xmalloc(sizeof(*opts->revs));
+		repo_init_revisions(the_repository, opts->revs, NULL);
+		opts->revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
+		if (argc < 2)
+			usage_with_options(usage_str, options);
+		if (!strcmp(argv[1], "-"))
+			argv[1] = "@{-1}";
+		memset(&s_r_opt, 0, sizeof(s_r_opt));
+		s_r_opt.assume_dashdash = 1;
+		argc = setup_revisions(argc, argv, opts->revs, &s_r_opt);
+	}
+
+	if (argc > 1)
+		usage_with_options(usage_str, options);
+
+	/* These option values will be free()d */
+	opts->gpg_sign = xstrdup_or_null(opts->gpg_sign);
+	opts->strategy = xstrdup_or_null(opts->strategy);
+
+	if (cmd == 'q') {
+		int ret = sequencer_remove_state(opts);
+		if (!ret)
+			remove_branch_state(the_repository, 0);
+		return ret;
+	}
+	if (cmd == 'c')
+		return sequencer_continue(the_repository, opts);
+	if (cmd == 'a')
+		return sequencer_rollback(the_repository, opts);
+	if (cmd == 's')
+		return sequencer_skip(the_repository, opts);
+	return sequencer_pick_revisions(the_repository, opts);
+}
+
+int cmd_revert(int argc, const char **argv, const char *prefix)
+{
+	struct replay_opts opts = REPLAY_OPTS_INIT;
+	int res;
+
+	if (isatty(0))
+		opts.edit = 1;
+	opts.action = REPLAY_REVERT;
+	sequencer_init_config(&opts);
+	res = run_sequencer(argc, argv, &opts);
+	if (res < 0)
+		die(_("revert failed"));
+	return res;
+}
+
+int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
+{
+	struct replay_opts opts = REPLAY_OPTS_INIT;
+	int res;
+
+	opts.action = REPLAY_PICK;
+	sequencer_init_config(&opts);
+	res = run_sequencer(argc, argv, &opts);
+	if (res < 0)
+		die(_("cherry-pick failed"));
+	return res;
+}
diff --git a/builtin/rm.c b/builtin/rm.c
new file mode 100644
index 0000000000..19ce95a901
--- /dev/null
+++ b/builtin/rm.c
@@ -0,0 +1,393 @@
+/*
+ * "git rm" builtin command
+ *
+ * Copyright (C) Linus Torvalds 2006
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "config.h"
+#include "lockfile.h"
+#include "dir.h"
+#include "cache-tree.h"
+#include "tree-walk.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "submodule.h"
+#include "pathspec.h"
+
+static const char * const builtin_rm_usage[] = {
+	N_("git rm [<options>] [--] <file>..."),
+	NULL
+};
+
+static struct {
+	int nr, alloc;
+	struct {
+		const char *name;
+		char is_submodule;
+	} *entry;
+} list;
+
+static int get_ours_cache_pos(const char *path, int pos)
+{
+	int i = -pos - 1;
+
+	while ((i < active_nr) && !strcmp(active_cache[i]->name, path)) {
+		if (ce_stage(active_cache[i]) == 2)
+			return i;
+		i++;
+	}
+	return -1;
+}
+
+static void print_error_files(struct string_list *files_list,
+			      const char *main_msg,
+			      const char *hints_msg,
+			      int *errs)
+{
+	if (files_list->nr) {
+		int i;
+		struct strbuf err_msg = STRBUF_INIT;
+
+		strbuf_addstr(&err_msg, main_msg);
+		for (i = 0; i < files_list->nr; i++)
+			strbuf_addf(&err_msg,
+				    "\n    %s",
+				    files_list->items[i].string);
+		if (advice_rm_hints)
+			strbuf_addstr(&err_msg, hints_msg);
+		*errs = error("%s", err_msg.buf);
+		strbuf_release(&err_msg);
+	}
+}
+
+static void submodules_absorb_gitdir_if_needed(void)
+{
+	int i;
+	for (i = 0; i < list.nr; i++) {
+		const char *name = list.entry[i].name;
+		int pos;
+		const struct cache_entry *ce;
+
+		pos = cache_name_pos(name, strlen(name));
+		if (pos < 0) {
+			pos = get_ours_cache_pos(name, pos);
+			if (pos < 0)
+				continue;
+		}
+		ce = active_cache[pos];
+
+		if (!S_ISGITLINK(ce->ce_mode) ||
+		    !file_exists(ce->name) ||
+		    is_empty_dir(name))
+			continue;
+
+		if (!submodule_uses_gitfile(name))
+			absorb_git_dir_into_superproject(name,
+				ABSORB_GITDIR_RECURSE_SUBMODULES);
+	}
+}
+
+static int check_local_mod(struct object_id *head, int index_only)
+{
+	/*
+	 * Items in list are already sorted in the cache order,
+	 * so we could do this a lot more efficiently by using
+	 * tree_desc based traversal if we wanted to, but I am
+	 * lazy, and who cares if removal of files is a tad
+	 * slower than the theoretical maximum speed?
+	 */
+	int i, no_head;
+	int errs = 0;
+	struct string_list files_staged = STRING_LIST_INIT_NODUP;
+	struct string_list files_cached = STRING_LIST_INIT_NODUP;
+	struct string_list files_local = STRING_LIST_INIT_NODUP;
+
+	no_head = is_null_oid(head);
+	for (i = 0; i < list.nr; i++) {
+		struct stat st;
+		int pos;
+		const struct cache_entry *ce;
+		const char *name = list.entry[i].name;
+		struct object_id oid;
+		unsigned short mode;
+		int local_changes = 0;
+		int staged_changes = 0;
+
+		pos = cache_name_pos(name, strlen(name));
+		if (pos < 0) {
+			/*
+			 * Skip unmerged entries except for populated submodules
+			 * that could lose history when removed.
+			 */
+			pos = get_ours_cache_pos(name, pos);
+			if (pos < 0)
+				continue;
+
+			if (!S_ISGITLINK(active_cache[pos]->ce_mode) ||
+			    is_empty_dir(name))
+				continue;
+		}
+		ce = active_cache[pos];
+
+		if (lstat(ce->name, &st) < 0) {
+			if (!is_missing_file_error(errno))
+				warning_errno(_("failed to stat '%s'"), ce->name);
+			/* It already vanished from the working tree */
+			continue;
+		}
+		else if (S_ISDIR(st.st_mode)) {
+			/* if a file was removed and it is now a
+			 * directory, that is the same as ENOENT as
+			 * far as git is concerned; we do not track
+			 * directories unless they are submodules.
+			 */
+			if (!S_ISGITLINK(ce->ce_mode))
+				continue;
+		}
+
+		/*
+		 * "rm" of a path that has changes need to be treated
+		 * carefully not to allow losing local changes
+		 * accidentally.  A local change could be (1) file in
+		 * work tree is different since the index; and/or (2)
+		 * the user staged a content that is different from
+		 * the current commit in the index.
+		 *
+		 * In such a case, you would need to --force the
+		 * removal.  However, "rm --cached" (remove only from
+		 * the index) is safe if the index matches the file in
+		 * the work tree or the HEAD commit, as it means that
+		 * the content being removed is available elsewhere.
+		 */
+
+		/*
+		 * Is the index different from the file in the work tree?
+		 * If it's a submodule, is its work tree modified?
+		 */
+		if (ce_match_stat(ce, &st, 0) ||
+		    (S_ISGITLINK(ce->ce_mode) &&
+		     bad_to_remove_submodule(ce->name,
+				SUBMODULE_REMOVAL_DIE_ON_ERROR |
+				SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED)))
+			local_changes = 1;
+
+		/*
+		 * Is the index different from the HEAD commit?  By
+		 * definition, before the very initial commit,
+		 * anything staged in the index is treated by the same
+		 * way as changed from the HEAD.
+		 */
+		if (no_head
+		     || get_tree_entry(the_repository, head, name, &oid, &mode)
+		     || ce->ce_mode != create_ce_mode(mode)
+		     || !oideq(&ce->oid, &oid))
+			staged_changes = 1;
+
+		/*
+		 * If the index does not match the file in the work
+		 * tree and if it does not match the HEAD commit
+		 * either, (1) "git rm" without --cached definitely
+		 * will lose information; (2) "git rm --cached" will
+		 * lose information unless it is about removing an
+		 * "intent to add" entry.
+		 */
+		if (local_changes && staged_changes) {
+			if (!index_only || !ce_intent_to_add(ce))
+				string_list_append(&files_staged, name);
+		}
+		else if (!index_only) {
+			if (staged_changes)
+				string_list_append(&files_cached, name);
+			if (local_changes)
+				string_list_append(&files_local, name);
+		}
+	}
+	print_error_files(&files_staged,
+			  Q_("the following file has staged content different "
+			     "from both the\nfile and the HEAD:",
+			     "the following files have staged content different"
+			     " from both the\nfile and the HEAD:",
+			     files_staged.nr),
+			  _("\n(use -f to force removal)"),
+			  &errs);
+	string_list_clear(&files_staged, 0);
+	print_error_files(&files_cached,
+			  Q_("the following file has changes "
+			     "staged in the index:",
+			     "the following files have changes "
+			     "staged in the index:", files_cached.nr),
+			  _("\n(use --cached to keep the file,"
+			    " or -f to force removal)"),
+			  &errs);
+	string_list_clear(&files_cached, 0);
+
+	print_error_files(&files_local,
+			  Q_("the following file has local modifications:",
+			     "the following files have local modifications:",
+			     files_local.nr),
+			  _("\n(use --cached to keep the file,"
+			    " or -f to force removal)"),
+			  &errs);
+	string_list_clear(&files_local, 0);
+
+	return errs;
+}
+
+static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
+static int ignore_unmatch = 0;
+
+static struct option builtin_rm_options[] = {
+	OPT__DRY_RUN(&show_only, N_("dry run")),
+	OPT__QUIET(&quiet, N_("do not list removed files")),
+	OPT_BOOL( 0 , "cached",         &index_only, N_("only remove from the index")),
+	OPT__FORCE(&force, N_("override the up-to-date check"), PARSE_OPT_NOCOMPLETE),
+	OPT_BOOL('r', NULL,             &recursive,  N_("allow recursive removal")),
+	OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch,
+				N_("exit with a zero status even if nothing matched")),
+	OPT_END(),
+};
+
+int cmd_rm(int argc, const char **argv, const char *prefix)
+{
+	struct lock_file lock_file = LOCK_INIT;
+	int i;
+	struct pathspec 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();
+
+	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+
+	if (read_cache() < 0)
+		die(_("index file corrupt"));
+
+	parse_pathspec(&pathspec, 0,
+		       PATHSPEC_PREFER_CWD,
+		       prefix, argv);
+	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
+
+	seen = xcalloc(pathspec.nr, 1);
+
+	for (i = 0; i < active_nr; i++) {
+		const struct cache_entry *ce = active_cache[i];
+		if (!ce_path_match(&the_index, ce, &pathspec, seen))
+			continue;
+		ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
+		list.entry[list.nr].name = xstrdup(ce->name);
+		list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
+		if (list.entry[list.nr++].is_submodule &&
+		    !is_staging_gitmodules_ok(&the_index))
+			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 (!index_only)
+		submodules_absorb_gitdir_if_needed();
+
+	/*
+	 * 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) {
+		struct object_id oid;
+		if (get_oid("HEAD", &oid))
+			oidclr(&oid);
+		if (check_local_mod(&oid, 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.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;
+		struct strbuf buf = STRBUF_INIT;
+		for (i = 0; i < list.nr; i++) {
+			const char *path = list.entry[i].name;
+			if (list.entry[i].is_submodule) {
+				strbuf_reset(&buf);
+				strbuf_addstr(&buf, path);
+				if (remove_dir_recursively(&buf, 0))
+					die(_("could not remove '%s'"), path);
+
+				removed = 1;
+				if (!remove_path_from_gitmodules(path))
+					gitmodules_modified = 1;
+				continue;
+			}
+			if (!remove_path(path)) {
+				removed = 1;
+				continue;
+			}
+			if (!removed)
+				die_errno("git rm: '%s'", path);
+		}
+		strbuf_release(&buf);
+		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;
+}
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
new file mode 100644
index 0000000000..098ebf22d0
--- /dev/null
+++ b/builtin/send-pack.c
@@ -0,0 +1,311 @@
+#include "builtin.h"
+#include "config.h"
+#include "commit.h"
+#include "refs.h"
+#include "pkt-line.h"
+#include "sideband.h"
+#include "run-command.h"
+#include "remote.h"
+#include "connect.h"
+#include "send-pack.h"
+#include "quote.h"
+#include "transport.h"
+#include "version.h"
+#include "sha1-array.h"
+#include "gpg-interface.h"
+#include "gettext.h"
+#include "protocol.h"
+
+static const char * const send_pack_usage[] = {
+	N_("git send-pack [--all | --mirror] [--dry-run] [--force] "
+	  "[--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] "
+	  "[<host>:]<directory> [<ref>...]\n"
+	  "  --all and explicit <ref> specification are mutually exclusive."),
+	NULL,
+};
+
+static struct send_pack_args args;
+
+static void print_helper_status(struct ref *ref)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	for (; ref; ref = ref->next) {
+		const char *msg = NULL;
+		const char *res;
+
+		switch(ref->status) {
+		case REF_STATUS_NONE:
+			res = "error";
+			msg = "no match";
+			break;
+
+		case REF_STATUS_OK:
+			res = "ok";
+			break;
+
+		case REF_STATUS_UPTODATE:
+			res = "ok";
+			msg = "up to date";
+			break;
+
+		case REF_STATUS_REJECT_NONFASTFORWARD:
+			res = "error";
+			msg = "non-fast forward";
+			break;
+
+		case REF_STATUS_REJECT_FETCH_FIRST:
+			res = "error";
+			msg = "fetch first";
+			break;
+
+		case REF_STATUS_REJECT_NEEDS_FORCE:
+			res = "error";
+			msg = "needs force";
+			break;
+
+		case REF_STATUS_REJECT_STALE:
+			res = "error";
+			msg = "stale info";
+			break;
+
+		case REF_STATUS_REJECT_ALREADY_EXISTS:
+			res = "error";
+			msg = "already exists";
+			break;
+
+		case REF_STATUS_REJECT_NODELETE:
+		case REF_STATUS_REMOTE_REJECT:
+			res = "error";
+			break;
+
+		case REF_STATUS_EXPECTING_REPORT:
+		default:
+			continue;
+		}
+
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "%s %s", res, ref->name);
+		if (ref->remote_status)
+			msg = ref->remote_status;
+		if (msg) {
+			strbuf_addch(&buf, ' ');
+			quote_two_c_style(&buf, "", msg, 0);
+		}
+		strbuf_addch(&buf, '\n');
+
+		write_or_die(1, buf.buf, buf.len);
+	}
+	strbuf_release(&buf);
+}
+
+static int send_pack_config(const char *k, const char *v, void *cb)
+{
+	git_gpg_config(k, v, NULL);
+
+	if (!strcmp(k, "push.gpgsign")) {
+		const char *value;
+		if (!git_config_get_value("push.gpgsign", &value)) {
+			switch (git_parse_maybe_bool(value)) {
+			case 0:
+				args.push_cert = SEND_PACK_PUSH_CERT_NEVER;
+				break;
+			case 1:
+				args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
+				break;
+			default:
+				if (value && !strcasecmp(value, "if-asked"))
+					args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;
+				else
+					return error("Invalid value for '%s'", k);
+			}
+		}
+	}
+	return git_default_config(k, v, cb);
+}
+
+int cmd_send_pack(int argc, const char **argv, const char *prefix)
+{
+	struct refspec rs = REFSPEC_INIT_PUSH;
+	const char *remote_name = NULL;
+	struct remote *remote = NULL;
+	const char *dest = NULL;
+	int fd[2];
+	struct child_process *conn;
+	struct oid_array extra_have = OID_ARRAY_INIT;
+	struct oid_array shallow = OID_ARRAY_INIT;
+	struct ref *remote_refs, *local_refs;
+	int ret;
+	int helper_status = 0;
+	int send_all = 0;
+	int verbose = 0;
+	const char *receivepack = "git-receive-pack";
+	unsigned dry_run = 0;
+	unsigned send_mirror = 0;
+	unsigned force_update = 0;
+	unsigned quiet = 0;
+	int push_cert = 0;
+	struct string_list push_options = STRING_LIST_INIT_NODUP;
+	unsigned use_thin_pack = 0;
+	unsigned atomic = 0;
+	unsigned stateless_rpc = 0;
+	int flags;
+	unsigned int reject_reasons;
+	int progress = -1;
+	int from_stdin = 0;
+	struct push_cas_option cas = {0};
+	struct packet_reader reader;
+
+	struct option options[] = {
+		OPT__VERBOSITY(&verbose),
+		OPT_STRING(0, "receive-pack", &receivepack, "receive-pack", N_("receive pack program")),
+		OPT_STRING(0, "exec", &receivepack, "receive-pack", N_("receive pack program")),
+		OPT_STRING(0, "remote", &remote_name, "remote", N_("remote name")),
+		OPT_BOOL(0, "all", &send_all, N_("push all refs")),
+		OPT_BOOL('n' , "dry-run", &dry_run, N_("dry run")),
+		OPT_BOOL(0, "mirror", &send_mirror, N_("mirror all refs")),
+		OPT_BOOL('f', "force", &force_update, N_("force updates")),
+		{ OPTION_CALLBACK,
+		  0, "signed", &push_cert, "(yes|no|if-asked)", N_("GPG sign the push"),
+		  PARSE_OPT_OPTARG, option_parse_push_signed },
+		OPT_STRING_LIST(0, "push-option", &push_options,
+				N_("server-specific"),
+				N_("option to transmit")),
+		OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
+		OPT_BOOL(0, "thin", &use_thin_pack, N_("use thin pack")),
+		OPT_BOOL(0, "atomic", &atomic, N_("request atomic transaction on remote side")),
+		OPT_BOOL(0, "stateless-rpc", &stateless_rpc, N_("use stateless RPC protocol")),
+		OPT_BOOL(0, "stdin", &from_stdin, N_("read refs from stdin")),
+		OPT_BOOL(0, "helper-status", &helper_status, N_("print status from remote helper")),
+		{ OPTION_CALLBACK,
+		  0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
+		  N_("require old value of ref to be at this value"),
+		  PARSE_OPT_OPTARG, parseopt_push_cas_option },
+		OPT_END()
+	};
+
+	git_config(send_pack_config, NULL);
+	argc = parse_options(argc, argv, prefix, options, send_pack_usage, 0);
+	if (argc > 0) {
+		dest = argv[0];
+		refspec_appendn(&rs, argv + 1, argc - 1);
+	}
+
+	if (!dest)
+		usage_with_options(send_pack_usage, options);
+
+	args.verbose = verbose;
+	args.dry_run = dry_run;
+	args.send_mirror = send_mirror;
+	args.force_update = force_update;
+	args.quiet = quiet;
+	args.push_cert = push_cert;
+	args.progress = progress;
+	args.use_thin_pack = use_thin_pack;
+	args.atomic = atomic;
+	args.stateless_rpc = stateless_rpc;
+	args.push_options = push_options.nr ? &push_options : NULL;
+
+	if (from_stdin) {
+		if (args.stateless_rpc) {
+			const char *buf;
+			while ((buf = packet_read_line(0, NULL)))
+				refspec_append(&rs, buf);
+		} else {
+			struct strbuf line = STRBUF_INIT;
+			while (strbuf_getline(&line, stdin) != EOF)
+				refspec_append(&rs, line.buf);
+			strbuf_release(&line);
+		}
+	}
+
+	/*
+	 * --all and --mirror are incompatible; neither makes sense
+	 * with any refspecs.
+	 */
+	if ((rs.nr > 0 && (send_all || args.send_mirror)) ||
+	    (send_all && args.send_mirror))
+		usage_with_options(send_pack_usage, options);
+
+	if (remote_name) {
+		remote = remote_get(remote_name);
+		if (!remote_has_url(remote, dest)) {
+			die("Destination %s is not a uri for %s",
+			    dest, remote_name);
+		}
+	}
+
+	if (progress == -1)
+		progress = !args.quiet && isatty(2);
+	args.progress = progress;
+
+	if (args.stateless_rpc) {
+		conn = NULL;
+		fd[0] = 0;
+		fd[1] = 1;
+	} else {
+		conn = git_connect(fd, dest, receivepack,
+			args.verbose ? CONNECT_VERBOSE : 0);
+	}
+
+	packet_reader_init(&reader, fd[0], NULL, 0,
+			   PACKET_READ_CHOMP_NEWLINE |
+			   PACKET_READ_GENTLE_ON_EOF |
+			   PACKET_READ_DIE_ON_ERR_PACKET);
+
+	switch (discover_version(&reader)) {
+	case protocol_v2:
+		die("support for protocol v2 not implemented yet");
+		break;
+	case protocol_v1:
+	case protocol_v0:
+		get_remote_heads(&reader, &remote_refs, REF_NORMAL,
+				 &extra_have, &shallow);
+		break;
+	case protocol_unknown_version:
+		BUG("unknown protocol version");
+	}
+
+	local_refs = get_local_heads();
+
+	flags = MATCH_REFS_NONE;
+
+	if (send_all)
+		flags |= MATCH_REFS_ALL;
+	if (args.send_mirror)
+		flags |= MATCH_REFS_MIRROR;
+
+	/* match them up */
+	if (match_push_refs(local_refs, &remote_refs, &rs, flags))
+		return -1;
+
+	if (!is_empty_cas(&cas))
+		apply_push_cas(&cas, remote, remote_refs);
+
+	set_ref_status_for_push(remote_refs, args.send_mirror,
+		args.force_update);
+
+	ret = send_pack(&args, fd, conn, remote_refs, &extra_have);
+
+	if (helper_status)
+		print_helper_status(remote_refs);
+
+	close(fd[1]);
+	close(fd[0]);
+
+	ret |= finish_connect(conn);
+
+	if (!helper_status)
+		transport_print_push_status(dest, remote_refs, args.verbose, 0, &reject_reasons);
+
+	if (!args.dry_run && remote) {
+		struct ref *ref;
+		for (ref = remote_refs; ref; ref = ref->next)
+			transport_update_tracking_ref(remote, ref, args.verbose);
+	}
+
+	if (!ret && !transport_refs_pushed(remote_refs))
+		fprintf(stderr, "Everything up-to-date\n");
+
+	return ret;
+}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
new file mode 100644
index 0000000000..65cd41392c
--- /dev/null
+++ b/builtin/shortlog.c
@@ -0,0 +1,379 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "commit.h"
+#include "diff.h"
+#include "string-list.h"
+#include "revision.h"
+#include "utf8.h"
+#include "mailmap.h"
+#include "shortlog.h"
+#include "parse-options.h"
+
+static char const * const shortlog_usage[] = {
+	N_("git shortlog [<options>] [<revision-range>] [[--] <path>...]"),
+	N_("git log --pretty=short | git shortlog [<options>]"),
+	NULL
+};
+
+/*
+ * The util field of our string_list_items will contain one of two things:
+ *
+ *   - if --summary is not in use, it will point to a string list of the
+ *     oneline subjects assigned to this author
+ *
+ *   - if --summary is in use, we don't need that list; we only need to know
+ *     its size. So we abuse the pointer slot to store our integer counter.
+ *
+ *  This macro accesses the latter.
+ */
+#define UTIL_TO_INT(x) ((intptr_t)(x)->util)
+
+static int compare_by_counter(const void *a1, const void *a2)
+{
+	const struct string_list_item *i1 = a1, *i2 = a2;
+	return UTIL_TO_INT(i2) - UTIL_TO_INT(i1);
+}
+
+static int compare_by_list(const void *a1, const void *a2)
+{
+	const struct string_list_item *i1 = a1, *i2 = a2;
+	const struct string_list *l1 = i1->util, *l2 = i2->util;
+
+	if (l1->nr < l2->nr)
+		return 1;
+	else if (l1->nr == l2->nr)
+		return 0;
+	else
+		return -1;
+}
+
+static void insert_one_record(struct shortlog *log,
+			      const char *author,
+			      const char *oneline)
+{
+	struct string_list_item *item;
+
+	item = string_list_insert(&log->list, author);
+
+	if (log->summary)
+		item->util = (void *)(UTIL_TO_INT(item) + 1);
+	else {
+		const char *dot3 = log->common_repo_prefix;
+		char *buffer, *p;
+		struct strbuf subject = STRBUF_INIT;
+		const char *eol;
+
+		/* Skip any leading whitespace, including any blank lines. */
+		while (*oneline && isspace(*oneline))
+			oneline++;
+		eol = strchr(oneline, '\n');
+		if (!eol)
+			eol = oneline + strlen(oneline);
+		if (starts_with(oneline, "[PATCH")) {
+			char *eob = strchr(oneline, ']');
+			if (eob && (!eol || eob < eol))
+				oneline = eob + 1;
+		}
+		while (*oneline && isspace(*oneline) && *oneline != '\n')
+			oneline++;
+		format_subject(&subject, oneline, " ");
+		buffer = strbuf_detach(&subject, NULL);
+
+		if (dot3) {
+			int dot3len = strlen(dot3);
+			if (dot3len > 5) {
+				while ((p = strstr(buffer, dot3)) != NULL) {
+					int taillen = strlen(p) - dot3len;
+					memcpy(p, "/.../", 5);
+					memmove(p + 5, p + dot3len, taillen + 1);
+				}
+			}
+		}
+
+		if (item->util == NULL)
+			item->util = xcalloc(1, sizeof(struct string_list));
+		string_list_append(item->util, buffer);
+	}
+}
+
+static int parse_stdin_author(struct shortlog *log,
+			       struct strbuf *out, const char *in)
+{
+	const char *mailbuf, *namebuf;
+	size_t namelen, maillen;
+	struct ident_split ident;
+
+	if (split_ident_line(&ident, in, strlen(in)))
+		return -1;
+
+	namebuf = ident.name_begin;
+	mailbuf = ident.mail_begin;
+	namelen = ident.name_end - ident.name_begin;
+	maillen = ident.mail_end - ident.mail_begin;
+
+	map_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
+	strbuf_add(out, namebuf, namelen);
+	if (log->email)
+		strbuf_addf(out, " <%.*s>", (int)maillen, mailbuf);
+
+	return 0;
+}
+
+static void read_from_stdin(struct shortlog *log)
+{
+	struct strbuf author = STRBUF_INIT;
+	struct strbuf mapped_author = STRBUF_INIT;
+	struct strbuf oneline = STRBUF_INIT;
+	static const char *author_match[2] = { "Author: ", "author " };
+	static const char *committer_match[2] = { "Commit: ", "committer " };
+	const char **match;
+
+	match = log->committer ? committer_match : author_match;
+	while (strbuf_getline_lf(&author, stdin) != EOF) {
+		const char *v;
+		if (!skip_prefix(author.buf, match[0], &v) &&
+		    !skip_prefix(author.buf, match[1], &v))
+			continue;
+		while (strbuf_getline_lf(&oneline, stdin) != EOF &&
+		       oneline.len)
+			; /* discard headers */
+		while (strbuf_getline_lf(&oneline, stdin) != EOF &&
+		       !oneline.len)
+			; /* discard blanks */
+
+		strbuf_reset(&mapped_author);
+		if (parse_stdin_author(log, &mapped_author, v) < 0)
+			continue;
+
+		insert_one_record(log, mapped_author.buf, oneline.buf);
+	}
+	strbuf_release(&author);
+	strbuf_release(&mapped_author);
+	strbuf_release(&oneline);
+}
+
+void shortlog_add_commit(struct shortlog *log, struct commit *commit)
+{
+	struct strbuf author = STRBUF_INIT;
+	struct strbuf oneline = STRBUF_INIT;
+	struct pretty_print_context ctx = {0};
+	const char *fmt;
+
+	ctx.fmt = CMIT_FMT_USERFORMAT;
+	ctx.abbrev = log->abbrev;
+	ctx.print_email_subject = 1;
+	ctx.date_mode.type = DATE_NORMAL;
+	ctx.output_encoding = get_log_output_encoding();
+
+	fmt = log->committer ?
+		(log->email ? "%cN <%cE>" : "%cN") :
+		(log->email ? "%aN <%aE>" : "%aN");
+
+	format_commit_message(commit, fmt, &author, &ctx);
+	if (!log->summary) {
+		if (log->user_format)
+			pretty_print_commit(&ctx, commit, &oneline);
+		else
+			format_commit_message(commit, "%s", &oneline, &ctx);
+	}
+
+	insert_one_record(log, author.buf, oneline.len ? oneline.buf : "<none>");
+
+	strbuf_release(&author);
+	strbuf_release(&oneline);
+}
+
+static void get_from_rev(struct rev_info *rev, struct shortlog *log)
+{
+	struct commit *commit;
+
+	if (prepare_revision_walk(rev))
+		die(_("revision walk setup failed"));
+	while ((commit = get_revision(rev)) != NULL)
+		shortlog_add_commit(log, commit);
+}
+
+static int parse_uint(char const **arg, int comma, int defval)
+{
+	unsigned long ul;
+	int ret;
+	char *endp;
+
+	ul = strtoul(*arg, &endp, 10);
+	if (*endp && *endp != comma)
+		return -1;
+	if (ul > INT_MAX)
+		return -1;
+	ret = *arg == endp ? defval : (int)ul;
+	*arg = *endp ? endp + 1 : endp;
+	return ret;
+}
+
+static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]";
+#define DEFAULT_WRAPLEN 76
+#define DEFAULT_INDENT1 6
+#define DEFAULT_INDENT2 9
+
+static int parse_wrap_args(const struct option *opt, const char *arg, int unset)
+{
+	struct shortlog *log = opt->value;
+
+	log->wrap_lines = !unset;
+	if (unset)
+		return 0;
+	if (!arg) {
+		log->wrap = DEFAULT_WRAPLEN;
+		log->in1 = DEFAULT_INDENT1;
+		log->in2 = DEFAULT_INDENT2;
+		return 0;
+	}
+
+	log->wrap = parse_uint(&arg, ',', DEFAULT_WRAPLEN);
+	log->in1 = parse_uint(&arg, ',', DEFAULT_INDENT1);
+	log->in2 = parse_uint(&arg, '\0', DEFAULT_INDENT2);
+	if (log->wrap < 0 || log->in1 < 0 || log->in2 < 0)
+		return error(wrap_arg_usage);
+	if (log->wrap &&
+	    ((log->in1 && log->wrap <= log->in1) ||
+	     (log->in2 && log->wrap <= log->in2)))
+		return error(wrap_arg_usage);
+	return 0;
+}
+
+void shortlog_init(struct shortlog *log)
+{
+	memset(log, 0, sizeof(*log));
+
+	read_mailmap(&log->mailmap, &log->common_repo_prefix);
+
+	log->list.strdup_strings = 1;
+	log->wrap = DEFAULT_WRAPLEN;
+	log->in1 = DEFAULT_INDENT1;
+	log->in2 = DEFAULT_INDENT2;
+}
+
+int cmd_shortlog(int argc, const char **argv, const char *prefix)
+{
+	struct shortlog log = { STRING_LIST_INIT_NODUP };
+	struct rev_info rev;
+	int nongit = !startup_info->have_repository;
+
+	const struct option options[] = {
+		OPT_BOOL('c', "committer", &log.committer,
+			 N_("Group by committer rather than author")),
+		OPT_BOOL('n', "numbered", &log.sort_by_number,
+			 N_("sort output according to the number of commits per author")),
+		OPT_BOOL('s', "summary", &log.summary,
+			 N_("Suppress commit descriptions, only provides commit count")),
+		OPT_BOOL('e', "email", &log.email,
+			 N_("Show the email address of each author")),
+		{ OPTION_CALLBACK, 'w', NULL, &log, N_("<w>[,<i1>[,<i2>]]"),
+			N_("Linewrap output"), PARSE_OPT_OPTARG,
+			&parse_wrap_args },
+		OPT_END(),
+	};
+
+	struct parse_opt_ctx_t ctx;
+
+	git_config(git_default_config, NULL);
+	shortlog_init(&log);
+	repo_init_revisions(the_repository, &rev, prefix);
+	parse_options_start(&ctx, argc, argv, prefix, options,
+			    PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
+
+	for (;;) {
+		switch (parse_options_step(&ctx, options, shortlog_usage)) {
+		case PARSE_OPT_HELP:
+		case PARSE_OPT_ERROR:
+			exit(129);
+		case PARSE_OPT_COMPLETE:
+			exit(0);
+		case PARSE_OPT_DONE:
+			goto parse_done;
+		}
+		parse_revision_opt(&rev, &ctx, options, shortlog_usage);
+	}
+parse_done:
+	argc = parse_options_end(&ctx);
+
+	if (nongit && argc > 1) {
+		error(_("too many arguments given outside repository"));
+		usage_with_options(shortlog_usage, options);
+	}
+
+	if (setup_revisions(argc, argv, &rev, NULL) != 1) {
+		error(_("unrecognized argument: %s"), argv[1]);
+		usage_with_options(shortlog_usage, options);
+	}
+
+	log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;
+	log.abbrev = rev.abbrev;
+	log.file = rev.diffopt.file;
+
+	/* assume HEAD if from a tty */
+	if (!nongit && !rev.pending.nr && isatty(0))
+		add_head_to_pending(&rev);
+	if (rev.pending.nr == 0) {
+		if (isatty(0))
+			fprintf(stderr, _("(reading log message from standard input)\n"));
+		read_from_stdin(&log);
+	}
+	else
+		get_from_rev(&rev, &log);
+
+	shortlog_output(&log);
+	if (log.file != stdout)
+		fclose(log.file);
+	return 0;
+}
+
+static void add_wrapped_shortlog_msg(struct strbuf *sb, const char *s,
+				     const struct shortlog *log)
+{
+	strbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap);
+	strbuf_addch(sb, '\n');
+}
+
+void shortlog_output(struct shortlog *log)
+{
+	int i, j;
+	struct strbuf sb = STRBUF_INIT;
+
+	if (log->sort_by_number)
+		QSORT(log->list.items, log->list.nr,
+		      log->summary ? compare_by_counter : compare_by_list);
+	for (i = 0; i < log->list.nr; i++) {
+		const struct string_list_item *item = &log->list.items[i];
+		if (log->summary) {
+			fprintf(log->file, "%6d\t%s\n",
+				(int)UTIL_TO_INT(item), item->string);
+		} else {
+			struct string_list *onelines = item->util;
+			fprintf(log->file, "%s (%d):\n",
+				item->string, onelines->nr);
+			for (j = onelines->nr - 1; j >= 0; j--) {
+				const char *msg = onelines->items[j].string;
+
+				if (log->wrap_lines) {
+					strbuf_reset(&sb);
+					add_wrapped_shortlog_msg(&sb, msg, log);
+					fwrite(sb.buf, sb.len, 1, log->file);
+				}
+				else
+					fprintf(log->file, "      %s\n", msg);
+			}
+			putc('\n', log->file);
+			onelines->strdup_strings = 1;
+			string_list_clear(onelines, 0);
+			free(onelines);
+		}
+
+		log->list.items[i].util = NULL;
+	}
+
+	strbuf_release(&sb);
+	log->list.strdup_strings = 1;
+	string_list_clear(&log->list, 1);
+	clear_mailmap(&log->mailmap);
+}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
new file mode 100644
index 0000000000..35d7f51c23
--- /dev/null
+++ b/builtin/show-branch.c
@@ -0,0 +1,954 @@
+#include "cache.h"
+#include "config.h"
+#include "pretty.h"
+#include "refs.h"
+#include "builtin.h"
+#include "color.h"
+#include "argv-array.h"
+#include "parse-options.h"
+#include "dir.h"
+#include "commit-slab.h"
+
+static const char* show_branch_usage[] = {
+    N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
+       "		[--current] [--color[=<when>] | --no-color] [--sparse]\n"
+       "		[--more=<n> | --list | --independent | --merge-base]\n"
+       "		[--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"),
+    N_("git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"),
+    NULL
+};
+
+static int showbranch_use_color = -1;
+
+static struct argv_array default_args = ARGV_ARRAY_INIT;
+
+/*
+ * TODO: convert this use of commit->object.flags to commit-slab
+ * instead to store a pointer to ref name directly. Then use the same
+ * UNINTERESTING definition from revision.h here.
+ */
+#define UNINTERESTING	01
+
+#define REV_SHIFT	 2
+#define MAX_REVS	(FLAG_BITS - REV_SHIFT) /* should not exceed bits_per_int - REV_SHIFT */
+
+#define DEFAULT_REFLOG	4
+
+static const char *get_color_code(int idx)
+{
+	if (want_color(showbranch_use_color))
+		return column_colors_ansi[idx % column_colors_ansi_max];
+	return "";
+}
+
+static const char *get_color_reset_code(void)
+{
+	if (want_color(showbranch_use_color))
+		return GIT_COLOR_RESET;
+	return "";
+}
+
+static struct commit *interesting(struct commit_list *list)
+{
+	while (list) {
+		struct commit *commit = list->item;
+		list = list->next;
+		if (commit->object.flags & UNINTERESTING)
+			continue;
+		return commit;
+	}
+	return NULL;
+}
+
+struct commit_name {
+	const char *head_name; /* which head's ancestor? */
+	int generation; /* how many parents away from head_name */
+};
+
+define_commit_slab(commit_name_slab, struct commit_name *);
+static struct commit_name_slab name_slab;
+
+static struct commit_name *commit_to_name(struct commit *commit)
+{
+	return *commit_name_slab_at(&name_slab, commit);
+}
+
+
+/* Name the commit as nth generation ancestor of head_name;
+ * we count only the first-parent relationship for naming purposes.
+ */
+static void name_commit(struct commit *commit, const char *head_name, int nth)
+{
+	struct commit_name *name;
+
+	name = *commit_name_slab_at(&name_slab, commit);
+	if (!name) {
+		name = xmalloc(sizeof(*name));
+		*commit_name_slab_at(&name_slab, commit) = name;
+	}
+	name->head_name = head_name;
+	name->generation = nth;
+}
+
+/* Parent is the first parent of the commit.  We may name it
+ * as (n+1)th generation ancestor of the same head_name as
+ * commit is nth generation ancestor of, if that generation
+ * number is better than the name it already has.
+ */
+static void name_parent(struct commit *commit, struct commit *parent)
+{
+	struct commit_name *commit_name = commit_to_name(commit);
+	struct commit_name *parent_name = commit_to_name(parent);
+	if (!commit_name)
+		return;
+	if (!parent_name ||
+	    commit_name->generation + 1 < parent_name->generation)
+		name_commit(parent, commit_name->head_name,
+			    commit_name->generation + 1);
+}
+
+static int name_first_parent_chain(struct commit *c)
+{
+	int i = 0;
+	while (c) {
+		struct commit *p;
+		if (!commit_to_name(c))
+			break;
+		if (!c->parents)
+			break;
+		p = c->parents->item;
+		if (!commit_to_name(p)) {
+			name_parent(c, p);
+			i++;
+		}
+		else
+			break;
+		c = p;
+	}
+	return i;
+}
+
+static void name_commits(struct commit_list *list,
+			 struct commit **rev,
+			 char **ref_name,
+			 int num_rev)
+{
+	struct commit_list *cl;
+	struct commit *c;
+	int i;
+
+	/* First give names to the given heads */
+	for (cl = list; cl; cl = cl->next) {
+		c = cl->item;
+		if (commit_to_name(c))
+			continue;
+		for (i = 0; i < num_rev; i++) {
+			if (rev[i] == c) {
+				name_commit(c, ref_name[i], 0);
+				break;
+			}
+		}
+	}
+
+	/* Then commits on the first parent ancestry chain */
+	do {
+		i = 0;
+		for (cl = list; cl; cl = cl->next) {
+			i += name_first_parent_chain(cl->item);
+		}
+	} while (i);
+
+	/* Finally, any unnamed commits */
+	do {
+		i = 0;
+		for (cl = list; cl; cl = cl->next) {
+			struct commit_list *parents;
+			struct commit_name *n;
+			int nth;
+			c = cl->item;
+			if (!commit_to_name(c))
+				continue;
+			n = commit_to_name(c);
+			parents = c->parents;
+			nth = 0;
+			while (parents) {
+				struct commit *p = parents->item;
+				struct strbuf newname = STRBUF_INIT;
+				parents = parents->next;
+				nth++;
+				if (commit_to_name(p))
+					continue;
+				switch (n->generation) {
+				case 0:
+					strbuf_addstr(&newname, n->head_name);
+					break;
+				case 1:
+					strbuf_addf(&newname, "%s^", n->head_name);
+					break;
+				default:
+					strbuf_addf(&newname, "%s~%d",
+						    n->head_name, n->generation);
+					break;
+				}
+				if (nth == 1)
+					strbuf_addch(&newname, '^');
+				else
+					strbuf_addf(&newname, "^%d", nth);
+				name_commit(p, strbuf_detach(&newname, NULL), 0);
+				i++;
+				name_first_parent_chain(p);
+			}
+		}
+	} while (i);
+}
+
+static int mark_seen(struct commit *commit, struct commit_list **seen_p)
+{
+	if (!commit->object.flags) {
+		commit_list_insert(commit, seen_p);
+		return 1;
+	}
+	return 0;
+}
+
+static void join_revs(struct commit_list **list_p,
+		      struct commit_list **seen_p,
+		      int num_rev, int extra)
+{
+	int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
+	int all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
+
+	while (*list_p) {
+		struct commit_list *parents;
+		int still_interesting = !!interesting(*list_p);
+		struct commit *commit = pop_commit(list_p);
+		int flags = commit->object.flags & all_mask;
+
+		if (!still_interesting && extra <= 0)
+			break;
+
+		mark_seen(commit, seen_p);
+		if ((flags & all_revs) == all_revs)
+			flags |= UNINTERESTING;
+		parents = commit->parents;
+
+		while (parents) {
+			struct commit *p = parents->item;
+			int this_flag = p->object.flags;
+			parents = parents->next;
+			if ((this_flag & flags) == flags)
+				continue;
+			parse_commit(p);
+			if (mark_seen(p, seen_p) && !still_interesting)
+				extra--;
+			p->object.flags |= flags;
+			commit_list_insert_by_date(p, list_p);
+		}
+	}
+
+	/*
+	 * Postprocess to complete well-poisoning.
+	 *
+	 * At this point we have all the commits we have seen in
+	 * seen_p list.  Mark anything that can be reached from
+	 * uninteresting commits not interesting.
+	 */
+	for (;;) {
+		int changed = 0;
+		struct commit_list *s;
+		for (s = *seen_p; s; s = s->next) {
+			struct commit *c = s->item;
+			struct commit_list *parents;
+
+			if (((c->object.flags & all_revs) != all_revs) &&
+			    !(c->object.flags & UNINTERESTING))
+				continue;
+
+			/* The current commit is either a merge base or
+			 * already uninteresting one.  Mark its parents
+			 * as uninteresting commits _only_ if they are
+			 * already parsed.  No reason to find new ones
+			 * here.
+			 */
+			parents = c->parents;
+			while (parents) {
+				struct commit *p = parents->item;
+				parents = parents->next;
+				if (!(p->object.flags & UNINTERESTING)) {
+					p->object.flags |= UNINTERESTING;
+					changed = 1;
+				}
+			}
+		}
+		if (!changed)
+			break;
+	}
+}
+
+static void show_one_commit(struct commit *commit, int no_name)
+{
+	struct strbuf pretty = STRBUF_INIT;
+	const char *pretty_str = "(unavailable)";
+	struct commit_name *name = commit_to_name(commit);
+
+	if (commit->object.parsed) {
+		pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty);
+		pretty_str = pretty.buf;
+	}
+	skip_prefix(pretty_str, "[PATCH] ", &pretty_str);
+
+	if (!no_name) {
+		if (name && name->head_name) {
+			printf("[%s", name->head_name);
+			if (name->generation) {
+				if (name->generation == 1)
+					printf("^");
+				else
+					printf("~%d", name->generation);
+			}
+			printf("] ");
+		}
+		else
+			printf("[%s] ",
+			       find_unique_abbrev(&commit->object.oid,
+						  DEFAULT_ABBREV));
+	}
+	puts(pretty_str);
+	strbuf_release(&pretty);
+}
+
+static char *ref_name[MAX_REVS + 1];
+static int ref_name_cnt;
+
+static const char *find_digit_prefix(const char *s, int *v)
+{
+	const char *p;
+	int ver;
+	char ch;
+
+	for (p = s, ver = 0;
+	     '0' <= (ch = *p) && ch <= '9';
+	     p++)
+		ver = ver * 10 + ch - '0';
+	*v = ver;
+	return p;
+}
+
+
+static int version_cmp(const char *a, const char *b)
+{
+	while (1) {
+		int va, vb;
+
+		a = find_digit_prefix(a, &va);
+		b = find_digit_prefix(b, &vb);
+		if (va != vb)
+			return va - vb;
+
+		while (1) {
+			int ca = *a;
+			int cb = *b;
+			if ('0' <= ca && ca <= '9')
+				ca = 0;
+			if ('0' <= cb && cb <= '9')
+				cb = 0;
+			if (ca != cb)
+				return ca - cb;
+			if (!ca)
+				break;
+			a++;
+			b++;
+		}
+		if (!*a && !*b)
+			return 0;
+	}
+}
+
+static int compare_ref_name(const void *a_, const void *b_)
+{
+	const char * const*a = a_, * const*b = b_;
+	return version_cmp(*a, *b);
+}
+
+static void sort_ref_range(int bottom, int top)
+{
+	QSORT(ref_name + bottom, top - bottom, compare_ref_name);
+}
+
+static int append_ref(const char *refname, const struct object_id *oid,
+		      int allow_dups)
+{
+	struct commit *commit = lookup_commit_reference_gently(the_repository,
+							       oid, 1);
+	int i;
+
+	if (!commit)
+		return 0;
+
+	if (!allow_dups) {
+		/* Avoid adding the same thing twice */
+		for (i = 0; i < ref_name_cnt; i++)
+			if (!strcmp(refname, ref_name[i]))
+				return 0;
+	}
+	if (MAX_REVS <= ref_name_cnt) {
+		warning(Q_("ignoring %s; cannot handle more than %d ref",
+			   "ignoring %s; cannot handle more than %d refs",
+			   MAX_REVS), refname, MAX_REVS);
+		return 0;
+	}
+	ref_name[ref_name_cnt++] = xstrdup(refname);
+	ref_name[ref_name_cnt] = NULL;
+	return 0;
+}
+
+static int append_head_ref(const char *refname, const struct object_id *oid,
+			   int flag, void *cb_data)
+{
+	struct object_id tmp;
+	int ofs = 11;
+	if (!starts_with(refname, "refs/heads/"))
+		return 0;
+	/* If both heads/foo and tags/foo exists, get_sha1 would
+	 * get confused.
+	 */
+	if (get_oid(refname + ofs, &tmp) || !oideq(&tmp, oid))
+		ofs = 5;
+	return append_ref(refname + ofs, oid, 0);
+}
+
+static int append_remote_ref(const char *refname, const struct object_id *oid,
+			     int flag, void *cb_data)
+{
+	struct object_id tmp;
+	int ofs = 13;
+	if (!starts_with(refname, "refs/remotes/"))
+		return 0;
+	/* If both heads/foo and tags/foo exists, get_sha1 would
+	 * get confused.
+	 */
+	if (get_oid(refname + ofs, &tmp) || !oideq(&tmp, oid))
+		ofs = 5;
+	return append_ref(refname + ofs, oid, 0);
+}
+
+static int append_tag_ref(const char *refname, const struct object_id *oid,
+			  int flag, void *cb_data)
+{
+	if (!starts_with(refname, "refs/tags/"))
+		return 0;
+	return append_ref(refname + 5, oid, 0);
+}
+
+static const char *match_ref_pattern = NULL;
+static int match_ref_slash = 0;
+
+static int append_matching_ref(const char *refname, const struct object_id *oid,
+			       int flag, void *cb_data)
+{
+	/* we want to allow pattern hold/<asterisk> to show all
+	 * branches under refs/heads/hold/, and v0.99.9? to show
+	 * refs/tags/v0.99.9a and friends.
+	 */
+	const char *tail;
+	int slash = count_slashes(refname);
+	for (tail = refname; *tail && match_ref_slash < slash; )
+		if (*tail++ == '/')
+			slash--;
+	if (!*tail)
+		return 0;
+	if (wildmatch(match_ref_pattern, tail, 0))
+		return 0;
+	if (starts_with(refname, "refs/heads/"))
+		return append_head_ref(refname, oid, flag, cb_data);
+	if (starts_with(refname, "refs/tags/"))
+		return append_tag_ref(refname, oid, flag, cb_data);
+	return append_ref(refname, oid, 0);
+}
+
+static void snarf_refs(int head, int remotes)
+{
+	if (head) {
+		int orig_cnt = ref_name_cnt;
+
+		for_each_ref(append_head_ref, NULL);
+		sort_ref_range(orig_cnt, ref_name_cnt);
+	}
+	if (remotes) {
+		int orig_cnt = ref_name_cnt;
+
+		for_each_ref(append_remote_ref, NULL);
+		sort_ref_range(orig_cnt, ref_name_cnt);
+	}
+}
+
+static int rev_is_head(const char *head, const char *name,
+		       unsigned char *head_sha1, unsigned char *sha1)
+{
+	if (!head || (head_sha1 && sha1 && !hasheq(head_sha1, sha1)))
+		return 0;
+	skip_prefix(head, "refs/heads/", &head);
+	if (!skip_prefix(name, "refs/heads/", &name))
+		skip_prefix(name, "heads/", &name);
+	return !strcmp(head, name);
+}
+
+static int show_merge_base(struct commit_list *seen, int num_rev)
+{
+	int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
+	int all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
+	int exit_status = 1;
+
+	while (seen) {
+		struct commit *commit = pop_commit(&seen);
+		int flags = commit->object.flags & all_mask;
+		if (!(flags & UNINTERESTING) &&
+		    ((flags & all_revs) == all_revs)) {
+			puts(oid_to_hex(&commit->object.oid));
+			exit_status = 0;
+			commit->object.flags |= UNINTERESTING;
+		}
+	}
+	return exit_status;
+}
+
+static int show_independent(struct commit **rev,
+			    int num_rev,
+			    unsigned int *rev_mask)
+{
+	int i;
+
+	for (i = 0; i < num_rev; i++) {
+		struct commit *commit = rev[i];
+		unsigned int flag = rev_mask[i];
+
+		if (commit->object.flags == flag)
+			puts(oid_to_hex(&commit->object.oid));
+		commit->object.flags |= UNINTERESTING;
+	}
+	return 0;
+}
+
+static void append_one_rev(const char *av)
+{
+	struct object_id revkey;
+	if (!get_oid(av, &revkey)) {
+		append_ref(av, &revkey, 0);
+		return;
+	}
+	if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {
+		/* glob style match */
+		int saved_matches = ref_name_cnt;
+
+		match_ref_pattern = av;
+		match_ref_slash = count_slashes(av);
+		for_each_ref(append_matching_ref, NULL);
+		if (saved_matches == ref_name_cnt &&
+		    ref_name_cnt < MAX_REVS)
+			error(_("no matching refs with %s"), av);
+		sort_ref_range(saved_matches, ref_name_cnt);
+		return;
+	}
+	die("bad sha1 reference %s", av);
+}
+
+static int git_show_branch_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "showbranch.default")) {
+		if (!value)
+			return config_error_nonbool(var);
+		/*
+		 * default_arg is now passed to parse_options(), so we need to
+		 * mimic the real argv a bit better.
+		 */
+		if (!default_args.argc)
+			argv_array_push(&default_args, "show-branch");
+		argv_array_push(&default_args, value);
+		return 0;
+	}
+
+	if (!strcmp(var, "color.showbranch")) {
+		showbranch_use_color = git_config_colorbool(var, value);
+		return 0;
+	}
+
+	return git_color_default_config(var, value, cb);
+}
+
+static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
+{
+	/* If the commit is tip of the named branches, do not
+	 * omit it.
+	 * Otherwise, if it is a merge that is reachable from only one
+	 * tip, it is not that interesting.
+	 */
+	int i, flag, count;
+	for (i = 0; i < n; i++)
+		if (rev[i] == commit)
+			return 0;
+	flag = commit->object.flags;
+	for (i = count = 0; i < n; i++) {
+		if (flag & (1u << (i + REV_SHIFT)))
+			count++;
+	}
+	if (count == 1)
+		return 1;
+	return 0;
+}
+
+static int reflog = 0;
+
+static int parse_reflog_param(const struct option *opt, const char *arg,
+			      int unset)
+{
+	char *ep;
+	const char **base = (const char **)opt->value;
+	BUG_ON_OPT_NEG(unset);
+	if (!arg)
+		arg = "";
+	reflog = strtoul(arg, &ep, 10);
+	if (*ep == ',')
+		*base = ep + 1;
+	else if (*ep)
+		return error("unrecognized reflog param '%s'", arg);
+	else
+		*base = NULL;
+	if (reflog <= 0)
+		reflog = DEFAULT_REFLOG;
+	return 0;
+}
+
+int cmd_show_branch(int ac, const char **av, const char *prefix)
+{
+	struct commit *rev[MAX_REVS], *commit;
+	char *reflog_msg[MAX_REVS];
+	struct commit_list *list = NULL, *seen = NULL;
+	unsigned int rev_mask[MAX_REVS];
+	int num_rev, i, extra = 0;
+	int all_heads = 0, all_remotes = 0;
+	int all_mask, all_revs;
+	enum rev_sort_order sort_order = REV_SORT_IN_GRAPH_ORDER;
+	char *head;
+	struct object_id head_oid;
+	int merge_base = 0;
+	int independent = 0;
+	int no_name = 0;
+	int sha1_name = 0;
+	int shown_merge_point = 0;
+	int with_current_branch = 0;
+	int head_at = -1;
+	int topics = 0;
+	int dense = 1;
+	const char *reflog_base = NULL;
+	struct option builtin_show_branch_options[] = {
+		OPT_BOOL('a', "all", &all_heads,
+			 N_("show remote-tracking and local branches")),
+		OPT_BOOL('r', "remotes", &all_remotes,
+			 N_("show remote-tracking branches")),
+		OPT__COLOR(&showbranch_use_color,
+			    N_("color '*!+-' corresponding to the branch")),
+		{ OPTION_INTEGER, 0, "more", &extra, N_("n"),
+			    N_("show <n> more commits after the common ancestor"),
+			    PARSE_OPT_OPTARG, NULL, (intptr_t)1 },
+		OPT_SET_INT(0, "list", &extra, N_("synonym to more=-1"), -1),
+		OPT_BOOL(0, "no-name", &no_name, N_("suppress naming strings")),
+		OPT_BOOL(0, "current", &with_current_branch,
+			 N_("include the current branch")),
+		OPT_BOOL(0, "sha1-name", &sha1_name,
+			 N_("name commits with their object names")),
+		OPT_BOOL(0, "merge-base", &merge_base,
+			 N_("show possible merge bases")),
+		OPT_BOOL(0, "independent", &independent,
+			    N_("show refs unreachable from any other ref")),
+		OPT_SET_INT(0, "topo-order", &sort_order,
+			    N_("show commits in topological order"),
+			    REV_SORT_IN_GRAPH_ORDER),
+		OPT_BOOL(0, "topics", &topics,
+			 N_("show only commits not on the first branch")),
+		OPT_SET_INT(0, "sparse", &dense,
+			    N_("show merges reachable from only one tip"), 0),
+		OPT_SET_INT(0, "date-order", &sort_order,
+			    N_("topologically sort, maintaining date order "
+			       "where possible"),
+			    REV_SORT_BY_COMMIT_DATE),
+		{ OPTION_CALLBACK, 'g', "reflog", &reflog_base, N_("<n>[,<base>]"),
+			    N_("show <n> most recent ref-log entries starting at "
+			       "base"),
+			    PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
+			    parse_reflog_param },
+		OPT_END()
+	};
+
+	init_commit_name_slab(&name_slab);
+
+	git_config(git_show_branch_config, NULL);
+
+	/* If nothing is specified, try the default first */
+	if (ac == 1 && default_args.argc) {
+		ac = default_args.argc;
+		av = default_args.argv;
+	}
+
+	ac = parse_options(ac, av, prefix, builtin_show_branch_options,
+			   show_branch_usage, PARSE_OPT_STOP_AT_NON_OPTION);
+	if (all_heads)
+		all_remotes = 1;
+
+	if (extra || reflog) {
+		/* "listing" mode is incompatible with
+		 * independent nor merge-base modes.
+		 */
+		if (independent || merge_base)
+			usage_with_options(show_branch_usage,
+					   builtin_show_branch_options);
+		if (reflog && ((0 < extra) || all_heads || all_remotes))
+			/*
+			 * Asking for --more in reflog mode does not
+			 * make sense.  --list is Ok.
+			 *
+			 * Also --all and --remotes do not make sense either.
+			 */
+			die(_("--reflog is incompatible with --all, --remotes, "
+			      "--independent or --merge-base"));
+	}
+
+	/* If nothing is specified, show all branches by default */
+	if (ac <= topics && all_heads + all_remotes == 0)
+		all_heads = 1;
+
+	if (reflog) {
+		struct object_id oid;
+		char *ref;
+		int base = 0;
+		unsigned int flags = 0;
+
+		if (ac == 0) {
+			static const char *fake_av[2];
+
+			fake_av[0] = resolve_refdup("HEAD",
+						    RESOLVE_REF_READING, &oid,
+						    NULL);
+			fake_av[1] = NULL;
+			av = fake_av;
+			ac = 1;
+			if (!*av)
+				die(_("no branches given, and HEAD is not valid"));
+		}
+		if (ac != 1)
+			die(_("--reflog option needs one branch name"));
+
+		if (MAX_REVS < reflog)
+			die(Q_("only %d entry can be shown at one time.",
+			       "only %d entries can be shown at one time.",
+			       MAX_REVS), MAX_REVS);
+		if (!dwim_ref(*av, strlen(*av), &oid, &ref))
+			die(_("no such ref %s"), *av);
+
+		/* Has the base been specified? */
+		if (reflog_base) {
+			char *ep;
+			base = strtoul(reflog_base, &ep, 10);
+			if (*ep) {
+				/* Ah, that is a date spec... */
+				timestamp_t at;
+				at = approxidate(reflog_base);
+				read_ref_at(get_main_ref_store(the_repository),
+					    ref, flags, at, -1, &oid, NULL,
+					    NULL, NULL, &base);
+			}
+		}
+
+		for (i = 0; i < reflog; i++) {
+			char *logmsg;
+			char *nth_desc;
+			const char *msg;
+			timestamp_t timestamp;
+			int tz;
+
+			if (read_ref_at(get_main_ref_store(the_repository),
+					ref, flags, 0, base + i, &oid, &logmsg,
+					&timestamp, &tz, NULL)) {
+				reflog = i;
+				break;
+			}
+			msg = strchr(logmsg, '\t');
+			if (!msg)
+				msg = "(none)";
+			else
+				msg++;
+			reflog_msg[i] = xstrfmt("(%s) %s",
+						show_date(timestamp, tz,
+							  DATE_MODE(RELATIVE)),
+						msg);
+			free(logmsg);
+
+			nth_desc = xstrfmt("%s@{%d}", *av, base+i);
+			append_ref(nth_desc, &oid, 1);
+			free(nth_desc);
+		}
+		free(ref);
+	}
+	else {
+		while (0 < ac) {
+			append_one_rev(*av);
+			ac--; av++;
+		}
+		if (all_heads + all_remotes)
+			snarf_refs(all_heads, all_remotes);
+	}
+
+	head = resolve_refdup("HEAD", RESOLVE_REF_READING,
+			      &head_oid, NULL);
+
+	if (with_current_branch && head) {
+		int has_head = 0;
+		for (i = 0; !has_head && i < ref_name_cnt; i++) {
+			/* We are only interested in adding the branch
+			 * HEAD points at.
+			 */
+			if (rev_is_head(head,
+					ref_name[i],
+					head_oid.hash, NULL))
+				has_head++;
+		}
+		if (!has_head) {
+			const char *name = head;
+			skip_prefix(name, "refs/heads/", &name);
+			append_one_rev(name);
+		}
+	}
+
+	if (!ref_name_cnt) {
+		fprintf(stderr, "No revs to be shown.\n");
+		exit(0);
+	}
+
+	for (num_rev = 0; ref_name[num_rev]; num_rev++) {
+		struct object_id revkey;
+		unsigned int flag = 1u << (num_rev + REV_SHIFT);
+
+		if (MAX_REVS <= num_rev)
+			die(Q_("cannot handle more than %d rev.",
+			       "cannot handle more than %d revs.",
+			       MAX_REVS), MAX_REVS);
+		if (get_oid(ref_name[num_rev], &revkey))
+			die(_("'%s' is not a valid ref."), ref_name[num_rev]);
+		commit = lookup_commit_reference(the_repository, &revkey);
+		if (!commit)
+			die(_("cannot find commit %s (%s)"),
+			    ref_name[num_rev], oid_to_hex(&revkey));
+		parse_commit(commit);
+		mark_seen(commit, &seen);
+
+		/* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1,
+		 * and so on.  REV_SHIFT bits from bit 0 are used for
+		 * internal bookkeeping.
+		 */
+		commit->object.flags |= flag;
+		if (commit->object.flags == flag)
+			commit_list_insert_by_date(commit, &list);
+		rev[num_rev] = commit;
+	}
+	for (i = 0; i < num_rev; i++)
+		rev_mask[i] = rev[i]->object.flags;
+
+	if (0 <= extra)
+		join_revs(&list, &seen, num_rev, extra);
+
+	commit_list_sort_by_date(&seen);
+
+	if (merge_base)
+		return show_merge_base(seen, num_rev);
+
+	if (independent)
+		return show_independent(rev, num_rev, rev_mask);
+
+	/* Show list; --more=-1 means list-only */
+	if (1 < num_rev || extra < 0) {
+		for (i = 0; i < num_rev; i++) {
+			int j;
+			int is_head = rev_is_head(head,
+						  ref_name[i],
+						  head_oid.hash,
+						  rev[i]->object.oid.hash);
+			if (extra < 0)
+				printf("%c [%s] ",
+				       is_head ? '*' : ' ', ref_name[i]);
+			else {
+				for (j = 0; j < i; j++)
+					putchar(' ');
+				printf("%s%c%s [%s] ",
+				       get_color_code(i),
+				       is_head ? '*' : '!',
+				       get_color_reset_code(), ref_name[i]);
+			}
+
+			if (!reflog) {
+				/* header lines never need name */
+				show_one_commit(rev[i], 1);
+			}
+			else
+				puts(reflog_msg[i]);
+
+			if (is_head)
+				head_at = i;
+		}
+		if (0 <= extra) {
+			for (i = 0; i < num_rev; i++)
+				putchar('-');
+			putchar('\n');
+		}
+	}
+	if (extra < 0)
+		exit(0);
+
+	/* Sort topologically */
+	sort_in_topological_order(&seen, sort_order);
+
+	/* Give names to commits */
+	if (!sha1_name && !no_name)
+		name_commits(seen, rev, ref_name, num_rev);
+
+	all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
+	all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
+
+	while (seen) {
+		struct commit *commit = pop_commit(&seen);
+		int this_flag = commit->object.flags;
+		int is_merge_point = ((this_flag & all_revs) == all_revs);
+
+		shown_merge_point |= is_merge_point;
+
+		if (1 < num_rev) {
+			int is_merge = !!(commit->parents &&
+					  commit->parents->next);
+			if (topics &&
+			    !is_merge_point &&
+			    (this_flag & (1u << REV_SHIFT)))
+				continue;
+			if (dense && is_merge &&
+			    omit_in_dense(commit, rev, num_rev))
+				continue;
+			for (i = 0; i < num_rev; i++) {
+				int mark;
+				if (!(this_flag & (1u << (i + REV_SHIFT))))
+					mark = ' ';
+				else if (is_merge)
+					mark = '-';
+				else if (i == head_at)
+					mark = '*';
+				else
+					mark = '+';
+				printf("%s%c%s",
+				       get_color_code(i),
+				       mark, get_color_reset_code());
+			}
+			putchar(' ');
+		}
+		show_one_commit(commit, no_name);
+
+		if (shown_merge_point && --extra < 0)
+			break;
+	}
+	return 0;
+}
diff --git a/builtin/show-index.c b/builtin/show-index.c
new file mode 100644
index 0000000000..a6e678809e
--- /dev/null
+++ b/builtin/show-index.c
@@ -0,0 +1,86 @@
+#include "builtin.h"
+#include "cache.h"
+#include "pack.h"
+
+static const char show_index_usage[] =
+"git show-index";
+
+int cmd_show_index(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	unsigned nr;
+	unsigned int version;
+	static unsigned int top_index[256];
+
+	if (argc != 1)
+		usage(show_index_usage);
+	if (fread(top_index, 2 * 4, 1, stdin) != 1)
+		die("unable to read header");
+	if (top_index[0] == htonl(PACK_IDX_SIGNATURE)) {
+		version = ntohl(top_index[1]);
+		if (version < 2 || version > 2)
+			die("unknown index version");
+		if (fread(top_index, 256 * 4, 1, stdin) != 1)
+			die("unable to read index");
+	} else {
+		version = 1;
+		if (fread(&top_index[2], 254 * 4, 1, stdin) != 1)
+			die("unable to read index");
+	}
+	nr = 0;
+	for (i = 0; i < 256; i++) {
+		unsigned n = ntohl(top_index[i]);
+		if (n < nr)
+			die("corrupt index file");
+		nr = n;
+	}
+	if (version == 1) {
+		for (i = 0; i < nr; i++) {
+			unsigned int offset, entry[6];
+
+			if (fread(entry, 4 + 20, 1, stdin) != 1)
+				die("unable to read entry %u/%u", i, nr);
+			offset = ntohl(entry[0]);
+			printf("%u %s\n", offset, sha1_to_hex((void *)(entry+1)));
+		}
+	} else {
+		unsigned off64_nr = 0;
+		struct {
+			unsigned char sha1[20];
+			uint32_t crc;
+			uint32_t off;
+		} *entries;
+		ALLOC_ARRAY(entries, nr);
+		for (i = 0; i < nr; i++)
+			if (fread(entries[i].sha1, 20, 1, stdin) != 1)
+				die("unable to read sha1 %u/%u", i, nr);
+		for (i = 0; i < nr; i++)
+			if (fread(&entries[i].crc, 4, 1, stdin) != 1)
+				die("unable to read crc %u/%u", i, nr);
+		for (i = 0; i < nr; i++)
+			if (fread(&entries[i].off, 4, 1, stdin) != 1)
+				die("unable to read 32b offset %u/%u", i, nr);
+		for (i = 0; i < nr; i++) {
+			uint64_t offset;
+			uint32_t off = ntohl(entries[i].off);
+			if (!(off & 0x80000000)) {
+				offset = off;
+			} else {
+				uint32_t off64[2];
+				if ((off & 0x7fffffff) != off64_nr)
+					die("inconsistent 64b offset index");
+				if (fread(off64, 8, 1, stdin) != 1)
+					die("unable to read 64b offset %u", off64_nr);
+				offset = (((uint64_t)ntohl(off64[0])) << 32) |
+						     ntohl(off64[1]);
+				off64_nr++;
+			}
+			printf("%" PRIuMAX " %s (%08"PRIx32")\n",
+			       (uintmax_t) offset,
+			       sha1_to_hex(entries[i].sha1),
+			       ntohl(entries[i].crc));
+		}
+		free(entries);
+	}
+	return 0;
+}
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
new file mode 100644
index 0000000000..6456da70cc
--- /dev/null
+++ b/builtin/show-ref.c
@@ -0,0 +1,226 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "refs.h"
+#include "object-store.h"
+#include "object.h"
+#include "tag.h"
+#include "string-list.h"
+#include "parse-options.h"
+
+static const char * const show_ref_usage[] = {
+	N_("git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"),
+	N_("git show-ref --exclude-existing[=<pattern>]"),
+	NULL
+};
+
+static int deref_tags, show_head, tags_only, heads_only, found_match, verify,
+	   quiet, hash_only, abbrev, exclude_arg;
+static const char **pattern;
+static const char *exclude_existing_arg;
+
+static void show_one(const char *refname, const struct object_id *oid)
+{
+	const char *hex;
+	struct object_id peeled;
+
+	if (!has_object_file(oid))
+		die("git show-ref: bad ref %s (%s)", refname,
+		    oid_to_hex(oid));
+
+	if (quiet)
+		return;
+
+	hex = find_unique_abbrev(oid, abbrev);
+	if (hash_only)
+		printf("%s\n", hex);
+	else
+		printf("%s %s\n", hex, refname);
+
+	if (!deref_tags)
+		return;
+
+	if (!peel_ref(refname, &peeled)) {
+		hex = find_unique_abbrev(&peeled, abbrev);
+		printf("%s %s^{}\n", hex, refname);
+	}
+}
+
+static int show_ref(const char *refname, const struct object_id *oid,
+		    int flag, void *cbdata)
+{
+	if (show_head && !strcmp(refname, "HEAD"))
+		goto match;
+
+	if (tags_only || heads_only) {
+		int match;
+
+		match = heads_only && starts_with(refname, "refs/heads/");
+		match |= tags_only && starts_with(refname, "refs/tags/");
+		if (!match)
+			return 0;
+	}
+	if (pattern) {
+		int reflen = strlen(refname);
+		const char **p = pattern, *m;
+		while ((m = *p++) != NULL) {
+			int len = strlen(m);
+			if (len > reflen)
+				continue;
+			if (memcmp(m, refname + reflen - len, len))
+				continue;
+			if (len == reflen)
+				goto match;
+			if (refname[reflen - len - 1] == '/')
+				goto match;
+		}
+		return 0;
+	}
+
+match:
+	found_match++;
+
+	show_one(refname, oid);
+
+	return 0;
+}
+
+static int add_existing(const char *refname, const struct object_id *oid,
+			int flag, void *cbdata)
+{
+	struct string_list *list = (struct string_list *)cbdata;
+	string_list_insert(list, refname);
+	return 0;
+}
+
+/*
+ * read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input,
+ * and
+ * (1) strip "^{}" at the end of line if any;
+ * (2) ignore if match is provided and does not head-match refname;
+ * (3) warn if refname is not a well-formed refname and skip;
+ * (4) ignore if refname is a ref that exists in the local repository;
+ * (5) otherwise output the line.
+ */
+static int exclude_existing(const char *match)
+{
+	static struct string_list existing_refs = STRING_LIST_INIT_DUP;
+	char buf[1024];
+	int matchlen = match ? strlen(match) : 0;
+
+	for_each_ref(add_existing, &existing_refs);
+	while (fgets(buf, sizeof(buf), stdin)) {
+		char *ref;
+		int len = strlen(buf);
+
+		if (len > 0 && buf[len - 1] == '\n')
+			buf[--len] = '\0';
+		if (3 <= len && !strcmp(buf + len - 3, "^{}")) {
+			len -= 3;
+			buf[len] = '\0';
+		}
+		for (ref = buf + len; buf < ref; ref--)
+			if (isspace(ref[-1]))
+				break;
+		if (match) {
+			int reflen = buf + len - ref;
+			if (reflen < matchlen)
+				continue;
+			if (strncmp(ref, match, matchlen))
+				continue;
+		}
+		if (check_refname_format(ref, 0)) {
+			warning("ref '%s' ignored", ref);
+			continue;
+		}
+		if (!string_list_has_string(&existing_refs, ref)) {
+			printf("%s\n", buf);
+		}
+	}
+	return 0;
+}
+
+static int hash_callback(const struct option *opt, const char *arg, int unset)
+{
+	hash_only = 1;
+	/* Use full length SHA1 if no argument */
+	if (!arg)
+		return 0;
+	return parse_opt_abbrev_cb(opt, arg, unset);
+}
+
+static int exclude_existing_callback(const struct option *opt, const char *arg,
+				     int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+	exclude_arg = 1;
+	*(const char **)opt->value = arg;
+	return 0;
+}
+
+static const struct option show_ref_options[] = {
+	OPT_BOOL(0, "tags", &tags_only, N_("only show tags (can be combined with heads)")),
+	OPT_BOOL(0, "heads", &heads_only, N_("only show heads (can be combined with tags)")),
+	OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, "
+		    "requires exact ref path")),
+	OPT_HIDDEN_BOOL('h', NULL, &show_head,
+			N_("show the HEAD reference, even if it would be filtered out")),
+	OPT_BOOL(0, "head", &show_head,
+	  N_("show the HEAD reference, even if it would be filtered out")),
+	OPT_BOOL('d', "dereference", &deref_tags,
+		    N_("dereference tags into object IDs")),
+	{ OPTION_CALLBACK, 's', "hash", &abbrev, N_("n"),
+	  N_("only show SHA1 hash using <n> digits"),
+	  PARSE_OPT_OPTARG, &hash_callback },
+	OPT__ABBREV(&abbrev),
+	OPT__QUIET(&quiet,
+		   N_("do not print results to stdout (useful with --verify)")),
+	{ OPTION_CALLBACK, 0, "exclude-existing", &exclude_existing_arg,
+	  N_("pattern"), N_("show refs from stdin that aren't in local repository"),
+	  PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback },
+	OPT_END()
+};
+
+int cmd_show_ref(int argc, const char **argv, const char *prefix)
+{
+	git_config(git_default_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, show_ref_options,
+			     show_ref_usage, 0);
+
+	if (exclude_arg)
+		return exclude_existing(exclude_existing_arg);
+
+	pattern = argv;
+	if (!*pattern)
+		pattern = NULL;
+
+	if (verify) {
+		if (!pattern)
+			die("--verify requires a reference");
+		while (*pattern) {
+			struct object_id oid;
+
+			if ((starts_with(*pattern, "refs/") || !strcmp(*pattern, "HEAD")) &&
+			    !read_ref(*pattern, &oid)) {
+				show_one(*pattern, &oid);
+			}
+			else if (!quiet)
+				die("'%s' - not a valid ref", *pattern);
+			else
+				return 1;
+			pattern++;
+		}
+		return 0;
+	}
+
+	if (show_head)
+		head_ref(show_ref, NULL);
+	for_each_ref(show_ref, NULL);
+	if (!found_match) {
+		if (verify && !quiet)
+			die("No match");
+		return 1;
+	}
+	return 0;
+}
diff --git a/builtin/stash.c b/builtin/stash.c
new file mode 100644
index 0000000000..b5a301f24d
--- /dev/null
+++ b/builtin/stash.c
@@ -0,0 +1,1635 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "config.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "lockfile.h"
+#include "cache-tree.h"
+#include "unpack-trees.h"
+#include "merge-recursive.h"
+#include "argv-array.h"
+#include "run-command.h"
+#include "dir.h"
+#include "rerere.h"
+#include "revision.h"
+#include "log-tree.h"
+#include "diffcore.h"
+#include "exec-cmd.h"
+
+#define INCLUDE_ALL_FILES 2
+
+static const char * const git_stash_usage[] = {
+	N_("git stash list [<options>]"),
+	N_("git stash show [<options>] [<stash>]"),
+	N_("git stash drop [-q|--quiet] [<stash>]"),
+	N_("git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"),
+	N_("git stash branch <branchname> [<stash>]"),
+	N_("git stash clear"),
+	N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+	   "          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+	   "          [--] [<pathspec>...]]"),
+	N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+	   "          [-u|--include-untracked] [-a|--all] [<message>]"),
+	NULL
+};
+
+static const char * const git_stash_list_usage[] = {
+	N_("git stash list [<options>]"),
+	NULL
+};
+
+static const char * const git_stash_show_usage[] = {
+	N_("git stash show [<options>] [<stash>]"),
+	NULL
+};
+
+static const char * const git_stash_drop_usage[] = {
+	N_("git stash drop [-q|--quiet] [<stash>]"),
+	NULL
+};
+
+static const char * const git_stash_pop_usage[] = {
+	N_("git stash pop [--index] [-q|--quiet] [<stash>]"),
+	NULL
+};
+
+static const char * const git_stash_apply_usage[] = {
+	N_("git stash apply [--index] [-q|--quiet] [<stash>]"),
+	NULL
+};
+
+static const char * const git_stash_branch_usage[] = {
+	N_("git stash branch <branchname> [<stash>]"),
+	NULL
+};
+
+static const char * const git_stash_clear_usage[] = {
+	N_("git stash clear"),
+	NULL
+};
+
+static const char * const git_stash_store_usage[] = {
+	N_("git stash store [-m|--message <message>] [-q|--quiet] <commit>"),
+	NULL
+};
+
+static const char * const git_stash_push_usage[] = {
+	N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+	   "          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+	   "          [--] [<pathspec>...]]"),
+	NULL
+};
+
+static const char * const git_stash_save_usage[] = {
+	N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+	   "          [-u|--include-untracked] [-a|--all] [<message>]"),
+	NULL
+};
+
+static const char *ref_stash = "refs/stash";
+static struct strbuf stash_index_path = STRBUF_INIT;
+
+/*
+ * w_commit is set to the commit containing the working tree
+ * b_commit is set to the base commit
+ * i_commit is set to the commit containing the index tree
+ * u_commit is set to the commit containing the untracked files tree
+ * w_tree is set to the working tree
+ * b_tree is set to the base tree
+ * i_tree is set to the index tree
+ * u_tree is set to the untracked files tree
+ */
+struct stash_info {
+	struct object_id w_commit;
+	struct object_id b_commit;
+	struct object_id i_commit;
+	struct object_id u_commit;
+	struct object_id w_tree;
+	struct object_id b_tree;
+	struct object_id i_tree;
+	struct object_id u_tree;
+	struct strbuf revision;
+	int is_stash_ref;
+	int has_u;
+};
+
+static void free_stash_info(struct stash_info *info)
+{
+	strbuf_release(&info->revision);
+}
+
+static void assert_stash_like(struct stash_info *info, const char *revision)
+{
+	if (get_oidf(&info->b_commit, "%s^1", revision) ||
+	    get_oidf(&info->w_tree, "%s:", revision) ||
+	    get_oidf(&info->b_tree, "%s^1:", revision) ||
+	    get_oidf(&info->i_tree, "%s^2:", revision))
+		die(_("'%s' is not a stash-like commit"), revision);
+}
+
+static int get_stash_info(struct stash_info *info, int argc, const char **argv)
+{
+	int ret;
+	char *end_of_rev;
+	char *expanded_ref;
+	const char *revision;
+	const char *commit = NULL;
+	struct object_id dummy;
+	struct strbuf symbolic = STRBUF_INIT;
+
+	if (argc > 1) {
+		int i;
+		struct strbuf refs_msg = STRBUF_INIT;
+
+		for (i = 0; i < argc; i++)
+			strbuf_addf(&refs_msg, " '%s'", argv[i]);
+
+		fprintf_ln(stderr, _("Too many revisions specified:%s"),
+			   refs_msg.buf);
+		strbuf_release(&refs_msg);
+
+		return -1;
+	}
+
+	if (argc == 1)
+		commit = argv[0];
+
+	strbuf_init(&info->revision, 0);
+	if (!commit) {
+		if (!ref_exists(ref_stash)) {
+			free_stash_info(info);
+			fprintf_ln(stderr, _("No stash entries found."));
+			return -1;
+		}
+
+		strbuf_addf(&info->revision, "%s@{0}", ref_stash);
+	} else if (strspn(commit, "0123456789") == strlen(commit)) {
+		strbuf_addf(&info->revision, "%s@{%s}", ref_stash, commit);
+	} else {
+		strbuf_addstr(&info->revision, commit);
+	}
+
+	revision = info->revision.buf;
+
+	if (get_oid(revision, &info->w_commit)) {
+		error(_("%s is not a valid reference"), revision);
+		free_stash_info(info);
+		return -1;
+	}
+
+	assert_stash_like(info, revision);
+
+	info->has_u = !get_oidf(&info->u_tree, "%s^3:", revision);
+
+	end_of_rev = strchrnul(revision, '@');
+	strbuf_add(&symbolic, revision, end_of_rev - revision);
+
+	ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref);
+	strbuf_release(&symbolic);
+	switch (ret) {
+	case 0: /* Not found, but valid ref */
+		info->is_stash_ref = 0;
+		break;
+	case 1:
+		info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
+		break;
+	default: /* Invalid or ambiguous */
+		free_stash_info(info);
+	}
+
+	free(expanded_ref);
+	return !(ret == 0 || ret == 1);
+}
+
+static int do_clear_stash(void)
+{
+	struct object_id obj;
+	if (get_oid(ref_stash, &obj))
+		return 0;
+
+	return delete_ref(NULL, ref_stash, &obj, 0);
+}
+
+static int clear_stash(int argc, const char **argv, const char *prefix)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_stash_clear_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+
+	if (argc)
+		return error(_("git stash clear with parameters is "
+			       "unimplemented"));
+
+	return do_clear_stash();
+}
+
+static int reset_tree(struct object_id *i_tree, int update, int reset)
+{
+	int nr_trees = 1;
+	struct unpack_trees_options opts;
+	struct tree_desc t[MAX_UNPACK_TREES];
+	struct tree *tree;
+	struct lock_file lock_file = LOCK_INIT;
+
+	read_cache_preload(NULL);
+	if (refresh_cache(REFRESH_QUIET))
+		return -1;
+
+	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+
+	memset(&opts, 0, sizeof(opts));
+
+	tree = parse_tree_indirect(i_tree);
+	if (parse_tree(tree))
+		return -1;
+
+	init_tree_desc(t, tree->buffer, tree->size);
+
+	opts.head_idx = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.merge = 1;
+	opts.reset = reset;
+	opts.update = update;
+	opts.fn = oneway_merge;
+
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+
+	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+		return error(_("unable to write new index file"));
+
+	return 0;
+}
+
+static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit)
+{
+	struct child_process cp = CHILD_PROCESS_INIT;
+	const char *w_commit_hex = oid_to_hex(w_commit);
+
+	/*
+	 * Diff-tree would not be very hard to replace with a native function,
+	 * however it should be done together with apply_cached.
+	 */
+	cp.git_cmd = 1;
+	argv_array_pushl(&cp.args, "diff-tree", "--binary", NULL);
+	argv_array_pushf(&cp.args, "%s^2^..%s^2", w_commit_hex, w_commit_hex);
+
+	return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
+}
+
+static int apply_cached(struct strbuf *out)
+{
+	struct child_process cp = CHILD_PROCESS_INIT;
+
+	/*
+	 * Apply currently only reads either from stdin or a file, thus
+	 * apply_all_patches would have to be updated to optionally take a
+	 * buffer.
+	 */
+	cp.git_cmd = 1;
+	argv_array_pushl(&cp.args, "apply", "--cached", NULL);
+	return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
+}
+
+static int reset_head(void)
+{
+	struct child_process cp = CHILD_PROCESS_INIT;
+
+	/*
+	 * Reset is overall quite simple, however there is no current public
+	 * API for resetting.
+	 */
+	cp.git_cmd = 1;
+	argv_array_push(&cp.args, "reset");
+
+	return run_command(&cp);
+}
+
+static void add_diff_to_buf(struct diff_queue_struct *q,
+			    struct diff_options *options,
+			    void *data)
+{
+	int i;
+
+	for (i = 0; i < q->nr; i++) {
+		strbuf_addstr(data, q->queue[i]->one->path);
+
+		/* NUL-terminate: will be fed to update-index -z */
+		strbuf_addch(data, '\0');
+	}
+}
+
+static int get_newly_staged(struct strbuf *out, struct object_id *c_tree)
+{
+	struct child_process cp = CHILD_PROCESS_INIT;
+	const char *c_tree_hex = oid_to_hex(c_tree);
+
+	/*
+	 * diff-index is very similar to diff-tree above, and should be
+	 * converted together with update_index.
+	 */
+	cp.git_cmd = 1;
+	argv_array_pushl(&cp.args, "diff-index", "--cached", "--name-only",
+			 "--diff-filter=A", NULL);
+	argv_array_push(&cp.args, c_tree_hex);
+	return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
+}
+
+static int update_index(struct strbuf *out)
+{
+	struct child_process cp = CHILD_PROCESS_INIT;
+
+	/*
+	 * Update-index is very complicated and may need to have a public
+	 * function exposed in order to remove this forking.
+	 */
+	cp.git_cmd = 1;
+	argv_array_pushl(&cp.args, "update-index", "--add", "--stdin", NULL);
+	return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
+}
+
+static int restore_untracked(struct object_id *u_tree)
+{
+	int res;
+	struct child_process cp = CHILD_PROCESS_INIT;
+
+	/*
+	 * We need to run restore files from a given index, but without
+	 * affecting the current index, so we use GIT_INDEX_FILE with
+	 * run_command to fork processes that will not interfere.
+	 */
+	cp.git_cmd = 1;
+	argv_array_push(&cp.args, "read-tree");
+	argv_array_push(&cp.args, oid_to_hex(u_tree));
+	argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
+			 stash_index_path.buf);
+	if (run_command(&cp)) {
+		remove_path(stash_index_path.buf);
+		return -1;
+	}
+
+	child_process_init(&cp);
+	cp.git_cmd = 1;
+	argv_array_pushl(&cp.args, "checkout-index", "--all", NULL);
+	argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
+			 stash_index_path.buf);
+
+	res = run_command(&cp);
+	remove_path(stash_index_path.buf);
+	return res;
+}
+
+static int do_apply_stash(const char *prefix, struct stash_info *info,
+			  int index, int quiet)
+{
+	int ret;
+	int has_index = index;
+	struct merge_options o;
+	struct object_id c_tree;
+	struct object_id index_tree;
+	struct commit *result;
+	const struct object_id *bases[1];
+
+	read_cache_preload(NULL);
+	if (refresh_cache(REFRESH_QUIET))
+		return -1;
+
+	if (write_cache_as_tree(&c_tree, 0, NULL))
+		return error(_("cannot apply a stash in the middle of a merge"));
+
+	if (index) {
+		if (oideq(&info->b_tree, &info->i_tree) ||
+		    oideq(&c_tree, &info->i_tree)) {
+			has_index = 0;
+		} else {
+			struct strbuf out = STRBUF_INIT;
+
+			if (diff_tree_binary(&out, &info->w_commit)) {
+				strbuf_release(&out);
+				return error(_("could not generate diff %s^!."),
+					     oid_to_hex(&info->w_commit));
+			}
+
+			ret = apply_cached(&out);
+			strbuf_release(&out);
+			if (ret)
+				return error(_("conflicts in index."
+					       "Try without --index."));
+
+			discard_cache();
+			read_cache();
+			if (write_cache_as_tree(&index_tree, 0, NULL))
+				return error(_("could not save index tree"));
+
+			reset_head();
+		}
+	}
+
+	if (info->has_u && restore_untracked(&info->u_tree))
+		return error(_("could not restore untracked files from stash"));
+
+	init_merge_options(&o, the_repository);
+
+	o.branch1 = "Updated upstream";
+	o.branch2 = "Stashed changes";
+
+	if (oideq(&info->b_tree, &c_tree))
+		o.branch1 = "Version stash was based on";
+
+	if (quiet)
+		o.verbosity = 0;
+
+	if (o.verbosity >= 3)
+		printf_ln(_("Merging %s with %s"), o.branch1, o.branch2);
+
+	bases[0] = &info->b_tree;
+
+	ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,
+				      &result);
+	if (ret) {
+		rerere(0);
+
+		if (index)
+			fprintf_ln(stderr, _("Index was not unstashed."));
+
+		return ret;
+	}
+
+	if (has_index) {
+		if (reset_tree(&index_tree, 0, 0))
+			return -1;
+	} else {
+		struct strbuf out = STRBUF_INIT;
+
+		if (get_newly_staged(&out, &c_tree)) {
+			strbuf_release(&out);
+			return -1;
+		}
+
+		if (reset_tree(&c_tree, 0, 1)) {
+			strbuf_release(&out);
+			return -1;
+		}
+
+		ret = update_index(&out);
+		strbuf_release(&out);
+		if (ret)
+			return -1;
+
+		discard_cache();
+	}
+
+	if (quiet) {
+		if (refresh_cache(REFRESH_QUIET))
+			warning("could not refresh index");
+	} else {
+		struct child_process cp = CHILD_PROCESS_INIT;
+
+		/*
+		 * Status is quite simple and could be replaced with calls to
+		 * wt_status in the future, but it adds complexities which may
+		 * require more tests.
+		 */
+		cp.git_cmd = 1;
+		cp.dir = prefix;
+		argv_array_push(&cp.args, "status");
+		run_command(&cp);
+	}
+
+	return 0;
+}
+
+static int apply_stash(int argc, const char **argv, const char *prefix)
+{
+	int ret;
+	int quiet = 0;
+	int index = 0;
+	struct stash_info info;
+	struct option options[] = {
+		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
+		OPT_BOOL(0, "index", &index,
+			 N_("attempt to recreate the index")),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_stash_apply_usage, 0);
+
+	if (get_stash_info(&info, argc, argv))
+		return -1;
+
+	ret = do_apply_stash(prefix, &info, index, quiet);
+	free_stash_info(&info);
+	return ret;
+}
+
+static int do_drop_stash(struct stash_info *info, int quiet)
+{
+	int ret;
+	struct child_process cp_reflog = CHILD_PROCESS_INIT;
+	struct child_process cp = CHILD_PROCESS_INIT;
+
+	/*
+	 * reflog does not provide a simple function for deleting refs. One will
+	 * need to be added to avoid implementing too much reflog code here
+	 */
+
+	cp_reflog.git_cmd = 1;
+	argv_array_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
+			 "--rewrite", NULL);
+	argv_array_push(&cp_reflog.args, info->revision.buf);
+	ret = run_command(&cp_reflog);
+	if (!ret) {
+		if (!quiet)
+			printf_ln(_("Dropped %s (%s)"), info->revision.buf,
+				  oid_to_hex(&info->w_commit));
+	} else {
+		return error(_("%s: Could not drop stash entry"),
+			     info->revision.buf);
+	}
+
+	/*
+	 * This could easily be replaced by get_oid, but currently it will throw
+	 * a fatal error when a reflog is empty, which we can not recover from.
+	 */
+	cp.git_cmd = 1;
+	/* Even though --quiet is specified, rev-parse still outputs the hash */
+	cp.no_stdout = 1;
+	argv_array_pushl(&cp.args, "rev-parse", "--verify", "--quiet", NULL);
+	argv_array_pushf(&cp.args, "%s@{0}", ref_stash);
+	ret = run_command(&cp);
+
+	/* do_clear_stash if we just dropped the last stash entry */
+	if (ret)
+		do_clear_stash();
+
+	return 0;
+}
+
+static void assert_stash_ref(struct stash_info *info)
+{
+	if (!info->is_stash_ref) {
+		error(_("'%s' is not a stash reference"), info->revision.buf);
+		free_stash_info(info);
+		exit(1);
+	}
+}
+
+static int drop_stash(int argc, const char **argv, const char *prefix)
+{
+	int ret;
+	int quiet = 0;
+	struct stash_info info;
+	struct option options[] = {
+		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_stash_drop_usage, 0);
+
+	if (get_stash_info(&info, argc, argv))
+		return -1;
+
+	assert_stash_ref(&info);
+
+	ret = do_drop_stash(&info, quiet);
+	free_stash_info(&info);
+	return ret;
+}
+
+static int pop_stash(int argc, const char **argv, const char *prefix)
+{
+	int ret;
+	int index = 0;
+	int quiet = 0;
+	struct stash_info info;
+	struct option options[] = {
+		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
+		OPT_BOOL(0, "index", &index,
+			 N_("attempt to recreate the index")),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_stash_pop_usage, 0);
+
+	if (get_stash_info(&info, argc, argv))
+		return -1;
+
+	assert_stash_ref(&info);
+	if ((ret = do_apply_stash(prefix, &info, index, quiet)))
+		printf_ln(_("The stash entry is kept in case "
+			    "you need it again."));
+	else
+		ret = do_drop_stash(&info, quiet);
+
+	free_stash_info(&info);
+	return ret;
+}
+
+static int branch_stash(int argc, const char **argv, const char *prefix)
+{
+	int ret;
+	const char *branch = NULL;
+	struct stash_info info;
+	struct child_process cp = CHILD_PROCESS_INIT;
+	struct option options[] = {
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_stash_branch_usage, 0);
+
+	if (!argc) {
+		fprintf_ln(stderr, _("No branch name specified"));
+		return -1;
+	}
+
+	branch = argv[0];
+
+	if (get_stash_info(&info, argc - 1, argv + 1))
+		return -1;
+
+	cp.git_cmd = 1;
+	argv_array_pushl(&cp.args, "checkout", "-b", NULL);
+	argv_array_push(&cp.args, branch);
+	argv_array_push(&cp.args, oid_to_hex(&info.b_commit));
+	ret = run_command(&cp);
+	if (!ret)
+		ret = do_apply_stash(prefix, &info, 1, 0);
+	if (!ret && info.is_stash_ref)
+		ret = do_drop_stash(&info, 0);
+
+	free_stash_info(&info);
+
+	return ret;
+}
+
+static int list_stash(int argc, const char **argv, const char *prefix)
+{
+	struct child_process cp = CHILD_PROCESS_INIT;
+	struct option options[] = {
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_stash_list_usage,
+			     PARSE_OPT_KEEP_UNKNOWN);
+
+	if (!ref_exists(ref_stash))
+		return 0;
+
+	cp.git_cmd = 1;
+	argv_array_pushl(&cp.args, "log", "--format=%gd: %gs", "-g",
+			 "--first-parent", "-m", NULL);
+	argv_array_pushv(&cp.args, argv);
+	argv_array_push(&cp.args, ref_stash);
+	argv_array_push(&cp.args, "--");
+	return run_command(&cp);
+}
+
+static int show_stat = 1;
+static int show_patch;
+
+static int git_stash_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "stash.showstat")) {
+		show_stat = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "stash.showpatch")) {
+		show_patch = git_config_bool(var, value);
+		return 0;
+	}
+	return git_default_config(var, value, cb);
+}
+
+static int show_stash(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	int ret = 0;
+	struct stash_info info;
+	struct rev_info rev;
+	struct argv_array stash_args = ARGV_ARRAY_INIT;
+	struct argv_array revision_args = ARGV_ARRAY_INIT;
+	struct option options[] = {
+		OPT_END()
+	};
+
+	init_diff_ui_defaults();
+	git_config(git_diff_ui_config, NULL);
+	init_revisions(&rev, prefix);
+
+	argv_array_push(&revision_args, argv[0]);
+	for (i = 1; i < argc; i++) {
+		if (argv[i][0] != '-')
+			argv_array_push(&stash_args, argv[i]);
+		else
+			argv_array_push(&revision_args, argv[i]);
+	}
+
+	ret = get_stash_info(&info, stash_args.argc, stash_args.argv);
+	argv_array_clear(&stash_args);
+	if (ret)
+		return -1;
+
+	/*
+	 * The config settings are applied only if there are not passed
+	 * any options.
+	 */
+	if (revision_args.argc == 1) {
+		git_config(git_stash_config, NULL);
+		if (show_stat)
+			rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;
+
+		if (show_patch)
+			rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
+
+		if (!show_stat && !show_patch) {
+			free_stash_info(&info);
+			return 0;
+		}
+	}
+
+	argc = setup_revisions(revision_args.argc, revision_args.argv, &rev, NULL);
+	if (argc > 1) {
+		free_stash_info(&info);
+		usage_with_options(git_stash_show_usage, options);
+	}
+	if (!rev.diffopt.output_format) {
+		rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+		diff_setup_done(&rev.diffopt);
+	}
+
+	rev.diffopt.flags.recursive = 1;
+	setup_diff_pager(&rev.diffopt);
+	diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt);
+	log_tree_diff_flush(&rev);
+
+	free_stash_info(&info);
+	return diff_result_code(&rev.diffopt, 0);
+}
+
+static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
+			  int quiet)
+{
+	if (!stash_msg)
+		stash_msg = "Created via \"git stash store\".";
+
+	if (update_ref(stash_msg, ref_stash, w_commit, NULL,
+		       REF_FORCE_CREATE_REFLOG,
+		       quiet ? UPDATE_REFS_QUIET_ON_ERR :
+		       UPDATE_REFS_MSG_ON_ERR)) {
+		if (!quiet) {
+			fprintf_ln(stderr, _("Cannot update %s with %s"),
+				   ref_stash, oid_to_hex(w_commit));
+		}
+		return -1;
+	}
+
+	return 0;
+}
+
+static int store_stash(int argc, const char **argv, const char *prefix)
+{
+	int quiet = 0;
+	const char *stash_msg = NULL;
+	struct object_id obj;
+	struct object_context dummy;
+	struct option options[] = {
+		OPT__QUIET(&quiet, N_("be quiet")),
+		OPT_STRING('m', "message", &stash_msg, "message",
+			   N_("stash message")),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_stash_store_usage,
+			     PARSE_OPT_KEEP_UNKNOWN);
+
+	if (argc != 1) {
+		if (!quiet)
+			fprintf_ln(stderr, _("\"git stash store\" requires one "
+					     "<commit> argument"));
+		return -1;
+	}
+
+	if (get_oid_with_context(the_repository,
+				 argv[0], quiet ? GET_OID_QUIETLY : 0, &obj,
+				 &dummy)) {
+		if (!quiet)
+			fprintf_ln(stderr, _("Cannot update %s with %s"),
+					     ref_stash, argv[0]);
+		return -1;
+	}
+
+	return do_store_stash(&obj, stash_msg, quiet);
+}
+
+static void add_pathspecs(struct argv_array *args,
+			  const struct pathspec *ps) {
+	int i;
+
+	for (i = 0; i < ps->nr; i++)
+		argv_array_push(args, ps->items[i].original);
+}
+
+/*
+ * `untracked_files` will be filled with the names of untracked files.
+ * The return value is:
+ *
+ * = 0 if there are not any untracked files
+ * > 0 if there are untracked files
+ */
+static int get_untracked_files(const struct pathspec *ps, int include_untracked,
+			       struct strbuf *untracked_files)
+{
+	int i;
+	int max_len;
+	int found = 0;
+	char *seen;
+	struct dir_struct dir;
+
+	memset(&dir, 0, sizeof(dir));
+	if (include_untracked != INCLUDE_ALL_FILES)
+		setup_standard_excludes(&dir);
+
+	seen = xcalloc(ps->nr, 1);
+
+	max_len = fill_directory(&dir, the_repository->index, ps);
+	for (i = 0; i < dir.nr; i++) {
+		struct dir_entry *ent = dir.entries[i];
+		if (dir_path_match(&the_index, ent, ps, max_len, seen)) {
+			found++;
+			strbuf_addstr(untracked_files, ent->name);
+			/* NUL-terminate: will be fed to update-index -z */
+			strbuf_addch(untracked_files, '\0');
+		}
+		free(ent);
+	}
+
+	free(seen);
+	free(dir.entries);
+	free(dir.ignored);
+	clear_directory(&dir);
+	return found;
+}
+
+/*
+ * The return value of `check_changes_tracked_files()` can be:
+ *
+ * < 0 if there was an error
+ * = 0 if there are no changes.
+ * > 0 if there are changes.
+ */
+static int check_changes_tracked_files(const struct pathspec *ps)
+{
+	int result;
+	struct rev_info rev;
+	struct object_id dummy;
+	int ret = 0;
+
+	/* No initial commit. */
+	if (get_oid("HEAD", &dummy))
+		return -1;
+
+	if (read_cache() < 0)
+		return -1;
+
+	init_revisions(&rev, NULL);
+	copy_pathspec(&rev.prune_data, ps);
+
+	rev.diffopt.flags.quick = 1;
+	rev.diffopt.flags.ignore_submodules = 1;
+	rev.abbrev = 0;
+
+	add_head_to_pending(&rev);
+	diff_setup_done(&rev.diffopt);
+
+	result = run_diff_index(&rev, 1);
+	if (diff_result_code(&rev.diffopt, result)) {
+		ret = 1;
+		goto done;
+	}
+
+	object_array_clear(&rev.pending);
+	result = run_diff_files(&rev, 0);
+	if (diff_result_code(&rev.diffopt, result)) {
+		ret = 1;
+		goto done;
+	}
+
+done:
+	clear_pathspec(&rev.prune_data);
+	return ret;
+}
+
+/*
+ * The function will fill `untracked_files` with the names of untracked files
+ * It will return 1 if there were any changes and 0 if there were not.
+ */
+static int check_changes(const struct pathspec *ps, int include_untracked,
+			 struct strbuf *untracked_files)
+{
+	int ret = 0;
+	if (check_changes_tracked_files(ps))
+		ret = 1;
+
+	if (include_untracked && get_untracked_files(ps, include_untracked,
+						     untracked_files))
+		ret = 1;
+
+	return ret;
+}
+
+static int save_untracked_files(struct stash_info *info, struct strbuf *msg,
+				struct strbuf files)
+{
+	int ret = 0;
+	struct strbuf untracked_msg = STRBUF_INIT;
+	struct child_process cp_upd_index = CHILD_PROCESS_INIT;
+	struct index_state istate = { NULL };
+
+	cp_upd_index.git_cmd = 1;
+	argv_array_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
+			 "--remove", "--stdin", NULL);
+	argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
+			 stash_index_path.buf);
+
+	strbuf_addf(&untracked_msg, "untracked files on %s\n", msg->buf);
+	if (pipe_command(&cp_upd_index, files.buf, files.len, NULL, 0,
+			 NULL, 0)) {
+		ret = -1;
+		goto done;
+	}
+
+	if (write_index_as_tree(&info->u_tree, &istate, stash_index_path.buf, 0,
+				NULL)) {
+		ret = -1;
+		goto done;
+	}
+
+	if (commit_tree(untracked_msg.buf, untracked_msg.len,
+			&info->u_tree, NULL, &info->u_commit, NULL, NULL)) {
+		ret = -1;
+		goto done;
+	}
+
+done:
+	discard_index(&istate);
+	strbuf_release(&untracked_msg);
+	remove_path(stash_index_path.buf);
+	return ret;
+}
+
+static int stash_patch(struct stash_info *info, const struct pathspec *ps,
+		       struct strbuf *out_patch, int quiet)
+{
+	int ret = 0;
+	struct child_process cp_read_tree = CHILD_PROCESS_INIT;
+	struct child_process cp_add_i = CHILD_PROCESS_INIT;
+	struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
+	struct index_state istate = { NULL };
+
+	remove_path(stash_index_path.buf);
+
+	cp_read_tree.git_cmd = 1;
+	argv_array_pushl(&cp_read_tree.args, "read-tree", "HEAD", NULL);
+	argv_array_pushf(&cp_read_tree.env_array, "GIT_INDEX_FILE=%s",
+			 stash_index_path.buf);
+	if (run_command(&cp_read_tree)) {
+		ret = -1;
+		goto done;
+	}
+
+	/* Find out what the user wants. */
+	cp_add_i.git_cmd = 1;
+	argv_array_pushl(&cp_add_i.args, "add--interactive", "--patch=stash",
+			 "--", NULL);
+	add_pathspecs(&cp_add_i.args, ps);
+	argv_array_pushf(&cp_add_i.env_array, "GIT_INDEX_FILE=%s",
+			 stash_index_path.buf);
+	if (run_command(&cp_add_i)) {
+		ret = -1;
+		goto done;
+	}
+
+	/* State of the working tree. */
+	if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
+				NULL)) {
+		ret = -1;
+		goto done;
+	}
+
+	cp_diff_tree.git_cmd = 1;
+	argv_array_pushl(&cp_diff_tree.args, "diff-tree", "-p", "HEAD",
+			 oid_to_hex(&info->w_tree), "--", NULL);
+	if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
+		ret = -1;
+		goto done;
+	}
+
+	if (!out_patch->len) {
+		if (!quiet)
+			fprintf_ln(stderr, _("No changes selected"));
+		ret = 1;
+	}
+
+done:
+	discard_index(&istate);
+	remove_path(stash_index_path.buf);
+	return ret;
+}
+
+static int stash_working_tree(struct stash_info *info, const struct pathspec *ps)
+{
+	int ret = 0;
+	struct rev_info rev;
+	struct child_process cp_upd_index = CHILD_PROCESS_INIT;
+	struct strbuf diff_output = STRBUF_INIT;
+	struct index_state istate = { NULL };
+
+	init_revisions(&rev, NULL);
+	copy_pathspec(&rev.prune_data, ps);
+
+	set_alternate_index_output(stash_index_path.buf);
+	if (reset_tree(&info->i_tree, 0, 0)) {
+		ret = -1;
+		goto done;
+	}
+	set_alternate_index_output(NULL);
+
+	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = add_diff_to_buf;
+	rev.diffopt.format_callback_data = &diff_output;
+
+	if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
+		ret = -1;
+		goto done;
+	}
+
+	add_pending_object(&rev, parse_object(the_repository, &info->b_commit),
+			   "");
+	if (run_diff_index(&rev, 0)) {
+		ret = -1;
+		goto done;
+	}
+
+	cp_upd_index.git_cmd = 1;
+	argv_array_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
+			 "--remove", "--stdin", NULL);
+	argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
+			 stash_index_path.buf);
+
+	if (pipe_command(&cp_upd_index, diff_output.buf, diff_output.len,
+			 NULL, 0, NULL, 0)) {
+		ret = -1;
+		goto done;
+	}
+
+	if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
+				NULL)) {
+		ret = -1;
+		goto done;
+	}
+
+done:
+	discard_index(&istate);
+	UNLEAK(rev);
+	object_array_clear(&rev.pending);
+	clear_pathspec(&rev.prune_data);
+	strbuf_release(&diff_output);
+	remove_path(stash_index_path.buf);
+	return ret;
+}
+
+static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf,
+			   int include_untracked, int patch_mode,
+			   struct stash_info *info, struct strbuf *patch,
+			   int quiet)
+{
+	int ret = 0;
+	int flags = 0;
+	int untracked_commit_option = 0;
+	const char *head_short_sha1 = NULL;
+	const char *branch_ref = NULL;
+	const char *branch_name = "(no branch)";
+	struct commit *head_commit = NULL;
+	struct commit_list *parents = NULL;
+	struct strbuf msg = STRBUF_INIT;
+	struct strbuf commit_tree_label = STRBUF_INIT;
+	struct strbuf untracked_files = STRBUF_INIT;
+
+	prepare_fallback_ident("git stash", "git@stash");
+
+	read_cache_preload(NULL);
+	refresh_cache(REFRESH_QUIET);
+
+	if (get_oid("HEAD", &info->b_commit)) {
+		if (!quiet)
+			fprintf_ln(stderr, _("You do not have "
+					     "the initial commit yet"));
+		ret = -1;
+		goto done;
+	} else {
+		head_commit = lookup_commit(the_repository, &info->b_commit);
+	}
+
+	if (!check_changes(ps, include_untracked, &untracked_files)) {
+		ret = 1;
+		goto done;
+	}
+
+	branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+	if (flags & REF_ISSYMREF)
+		branch_name = strrchr(branch_ref, '/') + 1;
+	head_short_sha1 = find_unique_abbrev(&head_commit->object.oid,
+					     DEFAULT_ABBREV);
+	strbuf_addf(&msg, "%s: %s ", branch_name, head_short_sha1);
+	pp_commit_easy(CMIT_FMT_ONELINE, head_commit, &msg);
+
+	strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf);
+	commit_list_insert(head_commit, &parents);
+	if (write_cache_as_tree(&info->i_tree, 0, NULL) ||
+	    commit_tree(commit_tree_label.buf, commit_tree_label.len,
+			&info->i_tree, parents, &info->i_commit, NULL, NULL)) {
+		if (!quiet)
+			fprintf_ln(stderr, _("Cannot save the current "
+					     "index state"));
+		ret = -1;
+		goto done;
+	}
+
+	if (include_untracked) {
+		if (save_untracked_files(info, &msg, untracked_files)) {
+			if (!quiet)
+				fprintf_ln(stderr, _("Cannot save "
+						     "the untracked files"));
+			ret = -1;
+			goto done;
+		}
+		untracked_commit_option = 1;
+	}
+	if (patch_mode) {
+		ret = stash_patch(info, ps, patch, quiet);
+		if (ret < 0) {
+			if (!quiet)
+				fprintf_ln(stderr, _("Cannot save the current "
+						     "worktree state"));
+			goto done;
+		} else if (ret > 0) {
+			goto done;
+		}
+	} else {
+		if (stash_working_tree(info, ps)) {
+			if (!quiet)
+				fprintf_ln(stderr, _("Cannot save the current "
+						     "worktree state"));
+			ret = -1;
+			goto done;
+		}
+	}
+
+	if (!stash_msg_buf->len)
+		strbuf_addf(stash_msg_buf, "WIP on %s", msg.buf);
+	else
+		strbuf_insertf(stash_msg_buf, 0, "On %s: ", branch_name);
+
+	/*
+	 * `parents` will be empty after calling `commit_tree()`, so there is
+	 * no need to call `free_commit_list()`
+	 */
+	parents = NULL;
+	if (untracked_commit_option)
+		commit_list_insert(lookup_commit(the_repository,
+						 &info->u_commit),
+				   &parents);
+	commit_list_insert(lookup_commit(the_repository, &info->i_commit),
+			   &parents);
+	commit_list_insert(head_commit, &parents);
+
+	if (commit_tree(stash_msg_buf->buf, stash_msg_buf->len, &info->w_tree,
+			parents, &info->w_commit, NULL, NULL)) {
+		if (!quiet)
+			fprintf_ln(stderr, _("Cannot record "
+					     "working tree state"));
+		ret = -1;
+		goto done;
+	}
+
+done:
+	strbuf_release(&commit_tree_label);
+	strbuf_release(&msg);
+	strbuf_release(&untracked_files);
+	return ret;
+}
+
+static int create_stash(int argc, const char **argv, const char *prefix)
+{
+	int ret = 0;
+	struct strbuf stash_msg_buf = STRBUF_INIT;
+	struct stash_info info;
+	struct pathspec ps;
+
+	/* Starting with argv[1], since argv[0] is "create" */
+	strbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' ');
+
+	memset(&ps, 0, sizeof(ps));
+	if (!check_changes_tracked_files(&ps))
+		return 0;
+
+	ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, &info,
+			      NULL, 0);
+	if (!ret)
+		printf_ln("%s", oid_to_hex(&info.w_commit));
+
+	strbuf_release(&stash_msg_buf);
+	return ret;
+}
+
+static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet,
+			 int keep_index, int patch_mode, int include_untracked)
+{
+	int ret = 0;
+	struct stash_info info;
+	struct strbuf patch = STRBUF_INIT;
+	struct strbuf stash_msg_buf = STRBUF_INIT;
+	struct strbuf untracked_files = STRBUF_INIT;
+
+	if (patch_mode && keep_index == -1)
+		keep_index = 1;
+
+	if (patch_mode && include_untracked) {
+		fprintf_ln(stderr, _("Can't use --patch and --include-untracked"
+				     " or --all at the same time"));
+		ret = -1;
+		goto done;
+	}
+
+	read_cache_preload(NULL);
+	if (!include_untracked && ps->nr) {
+		int i;
+		char *ps_matched = xcalloc(ps->nr, 1);
+
+		for (i = 0; i < active_nr; i++)
+			ce_path_match(&the_index, active_cache[i], ps,
+				      ps_matched);
+
+		if (report_path_error(ps_matched, ps)) {
+			fprintf_ln(stderr, _("Did you forget to 'git add'?"));
+			ret = -1;
+			free(ps_matched);
+			goto done;
+		}
+		free(ps_matched);
+	}
+
+	if (refresh_cache(REFRESH_QUIET)) {
+		ret = -1;
+		goto done;
+	}
+
+	if (!check_changes(ps, include_untracked, &untracked_files)) {
+		if (!quiet)
+			printf_ln(_("No local changes to save"));
+		goto done;
+	}
+
+	if (!reflog_exists(ref_stash) && do_clear_stash()) {
+		ret = -1;
+		if (!quiet)
+			fprintf_ln(stderr, _("Cannot initialize stash"));
+		goto done;
+	}
+
+	if (stash_msg)
+		strbuf_addstr(&stash_msg_buf, stash_msg);
+	if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,
+			    &info, &patch, quiet)) {
+		ret = -1;
+		goto done;
+	}
+
+	if (do_store_stash(&info.w_commit, stash_msg_buf.buf, 1)) {
+		ret = -1;
+		if (!quiet)
+			fprintf_ln(stderr, _("Cannot save the current status"));
+		goto done;
+	}
+
+	if (!quiet)
+		printf_ln(_("Saved working directory and index state %s"),
+			  stash_msg_buf.buf);
+
+	if (!patch_mode) {
+		if (include_untracked && !ps->nr) {
+			struct child_process cp = CHILD_PROCESS_INIT;
+
+			cp.git_cmd = 1;
+			argv_array_pushl(&cp.args, "clean", "--force",
+					 "--quiet", "-d", NULL);
+			if (include_untracked == INCLUDE_ALL_FILES)
+				argv_array_push(&cp.args, "-x");
+			if (run_command(&cp)) {
+				ret = -1;
+				goto done;
+			}
+		}
+		discard_cache();
+		if (ps->nr) {
+			struct child_process cp_add = CHILD_PROCESS_INIT;
+			struct child_process cp_diff = CHILD_PROCESS_INIT;
+			struct child_process cp_apply = CHILD_PROCESS_INIT;
+			struct strbuf out = STRBUF_INIT;
+
+			cp_add.git_cmd = 1;
+			argv_array_push(&cp_add.args, "add");
+			if (!include_untracked)
+				argv_array_push(&cp_add.args, "-u");
+			if (include_untracked == INCLUDE_ALL_FILES)
+				argv_array_push(&cp_add.args, "--force");
+			argv_array_push(&cp_add.args, "--");
+			add_pathspecs(&cp_add.args, ps);
+			if (run_command(&cp_add)) {
+				ret = -1;
+				goto done;
+			}
+
+			cp_diff.git_cmd = 1;
+			argv_array_pushl(&cp_diff.args, "diff-index", "-p",
+					 "--cached", "--binary", "HEAD", "--",
+					 NULL);
+			add_pathspecs(&cp_diff.args, ps);
+			if (pipe_command(&cp_diff, NULL, 0, &out, 0, NULL, 0)) {
+				ret = -1;
+				goto done;
+			}
+
+			cp_apply.git_cmd = 1;
+			argv_array_pushl(&cp_apply.args, "apply", "--index",
+					 "-R", NULL);
+			if (pipe_command(&cp_apply, out.buf, out.len, NULL, 0,
+					 NULL, 0)) {
+				ret = -1;
+				goto done;
+			}
+		} else {
+			struct child_process cp = CHILD_PROCESS_INIT;
+			cp.git_cmd = 1;
+			argv_array_pushl(&cp.args, "reset", "--hard", "-q",
+					 NULL);
+			if (run_command(&cp)) {
+				ret = -1;
+				goto done;
+			}
+		}
+
+		if (keep_index == 1 && !is_null_oid(&info.i_tree)) {
+			struct child_process cp = CHILD_PROCESS_INIT;
+
+			cp.git_cmd = 1;
+			argv_array_pushl(&cp.args, "checkout", "--no-overlay",
+					 oid_to_hex(&info.i_tree), "--", NULL);
+			if (!ps->nr)
+				argv_array_push(&cp.args, ":/");
+			else
+				add_pathspecs(&cp.args, ps);
+			if (run_command(&cp)) {
+				ret = -1;
+				goto done;
+			}
+		}
+		goto done;
+	} else {
+		struct child_process cp = CHILD_PROCESS_INIT;
+
+		cp.git_cmd = 1;
+		argv_array_pushl(&cp.args, "apply", "-R", NULL);
+
+		if (pipe_command(&cp, patch.buf, patch.len, NULL, 0, NULL, 0)) {
+			if (!quiet)
+				fprintf_ln(stderr, _("Cannot remove "
+						     "worktree changes"));
+			ret = -1;
+			goto done;
+		}
+
+		if (keep_index < 1) {
+			struct child_process cp = CHILD_PROCESS_INIT;
+
+			cp.git_cmd = 1;
+			argv_array_pushl(&cp.args, "reset", "-q", "--", NULL);
+			add_pathspecs(&cp.args, ps);
+			if (run_command(&cp)) {
+				ret = -1;
+				goto done;
+			}
+		}
+		goto done;
+	}
+
+done:
+	strbuf_release(&stash_msg_buf);
+	return ret;
+}
+
+static int push_stash(int argc, const char **argv, const char *prefix)
+{
+	int keep_index = -1;
+	int patch_mode = 0;
+	int include_untracked = 0;
+	int quiet = 0;
+	const char *stash_msg = NULL;
+	struct pathspec ps;
+	struct option options[] = {
+		OPT_BOOL('k', "keep-index", &keep_index,
+			 N_("keep index")),
+		OPT_BOOL('p', "patch", &patch_mode,
+			 N_("stash in patch mode")),
+		OPT__QUIET(&quiet, N_("quiet mode")),
+		OPT_BOOL('u', "include-untracked", &include_untracked,
+			 N_("include untracked files in stash")),
+		OPT_SET_INT('a', "all", &include_untracked,
+			    N_("include ignore files"), 2),
+		OPT_STRING('m', "message", &stash_msg, N_("message"),
+			   N_("stash message")),
+		OPT_END()
+	};
+
+	if (argc)
+		argc = parse_options(argc, argv, prefix, options,
+				     git_stash_push_usage,
+				     0);
+
+	parse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
+		       prefix, argv);
+	return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
+			     include_untracked);
+}
+
+static int save_stash(int argc, const char **argv, const char *prefix)
+{
+	int keep_index = -1;
+	int patch_mode = 0;
+	int include_untracked = 0;
+	int quiet = 0;
+	int ret = 0;
+	const char *stash_msg = NULL;
+	struct pathspec ps;
+	struct strbuf stash_msg_buf = STRBUF_INIT;
+	struct option options[] = {
+		OPT_BOOL('k', "keep-index", &keep_index,
+			 N_("keep index")),
+		OPT_BOOL('p', "patch", &patch_mode,
+			 N_("stash in patch mode")),
+		OPT__QUIET(&quiet, N_("quiet mode")),
+		OPT_BOOL('u', "include-untracked", &include_untracked,
+			 N_("include untracked files in stash")),
+		OPT_SET_INT('a', "all", &include_untracked,
+			    N_("include ignore files"), 2),
+		OPT_STRING('m', "message", &stash_msg, "message",
+			   N_("stash message")),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_stash_save_usage,
+			     PARSE_OPT_KEEP_DASHDASH);
+
+	if (argc)
+		stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');
+
+	memset(&ps, 0, sizeof(ps));
+	ret = do_push_stash(&ps, stash_msg, quiet, keep_index,
+			    patch_mode, include_untracked);
+
+	strbuf_release(&stash_msg_buf);
+	return ret;
+}
+
+static int use_builtin_stash(void)
+{
+	struct child_process cp = CHILD_PROCESS_INIT;
+	struct strbuf out = STRBUF_INIT;
+	int ret, env = git_env_bool("GIT_TEST_STASH_USE_BUILTIN", -1);
+
+	if (env != -1)
+		return env;
+
+	argv_array_pushl(&cp.args,
+			 "config", "--bool", "stash.usebuiltin", NULL);
+	cp.git_cmd = 1;
+	if (capture_command(&cp, &out, 6)) {
+		strbuf_release(&out);
+		return 1;
+	}
+
+	strbuf_trim(&out);
+	ret = !strcmp("true", out.buf);
+	strbuf_release(&out);
+	return ret;
+}
+
+int cmd_stash(int argc, const char **argv, const char *prefix)
+{
+	int i = -1;
+	pid_t pid = getpid();
+	const char *index_file;
+	struct argv_array args = ARGV_ARRAY_INIT;
+
+	struct option options[] = {
+		OPT_END()
+	};
+
+	if (!use_builtin_stash()) {
+		const char *path = mkpath("%s/git-legacy-stash",
+					  git_exec_path());
+
+		if (sane_execvp(path, (char **)argv) < 0)
+			die_errno(_("could not exec %s"), path);
+		else
+			BUG("sane_execvp() returned???");
+	}
+
+	prefix = setup_git_directory();
+	trace_repo_setup(prefix);
+	setup_work_tree();
+
+	git_config(git_diff_basic_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, options, git_stash_usage,
+			     PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
+
+	index_file = get_index_file();
+	strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file,
+		    (uintmax_t)pid);
+
+	if (!argc)
+		return !!push_stash(0, NULL, prefix);
+	else if (!strcmp(argv[0], "apply"))
+		return !!apply_stash(argc, argv, prefix);
+	else if (!strcmp(argv[0], "clear"))
+		return !!clear_stash(argc, argv, prefix);
+	else if (!strcmp(argv[0], "drop"))
+		return !!drop_stash(argc, argv, prefix);
+	else if (!strcmp(argv[0], "pop"))
+		return !!pop_stash(argc, argv, prefix);
+	else if (!strcmp(argv[0], "branch"))
+		return !!branch_stash(argc, argv, prefix);
+	else if (!strcmp(argv[0], "list"))
+		return !!list_stash(argc, argv, prefix);
+	else if (!strcmp(argv[0], "show"))
+		return !!show_stash(argc, argv, prefix);
+	else if (!strcmp(argv[0], "store"))
+		return !!store_stash(argc, argv, prefix);
+	else if (!strcmp(argv[0], "create"))
+		return !!create_stash(argc, argv, prefix);
+	else if (!strcmp(argv[0], "push"))
+		return !!push_stash(argc, argv, prefix);
+	else if (!strcmp(argv[0], "save"))
+		return !!save_stash(argc, argv, prefix);
+	else if (*argv[0] != '-')
+		usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]),
+			      git_stash_usage, options);
+
+	if (strcmp(argv[0], "-p")) {
+		while (++i < argc && strcmp(argv[i], "--")) {
+			/*
+			 * `akpqu` is a string which contains all short options,
+			 * except `-m` which is verified separately.
+			 */
+			if ((strlen(argv[i]) == 2) && *argv[i] == '-' &&
+			    strchr("akpqu", argv[i][1]))
+				continue;
+
+			if (!strcmp(argv[i], "--all") ||
+			    !strcmp(argv[i], "--keep-index") ||
+			    !strcmp(argv[i], "--no-keep-index") ||
+			    !strcmp(argv[i], "--patch") ||
+			    !strcmp(argv[i], "--quiet") ||
+			    !strcmp(argv[i], "--include-untracked"))
+				continue;
+
+			/*
+			 * `-m` and `--message=` are verified separately because
+			 * they need to be immediately followed by a string
+			 * (i.e.`-m"foobar"` or `--message="foobar"`).
+			 */
+			if (starts_with(argv[i], "-m") ||
+			    starts_with(argv[i], "--message="))
+				continue;
+
+			usage_with_options(git_stash_usage, options);
+		}
+	}
+
+	argv_array_push(&args, "push");
+	argv_array_pushv(&args, argv);
+	return !!push_stash(args.argc, args.argv, prefix);
+}
diff --git a/builtin/stripspace.c b/builtin/stripspace.c
new file mode 100644
index 0000000000..be33eb83c1
--- /dev/null
+++ b/builtin/stripspace.c
@@ -0,0 +1,65 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "parse-options.h"
+#include "strbuf.h"
+
+static void comment_lines(struct strbuf *buf)
+{
+	char *msg;
+	size_t len;
+
+	msg = strbuf_detach(buf, &len);
+	strbuf_add_commented_lines(buf, msg, len);
+	free(msg);
+}
+
+static const char * const stripspace_usage[] = {
+	N_("git stripspace [-s | --strip-comments]"),
+	N_("git stripspace [-c | --comment-lines]"),
+	NULL
+};
+
+enum stripspace_mode {
+	STRIP_DEFAULT = 0,
+	STRIP_COMMENTS,
+	COMMENT_LINES
+};
+
+int cmd_stripspace(int argc, const char **argv, const char *prefix)
+{
+	struct strbuf buf = STRBUF_INIT;
+	enum stripspace_mode mode = STRIP_DEFAULT;
+	int nongit;
+
+	const struct option options[] = {
+		OPT_CMDMODE('s', "strip-comments", &mode,
+			    N_("skip and remove all lines starting with comment character"),
+			    STRIP_COMMENTS),
+		OPT_CMDMODE('c', "comment-lines", &mode,
+			    N_("prepend comment character and space to each line"),
+			    COMMENT_LINES),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options, stripspace_usage, 0);
+	if (argc)
+		usage_with_options(stripspace_usage, options);
+
+	if (mode == STRIP_COMMENTS || mode == COMMENT_LINES) {
+		setup_git_directory_gently(&nongit);
+		git_config(git_default_config, NULL);
+	}
+
+	if (strbuf_read(&buf, 0, 1024) < 0)
+		die_errno("could not read the input");
+
+	if (mode == STRIP_DEFAULT || mode == STRIP_COMMENTS)
+		strbuf_stripspace(&buf, mode == STRIP_COMMENTS);
+	else
+		comment_lines(&buf);
+
+	write_or_die(1, buf.buf, buf.len);
+	strbuf_release(&buf);
+	return 0;
+}
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
new file mode 100644
index 0000000000..909e77e802
--- /dev/null
+++ b/builtin/submodule--helper.c
@@ -0,0 +1,2243 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "repository.h"
+#include "cache.h"
+#include "config.h"
+#include "parse-options.h"
+#include "quote.h"
+#include "pathspec.h"
+#include "dir.h"
+#include "submodule.h"
+#include "submodule-config.h"
+#include "string-list.h"
+#include "run-command.h"
+#include "remote.h"
+#include "refs.h"
+#include "refspec.h"
+#include "connect.h"
+#include "revision.h"
+#include "diffcore.h"
+#include "diff.h"
+#include "object-store.h"
+
+#define OPT_QUIET (1 << 0)
+#define OPT_CACHED (1 << 1)
+#define OPT_RECURSIVE (1 << 2)
+#define OPT_FORCE (1 << 3)
+
+typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
+				  void *cb_data);
+
+static char *get_default_remote(void)
+{
+	char *dest = NULL, *ret;
+	struct strbuf sb = STRBUF_INIT;
+	const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+
+	if (!refname)
+		die(_("No such ref: %s"), "HEAD");
+
+	/* detached HEAD */
+	if (!strcmp(refname, "HEAD"))
+		return xstrdup("origin");
+
+	if (!skip_prefix(refname, "refs/heads/", &refname))
+		die(_("Expecting a full ref name, got %s"), refname);
+
+	strbuf_addf(&sb, "branch.%s.remote", refname);
+	if (git_config_get_string(sb.buf, &dest))
+		ret = xstrdup("origin");
+	else
+		ret = dest;
+
+	strbuf_release(&sb);
+	return ret;
+}
+
+static int print_default_remote(int argc, const char **argv, const char *prefix)
+{
+	char *remote;
+
+	if (argc != 1)
+		die(_("submodule--helper print-default-remote takes no arguments"));
+
+	remote = get_default_remote();
+	if (remote)
+		printf("%s\n", remote);
+
+	free(remote);
+	return 0;
+}
+
+static int starts_with_dot_slash(const char *str)
+{
+	return str[0] == '.' && is_dir_sep(str[1]);
+}
+
+static int starts_with_dot_dot_slash(const char *str)
+{
+	return str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]);
+}
+
+/*
+ * Returns 1 if it was the last chop before ':'.
+ */
+static int chop_last_dir(char **remoteurl, int is_relative)
+{
+	char *rfind = find_last_dir_sep(*remoteurl);
+	if (rfind) {
+		*rfind = '\0';
+		return 0;
+	}
+
+	rfind = strrchr(*remoteurl, ':');
+	if (rfind) {
+		*rfind = '\0';
+		return 1;
+	}
+
+	if (is_relative || !strcmp(".", *remoteurl))
+		die(_("cannot strip one component off url '%s'"),
+			*remoteurl);
+
+	free(*remoteurl);
+	*remoteurl = xstrdup(".");
+	return 0;
+}
+
+/*
+ * The `url` argument is the URL that navigates to the submodule origin
+ * repo. When relative, this URL is relative to the superproject origin
+ * URL repo. The `up_path` argument, if specified, is the relative
+ * path that navigates from the submodule working tree to the superproject
+ * working tree. Returns the origin URL of the submodule.
+ *
+ * Return either an absolute URL or filesystem path (if the superproject
+ * origin URL is an absolute URL or filesystem path, respectively) or a
+ * relative file system path (if the superproject origin URL is a relative
+ * file system path).
+ *
+ * When the output is a relative file system path, the path is either
+ * relative to the submodule working tree, if up_path is specified, or to
+ * the superproject working tree otherwise.
+ *
+ * NEEDSWORK: This works incorrectly on the domain and protocol part.
+ * remote_url      url              outcome          expectation
+ * http://a.com/b  ../c             http://a.com/c   as is
+ * http://a.com/b/ ../c             http://a.com/c   same as previous line, but
+ *                                                   ignore trailing slash in url
+ * http://a.com/b  ../../c          http://c         error out
+ * http://a.com/b  ../../../c       http:/c          error out
+ * http://a.com/b  ../../../../c    http:c           error out
+ * http://a.com/b  ../../../../../c    .:c           error out
+ * NEEDSWORK: Given how chop_last_dir() works, this function is broken
+ * when a local part has a colon in its path component, too.
+ */
+static char *relative_url(const char *remote_url,
+				const char *url,
+				const char *up_path)
+{
+	int is_relative = 0;
+	int colonsep = 0;
+	char *out;
+	char *remoteurl = xstrdup(remote_url);
+	struct strbuf sb = STRBUF_INIT;
+	size_t len = strlen(remoteurl);
+
+	if (is_dir_sep(remoteurl[len-1]))
+		remoteurl[len-1] = '\0';
+
+	if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
+		is_relative = 0;
+	else {
+		is_relative = 1;
+		/*
+		 * Prepend a './' to ensure all relative
+		 * remoteurls start with './' or '../'
+		 */
+		if (!starts_with_dot_slash(remoteurl) &&
+		    !starts_with_dot_dot_slash(remoteurl)) {
+			strbuf_reset(&sb);
+			strbuf_addf(&sb, "./%s", remoteurl);
+			free(remoteurl);
+			remoteurl = strbuf_detach(&sb, NULL);
+		}
+	}
+	/*
+	 * When the url starts with '../', remove that and the
+	 * last directory in remoteurl.
+	 */
+	while (url) {
+		if (starts_with_dot_dot_slash(url)) {
+			url += 3;
+			colonsep |= chop_last_dir(&remoteurl, is_relative);
+		} else if (starts_with_dot_slash(url))
+			url += 2;
+		else
+			break;
+	}
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
+	if (ends_with(url, "/"))
+		strbuf_setlen(&sb, sb.len - 1);
+	free(remoteurl);
+
+	if (starts_with_dot_slash(sb.buf))
+		out = xstrdup(sb.buf + 2);
+	else
+		out = xstrdup(sb.buf);
+	strbuf_reset(&sb);
+
+	if (!up_path || !is_relative)
+		return out;
+
+	strbuf_addf(&sb, "%s%s", up_path, out);
+	free(out);
+	return strbuf_detach(&sb, NULL);
+}
+
+static int resolve_relative_url(int argc, const char **argv, const char *prefix)
+{
+	char *remoteurl = NULL;
+	char *remote = get_default_remote();
+	const char *up_path = NULL;
+	char *res;
+	const char *url;
+	struct strbuf sb = STRBUF_INIT;
+
+	if (argc != 2 && argc != 3)
+		die("resolve-relative-url only accepts one or two arguments");
+
+	url = argv[1];
+	strbuf_addf(&sb, "remote.%s.url", remote);
+	free(remote);
+
+	if (git_config_get_string(sb.buf, &remoteurl))
+		/* the repository is its own authoritative upstream */
+		remoteurl = xgetcwd();
+
+	if (argc == 3)
+		up_path = argv[2];
+
+	res = relative_url(remoteurl, url, up_path);
+	puts(res);
+	free(res);
+	free(remoteurl);
+	return 0;
+}
+
+static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
+{
+	char *remoteurl, *res;
+	const char *up_path, *url;
+
+	if (argc != 4)
+		die("resolve-relative-url-test only accepts three arguments: <up_path> <remoteurl> <url>");
+
+	up_path = argv[1];
+	remoteurl = xstrdup(argv[2]);
+	url = argv[3];
+
+	if (!strcmp(up_path, "(null)"))
+		up_path = NULL;
+
+	res = relative_url(remoteurl, url, up_path);
+	puts(res);
+	free(res);
+	free(remoteurl);
+	return 0;
+}
+
+/* the result should be freed by the caller. */
+static char *get_submodule_displaypath(const char *path, const char *prefix)
+{
+	const char *super_prefix = get_super_prefix();
+
+	if (prefix && super_prefix) {
+		BUG("cannot have prefix '%s' and superprefix '%s'",
+		    prefix, super_prefix);
+	} else if (prefix) {
+		struct strbuf sb = STRBUF_INIT;
+		char *displaypath = xstrdup(relative_path(path, prefix, &sb));
+		strbuf_release(&sb);
+		return displaypath;
+	} else if (super_prefix) {
+		return xstrfmt("%s%s", super_prefix, path);
+	} else {
+		return xstrdup(path);
+	}
+}
+
+static char *compute_rev_name(const char *sub_path, const char* object_id)
+{
+	struct strbuf sb = STRBUF_INIT;
+	const char ***d;
+
+	static const char *describe_bare[] = { NULL };
+
+	static const char *describe_tags[] = { "--tags", NULL };
+
+	static const char *describe_contains[] = { "--contains", NULL };
+
+	static const char *describe_all_always[] = { "--all", "--always", NULL };
+
+	static const char **describe_argv[] = { describe_bare, describe_tags,
+						describe_contains,
+						describe_all_always, NULL };
+
+	for (d = describe_argv; *d; d++) {
+		struct child_process cp = CHILD_PROCESS_INIT;
+		prepare_submodule_repo_env(&cp.env_array);
+		cp.dir = sub_path;
+		cp.git_cmd = 1;
+		cp.no_stderr = 1;
+
+		argv_array_push(&cp.args, "describe");
+		argv_array_pushv(&cp.args, *d);
+		argv_array_push(&cp.args, object_id);
+
+		if (!capture_command(&cp, &sb, 0)) {
+			strbuf_strip_suffix(&sb, "\n");
+			return strbuf_detach(&sb, NULL);
+		}
+	}
+
+	strbuf_release(&sb);
+	return NULL;
+}
+
+struct module_list {
+	const struct cache_entry **entries;
+	int alloc, nr;
+};
+#define MODULE_LIST_INIT { NULL, 0, 0 }
+
+static int module_list_compute(int argc, const char **argv,
+			       const char *prefix,
+			       struct pathspec *pathspec,
+			       struct module_list *list)
+{
+	int i, result = 0;
+	char *ps_matched = NULL;
+	parse_pathspec(pathspec, 0,
+		       PATHSPEC_PREFER_FULL,
+		       prefix, argv);
+
+	if (pathspec->nr)
+		ps_matched = xcalloc(pathspec->nr, 1);
+
+	if (read_cache() < 0)
+		die(_("index file corrupt"));
+
+	for (i = 0; i < active_nr; i++) {
+		const struct cache_entry *ce = active_cache[i];
+
+		if (!match_pathspec(&the_index, pathspec, ce->name, ce_namelen(ce),
+				    0, ps_matched, 1) ||
+		    !S_ISGITLINK(ce->ce_mode))
+			continue;
+
+		ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
+		list->entries[list->nr++] = ce;
+		while (i + 1 < active_nr &&
+		       !strcmp(ce->name, active_cache[i + 1]->name))
+			/*
+			 * Skip entries with the same name in different stages
+			 * to make sure an entry is returned only once.
+			 */
+			i++;
+	}
+
+	if (ps_matched && report_path_error(ps_matched, pathspec))
+		result = -1;
+
+	free(ps_matched);
+
+	return result;
+}
+
+static void module_list_active(struct module_list *list)
+{
+	int i;
+	struct module_list active_modules = MODULE_LIST_INIT;
+
+	for (i = 0; i < list->nr; i++) {
+		const struct cache_entry *ce = list->entries[i];
+
+		if (!is_submodule_active(the_repository, ce->name))
+			continue;
+
+		ALLOC_GROW(active_modules.entries,
+			   active_modules.nr + 1,
+			   active_modules.alloc);
+		active_modules.entries[active_modules.nr++] = ce;
+	}
+
+	free(list->entries);
+	*list = active_modules;
+}
+
+static char *get_up_path(const char *path)
+{
+	int i;
+	struct strbuf sb = STRBUF_INIT;
+
+	for (i = count_slashes(path); i; i--)
+		strbuf_addstr(&sb, "../");
+
+	/*
+	 * Check if 'path' ends with slash or not
+	 * for having the same output for dir/sub_dir
+	 * and dir/sub_dir/
+	 */
+	if (!is_dir_sep(path[strlen(path) - 1]))
+		strbuf_addstr(&sb, "../");
+
+	return strbuf_detach(&sb, NULL);
+}
+
+static int module_list(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct pathspec pathspec;
+	struct module_list list = MODULE_LIST_INIT;
+
+	struct option module_list_options[] = {
+		OPT_STRING(0, "prefix", &prefix,
+			   N_("path"),
+			   N_("alternative anchor for relative paths")),
+		OPT_END()
+	};
+
+	const char *const git_submodule_helper_usage[] = {
+		N_("git submodule--helper list [--prefix=<path>] [<path>...]"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, module_list_options,
+			     git_submodule_helper_usage, 0);
+
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+		return 1;
+
+	for (i = 0; i < list.nr; i++) {
+		const struct cache_entry *ce = list.entries[i];
+
+		if (ce_stage(ce))
+			printf("%06o %s U\t", ce->ce_mode, sha1_to_hex(null_sha1));
+		else
+			printf("%06o %s %d\t", ce->ce_mode,
+			       oid_to_hex(&ce->oid), ce_stage(ce));
+
+		fprintf(stdout, "%s\n", ce->name);
+	}
+	return 0;
+}
+
+static void for_each_listed_submodule(const struct module_list *list,
+				      each_submodule_fn fn, void *cb_data)
+{
+	int i;
+	for (i = 0; i < list->nr; i++)
+		fn(list->entries[i], cb_data);
+}
+
+struct cb_foreach {
+	int argc;
+	const char **argv;
+	const char *prefix;
+	int quiet;
+	int recursive;
+};
+#define CB_FOREACH_INIT { 0 }
+
+static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
+				       void *cb_data)
+{
+	struct cb_foreach *info = cb_data;
+	const char *path = list_item->name;
+	const struct object_id *ce_oid = &list_item->oid;
+
+	const struct submodule *sub;
+	struct child_process cp = CHILD_PROCESS_INIT;
+	char *displaypath;
+
+	displaypath = get_submodule_displaypath(path, info->prefix);
+
+	sub = submodule_from_path(the_repository, &null_oid, path);
+
+	if (!sub)
+		die(_("No url found for submodule path '%s' in .gitmodules"),
+			displaypath);
+
+	if (!is_submodule_populated_gently(path, NULL))
+		goto cleanup;
+
+	prepare_submodule_repo_env(&cp.env_array);
+
+	/*
+	 * For the purpose of executing <command> in the submodule,
+	 * separate shell is used for the purpose of running the
+	 * child process.
+	 */
+	cp.use_shell = 1;
+	cp.dir = path;
+
+	/*
+	 * NEEDSWORK: the command currently has access to the variables $name,
+	 * $sm_path, $displaypath, $sha1 and $toplevel only when the command
+	 * contains a single argument. This is done for maintaining a faithful
+	 * translation from shell script.
+	 */
+	if (info->argc == 1) {
+		char *toplevel = xgetcwd();
+		struct strbuf sb = STRBUF_INIT;
+
+		argv_array_pushf(&cp.env_array, "name=%s", sub->name);
+		argv_array_pushf(&cp.env_array, "sm_path=%s", path);
+		argv_array_pushf(&cp.env_array, "displaypath=%s", displaypath);
+		argv_array_pushf(&cp.env_array, "sha1=%s",
+				oid_to_hex(ce_oid));
+		argv_array_pushf(&cp.env_array, "toplevel=%s", toplevel);
+
+		/*
+		 * Since the path variable was accessible from the script
+		 * before porting, it is also made available after porting.
+		 * The environment variable "PATH" has a very special purpose
+		 * on windows. And since environment variables are
+		 * case-insensitive in windows, it interferes with the
+		 * existing PATH variable. Hence, to avoid that, we expose
+		 * path via the args argv_array and not via env_array.
+		 */
+		sq_quote_buf(&sb, path);
+		argv_array_pushf(&cp.args, "path=%s; %s",
+				 sb.buf, info->argv[0]);
+		strbuf_release(&sb);
+		free(toplevel);
+	} else {
+		argv_array_pushv(&cp.args, info->argv);
+	}
+
+	if (!info->quiet)
+		printf(_("Entering '%s'\n"), displaypath);
+
+	if (info->argv[0] && run_command(&cp))
+		die(_("run_command returned non-zero status for %s\n."),
+			displaypath);
+
+	if (info->recursive) {
+		struct child_process cpr = CHILD_PROCESS_INIT;
+
+		cpr.git_cmd = 1;
+		cpr.dir = path;
+		prepare_submodule_repo_env(&cpr.env_array);
+
+		argv_array_pushl(&cpr.args, "--super-prefix", NULL);
+		argv_array_pushf(&cpr.args, "%s/", displaypath);
+		argv_array_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
+				NULL);
+
+		if (info->quiet)
+			argv_array_push(&cpr.args, "--quiet");
+
+		argv_array_push(&cpr.args, "--");
+		argv_array_pushv(&cpr.args, info->argv);
+
+		if (run_command(&cpr))
+			die(_("run_command returned non-zero status while "
+				"recursing in the nested submodules of %s\n."),
+				displaypath);
+	}
+
+cleanup:
+	free(displaypath);
+}
+
+static int module_foreach(int argc, const char **argv, const char *prefix)
+{
+	struct cb_foreach info = CB_FOREACH_INIT;
+	struct pathspec pathspec;
+	struct module_list list = MODULE_LIST_INIT;
+
+	struct option module_foreach_options[] = {
+		OPT__QUIET(&info.quiet, N_("Suppress output of entering each submodule command")),
+		OPT_BOOL(0, "recursive", &info.recursive,
+			 N_("Recurse into nested submodules")),
+		OPT_END()
+	};
+
+	const char *const git_submodule_helper_usage[] = {
+		N_("git submodule--helper foreach [--quiet] [--recursive] [--] <command>"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, module_foreach_options,
+			     git_submodule_helper_usage, 0);
+
+	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
+		return 1;
+
+	info.argc = argc;
+	info.argv = argv;
+	info.prefix = prefix;
+
+	for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
+
+	return 0;
+}
+
+static char *compute_submodule_clone_url(const char *rel_url)
+{
+	char *remoteurl, *relurl;
+	char *remote = get_default_remote();
+	struct strbuf remotesb = STRBUF_INIT;
+
+	strbuf_addf(&remotesb, "remote.%s.url", remote);
+	if (git_config_get_string(remotesb.buf, &remoteurl)) {
+		warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
+		remoteurl = xgetcwd();
+	}
+	relurl = relative_url(remoteurl, rel_url, NULL);
+
+	free(remote);
+	free(remoteurl);
+	strbuf_release(&remotesb);
+
+	return relurl;
+}
+
+struct init_cb {
+	const char *prefix;
+	unsigned int flags;
+};
+
+#define INIT_CB_INIT { NULL, 0 }
+
+static void init_submodule(const char *path, const char *prefix,
+			   unsigned int flags)
+{
+	const struct submodule *sub;
+	struct strbuf sb = STRBUF_INIT;
+	char *upd = NULL, *url = NULL, *displaypath;
+
+	displaypath = get_submodule_displaypath(path, prefix);
+
+	sub = submodule_from_path(the_repository, &null_oid, path);
+
+	if (!sub)
+		die(_("No url found for submodule path '%s' in .gitmodules"),
+			displaypath);
+
+	/*
+	 * NEEDSWORK: In a multi-working-tree world, this needs to be
+	 * set in the per-worktree config.
+	 *
+	 * Set active flag for the submodule being initialized
+	 */
+	if (!is_submodule_active(the_repository, path)) {
+		strbuf_addf(&sb, "submodule.%s.active", sub->name);
+		git_config_set_gently(sb.buf, "true");
+		strbuf_reset(&sb);
+	}
+
+	/*
+	 * Copy url setting when it is not set yet.
+	 * To look up the url in .git/config, we must not fall back to
+	 * .gitmodules, so look it up directly.
+	 */
+	strbuf_addf(&sb, "submodule.%s.url", sub->name);
+	if (git_config_get_string(sb.buf, &url)) {
+		if (!sub->url)
+			die(_("No url found for submodule path '%s' in .gitmodules"),
+				displaypath);
+
+		url = xstrdup(sub->url);
+
+		/* Possibly a url relative to parent */
+		if (starts_with_dot_dot_slash(url) ||
+		    starts_with_dot_slash(url)) {
+			char *oldurl = url;
+			url = compute_submodule_clone_url(oldurl);
+			free(oldurl);
+		}
+
+		if (git_config_set_gently(sb.buf, url))
+			die(_("Failed to register url for submodule path '%s'"),
+			    displaypath);
+		if (!(flags & OPT_QUIET))
+			fprintf(stderr,
+				_("Submodule '%s' (%s) registered for path '%s'\n"),
+				sub->name, url, displaypath);
+	}
+	strbuf_reset(&sb);
+
+	/* Copy "update" setting when it is not set yet */
+	strbuf_addf(&sb, "submodule.%s.update", sub->name);
+	if (git_config_get_string(sb.buf, &upd) &&
+	    sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
+		if (sub->update_strategy.type == SM_UPDATE_COMMAND) {
+			fprintf(stderr, _("warning: command update mode suggested for submodule '%s'\n"),
+				sub->name);
+			upd = xstrdup("none");
+		} else
+			upd = xstrdup(submodule_strategy_to_string(&sub->update_strategy));
+
+		if (git_config_set_gently(sb.buf, upd))
+			die(_("Failed to register update mode for submodule path '%s'"), displaypath);
+	}
+	strbuf_release(&sb);
+	free(displaypath);
+	free(url);
+	free(upd);
+}
+
+static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data)
+{
+	struct init_cb *info = cb_data;
+	init_submodule(list_item->name, info->prefix, info->flags);
+}
+
+static int module_init(int argc, const char **argv, const char *prefix)
+{
+	struct init_cb info = INIT_CB_INIT;
+	struct pathspec pathspec;
+	struct module_list list = MODULE_LIST_INIT;
+	int quiet = 0;
+
+	struct option module_init_options[] = {
+		OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")),
+		OPT_END()
+	};
+
+	const char *const git_submodule_helper_usage[] = {
+		N_("git submodule--helper init [<options>] [<path>]"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, module_init_options,
+			     git_submodule_helper_usage, 0);
+
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+		return 1;
+
+	/*
+	 * If there are no path args and submodule.active is set then,
+	 * by default, only initialize 'active' modules.
+	 */
+	if (!argc && git_config_get_value_multi("submodule.active"))
+		module_list_active(&list);
+
+	info.prefix = prefix;
+	if (quiet)
+		info.flags |= OPT_QUIET;
+
+	for_each_listed_submodule(&list, init_submodule_cb, &info);
+
+	return 0;
+}
+
+struct status_cb {
+	const char *prefix;
+	unsigned int flags;
+};
+
+#define STATUS_CB_INIT { NULL, 0 }
+
+static void print_status(unsigned int flags, char state, const char *path,
+			 const struct object_id *oid, const char *displaypath)
+{
+	if (flags & OPT_QUIET)
+		return;
+
+	printf("%c%s %s", state, oid_to_hex(oid), displaypath);
+
+	if (state == ' ' || state == '+') {
+		const char *name = compute_rev_name(path, oid_to_hex(oid));
+
+		if (name)
+			printf(" (%s)", name);
+	}
+
+	printf("\n");
+}
+
+static int handle_submodule_head_ref(const char *refname,
+				     const struct object_id *oid, int flags,
+				     void *cb_data)
+{
+	struct object_id *output = cb_data;
+	if (oid)
+		oidcpy(output, oid);
+
+	return 0;
+}
+
+static void status_submodule(const char *path, const struct object_id *ce_oid,
+			     unsigned int ce_flags, const char *prefix,
+			     unsigned int flags)
+{
+	char *displaypath;
+	struct argv_array diff_files_args = ARGV_ARRAY_INIT;
+	struct rev_info rev;
+	int diff_files_result;
+
+	if (!submodule_from_path(the_repository, &null_oid, path))
+		die(_("no submodule mapping found in .gitmodules for path '%s'"),
+		      path);
+
+	displaypath = get_submodule_displaypath(path, prefix);
+
+	if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
+		print_status(flags, 'U', path, &null_oid, displaypath);
+		goto cleanup;
+	}
+
+	if (!is_submodule_active(the_repository, path)) {
+		print_status(flags, '-', path, ce_oid, displaypath);
+		goto cleanup;
+	}
+
+	argv_array_pushl(&diff_files_args, "diff-files",
+			 "--ignore-submodules=dirty", "--quiet", "--",
+			 path, NULL);
+
+	git_config(git_diff_basic_config, NULL);
+	repo_init_revisions(the_repository, &rev, prefix);
+	rev.abbrev = 0;
+	diff_files_args.argc = setup_revisions(diff_files_args.argc,
+					       diff_files_args.argv,
+					       &rev, NULL);
+	diff_files_result = run_diff_files(&rev, 0);
+
+	if (!diff_result_code(&rev.diffopt, diff_files_result)) {
+		print_status(flags, ' ', path, ce_oid,
+			     displaypath);
+	} else if (!(flags & OPT_CACHED)) {
+		struct object_id oid;
+		struct ref_store *refs = get_submodule_ref_store(path);
+
+		if (!refs) {
+			print_status(flags, '-', path, ce_oid, displaypath);
+			goto cleanup;
+		}
+		if (refs_head_ref(refs, handle_submodule_head_ref, &oid))
+			die(_("could not resolve HEAD ref inside the "
+			      "submodule '%s'"), path);
+
+		print_status(flags, '+', path, &oid, displaypath);
+	} else {
+		print_status(flags, '+', path, ce_oid, displaypath);
+	}
+
+	if (flags & OPT_RECURSIVE) {
+		struct child_process cpr = CHILD_PROCESS_INIT;
+
+		cpr.git_cmd = 1;
+		cpr.dir = path;
+		prepare_submodule_repo_env(&cpr.env_array);
+
+		argv_array_push(&cpr.args, "--super-prefix");
+		argv_array_pushf(&cpr.args, "%s/", displaypath);
+		argv_array_pushl(&cpr.args, "submodule--helper", "status",
+				 "--recursive", NULL);
+
+		if (flags & OPT_CACHED)
+			argv_array_push(&cpr.args, "--cached");
+
+		if (flags & OPT_QUIET)
+			argv_array_push(&cpr.args, "--quiet");
+
+		if (run_command(&cpr))
+			die(_("failed to recurse into submodule '%s'"), path);
+	}
+
+cleanup:
+	argv_array_clear(&diff_files_args);
+	free(displaypath);
+}
+
+static void status_submodule_cb(const struct cache_entry *list_item,
+				void *cb_data)
+{
+	struct status_cb *info = cb_data;
+	status_submodule(list_item->name, &list_item->oid, list_item->ce_flags,
+			 info->prefix, info->flags);
+}
+
+static int module_status(int argc, const char **argv, const char *prefix)
+{
+	struct status_cb info = STATUS_CB_INIT;
+	struct pathspec pathspec;
+	struct module_list list = MODULE_LIST_INIT;
+	int quiet = 0;
+
+	struct option module_status_options[] = {
+		OPT__QUIET(&quiet, N_("Suppress submodule status output")),
+		OPT_BIT(0, "cached", &info.flags, N_("Use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED),
+		OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE),
+		OPT_END()
+	};
+
+	const char *const git_submodule_helper_usage[] = {
+		N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, module_status_options,
+			     git_submodule_helper_usage, 0);
+
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+		return 1;
+
+	info.prefix = prefix;
+	if (quiet)
+		info.flags |= OPT_QUIET;
+
+	for_each_listed_submodule(&list, status_submodule_cb, &info);
+
+	return 0;
+}
+
+static int module_name(int argc, const char **argv, const char *prefix)
+{
+	const struct submodule *sub;
+
+	if (argc != 2)
+		usage(_("git submodule--helper name <path>"));
+
+	sub = submodule_from_path(the_repository, &null_oid, argv[1]);
+
+	if (!sub)
+		die(_("no submodule mapping found in .gitmodules for path '%s'"),
+		    argv[1]);
+
+	printf("%s\n", sub->name);
+
+	return 0;
+}
+
+struct sync_cb {
+	const char *prefix;
+	unsigned int flags;
+};
+
+#define SYNC_CB_INIT { NULL, 0 }
+
+static void sync_submodule(const char *path, const char *prefix,
+			   unsigned int flags)
+{
+	const struct submodule *sub;
+	char *remote_key = NULL;
+	char *sub_origin_url, *super_config_url, *displaypath;
+	struct strbuf sb = STRBUF_INIT;
+	struct child_process cp = CHILD_PROCESS_INIT;
+	char *sub_config_path = NULL;
+
+	if (!is_submodule_active(the_repository, path))
+		return;
+
+	sub = submodule_from_path(the_repository, &null_oid, path);
+
+	if (sub && sub->url) {
+		if (starts_with_dot_dot_slash(sub->url) ||
+		    starts_with_dot_slash(sub->url)) {
+			char *remote_url, *up_path;
+			char *remote = get_default_remote();
+			strbuf_addf(&sb, "remote.%s.url", remote);
+
+			if (git_config_get_string(sb.buf, &remote_url))
+				remote_url = xgetcwd();
+
+			up_path = get_up_path(path);
+			sub_origin_url = relative_url(remote_url, sub->url, up_path);
+			super_config_url = relative_url(remote_url, sub->url, NULL);
+
+			free(remote);
+			free(up_path);
+			free(remote_url);
+		} else {
+			sub_origin_url = xstrdup(sub->url);
+			super_config_url = xstrdup(sub->url);
+		}
+	} else {
+		sub_origin_url = xstrdup("");
+		super_config_url = xstrdup("");
+	}
+
+	displaypath = get_submodule_displaypath(path, prefix);
+
+	if (!(flags & OPT_QUIET))
+		printf(_("Synchronizing submodule url for '%s'\n"),
+			 displaypath);
+
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "submodule.%s.url", sub->name);
+	if (git_config_set_gently(sb.buf, super_config_url))
+		die(_("failed to register url for submodule path '%s'"),
+		      displaypath);
+
+	if (!is_submodule_populated_gently(path, NULL))
+		goto cleanup;
+
+	prepare_submodule_repo_env(&cp.env_array);
+	cp.git_cmd = 1;
+	cp.dir = path;
+	argv_array_pushl(&cp.args, "submodule--helper",
+			 "print-default-remote", NULL);
+
+	strbuf_reset(&sb);
+	if (capture_command(&cp, &sb, 0))
+		die(_("failed to get the default remote for submodule '%s'"),
+		      path);
+
+	strbuf_strip_suffix(&sb, "\n");
+	remote_key = xstrfmt("remote.%s.url", sb.buf);
+
+	strbuf_reset(&sb);
+	submodule_to_gitdir(&sb, path);
+	strbuf_addstr(&sb, "/config");
+
+	if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url))
+		die(_("failed to update remote for submodule '%s'"),
+		      path);
+
+	if (flags & OPT_RECURSIVE) {
+		struct child_process cpr = CHILD_PROCESS_INIT;
+
+		cpr.git_cmd = 1;
+		cpr.dir = path;
+		prepare_submodule_repo_env(&cpr.env_array);
+
+		argv_array_push(&cpr.args, "--super-prefix");
+		argv_array_pushf(&cpr.args, "%s/", displaypath);
+		argv_array_pushl(&cpr.args, "submodule--helper", "sync",
+				 "--recursive", NULL);
+
+		if (flags & OPT_QUIET)
+			argv_array_push(&cpr.args, "--quiet");
+
+		if (run_command(&cpr))
+			die(_("failed to recurse into submodule '%s'"),
+			      path);
+	}
+
+cleanup:
+	free(super_config_url);
+	free(sub_origin_url);
+	strbuf_release(&sb);
+	free(remote_key);
+	free(displaypath);
+	free(sub_config_path);
+}
+
+static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data)
+{
+	struct sync_cb *info = cb_data;
+	sync_submodule(list_item->name, info->prefix, info->flags);
+}
+
+static int module_sync(int argc, const char **argv, const char *prefix)
+{
+	struct sync_cb info = SYNC_CB_INIT;
+	struct pathspec pathspec;
+	struct module_list list = MODULE_LIST_INIT;
+	int quiet = 0;
+	int recursive = 0;
+
+	struct option module_sync_options[] = {
+		OPT__QUIET(&quiet, N_("Suppress output of synchronizing submodule url")),
+		OPT_BOOL(0, "recursive", &recursive,
+			N_("Recurse into nested submodules")),
+		OPT_END()
+	};
+
+	const char *const git_submodule_helper_usage[] = {
+		N_("git submodule--helper sync [--quiet] [--recursive] [<path>]"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, module_sync_options,
+			     git_submodule_helper_usage, 0);
+
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+		return 1;
+
+	info.prefix = prefix;
+	if (quiet)
+		info.flags |= OPT_QUIET;
+	if (recursive)
+		info.flags |= OPT_RECURSIVE;
+
+	for_each_listed_submodule(&list, sync_submodule_cb, &info);
+
+	return 0;
+}
+
+struct deinit_cb {
+	const char *prefix;
+	unsigned int flags;
+};
+#define DEINIT_CB_INIT { NULL, 0 }
+
+static void deinit_submodule(const char *path, const char *prefix,
+			     unsigned int flags)
+{
+	const struct submodule *sub;
+	char *displaypath = NULL;
+	struct child_process cp_config = CHILD_PROCESS_INIT;
+	struct strbuf sb_config = STRBUF_INIT;
+	char *sub_git_dir = xstrfmt("%s/.git", path);
+
+	sub = submodule_from_path(the_repository, &null_oid, path);
+
+	if (!sub || !sub->name)
+		goto cleanup;
+
+	displaypath = get_submodule_displaypath(path, prefix);
+
+	/* remove the submodule work tree (unless the user already did it) */
+	if (is_directory(path)) {
+		struct strbuf sb_rm = STRBUF_INIT;
+		const char *format;
+
+		/*
+		 * protect submodules containing a .git directory
+		 * NEEDSWORK: instead of dying, automatically call
+		 * absorbgitdirs and (possibly) warn.
+		 */
+		if (is_directory(sub_git_dir))
+			die(_("Submodule work tree '%s' contains a .git "
+			      "directory (use 'rm -rf' if you really want "
+			      "to remove it including all of its history)"),
+			    displaypath);
+
+		if (!(flags & OPT_FORCE)) {
+			struct child_process cp_rm = CHILD_PROCESS_INIT;
+			cp_rm.git_cmd = 1;
+			argv_array_pushl(&cp_rm.args, "rm", "-qn",
+					 path, NULL);
+
+			if (run_command(&cp_rm))
+				die(_("Submodule work tree '%s' contains local "
+				      "modifications; use '-f' to discard them"),
+				      displaypath);
+		}
+
+		strbuf_addstr(&sb_rm, path);
+
+		if (!remove_dir_recursively(&sb_rm, 0))
+			format = _("Cleared directory '%s'\n");
+		else
+			format = _("Could not remove submodule work tree '%s'\n");
+
+		if (!(flags & OPT_QUIET))
+			printf(format, displaypath);
+
+		submodule_unset_core_worktree(sub);
+
+		strbuf_release(&sb_rm);
+	}
+
+	if (mkdir(path, 0777))
+		printf(_("could not create empty submodule directory %s"),
+		      displaypath);
+
+	cp_config.git_cmd = 1;
+	argv_array_pushl(&cp_config.args, "config", "--get-regexp", NULL);
+	argv_array_pushf(&cp_config.args, "submodule.%s\\.", sub->name);
+
+	/* remove the .git/config entries (unless the user already did it) */
+	if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) {
+		char *sub_key = xstrfmt("submodule.%s", sub->name);
+		/*
+		 * remove the whole section so we have a clean state when
+		 * the user later decides to init this submodule again
+		 */
+		git_config_rename_section_in_file(NULL, sub_key, NULL);
+		if (!(flags & OPT_QUIET))
+			printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"),
+				 sub->name, sub->url, displaypath);
+		free(sub_key);
+	}
+
+cleanup:
+	free(displaypath);
+	free(sub_git_dir);
+	strbuf_release(&sb_config);
+}
+
+static void deinit_submodule_cb(const struct cache_entry *list_item,
+				void *cb_data)
+{
+	struct deinit_cb *info = cb_data;
+	deinit_submodule(list_item->name, info->prefix, info->flags);
+}
+
+static int module_deinit(int argc, const char **argv, const char *prefix)
+{
+	struct deinit_cb info = DEINIT_CB_INIT;
+	struct pathspec pathspec;
+	struct module_list list = MODULE_LIST_INIT;
+	int quiet = 0;
+	int force = 0;
+	int all = 0;
+
+	struct option module_deinit_options[] = {
+		OPT__QUIET(&quiet, N_("Suppress submodule status output")),
+		OPT__FORCE(&force, N_("Remove submodule working trees even if they contain local changes"), 0),
+		OPT_BOOL(0, "all", &all, N_("Unregister all submodules")),
+		OPT_END()
+	};
+
+	const char *const git_submodule_helper_usage[] = {
+		N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, module_deinit_options,
+			     git_submodule_helper_usage, 0);
+
+	if (all && argc) {
+		error("pathspec and --all are incompatible");
+		usage_with_options(git_submodule_helper_usage,
+				   module_deinit_options);
+	}
+
+	if (!argc && !all)
+		die(_("Use '--all' if you really want to deinitialize all submodules"));
+
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+		return 1;
+
+	info.prefix = prefix;
+	if (quiet)
+		info.flags |= OPT_QUIET;
+	if (force)
+		info.flags |= OPT_FORCE;
+
+	for_each_listed_submodule(&list, deinit_submodule_cb, &info);
+
+	return 0;
+}
+
+static int clone_submodule(const char *path, const char *gitdir, const char *url,
+			   const char *depth, struct string_list *reference, int dissociate,
+			   int quiet, int progress)
+{
+	struct child_process cp = CHILD_PROCESS_INIT;
+
+	argv_array_push(&cp.args, "clone");
+	argv_array_push(&cp.args, "--no-checkout");
+	if (quiet)
+		argv_array_push(&cp.args, "--quiet");
+	if (progress)
+		argv_array_push(&cp.args, "--progress");
+	if (depth && *depth)
+		argv_array_pushl(&cp.args, "--depth", depth, NULL);
+	if (reference->nr) {
+		struct string_list_item *item;
+		for_each_string_list_item(item, reference)
+			argv_array_pushl(&cp.args, "--reference",
+					 item->string, NULL);
+	}
+	if (dissociate)
+		argv_array_push(&cp.args, "--dissociate");
+	if (gitdir && *gitdir)
+		argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
+
+	argv_array_push(&cp.args, "--");
+	argv_array_push(&cp.args, url);
+	argv_array_push(&cp.args, path);
+
+	cp.git_cmd = 1;
+	prepare_submodule_repo_env(&cp.env_array);
+	cp.no_stdin = 1;
+
+	return run_command(&cp);
+}
+
+struct submodule_alternate_setup {
+	const char *submodule_name;
+	enum SUBMODULE_ALTERNATE_ERROR_MODE {
+		SUBMODULE_ALTERNATE_ERROR_DIE,
+		SUBMODULE_ALTERNATE_ERROR_INFO,
+		SUBMODULE_ALTERNATE_ERROR_IGNORE
+	} error_mode;
+	struct string_list *reference;
+};
+#define SUBMODULE_ALTERNATE_SETUP_INIT { NULL, \
+	SUBMODULE_ALTERNATE_ERROR_IGNORE, NULL }
+
+static int add_possible_reference_from_superproject(
+		struct object_directory *odb, void *sas_cb)
+{
+	struct submodule_alternate_setup *sas = sas_cb;
+	size_t len;
+
+	/*
+	 * If the alternate object store is another repository, try the
+	 * standard layout with .git/(modules/<name>)+/objects
+	 */
+	if (strip_suffix(odb->path, "/objects", &len)) {
+		char *sm_alternate;
+		struct strbuf sb = STRBUF_INIT;
+		struct strbuf err = STRBUF_INIT;
+		strbuf_add(&sb, odb->path, len);
+
+		/*
+		 * We need to end the new path with '/' to mark it as a dir,
+		 * otherwise a submodule name containing '/' will be broken
+		 * as the last part of a missing submodule reference would
+		 * be taken as a file name.
+		 */
+		strbuf_addf(&sb, "/modules/%s/", sas->submodule_name);
+
+		sm_alternate = compute_alternate_path(sb.buf, &err);
+		if (sm_alternate) {
+			string_list_append(sas->reference, xstrdup(sb.buf));
+			free(sm_alternate);
+		} else {
+			switch (sas->error_mode) {
+			case SUBMODULE_ALTERNATE_ERROR_DIE:
+				die(_("submodule '%s' cannot add alternate: %s"),
+				    sas->submodule_name, err.buf);
+			case SUBMODULE_ALTERNATE_ERROR_INFO:
+				fprintf_ln(stderr, _("submodule '%s' cannot add alternate: %s"),
+					sas->submodule_name, err.buf);
+			case SUBMODULE_ALTERNATE_ERROR_IGNORE:
+				; /* nothing */
+			}
+		}
+		strbuf_release(&sb);
+	}
+
+	return 0;
+}
+
+static void prepare_possible_alternates(const char *sm_name,
+		struct string_list *reference)
+{
+	char *sm_alternate = NULL, *error_strategy = NULL;
+	struct submodule_alternate_setup sas = SUBMODULE_ALTERNATE_SETUP_INIT;
+
+	git_config_get_string("submodule.alternateLocation", &sm_alternate);
+	if (!sm_alternate)
+		return;
+
+	git_config_get_string("submodule.alternateErrorStrategy", &error_strategy);
+
+	if (!error_strategy)
+		error_strategy = xstrdup("die");
+
+	sas.submodule_name = sm_name;
+	sas.reference = reference;
+	if (!strcmp(error_strategy, "die"))
+		sas.error_mode = SUBMODULE_ALTERNATE_ERROR_DIE;
+	else if (!strcmp(error_strategy, "info"))
+		sas.error_mode = SUBMODULE_ALTERNATE_ERROR_INFO;
+	else if (!strcmp(error_strategy, "ignore"))
+		sas.error_mode = SUBMODULE_ALTERNATE_ERROR_IGNORE;
+	else
+		die(_("Value '%s' for submodule.alternateErrorStrategy is not recognized"), error_strategy);
+
+	if (!strcmp(sm_alternate, "superproject"))
+		foreach_alt_odb(add_possible_reference_from_superproject, &sas);
+	else if (!strcmp(sm_alternate, "no"))
+		; /* do nothing */
+	else
+		die(_("Value '%s' for submodule.alternateLocation is not recognized"), sm_alternate);
+
+	free(sm_alternate);
+	free(error_strategy);
+}
+
+static int module_clone(int argc, const char **argv, const char *prefix)
+{
+	const char *name = NULL, *url = NULL, *depth = NULL;
+	int quiet = 0;
+	int progress = 0;
+	char *p, *path = NULL, *sm_gitdir;
+	struct strbuf sb = STRBUF_INIT;
+	struct string_list reference = STRING_LIST_INIT_NODUP;
+	int dissociate = 0;
+	char *sm_alternate = NULL, *error_strategy = NULL;
+
+	struct option module_clone_options[] = {
+		OPT_STRING(0, "prefix", &prefix,
+			   N_("path"),
+			   N_("alternative anchor for relative paths")),
+		OPT_STRING(0, "path", &path,
+			   N_("path"),
+			   N_("where the new submodule will be cloned to")),
+		OPT_STRING(0, "name", &name,
+			   N_("string"),
+			   N_("name of the new submodule")),
+		OPT_STRING(0, "url", &url,
+			   N_("string"),
+			   N_("url where to clone the submodule from")),
+		OPT_STRING_LIST(0, "reference", &reference,
+			   N_("repo"),
+			   N_("reference repository")),
+		OPT_BOOL(0, "dissociate", &dissociate,
+			   N_("use --reference only while cloning")),
+		OPT_STRING(0, "depth", &depth,
+			   N_("string"),
+			   N_("depth for shallow clones")),
+		OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
+		OPT_BOOL(0, "progress", &progress,
+			   N_("force cloning progress")),
+		OPT_END()
+	};
+
+	const char *const git_submodule_helper_usage[] = {
+		N_("git submodule--helper clone [--prefix=<path>] [--quiet] "
+		   "[--reference <repository>] [--name <name>] [--depth <depth>] "
+		   "--url <url> --path <path>"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, module_clone_options,
+			     git_submodule_helper_usage, 0);
+
+	if (argc || !url || !path || !*path)
+		usage_with_options(git_submodule_helper_usage,
+				   module_clone_options);
+
+	strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
+	sm_gitdir = absolute_pathdup(sb.buf);
+	strbuf_reset(&sb);
+
+	if (!is_absolute_path(path)) {
+		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), path);
+		path = strbuf_detach(&sb, NULL);
+	} else
+		path = xstrdup(path);
+
+	if (!file_exists(sm_gitdir)) {
+		if (safe_create_leading_directories_const(sm_gitdir) < 0)
+			die(_("could not create directory '%s'"), sm_gitdir);
+
+		prepare_possible_alternates(name, &reference);
+
+		if (clone_submodule(path, sm_gitdir, url, depth, &reference, dissociate,
+				    quiet, progress))
+			die(_("clone of '%s' into submodule path '%s' failed"),
+			    url, path);
+	} else {
+		if (safe_create_leading_directories_const(path) < 0)
+			die(_("could not create directory '%s'"), path);
+		strbuf_addf(&sb, "%s/index", sm_gitdir);
+		unlink_or_warn(sb.buf);
+		strbuf_reset(&sb);
+	}
+
+	connect_work_tree_and_git_dir(path, sm_gitdir, 0);
+
+	p = git_pathdup_submodule(path, "config");
+	if (!p)
+		die(_("could not get submodule directory for '%s'"), path);
+
+	/* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */
+	git_config_get_string("submodule.alternateLocation", &sm_alternate);
+	if (sm_alternate)
+		git_config_set_in_file(p, "submodule.alternateLocation",
+					   sm_alternate);
+	git_config_get_string("submodule.alternateErrorStrategy", &error_strategy);
+	if (error_strategy)
+		git_config_set_in_file(p, "submodule.alternateErrorStrategy",
+					   error_strategy);
+
+	free(sm_alternate);
+	free(error_strategy);
+
+	strbuf_release(&sb);
+	free(sm_gitdir);
+	free(path);
+	free(p);
+	return 0;
+}
+
+static void determine_submodule_update_strategy(struct repository *r,
+						int just_cloned,
+						const char *path,
+						const char *update,
+						struct submodule_update_strategy *out)
+{
+	const struct submodule *sub = submodule_from_path(r, &null_oid, path);
+	char *key;
+	const char *val;
+
+	key = xstrfmt("submodule.%s.update", sub->name);
+
+	if (update) {
+		if (parse_submodule_update_strategy(update, out) < 0)
+			die(_("Invalid update mode '%s' for submodule path '%s'"),
+				update, path);
+	} else if (!repo_config_get_string_const(r, key, &val)) {
+		if (parse_submodule_update_strategy(val, out) < 0)
+			die(_("Invalid update mode '%s' configured for submodule path '%s'"),
+				val, path);
+	} else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
+		out->type = sub->update_strategy.type;
+		out->command = sub->update_strategy.command;
+	} else
+		out->type = SM_UPDATE_CHECKOUT;
+
+	if (just_cloned &&
+	    (out->type == SM_UPDATE_MERGE ||
+	     out->type == SM_UPDATE_REBASE ||
+	     out->type == SM_UPDATE_NONE))
+		out->type = SM_UPDATE_CHECKOUT;
+
+	free(key);
+}
+
+static int module_update_module_mode(int argc, const char **argv, const char *prefix)
+{
+	const char *path, *update = NULL;
+	int just_cloned;
+	struct submodule_update_strategy update_strategy = { .type = SM_UPDATE_CHECKOUT };
+
+	if (argc < 3 || argc > 4)
+		die("submodule--helper update-module-clone expects <just-cloned> <path> [<update>]");
+
+	just_cloned = git_config_int("just_cloned", argv[1]);
+	path = argv[2];
+
+	if (argc == 4)
+		update = argv[3];
+
+	determine_submodule_update_strategy(the_repository,
+					    just_cloned, path, update,
+					    &update_strategy);
+	fputs(submodule_strategy_to_string(&update_strategy), stdout);
+
+	return 0;
+}
+
+struct update_clone_data {
+	const struct submodule *sub;
+	struct object_id oid;
+	unsigned just_cloned;
+};
+
+struct submodule_update_clone {
+	/* index into 'list', the list of submodules to look into for cloning */
+	int current;
+	struct module_list list;
+	unsigned warn_if_uninitialized : 1;
+
+	/* update parameter passed via commandline */
+	struct submodule_update_strategy update;
+
+	/* configuration parameters which are passed on to the children */
+	int progress;
+	int quiet;
+	int recommend_shallow;
+	struct string_list references;
+	int dissociate;
+	const char *depth;
+	const char *recursive_prefix;
+	const char *prefix;
+
+	/* to be consumed by git-submodule.sh */
+	struct update_clone_data *update_clone;
+	int update_clone_nr; int update_clone_alloc;
+
+	/* If we want to stop as fast as possible and return an error */
+	unsigned quickstop : 1;
+
+	/* failed clones to be retried again */
+	const struct cache_entry **failed_clones;
+	int failed_clones_nr, failed_clones_alloc;
+
+	int max_jobs;
+};
+#define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
+	SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
+	NULL, NULL, NULL, \
+	NULL, 0, 0, 0, NULL, 0, 0, 1}
+
+
+static void next_submodule_warn_missing(struct submodule_update_clone *suc,
+		struct strbuf *out, const char *displaypath)
+{
+	/*
+	 * Only mention uninitialized submodules when their
+	 * paths have been specified.
+	 */
+	if (suc->warn_if_uninitialized) {
+		strbuf_addf(out,
+			_("Submodule path '%s' not initialized"),
+			displaypath);
+		strbuf_addch(out, '\n');
+		strbuf_addstr(out,
+			_("Maybe you want to use 'update --init'?"));
+		strbuf_addch(out, '\n');
+	}
+}
+
+/**
+ * Determine whether 'ce' needs to be cloned. If so, prepare the 'child' to
+ * run the clone. Returns 1 if 'ce' needs to be cloned, 0 otherwise.
+ */
+static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
+					   struct child_process *child,
+					   struct submodule_update_clone *suc,
+					   struct strbuf *out)
+{
+	const struct submodule *sub = NULL;
+	const char *url = NULL;
+	const char *update_string;
+	enum submodule_update_type update_type;
+	char *key;
+	struct strbuf displaypath_sb = STRBUF_INIT;
+	struct strbuf sb = STRBUF_INIT;
+	const char *displaypath = NULL;
+	int needs_cloning = 0;
+	int need_free_url = 0;
+
+	if (ce_stage(ce)) {
+		if (suc->recursive_prefix)
+			strbuf_addf(&sb, "%s/%s", suc->recursive_prefix, ce->name);
+		else
+			strbuf_addstr(&sb, ce->name);
+		strbuf_addf(out, _("Skipping unmerged submodule %s"), sb.buf);
+		strbuf_addch(out, '\n');
+		goto cleanup;
+	}
+
+	sub = submodule_from_path(the_repository, &null_oid, ce->name);
+
+	if (suc->recursive_prefix)
+		displaypath = relative_path(suc->recursive_prefix,
+					    ce->name, &displaypath_sb);
+	else
+		displaypath = ce->name;
+
+	if (!sub) {
+		next_submodule_warn_missing(suc, out, displaypath);
+		goto cleanup;
+	}
+
+	key = xstrfmt("submodule.%s.update", sub->name);
+	if (!repo_config_get_string_const(the_repository, key, &update_string)) {
+		update_type = parse_submodule_update_type(update_string);
+	} else {
+		update_type = sub->update_strategy.type;
+	}
+	free(key);
+
+	if (suc->update.type == SM_UPDATE_NONE
+	    || (suc->update.type == SM_UPDATE_UNSPECIFIED
+		&& update_type == SM_UPDATE_NONE)) {
+		strbuf_addf(out, _("Skipping submodule '%s'"), displaypath);
+		strbuf_addch(out, '\n');
+		goto cleanup;
+	}
+
+	/* Check if the submodule has been initialized. */
+	if (!is_submodule_active(the_repository, ce->name)) {
+		next_submodule_warn_missing(suc, out, displaypath);
+		goto cleanup;
+	}
+
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "submodule.%s.url", sub->name);
+	if (repo_config_get_string_const(the_repository, sb.buf, &url)) {
+		if (starts_with_dot_slash(sub->url) ||
+		    starts_with_dot_dot_slash(sub->url)) {
+			url = compute_submodule_clone_url(sub->url);
+			need_free_url = 1;
+		} else
+			url = sub->url;
+	}
+
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s/.git", ce->name);
+	needs_cloning = !file_exists(sb.buf);
+
+	ALLOC_GROW(suc->update_clone, suc->update_clone_nr + 1,
+		   suc->update_clone_alloc);
+	oidcpy(&suc->update_clone[suc->update_clone_nr].oid, &ce->oid);
+	suc->update_clone[suc->update_clone_nr].just_cloned = needs_cloning;
+	suc->update_clone[suc->update_clone_nr].sub = sub;
+	suc->update_clone_nr++;
+
+	if (!needs_cloning)
+		goto cleanup;
+
+	child->git_cmd = 1;
+	child->no_stdin = 1;
+	child->stdout_to_stderr = 1;
+	child->err = -1;
+	argv_array_push(&child->args, "submodule--helper");
+	argv_array_push(&child->args, "clone");
+	if (suc->progress)
+		argv_array_push(&child->args, "--progress");
+	if (suc->quiet)
+		argv_array_push(&child->args, "--quiet");
+	if (suc->prefix)
+		argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL);
+	if (suc->recommend_shallow && sub->recommend_shallow == 1)
+		argv_array_push(&child->args, "--depth=1");
+	argv_array_pushl(&child->args, "--path", sub->path, NULL);
+	argv_array_pushl(&child->args, "--name", sub->name, NULL);
+	argv_array_pushl(&child->args, "--url", url, NULL);
+	if (suc->references.nr) {
+		struct string_list_item *item;
+		for_each_string_list_item(item, &suc->references)
+			argv_array_pushl(&child->args, "--reference", item->string, NULL);
+	}
+	if (suc->dissociate)
+		argv_array_push(&child->args, "--dissociate");
+	if (suc->depth)
+		argv_array_push(&child->args, suc->depth);
+
+cleanup:
+	strbuf_reset(&displaypath_sb);
+	strbuf_reset(&sb);
+	if (need_free_url)
+		free((void*)url);
+
+	return needs_cloning;
+}
+
+static int update_clone_get_next_task(struct child_process *child,
+				      struct strbuf *err,
+				      void *suc_cb,
+				      void **idx_task_cb)
+{
+	struct submodule_update_clone *suc = suc_cb;
+	const struct cache_entry *ce;
+	int index;
+
+	for (; suc->current < suc->list.nr; suc->current++) {
+		ce = suc->list.entries[suc->current];
+		if (prepare_to_clone_next_submodule(ce, child, suc, err)) {
+			int *p = xmalloc(sizeof(*p));
+			*p = suc->current;
+			*idx_task_cb = p;
+			suc->current++;
+			return 1;
+		}
+	}
+
+	/*
+	 * The loop above tried cloning each submodule once, now try the
+	 * stragglers again, which we can imagine as an extension of the
+	 * entry list.
+	 */
+	index = suc->current - suc->list.nr;
+	if (index < suc->failed_clones_nr) {
+		int *p;
+		ce = suc->failed_clones[index];
+		if (!prepare_to_clone_next_submodule(ce, child, suc, err)) {
+			suc->current ++;
+			strbuf_addstr(err, "BUG: submodule considered for "
+					   "cloning, doesn't need cloning "
+					   "any more?\n");
+			return 0;
+		}
+		p = xmalloc(sizeof(*p));
+		*p = suc->current;
+		*idx_task_cb = p;
+		suc->current ++;
+		return 1;
+	}
+
+	return 0;
+}
+
+static int update_clone_start_failure(struct strbuf *err,
+				      void *suc_cb,
+				      void *idx_task_cb)
+{
+	struct submodule_update_clone *suc = suc_cb;
+	suc->quickstop = 1;
+	return 1;
+}
+
+static int update_clone_task_finished(int result,
+				      struct strbuf *err,
+				      void *suc_cb,
+				      void *idx_task_cb)
+{
+	const struct cache_entry *ce;
+	struct submodule_update_clone *suc = suc_cb;
+
+	int *idxP = idx_task_cb;
+	int idx = *idxP;
+	free(idxP);
+
+	if (!result)
+		return 0;
+
+	if (idx < suc->list.nr) {
+		ce  = suc->list.entries[idx];
+		strbuf_addf(err, _("Failed to clone '%s'. Retry scheduled"),
+			    ce->name);
+		strbuf_addch(err, '\n');
+		ALLOC_GROW(suc->failed_clones,
+			   suc->failed_clones_nr + 1,
+			   suc->failed_clones_alloc);
+		suc->failed_clones[suc->failed_clones_nr++] = ce;
+		return 0;
+	} else {
+		idx -= suc->list.nr;
+		ce  = suc->failed_clones[idx];
+		strbuf_addf(err, _("Failed to clone '%s' a second time, aborting"),
+			    ce->name);
+		strbuf_addch(err, '\n');
+		suc->quickstop = 1;
+		return 1;
+	}
+
+	return 0;
+}
+
+static int git_update_clone_config(const char *var, const char *value,
+				   void *cb)
+{
+	int *max_jobs = cb;
+	if (!strcmp(var, "submodule.fetchjobs"))
+		*max_jobs = parse_submodule_fetchjobs(var, value);
+	return 0;
+}
+
+static void update_submodule(struct update_clone_data *ucd)
+{
+	fprintf(stdout, "dummy %s %d\t%s\n",
+		oid_to_hex(&ucd->oid),
+		ucd->just_cloned,
+		ucd->sub->path);
+}
+
+static int update_submodules(struct submodule_update_clone *suc)
+{
+	int i;
+
+	run_processes_parallel_tr2(suc->max_jobs, update_clone_get_next_task,
+				   update_clone_start_failure,
+				   update_clone_task_finished, suc, "submodule",
+				   "parallel/update");
+
+	/*
+	 * We saved the output and put it out all at once now.
+	 * That means:
+	 * - the listener does not have to interleave their (checkout)
+	 *   work with our fetching.  The writes involved in a
+	 *   checkout involve more straightforward sequential I/O.
+	 * - the listener can avoid doing any work if fetching failed.
+	 */
+	if (suc->quickstop)
+		return 1;
+
+	for (i = 0; i < suc->update_clone_nr; i++)
+		update_submodule(&suc->update_clone[i]);
+
+	return 0;
+}
+
+static int update_clone(int argc, const char **argv, const char *prefix)
+{
+	const char *update = NULL;
+	struct pathspec pathspec;
+	struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
+
+	struct option module_update_clone_options[] = {
+		OPT_STRING(0, "prefix", &prefix,
+			   N_("path"),
+			   N_("path into the working tree")),
+		OPT_STRING(0, "recursive-prefix", &suc.recursive_prefix,
+			   N_("path"),
+			   N_("path into the working tree, across nested "
+			      "submodule boundaries")),
+		OPT_STRING(0, "update", &update,
+			   N_("string"),
+			   N_("rebase, merge, checkout or none")),
+		OPT_STRING_LIST(0, "reference", &suc.references, N_("repo"),
+			   N_("reference repository")),
+		OPT_BOOL(0, "dissociate", &suc.dissociate,
+			   N_("use --reference only while cloning")),
+		OPT_STRING(0, "depth", &suc.depth, "<depth>",
+			   N_("Create a shallow clone truncated to the "
+			      "specified number of revisions")),
+		OPT_INTEGER('j', "jobs", &suc.max_jobs,
+			    N_("parallel jobs")),
+		OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow,
+			    N_("whether the initial clone should follow the shallow recommendation")),
+		OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
+		OPT_BOOL(0, "progress", &suc.progress,
+			    N_("force cloning progress")),
+		OPT_END()
+	};
+
+	const char *const git_submodule_helper_usage[] = {
+		N_("git submodule--helper update_clone [--prefix=<path>] [<path>...]"),
+		NULL
+	};
+	suc.prefix = prefix;
+
+	update_clone_config_from_gitmodules(&suc.max_jobs);
+	git_config(git_update_clone_config, &suc.max_jobs);
+
+	argc = parse_options(argc, argv, prefix, module_update_clone_options,
+			     git_submodule_helper_usage, 0);
+
+	if (update)
+		if (parse_submodule_update_strategy(update, &suc.update) < 0)
+			die(_("bad value for update parameter"));
+
+	if (module_list_compute(argc, argv, prefix, &pathspec, &suc.list) < 0)
+		return 1;
+
+	if (pathspec.nr)
+		suc.warn_if_uninitialized = 1;
+
+	return update_submodules(&suc);
+}
+
+static int resolve_relative_path(int argc, const char **argv, const char *prefix)
+{
+	struct strbuf sb = STRBUF_INIT;
+	if (argc != 3)
+		die("submodule--helper relative-path takes exactly 2 arguments, got %d", argc);
+
+	printf("%s", relative_path(argv[1], argv[2], &sb));
+	strbuf_release(&sb);
+	return 0;
+}
+
+static const char *remote_submodule_branch(const char *path)
+{
+	const struct submodule *sub;
+	const char *branch = NULL;
+	char *key;
+
+	sub = submodule_from_path(the_repository, &null_oid, path);
+	if (!sub)
+		return NULL;
+
+	key = xstrfmt("submodule.%s.branch", sub->name);
+	if (repo_config_get_string_const(the_repository, key, &branch))
+		branch = sub->branch;
+	free(key);
+
+	if (!branch)
+		return "master";
+
+	if (!strcmp(branch, ".")) {
+		const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+
+		if (!refname)
+			die(_("No such ref: %s"), "HEAD");
+
+		/* detached HEAD */
+		if (!strcmp(refname, "HEAD"))
+			die(_("Submodule (%s) branch configured to inherit "
+			      "branch from superproject, but the superproject "
+			      "is not on any branch"), sub->name);
+
+		if (!skip_prefix(refname, "refs/heads/", &refname))
+			die(_("Expecting a full ref name, got %s"), refname);
+		return refname;
+	}
+
+	return branch;
+}
+
+static int resolve_remote_submodule_branch(int argc, const char **argv,
+		const char *prefix)
+{
+	const char *ret;
+	struct strbuf sb = STRBUF_INIT;
+	if (argc != 2)
+		die("submodule--helper remote-branch takes exactly one arguments, got %d", argc);
+
+	ret = remote_submodule_branch(argv[1]);
+	if (!ret)
+		die("submodule %s doesn't exist", argv[1]);
+
+	printf("%s", ret);
+	strbuf_release(&sb);
+	return 0;
+}
+
+static int push_check(int argc, const char **argv, const char *prefix)
+{
+	struct remote *remote;
+	const char *superproject_head;
+	char *head;
+	int detached_head = 0;
+	struct object_id head_oid;
+
+	if (argc < 3)
+		die("submodule--helper push-check requires at least 2 arguments");
+
+	/*
+	 * superproject's resolved head ref.
+	 * if HEAD then the superproject is in a detached head state, otherwise
+	 * it will be the resolved head ref.
+	 */
+	superproject_head = argv[1];
+	argv++;
+	argc--;
+	/* Get the submodule's head ref and determine if it is detached */
+	head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+	if (!head)
+		die(_("Failed to resolve HEAD as a valid ref."));
+	if (!strcmp(head, "HEAD"))
+		detached_head = 1;
+
+	/*
+	 * The remote must be configured.
+	 * This is to avoid pushing to the exact same URL as the parent.
+	 */
+	remote = pushremote_get(argv[1]);
+	if (!remote || remote->origin == REMOTE_UNCONFIGURED)
+		die("remote '%s' not configured", argv[1]);
+
+	/* Check the refspec */
+	if (argc > 2) {
+		int i;
+		struct ref *local_refs = get_local_heads();
+		struct refspec refspec = REFSPEC_INIT_PUSH;
+
+		refspec_appendn(&refspec, argv + 2, argc - 2);
+
+		for (i = 0; i < refspec.nr; i++) {
+			const struct refspec_item *rs = &refspec.items[i];
+
+			if (rs->pattern || rs->matching)
+				continue;
+
+			/* LHS must match a single ref */
+			switch (count_refspec_match(rs->src, local_refs, NULL)) {
+			case 1:
+				break;
+			case 0:
+				/*
+				 * If LHS matches 'HEAD' then we need to ensure
+				 * that it matches the same named branch
+				 * checked out in the superproject.
+				 */
+				if (!strcmp(rs->src, "HEAD")) {
+					if (!detached_head &&
+					    !strcmp(head, superproject_head))
+						break;
+					die("HEAD does not match the named branch in the superproject");
+				}
+				/* fallthrough */
+			default:
+				die("src refspec '%s' must name a ref",
+				    rs->src);
+			}
+		}
+		refspec_clear(&refspec);
+	}
+	free(head);
+
+	return 0;
+}
+
+static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
+{
+	const struct submodule *sub;
+	const char *path;
+	char *cw;
+	struct repository subrepo;
+
+	if (argc != 2)
+		BUG("submodule--helper ensure-core-worktree <path>");
+
+	path = argv[1];
+
+	sub = submodule_from_path(the_repository, &null_oid, path);
+	if (!sub)
+		BUG("We could get the submodule handle before?");
+
+	if (repo_submodule_init(&subrepo, the_repository, sub))
+		die(_("could not get a repository handle for submodule '%s'"), path);
+
+	if (!repo_config_get_string(&subrepo, "core.worktree", &cw)) {
+		char *cfg_file, *abs_path;
+		const char *rel_path;
+		struct strbuf sb = STRBUF_INIT;
+
+		cfg_file = repo_git_path(&subrepo, "config");
+
+		abs_path = absolute_pathdup(path);
+		rel_path = relative_path(abs_path, subrepo.gitdir, &sb);
+
+		git_config_set_in_file(cfg_file, "core.worktree", rel_path);
+
+		free(cfg_file);
+		free(abs_path);
+		strbuf_release(&sb);
+	}
+
+	return 0;
+}
+
+static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct pathspec pathspec;
+	struct module_list list = MODULE_LIST_INIT;
+	unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
+
+	struct option embed_gitdir_options[] = {
+		OPT_STRING(0, "prefix", &prefix,
+			   N_("path"),
+			   N_("path into the working tree")),
+		OPT_BIT(0, "--recursive", &flags, N_("recurse into submodules"),
+			ABSORB_GITDIR_RECURSE_SUBMODULES),
+		OPT_END()
+	};
+
+	const char *const git_submodule_helper_usage[] = {
+		N_("git submodule--helper absorb-git-dirs [<options>] [<path>...]"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, embed_gitdir_options,
+			     git_submodule_helper_usage, 0);
+
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+		return 1;
+
+	for (i = 0; i < list.nr; i++)
+		absorb_git_dir_into_superproject(list.entries[i]->name, flags);
+
+	return 0;
+}
+
+static int is_active(int argc, const char **argv, const char *prefix)
+{
+	if (argc != 2)
+		die("submodule--helper is-active takes exactly 1 argument");
+
+	return !is_submodule_active(the_repository, argv[1]);
+}
+
+/*
+ * Exit non-zero if any of the submodule names given on the command line is
+ * invalid. If no names are given, filter stdin to print only valid names
+ * (which is primarily intended for testing).
+ */
+static int check_name(int argc, const char **argv, const char *prefix)
+{
+	if (argc > 1) {
+		while (*++argv) {
+			if (check_submodule_name(*argv) < 0)
+				return 1;
+		}
+	} else {
+		struct strbuf buf = STRBUF_INIT;
+		while (strbuf_getline(&buf, stdin) != EOF) {
+			if (!check_submodule_name(buf.buf))
+				printf("%s\n", buf.buf);
+		}
+		strbuf_release(&buf);
+	}
+	return 0;
+}
+
+static int module_config(int argc, const char **argv, const char *prefix)
+{
+	enum {
+		CHECK_WRITEABLE = 1,
+		DO_UNSET = 2
+	} command = 0;
+
+	struct option module_config_options[] = {
+		OPT_CMDMODE(0, "check-writeable", &command,
+			    N_("check if it is safe to write to the .gitmodules file"),
+			    CHECK_WRITEABLE),
+		OPT_CMDMODE(0, "unset", &command,
+			    N_("unset the config in the .gitmodules file"),
+			    DO_UNSET),
+		OPT_END()
+	};
+	const char *const git_submodule_helper_usage[] = {
+		N_("git submodule--helper config <name> [<value>]"),
+		N_("git submodule--helper config --unset <name>"),
+		N_("git submodule--helper config --check-writeable"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, module_config_options,
+			     git_submodule_helper_usage, PARSE_OPT_KEEP_ARGV0);
+
+	if (argc == 1 && command == CHECK_WRITEABLE)
+		return is_writing_gitmodules_ok() ? 0 : -1;
+
+	/* Equivalent to ACTION_GET in builtin/config.c */
+	if (argc == 2 && command != DO_UNSET)
+		return print_config_from_gitmodules(the_repository, argv[1]);
+
+	/* Equivalent to ACTION_SET in builtin/config.c */
+	if (argc == 3 || (argc == 2 && command == DO_UNSET)) {
+		const char *value = (argc == 3) ? argv[2] : NULL;
+
+		if (!is_writing_gitmodules_ok())
+			die(_("please make sure that the .gitmodules file is in the working tree"));
+
+		return config_set_in_gitmodules_file_gently(argv[1], value);
+	}
+
+	usage_with_options(git_submodule_helper_usage, module_config_options);
+}
+
+#define SUPPORT_SUPER_PREFIX (1<<0)
+
+struct cmd_struct {
+	const char *cmd;
+	int (*fn)(int, const char **, const char *);
+	unsigned option;
+};
+
+static struct cmd_struct commands[] = {
+	{"list", module_list, 0},
+	{"name", module_name, 0},
+	{"clone", module_clone, 0},
+	{"update-module-mode", module_update_module_mode, 0},
+	{"update-clone", update_clone, 0},
+	{"ensure-core-worktree", ensure_core_worktree, 0},
+	{"relative-path", resolve_relative_path, 0},
+	{"resolve-relative-url", resolve_relative_url, 0},
+	{"resolve-relative-url-test", resolve_relative_url_test, 0},
+	{"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
+	{"init", module_init, SUPPORT_SUPER_PREFIX},
+	{"status", module_status, SUPPORT_SUPER_PREFIX},
+	{"print-default-remote", print_default_remote, 0},
+	{"sync", module_sync, SUPPORT_SUPER_PREFIX},
+	{"deinit", module_deinit, 0},
+	{"remote-branch", resolve_remote_submodule_branch, 0},
+	{"push-check", push_check, 0},
+	{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
+	{"is-active", is_active, 0},
+	{"check-name", check_name, 0},
+	{"config", module_config, 0},
+};
+
+int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	if (argc < 2 || !strcmp(argv[1], "-h"))
+		usage("git submodule--helper <command>");
+
+	for (i = 0; i < ARRAY_SIZE(commands); i++) {
+		if (!strcmp(argv[1], commands[i].cmd)) {
+			if (get_super_prefix() &&
+			    !(commands[i].option & SUPPORT_SUPER_PREFIX))
+				die(_("%s doesn't support --super-prefix"),
+				    commands[i].cmd);
+			return commands[i].fn(argc - 1, argv + 1, prefix);
+		}
+	}
+
+	die(_("'%s' is not a valid submodule--helper "
+	      "subcommand"), argv[1]);
+}
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
new file mode 100644
index 0000000000..80237f0df1
--- /dev/null
+++ b/builtin/symbolic-ref.c
@@ -0,0 +1,78 @@
+#include "builtin.h"
+#include "config.h"
+#include "cache.h"
+#include "refs.h"
+#include "parse-options.h"
+
+static const char * const git_symbolic_ref_usage[] = {
+	N_("git symbolic-ref [<options>] <name> [<ref>]"),
+	N_("git symbolic-ref -d [-q] <name>"),
+	NULL
+};
+
+static int check_symref(const char *HEAD, int quiet, int shorten, int print)
+{
+	int flag;
+	const char *refname = resolve_ref_unsafe(HEAD, 0, NULL, &flag);
+
+	if (!refname)
+		die("No such ref: %s", HEAD);
+	else if (!(flag & REF_ISSYMREF)) {
+		if (!quiet)
+			die("ref %s is not a symbolic ref", HEAD);
+		else
+			return 1;
+	}
+	if (print) {
+		if (shorten)
+			refname = shorten_unambiguous_ref(refname, 0);
+		puts(refname);
+	}
+	return 0;
+}
+
+int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
+{
+	int quiet = 0, delete = 0, shorten = 0, ret = 0;
+	const char *msg = NULL;
+	struct option options[] = {
+		OPT__QUIET(&quiet,
+			N_("suppress error message for non-symbolic (detached) refs")),
+		OPT_BOOL('d', "delete", &delete, N_("delete symbolic ref")),
+		OPT_BOOL(0, "short", &shorten, N_("shorten ref output")),
+		OPT_STRING('m', NULL, &msg, N_("reason"), N_("reason of the update")),
+		OPT_END(),
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, options,
+			     git_symbolic_ref_usage, 0);
+	if (msg && !*msg)
+		die("Refusing to perform update with empty message");
+
+	if (delete) {
+		if (argc != 1)
+			usage_with_options(git_symbolic_ref_usage, options);
+		ret = check_symref(argv[0], 1, 0, 0);
+		if (ret)
+			die("Cannot delete %s, not a symbolic ref", argv[0]);
+		if (!strcmp(argv[0], "HEAD"))
+			die("deleting '%s' is not allowed", argv[0]);
+		return delete_ref(NULL, argv[0], NULL, REF_NO_DEREF);
+	}
+
+	switch (argc) {
+	case 1:
+		ret = check_symref(argv[0], quiet, shorten, 1);
+		break;
+	case 2:
+		if (!strcmp(argv[0], "HEAD") &&
+		    !starts_with(argv[1], "refs/"))
+			die("Refusing to point HEAD outside of refs/");
+		ret = !!create_symref(argv[0], argv[1], msg);
+		break;
+	default:
+		usage_with_options(git_symbolic_ref_usage, options);
+	}
+	return ret;
+}
diff --git a/builtin/tag.c b/builtin/tag.c
new file mode 100644
index 0000000000..e0a4c25382
--- /dev/null
+++ b/builtin/tag.c
@@ -0,0 +1,593 @@
+/*
+ * Builtin "git tag"
+ *
+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>,
+ *                    Carlos Rica <jasampler@gmail.com>
+ * Based on git-tag.sh and mktag.c by Linus Torvalds.
+ */
+
+#include "cache.h"
+#include "config.h"
+#include "builtin.h"
+#include "refs.h"
+#include "object-store.h"
+#include "tag.h"
+#include "run-command.h"
+#include "parse-options.h"
+#include "diff.h"
+#include "revision.h"
+#include "gpg-interface.h"
+#include "sha1-array.h"
+#include "column.h"
+#include "ref-filter.h"
+
+static const char * const git_tag_usage[] = {
+	N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
+		"\t\t<tagname> [<head>]"),
+	N_("git tag -d <tagname>..."),
+	N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]\n"
+		"\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
+	N_("git tag -v [--format=<format>] <tagname>..."),
+	NULL
+};
+
+static unsigned int colopts;
+static int force_sign_annotate;
+static int config_sign_tag = -1; /* unspecified */
+
+static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
+		     struct ref_format *format)
+{
+	struct ref_array array;
+	char *to_free = NULL;
+	int i;
+
+	memset(&array, 0, sizeof(array));
+
+	if (filter->lines == -1)
+		filter->lines = 0;
+
+	if (!format->format) {
+		if (filter->lines) {
+			to_free = xstrfmt("%s %%(contents:lines=%d)",
+					  "%(align:15)%(refname:lstrip=2)%(end)",
+					  filter->lines);
+			format->format = to_free;
+		} else
+			format->format = "%(refname:lstrip=2)";
+	}
+
+	if (verify_ref_format(format))
+		die(_("unable to parse format string"));
+	filter->with_commit_tag_algo = 1;
+	filter_refs(&array, filter, FILTER_REFS_TAGS);
+	ref_array_sort(sorting, &array);
+
+	for (i = 0; i < array.nr; i++)
+		show_ref_array_item(array.items[i], format);
+	ref_array_clear(&array);
+	free(to_free);
+
+	return 0;
+}
+
+typedef int (*each_tag_name_fn)(const char *name, const char *ref,
+				const struct object_id *oid, const void *cb_data);
+
+static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
+			     const void *cb_data)
+{
+	const char **p;
+	struct strbuf ref = STRBUF_INIT;
+	int had_error = 0;
+	struct object_id oid;
+
+	for (p = argv; *p; p++) {
+		strbuf_reset(&ref);
+		strbuf_addf(&ref, "refs/tags/%s", *p);
+		if (read_ref(ref.buf, &oid)) {
+			error(_("tag '%s' not found."), *p);
+			had_error = 1;
+			continue;
+		}
+		if (fn(*p, ref.buf, &oid, cb_data))
+			had_error = 1;
+	}
+	strbuf_release(&ref);
+	return had_error;
+}
+
+static int delete_tag(const char *name, const char *ref,
+		      const struct object_id *oid, const void *cb_data)
+{
+	if (delete_ref(NULL, ref, oid, 0))
+		return 1;
+	printf(_("Deleted tag '%s' (was %s)\n"), name,
+	       find_unique_abbrev(oid, DEFAULT_ABBREV));
+	return 0;
+}
+
+static int verify_tag(const char *name, const char *ref,
+		      const struct object_id *oid, const void *cb_data)
+{
+	int flags;
+	const struct ref_format *format = cb_data;
+	flags = GPG_VERIFY_VERBOSE;
+
+	if (format->format)
+		flags = GPG_VERIFY_OMIT_STATUS;
+
+	if (gpg_verify_tag(oid, name, flags))
+		return -1;
+
+	if (format->format)
+		pretty_print_ref(name, oid, format);
+
+	return 0;
+}
+
+static int do_sign(struct strbuf *buffer)
+{
+	return sign_buffer(buffer, buffer, get_signing_key());
+}
+
+static const char tag_template[] =
+	N_("\nWrite a message for tag:\n  %s\n"
+	"Lines starting with '%c' will be ignored.\n");
+
+static const char tag_template_nocleanup[] =
+	N_("\nWrite a message for tag:\n  %s\n"
+	"Lines starting with '%c' will be kept; you may remove them"
+	" yourself if you want to.\n");
+
+static int git_tag_config(const char *var, const char *value, void *cb)
+{
+	int status;
+	struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
+
+	if (!strcmp(var, "tag.gpgsign")) {
+		config_sign_tag = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (!strcmp(var, "tag.sort")) {
+		if (!value)
+			return config_error_nonbool(var);
+		parse_ref_sorting(sorting_tail, value);
+		return 0;
+	}
+
+	status = git_gpg_config(var, value, cb);
+	if (status)
+		return status;
+	if (!strcmp(var, "tag.forcesignannotated")) {
+		force_sign_annotate = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (starts_with(var, "column."))
+		return git_column_config(var, value, "tag", &colopts);
+	return git_color_default_config(var, value, cb);
+}
+
+static void write_tag_body(int fd, const struct object_id *oid)
+{
+	unsigned long size;
+	enum object_type type;
+	char *buf, *sp;
+
+	buf = read_object_file(oid, &type, &size);
+	if (!buf)
+		return;
+	/* skip header */
+	sp = strstr(buf, "\n\n");
+
+	if (!sp || !size || type != OBJ_TAG) {
+		free(buf);
+		return;
+	}
+	sp += 2; /* skip the 2 LFs */
+	write_or_die(fd, sp, parse_signature(sp, buf + size - sp));
+
+	free(buf);
+}
+
+static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result)
+{
+	if (sign && do_sign(buf) < 0)
+		return error(_("unable to sign the tag"));
+	if (write_object_file(buf->buf, buf->len, tag_type, result) < 0)
+		return error(_("unable to write tag file"));
+	return 0;
+}
+
+struct create_tag_options {
+	unsigned int message_given:1;
+	unsigned int use_editor:1;
+	unsigned int sign;
+	enum {
+		CLEANUP_NONE,
+		CLEANUP_SPACE,
+		CLEANUP_ALL
+	} cleanup_mode;
+};
+
+static const char message_advice_nested_tag[] =
+	N_("You have created a nested tag. The object referred to by your new tag is\n"
+	   "already a tag. If you meant to tag the object that it points to, use:\n"
+	   "\n"
+	   "\tgit tag -f %s %s^{}");
+
+static void create_tag(const struct object_id *object, const char *object_ref,
+		       const char *tag,
+		       struct strbuf *buf, struct create_tag_options *opt,
+		       struct object_id *prev, struct object_id *result)
+{
+	enum object_type type;
+	struct strbuf header = STRBUF_INIT;
+	char *path = NULL;
+
+	type = oid_object_info(the_repository, object, NULL);
+	if (type <= OBJ_NONE)
+		die(_("bad object type."));
+
+	if (type == OBJ_TAG && advice_nested_tag)
+		advise(_(message_advice_nested_tag), tag, object_ref);
+
+	strbuf_addf(&header,
+		    "object %s\n"
+		    "type %s\n"
+		    "tag %s\n"
+		    "tagger %s\n\n",
+		    oid_to_hex(object),
+		    type_name(type),
+		    tag,
+		    git_committer_info(IDENT_STRICT));
+
+	if (!opt->message_given || opt->use_editor) {
+		int fd;
+
+		/* write the template message before editing: */
+		path = git_pathdup("TAG_EDITMSG");
+		fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+		if (fd < 0)
+			die_errno(_("could not create file '%s'"), path);
+
+		if (opt->message_given) {
+			write_or_die(fd, buf->buf, buf->len);
+			strbuf_reset(buf);
+		} else if (!is_null_oid(prev)) {
+			write_tag_body(fd, prev);
+		} else {
+			struct strbuf buf = STRBUF_INIT;
+			strbuf_addch(&buf, '\n');
+			if (opt->cleanup_mode == CLEANUP_ALL)
+				strbuf_commented_addf(&buf, _(tag_template), tag, comment_line_char);
+			else
+				strbuf_commented_addf(&buf, _(tag_template_nocleanup), tag, comment_line_char);
+			write_or_die(fd, buf.buf, buf.len);
+			strbuf_release(&buf);
+		}
+		close(fd);
+
+		if (launch_editor(path, buf, NULL)) {
+			fprintf(stderr,
+			_("Please supply the message using either -m or -F option.\n"));
+			exit(1);
+		}
+	}
+
+	if (opt->cleanup_mode != CLEANUP_NONE)
+		strbuf_stripspace(buf, opt->cleanup_mode == CLEANUP_ALL);
+
+	if (!opt->message_given && !buf->len)
+		die(_("no tag message?"));
+
+	strbuf_insert(buf, 0, header.buf, header.len);
+	strbuf_release(&header);
+
+	if (build_tag_object(buf, opt->sign, result) < 0) {
+		if (path)
+			fprintf(stderr, _("The tag message has been left in %s\n"),
+				path);
+		exit(128);
+	}
+	if (path) {
+		unlink_or_warn(path);
+		free(path);
+	}
+}
+
+static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb)
+{
+	enum object_type type;
+	struct commit *c;
+	char *buf;
+	unsigned long size;
+	int subject_len = 0;
+	const char *subject_start;
+
+	char *rla = getenv("GIT_REFLOG_ACTION");
+	if (rla) {
+		strbuf_addstr(sb, rla);
+	} else {
+		strbuf_addstr(sb, "tag: tagging ");
+		strbuf_add_unique_abbrev(sb, oid, DEFAULT_ABBREV);
+	}
+
+	strbuf_addstr(sb, " (");
+	type = oid_object_info(the_repository, oid, NULL);
+	switch (type) {
+	default:
+		strbuf_addstr(sb, "object of unknown type");
+		break;
+	case OBJ_COMMIT:
+		if ((buf = read_object_file(oid, &type, &size)) != NULL) {
+			subject_len = find_commit_subject(buf, &subject_start);
+			strbuf_insert(sb, sb->len, subject_start, subject_len);
+		} else {
+			strbuf_addstr(sb, "commit object");
+		}
+		free(buf);
+
+		if ((c = lookup_commit_reference(the_repository, oid)) != NULL)
+			strbuf_addf(sb, ", %s", show_date(c->date, 0, DATE_MODE(SHORT)));
+		break;
+	case OBJ_TREE:
+		strbuf_addstr(sb, "tree object");
+		break;
+	case OBJ_BLOB:
+		strbuf_addstr(sb, "blob object");
+		break;
+	case OBJ_TAG:
+		strbuf_addstr(sb, "other tag object");
+		break;
+	}
+	strbuf_addch(sb, ')');
+}
+
+struct msg_arg {
+	int given;
+	struct strbuf buf;
+};
+
+static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
+{
+	struct msg_arg *msg = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!arg)
+		return -1;
+	if (msg->buf.len)
+		strbuf_addstr(&(msg->buf), "\n\n");
+	strbuf_addstr(&(msg->buf), arg);
+	msg->given = 1;
+	return 0;
+}
+
+static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)
+{
+	if (name[0] == '-')
+		return -1;
+
+	strbuf_reset(sb);
+	strbuf_addf(sb, "refs/tags/%s", name);
+
+	return check_refname_format(sb->buf, 0);
+}
+
+int cmd_tag(int argc, const char **argv, const char *prefix)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf ref = STRBUF_INIT;
+	struct strbuf reflog_msg = STRBUF_INIT;
+	struct object_id object, prev;
+	const char *object_ref, *tag;
+	struct create_tag_options opt;
+	char *cleanup_arg = NULL;
+	int create_reflog = 0;
+	int annotate = 0, force = 0;
+	int cmdmode = 0, create_tag_object = 0;
+	const char *msgfile = NULL, *keyid = NULL;
+	struct msg_arg msg = { 0, STRBUF_INIT };
+	struct ref_transaction *transaction;
+	struct strbuf err = STRBUF_INIT;
+	struct ref_filter filter;
+	static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+	struct ref_format format = REF_FORMAT_INIT;
+	int icase = 0;
+	int edit_flag = 0;
+	struct option options[] = {
+		OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
+		{ OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
+				N_("print <n> lines of each tag message"),
+				PARSE_OPT_OPTARG, NULL, 1 },
+		OPT_CMDMODE('d', "delete", &cmdmode, N_("delete tags"), 'd'),
+		OPT_CMDMODE('v', "verify", &cmdmode, N_("verify tags"), 'v'),
+
+		OPT_GROUP(N_("Tag creation options")),
+		OPT_BOOL('a', "annotate", &annotate,
+					N_("annotated tag, needs a message")),
+		{ OPTION_CALLBACK, 'm', "message", &msg, N_("message"),
+		  N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg },
+		OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
+		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
+		OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
+		OPT_CLEANUP(&cleanup_arg),
+		OPT_STRING('u', "local-user", &keyid, N_("key-id"),
+					N_("use another key to sign the tag")),
+		OPT__FORCE(&force, N_("replace the tag if exists"), 0),
+		OPT_BOOL(0, "create-reflog", &create_reflog, N_("create a reflog")),
+
+		OPT_GROUP(N_("Tag listing options")),
+		OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
+		OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")),
+		OPT_NO_CONTAINS(&filter.no_commit, N_("print only tags that don't contain the commit")),
+		OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")),
+		OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
+		OPT_MERGED(&filter, N_("print only tags that are merged")),
+		OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
+		OPT_REF_SORT(sorting_tail),
+		{
+			OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
+			N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
+			parse_opt_object_name, (intptr_t) "HEAD"
+		},
+		OPT_STRING(  0 , "format", &format.format, N_("format"),
+			   N_("format to use for the output")),
+		OPT__COLOR(&format.use_color, N_("respect format colors")),
+		OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
+		OPT_END()
+	};
+
+	setup_ref_filter_porcelain_msg();
+
+	git_config(git_tag_config, sorting_tail);
+
+	memset(&opt, 0, sizeof(opt));
+	memset(&filter, 0, sizeof(filter));
+	filter.lines = -1;
+	opt.sign = -1;
+
+	argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
+
+	if (!cmdmode) {
+		if (argc == 0)
+			cmdmode = 'l';
+		else if (filter.with_commit || filter.no_commit ||
+			 filter.points_at.nr || filter.merge_commit ||
+			 filter.lines != -1)
+			cmdmode = 'l';
+	}
+
+	if (cmdmode == 'l')
+		setup_auto_pager("tag", 1);
+
+	if (opt.sign == -1)
+		opt.sign = cmdmode ? 0 : config_sign_tag > 0;
+
+	if (keyid) {
+		opt.sign = 1;
+		set_signing_key(keyid);
+	}
+	create_tag_object = (opt.sign || annotate || msg.given || msgfile);
+
+	if ((create_tag_object || force) && (cmdmode != 0))
+		usage_with_options(git_tag_usage, options);
+
+	finalize_colopts(&colopts, -1);
+	if (cmdmode == 'l' && filter.lines != -1) {
+		if (explicitly_enable_column(colopts))
+			die(_("--column and -n are incompatible"));
+		colopts = 0;
+	}
+	if (!sorting)
+		sorting = ref_default_sorting();
+	sorting->ignore_case = icase;
+	filter.ignore_case = icase;
+	if (cmdmode == 'l') {
+		int ret;
+		if (column_active(colopts)) {
+			struct column_options copts;
+			memset(&copts, 0, sizeof(copts));
+			copts.padding = 2;
+			run_column_filter(colopts, &copts);
+		}
+		filter.name_patterns = argv;
+		ret = list_tags(&filter, sorting, &format);
+		if (column_active(colopts))
+			stop_column_filter();
+		return ret;
+	}
+	if (filter.lines != -1)
+		die(_("-n option is only allowed in list mode"));
+	if (filter.with_commit)
+		die(_("--contains option is only allowed in list mode"));
+	if (filter.no_commit)
+		die(_("--no-contains option is only allowed in list mode"));
+	if (filter.points_at.nr)
+		die(_("--points-at option is only allowed in list mode"));
+	if (filter.merge_commit)
+		die(_("--merged and --no-merged options are only allowed in list mode"));
+	if (cmdmode == 'd')
+		return for_each_tag_name(argv, delete_tag, NULL);
+	if (cmdmode == 'v') {
+		if (format.format && verify_ref_format(&format))
+			usage_with_options(git_tag_usage, options);
+		return for_each_tag_name(argv, verify_tag, &format);
+	}
+
+	if (msg.given || msgfile) {
+		if (msg.given && msgfile)
+			die(_("only one -F or -m option is allowed."));
+		if (msg.given)
+			strbuf_addbuf(&buf, &(msg.buf));
+		else {
+			if (!strcmp(msgfile, "-")) {
+				if (strbuf_read(&buf, 0, 1024) < 0)
+					die_errno(_("cannot read '%s'"), msgfile);
+			} else {
+				if (strbuf_read_file(&buf, msgfile, 1024) < 0)
+					die_errno(_("could not open or read '%s'"),
+						msgfile);
+			}
+		}
+	}
+
+	tag = argv[0];
+
+	object_ref = argc == 2 ? argv[1] : "HEAD";
+	if (argc > 2)
+		die(_("too many params"));
+
+	if (get_oid(object_ref, &object))
+		die(_("Failed to resolve '%s' as a valid ref."), object_ref);
+
+	if (strbuf_check_tag_ref(&ref, tag))
+		die(_("'%s' is not a valid tag name."), tag);
+
+	if (read_ref(ref.buf, &prev))
+		oidclr(&prev);
+	else if (!force)
+		die(_("tag '%s' already exists"), tag);
+
+	opt.message_given = msg.given || msgfile;
+	opt.use_editor = edit_flag;
+
+	if (!cleanup_arg || !strcmp(cleanup_arg, "strip"))
+		opt.cleanup_mode = CLEANUP_ALL;
+	else if (!strcmp(cleanup_arg, "verbatim"))
+		opt.cleanup_mode = CLEANUP_NONE;
+	else if (!strcmp(cleanup_arg, "whitespace"))
+		opt.cleanup_mode = CLEANUP_SPACE;
+	else
+		die(_("Invalid cleanup mode %s"), cleanup_arg);
+
+	create_reflog_msg(&object, &reflog_msg);
+
+	if (create_tag_object) {
+		if (force_sign_annotate && !annotate)
+			opt.sign = 1;
+		create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object);
+	}
+
+	transaction = ref_transaction_begin(&err);
+	if (!transaction ||
+	    ref_transaction_update(transaction, ref.buf, &object, &prev,
+				   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
+				   reflog_msg.buf, &err) ||
+	    ref_transaction_commit(transaction, &err))
+		die("%s", err.buf);
+	ref_transaction_free(transaction);
+	if (force && !is_null_oid(&prev) && !oideq(&prev, &object))
+		printf(_("Updated tag '%s' (was %s)\n"), tag,
+		       find_unique_abbrev(&prev, DEFAULT_ABBREV));
+
+	UNLEAK(buf);
+	UNLEAK(ref);
+	UNLEAK(reflog_msg);
+	UNLEAK(msg);
+	UNLEAK(err);
+	return 0;
+}
diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c
new file mode 100644
index 0000000000..58652229f2
--- /dev/null
+++ b/builtin/unpack-file.c
@@ -0,0 +1,38 @@
+#include "builtin.h"
+#include "config.h"
+#include "object-store.h"
+
+static char *create_temp_file(struct object_id *oid)
+{
+	static char path[50];
+	void *buf;
+	enum object_type type;
+	unsigned long size;
+	int fd;
+
+	buf = read_object_file(oid, &type, &size);
+	if (!buf || type != OBJ_BLOB)
+		die("unable to read blob object %s", oid_to_hex(oid));
+
+	xsnprintf(path, sizeof(path), ".merge_file_XXXXXX");
+	fd = xmkstemp(path);
+	if (write_in_full(fd, buf, size) < 0)
+		die_errno("unable to write temp-file");
+	close(fd);
+	return path;
+}
+
+int cmd_unpack_file(int argc, const char **argv, const char *prefix)
+{
+	struct object_id oid;
+
+	if (argc != 2 || !strcmp(argv[1], "-h"))
+		usage("git unpack-file <sha1>");
+	if (get_oid(argv[1], &oid))
+		die("Not a valid object name %s", argv[1]);
+
+	git_config(git_default_config, NULL);
+
+	puts(create_temp_file(&oid));
+	return 0;
+}
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
new file mode 100644
index 0000000000..a87a4bfd2c
--- /dev/null
+++ b/builtin/unpack-objects.c
@@ -0,0 +1,597 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "object-store.h"
+#include "object.h"
+#include "delta.h"
+#include "pack.h"
+#include "blob.h"
+#include "commit.h"
+#include "tag.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "progress.h"
+#include "decorate.h"
+#include "fsck.h"
+
+static int dry_run, quiet, recover, has_errors, strict;
+static const char unpack_usage[] = "git unpack-objects [-n] [-q] [-r] [--strict]";
+
+/* We always read in 4kB chunks. */
+static unsigned char buffer[4096];
+static unsigned int offset, len;
+static off_t consumed_bytes;
+static off_t max_input_size;
+static git_hash_ctx ctx;
+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
+
+/*
+ * When running under --strict mode, objects whose reachability are
+ * suspect are kept in core without getting written in the object
+ * store.
+ */
+struct obj_buffer {
+	char *buffer;
+	unsigned long size;
+};
+
+static struct decoration obj_decorate;
+
+static struct obj_buffer *lookup_object_buffer(struct object *base)
+{
+	return lookup_decoration(&obj_decorate, base);
+}
+
+static void add_object_buffer(struct object *object, char *buffer, unsigned long size)
+{
+	struct obj_buffer *obj;
+	obj = xcalloc(1, sizeof(struct obj_buffer));
+	obj->buffer = buffer;
+	obj->size = size;
+	if (add_decoration(&obj_decorate, object, obj))
+		die("object %s tried to add buffer twice!", oid_to_hex(&object->oid));
+}
+
+/*
+ * Make sure at least "min" bytes are available in the buffer, and
+ * return the pointer to the buffer.
+ */
+static void *fill(int min)
+{
+	if (min <= len)
+		return buffer + offset;
+	if (min > sizeof(buffer))
+		die("cannot fill %d bytes", min);
+	if (offset) {
+		the_hash_algo->update_fn(&ctx, buffer, offset);
+		memmove(buffer, buffer + offset, len);
+		offset = 0;
+	}
+	do {
+		ssize_t ret = xread(0, buffer + len, sizeof(buffer) - len);
+		if (ret <= 0) {
+			if (!ret)
+				die("early EOF");
+			die_errno("read error on input");
+		}
+		len += ret;
+	} while (len < min);
+	return buffer;
+}
+
+static void use(int bytes)
+{
+	if (bytes > len)
+		die("used more bytes than were available");
+	len -= bytes;
+	offset += bytes;
+
+	/* make sure off_t is sufficiently large not to wrap */
+	if (signed_add_overflows(consumed_bytes, bytes))
+		die("pack too large for current definition of off_t");
+	consumed_bytes += bytes;
+	if (max_input_size && consumed_bytes > max_input_size)
+		die(_("pack exceeds maximum allowed size"));
+}
+
+static void *get_data(unsigned long size)
+{
+	git_zstream stream;
+	void *buf = xmallocz(size);
+
+	memset(&stream, 0, sizeof(stream));
+
+	stream.next_out = buf;
+	stream.avail_out = size;
+	stream.next_in = fill(1);
+	stream.avail_in = len;
+	git_inflate_init(&stream);
+
+	for (;;) {
+		int ret = git_inflate(&stream, 0);
+		use(len - stream.avail_in);
+		if (stream.total_out == size && ret == Z_STREAM_END)
+			break;
+		if (ret != Z_OK) {
+			error("inflate returned %d", ret);
+			FREE_AND_NULL(buf);
+			if (!recover)
+				exit(1);
+			has_errors = 1;
+			break;
+		}
+		stream.next_in = fill(1);
+		stream.avail_in = len;
+	}
+	git_inflate_end(&stream);
+	return buf;
+}
+
+struct delta_info {
+	struct object_id base_oid;
+	unsigned nr;
+	off_t base_offset;
+	unsigned long size;
+	void *delta;
+	struct delta_info *next;
+};
+
+static struct delta_info *delta_list;
+
+static void add_delta_to_list(unsigned nr, const struct object_id *base_oid,
+			      off_t base_offset,
+			      void *delta, unsigned long size)
+{
+	struct delta_info *info = xmalloc(sizeof(*info));
+
+	oidcpy(&info->base_oid, base_oid);
+	info->base_offset = base_offset;
+	info->size = size;
+	info->delta = delta;
+	info->nr = nr;
+	info->next = delta_list;
+	delta_list = info;
+}
+
+struct obj_info {
+	off_t offset;
+	struct object_id oid;
+	struct object *obj;
+};
+
+/* Remember to update object flag allocation in object.h */
+#define FLAG_OPEN (1u<<20)
+#define FLAG_WRITTEN (1u<<21)
+
+static struct obj_info *obj_list;
+static unsigned nr_objects;
+
+/*
+ * Called only from check_object() after it verified this object
+ * is Ok.
+ */
+static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)
+{
+	struct object_id oid;
+
+	if (write_object_file(obj_buf->buffer, obj_buf->size,
+			      type_name(obj->type), &oid) < 0)
+		die("failed to write object %s", oid_to_hex(&obj->oid));
+	obj->flags |= FLAG_WRITTEN;
+}
+
+/*
+ * At the very end of the processing, write_rest() scans the objects
+ * that have reachability requirements and calls this function.
+ * Verify its reachability and validity recursively and write it out.
+ */
+static int check_object(struct object *obj, int type, void *data, struct fsck_options *options)
+{
+	struct obj_buffer *obj_buf;
+
+	if (!obj)
+		return 1;
+
+	if (obj->flags & FLAG_WRITTEN)
+		return 0;
+
+	if (type != OBJ_ANY && obj->type != type)
+		die("object type mismatch");
+
+	if (!(obj->flags & FLAG_OPEN)) {
+		unsigned long size;
+		int type = oid_object_info(the_repository, &obj->oid, &size);
+		if (type != obj->type || type <= 0)
+			die("object of unexpected type");
+		obj->flags |= FLAG_WRITTEN;
+		return 0;
+	}
+
+	obj_buf = lookup_object_buffer(obj);
+	if (!obj_buf)
+		die("Whoops! Cannot find object '%s'", oid_to_hex(&obj->oid));
+	if (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options))
+		die("fsck error in packed object");
+	fsck_options.walk = check_object;
+	if (fsck_walk(obj, NULL, &fsck_options))
+		die("Error on reachable objects of %s", oid_to_hex(&obj->oid));
+	write_cached_object(obj, obj_buf);
+	return 0;
+}
+
+static void write_rest(void)
+{
+	unsigned i;
+	for (i = 0; i < nr_objects; i++) {
+		if (obj_list[i].obj)
+			check_object(obj_list[i].obj, OBJ_ANY, NULL, NULL);
+	}
+}
+
+static void added_object(unsigned nr, enum object_type type,
+			 void *data, unsigned long size);
+
+/*
+ * Write out nr-th object from the list, now we know the contents
+ * of it.  Under --strict, this buffers structured objects in-core,
+ * to be checked at the end.
+ */
+static void write_object(unsigned nr, enum object_type type,
+			 void *buf, unsigned long size)
+{
+	if (!strict) {
+		if (write_object_file(buf, size, type_name(type),
+				      &obj_list[nr].oid) < 0)
+			die("failed to write object");
+		added_object(nr, type, buf, size);
+		free(buf);
+		obj_list[nr].obj = NULL;
+	} else if (type == OBJ_BLOB) {
+		struct blob *blob;
+		if (write_object_file(buf, size, type_name(type),
+				      &obj_list[nr].oid) < 0)
+			die("failed to write object");
+		added_object(nr, type, buf, size);
+		free(buf);
+
+		blob = lookup_blob(the_repository, &obj_list[nr].oid);
+		if (blob)
+			blob->object.flags |= FLAG_WRITTEN;
+		else
+			die("invalid blob object");
+		obj_list[nr].obj = NULL;
+	} else {
+		struct object *obj;
+		int eaten;
+		hash_object_file(buf, size, type_name(type), &obj_list[nr].oid);
+		added_object(nr, type, buf, size);
+		obj = parse_object_buffer(the_repository, &obj_list[nr].oid,
+					  type, size, buf,
+					  &eaten);
+		if (!obj)
+			die("invalid %s", type_name(type));
+		add_object_buffer(obj, buf, size);
+		obj->flags |= FLAG_OPEN;
+		obj_list[nr].obj = obj;
+	}
+}
+
+static void resolve_delta(unsigned nr, enum object_type type,
+			  void *base, unsigned long base_size,
+			  void *delta, unsigned long delta_size)
+{
+	void *result;
+	unsigned long result_size;
+
+	result = patch_delta(base, base_size,
+			     delta, delta_size,
+			     &result_size);
+	if (!result)
+		die("failed to apply delta");
+	free(delta);
+	write_object(nr, type, result, result_size);
+}
+
+/*
+ * We now know the contents of an object (which is nr-th in the pack);
+ * resolve all the deltified objects that are based on it.
+ */
+static void added_object(unsigned nr, enum object_type type,
+			 void *data, unsigned long size)
+{
+	struct delta_info **p = &delta_list;
+	struct delta_info *info;
+
+	while ((info = *p) != NULL) {
+		if (oideq(&info->base_oid, &obj_list[nr].oid) ||
+		    info->base_offset == obj_list[nr].offset) {
+			*p = info->next;
+			p = &delta_list;
+			resolve_delta(info->nr, type, data, size,
+				      info->delta, info->size);
+			free(info);
+			continue;
+		}
+		p = &info->next;
+	}
+}
+
+static void unpack_non_delta_entry(enum object_type type, unsigned long size,
+				   unsigned nr)
+{
+	void *buf = get_data(size);
+
+	if (!dry_run && buf)
+		write_object(nr, type, buf, size);
+	else
+		free(buf);
+}
+
+static int resolve_against_held(unsigned nr, const struct object_id *base,
+				void *delta_data, unsigned long delta_size)
+{
+	struct object *obj;
+	struct obj_buffer *obj_buffer;
+	obj = lookup_object(the_repository, base);
+	if (!obj)
+		return 0;
+	obj_buffer = lookup_object_buffer(obj);
+	if (!obj_buffer)
+		return 0;
+	resolve_delta(nr, obj->type, obj_buffer->buffer,
+		      obj_buffer->size, delta_data, delta_size);
+	return 1;
+}
+
+static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
+			       unsigned nr)
+{
+	void *delta_data, *base;
+	unsigned long base_size;
+	struct object_id base_oid;
+
+	if (type == OBJ_REF_DELTA) {
+		hashcpy(base_oid.hash, fill(the_hash_algo->rawsz));
+		use(the_hash_algo->rawsz);
+		delta_data = get_data(delta_size);
+		if (dry_run || !delta_data) {
+			free(delta_data);
+			return;
+		}
+		if (has_object_file(&base_oid))
+			; /* Ok we have this one */
+		else if (resolve_against_held(nr, &base_oid,
+					      delta_data, delta_size))
+			return; /* we are done */
+		else {
+			/* cannot resolve yet --- queue it */
+			oidclr(&obj_list[nr].oid);
+			add_delta_to_list(nr, &base_oid, 0, delta_data, delta_size);
+			return;
+		}
+	} else {
+		unsigned base_found = 0;
+		unsigned char *pack, c;
+		off_t base_offset;
+		unsigned lo, mid, hi;
+
+		pack = fill(1);
+		c = *pack;
+		use(1);
+		base_offset = c & 127;
+		while (c & 128) {
+			base_offset += 1;
+			if (!base_offset || MSB(base_offset, 7))
+				die("offset value overflow for delta base object");
+			pack = fill(1);
+			c = *pack;
+			use(1);
+			base_offset = (base_offset << 7) + (c & 127);
+		}
+		base_offset = obj_list[nr].offset - base_offset;
+		if (base_offset <= 0 || base_offset >= obj_list[nr].offset)
+			die("offset value out of bound for delta base object");
+
+		delta_data = get_data(delta_size);
+		if (dry_run || !delta_data) {
+			free(delta_data);
+			return;
+		}
+		lo = 0;
+		hi = nr;
+		while (lo < hi) {
+			mid = lo + (hi - lo) / 2;
+			if (base_offset < obj_list[mid].offset) {
+				hi = mid;
+			} else if (base_offset > obj_list[mid].offset) {
+				lo = mid + 1;
+			} else {
+				oidcpy(&base_oid, &obj_list[mid].oid);
+				base_found = !is_null_oid(&base_oid);
+				break;
+			}
+		}
+		if (!base_found) {
+			/*
+			 * The delta base object is itself a delta that
+			 * has not been resolved yet.
+			 */
+			oidclr(&obj_list[nr].oid);
+			add_delta_to_list(nr, &null_oid, base_offset, delta_data, delta_size);
+			return;
+		}
+	}
+
+	if (resolve_against_held(nr, &base_oid, delta_data, delta_size))
+		return;
+
+	base = read_object_file(&base_oid, &type, &base_size);
+	if (!base) {
+		error("failed to read delta-pack base object %s",
+		      oid_to_hex(&base_oid));
+		if (!recover)
+			exit(1);
+		has_errors = 1;
+		return;
+	}
+	resolve_delta(nr, type, base, base_size, delta_data, delta_size);
+	free(base);
+}
+
+static void unpack_one(unsigned nr)
+{
+	unsigned shift;
+	unsigned char *pack;
+	unsigned long size, c;
+	enum object_type type;
+
+	obj_list[nr].offset = consumed_bytes;
+
+	pack = fill(1);
+	c = *pack;
+	use(1);
+	type = (c >> 4) & 7;
+	size = (c & 15);
+	shift = 4;
+	while (c & 0x80) {
+		pack = fill(1);
+		c = *pack;
+		use(1);
+		size += (c & 0x7f) << shift;
+		shift += 7;
+	}
+
+	switch (type) {
+	case OBJ_COMMIT:
+	case OBJ_TREE:
+	case OBJ_BLOB:
+	case OBJ_TAG:
+		unpack_non_delta_entry(type, size, nr);
+		return;
+	case OBJ_REF_DELTA:
+	case OBJ_OFS_DELTA:
+		unpack_delta_entry(type, size, nr);
+		return;
+	default:
+		error("bad object type %d", type);
+		has_errors = 1;
+		if (recover)
+			return;
+		exit(1);
+	}
+}
+
+static void unpack_all(void)
+{
+	int i;
+	struct progress *progress = NULL;
+	struct pack_header *hdr = fill(sizeof(struct pack_header));
+
+	nr_objects = ntohl(hdr->hdr_entries);
+
+	if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE)
+		die("bad pack file");
+	if (!pack_version_ok(hdr->hdr_version))
+		die("unknown pack file version %"PRIu32,
+			ntohl(hdr->hdr_version));
+	use(sizeof(struct pack_header));
+
+	if (!quiet)
+		progress = start_progress(_("Unpacking objects"), nr_objects);
+	obj_list = xcalloc(nr_objects, sizeof(*obj_list));
+	for (i = 0; i < nr_objects; i++) {
+		unpack_one(i);
+		display_progress(progress, i + 1);
+	}
+	stop_progress(&progress);
+
+	if (delta_list)
+		die("unresolved deltas left after unpacking");
+}
+
+int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct object_id oid;
+
+	read_replace_refs = 0;
+
+	git_config(git_default_config, NULL);
+
+	quiet = !isatty(2);
+
+	for (i = 1 ; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (*arg == '-') {
+			if (!strcmp(arg, "-n")) {
+				dry_run = 1;
+				continue;
+			}
+			if (!strcmp(arg, "-q")) {
+				quiet = 1;
+				continue;
+			}
+			if (!strcmp(arg, "-r")) {
+				recover = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--strict")) {
+				strict = 1;
+				continue;
+			}
+			if (skip_prefix(arg, "--strict=", &arg)) {
+				strict = 1;
+				fsck_set_msg_types(&fsck_options, arg);
+				continue;
+			}
+			if (starts_with(arg, "--pack_header=")) {
+				struct pack_header *hdr;
+				char *c;
+
+				hdr = (struct pack_header *)buffer;
+				hdr->hdr_signature = htonl(PACK_SIGNATURE);
+				hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10));
+				if (*c != ',')
+					die("bad %s", arg);
+				hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10));
+				if (*c)
+					die("bad %s", arg);
+				len = sizeof(*hdr);
+				continue;
+			}
+			if (skip_prefix(arg, "--max-input-size=", &arg)) {
+				max_input_size = strtoumax(arg, NULL, 10);
+				continue;
+			}
+			usage(unpack_usage);
+		}
+
+		/* We don't take any non-flag arguments now.. Maybe some day */
+		usage(unpack_usage);
+	}
+	the_hash_algo->init_fn(&ctx);
+	unpack_all();
+	the_hash_algo->update_fn(&ctx, buffer, offset);
+	the_hash_algo->final_fn(oid.hash, &ctx);
+	if (strict) {
+		write_rest();
+		if (fsck_finish(&fsck_options))
+			die(_("fsck error in pack objects"));
+	}
+	if (!hasheq(fill(the_hash_algo->rawsz), oid.hash))
+		die("final sha1 did not match");
+	use(the_hash_algo->rawsz);
+
+	/* Write the last part of the buffer to stdout */
+	while (len) {
+		int ret = xwrite(1, buffer + offset, len);
+		if (ret <= 0)
+			break;
+		len -= ret;
+		offset += ret;
+	}
+
+	/* All done */
+	return has_errors;
+}
diff --git a/builtin/update-index.c b/builtin/update-index.c
new file mode 100644
index 0000000000..dff2f4b837
--- /dev/null
+++ b/builtin/update-index.c
@@ -0,0 +1,1239 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+#include "config.h"
+#include "lockfile.h"
+#include "quote.h"
+#include "cache-tree.h"
+#include "tree-walk.h"
+#include "builtin.h"
+#include "refs.h"
+#include "resolve-undo.h"
+#include "parse-options.h"
+#include "pathspec.h"
+#include "dir.h"
+#include "split-index.h"
+#include "fsmonitor.h"
+
+/*
+ * Default to not allowing changes to the list of files. The
+ * tool doesn't actually care, but this makes it harder to add
+ * files to the revision control by mistake by doing something
+ * like "git update-index *" and suddenly having all the object
+ * files be revision controlled.
+ */
+static int allow_add;
+static int allow_remove;
+static int allow_replace;
+static int info_only;
+static int force_remove;
+static int verbose;
+static int mark_valid_only;
+static int mark_skip_worktree_only;
+static int mark_fsmonitor_only;
+#define MARK_FLAG 1
+#define UNMARK_FLAG 2
+static struct strbuf mtime_dir = STRBUF_INIT;
+
+/* Untracked cache mode */
+enum uc_mode {
+	UC_UNSPECIFIED = -1,
+	UC_DISABLE = 0,
+	UC_ENABLE,
+	UC_TEST,
+	UC_FORCE
+};
+
+__attribute__((format (printf, 1, 2)))
+static void report(const char *fmt, ...)
+{
+	va_list vp;
+
+	if (!verbose)
+		return;
+
+	va_start(vp, fmt);
+	vprintf(fmt, vp);
+	putchar('\n');
+	va_end(vp);
+}
+
+static void remove_test_directory(void)
+{
+	if (mtime_dir.len)
+		remove_dir_recursively(&mtime_dir, 0);
+}
+
+static const char *get_mtime_path(const char *path)
+{
+	static struct strbuf sb = STRBUF_INIT;
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s/%s", mtime_dir.buf, path);
+	return sb.buf;
+}
+
+static void xmkdir(const char *path)
+{
+	path = get_mtime_path(path);
+	if (mkdir(path, 0700))
+		die_errno(_("failed to create directory %s"), path);
+}
+
+static int xstat_mtime_dir(struct stat *st)
+{
+	if (stat(mtime_dir.buf, st))
+		die_errno(_("failed to stat %s"), mtime_dir.buf);
+	return 0;
+}
+
+static int create_file(const char *path)
+{
+	int fd;
+	path = get_mtime_path(path);
+	fd = open(path, O_CREAT | O_RDWR, 0644);
+	if (fd < 0)
+		die_errno(_("failed to create file %s"), path);
+	return fd;
+}
+
+static void xunlink(const char *path)
+{
+	path = get_mtime_path(path);
+	if (unlink(path))
+		die_errno(_("failed to delete file %s"), path);
+}
+
+static void xrmdir(const char *path)
+{
+	path = get_mtime_path(path);
+	if (rmdir(path))
+		die_errno(_("failed to delete directory %s"), path);
+}
+
+static void avoid_racy(void)
+{
+	/*
+	 * not use if we could usleep(10) if USE_NSEC is defined. The
+	 * field nsec could be there, but the OS could choose to
+	 * ignore it?
+	 */
+	sleep(1);
+}
+
+static int test_if_untracked_cache_is_supported(void)
+{
+	struct stat st;
+	struct stat_data base;
+	int fd, ret = 0;
+	char *cwd;
+
+	strbuf_addstr(&mtime_dir, "mtime-test-XXXXXX");
+	if (!mkdtemp(mtime_dir.buf))
+		die_errno("Could not make temporary directory");
+
+	cwd = xgetcwd();
+	fprintf(stderr, _("Testing mtime in '%s' "), cwd);
+	free(cwd);
+
+	atexit(remove_test_directory);
+	xstat_mtime_dir(&st);
+	fill_stat_data(&base, &st);
+	fputc('.', stderr);
+
+	avoid_racy();
+	fd = create_file("newfile");
+	xstat_mtime_dir(&st);
+	if (!match_stat_data(&base, &st)) {
+		close(fd);
+		fputc('\n', stderr);
+		fprintf_ln(stderr,_("directory stat info does not "
+				    "change after adding a new file"));
+		goto done;
+	}
+	fill_stat_data(&base, &st);
+	fputc('.', stderr);
+
+	avoid_racy();
+	xmkdir("new-dir");
+	xstat_mtime_dir(&st);
+	if (!match_stat_data(&base, &st)) {
+		close(fd);
+		fputc('\n', stderr);
+		fprintf_ln(stderr, _("directory stat info does not change "
+				     "after adding a new directory"));
+		goto done;
+	}
+	fill_stat_data(&base, &st);
+	fputc('.', stderr);
+
+	avoid_racy();
+	write_or_die(fd, "data", 4);
+	close(fd);
+	xstat_mtime_dir(&st);
+	if (match_stat_data(&base, &st)) {
+		fputc('\n', stderr);
+		fprintf_ln(stderr, _("directory stat info changes "
+				     "after updating a file"));
+		goto done;
+	}
+	fputc('.', stderr);
+
+	avoid_racy();
+	close(create_file("new-dir/new"));
+	xstat_mtime_dir(&st);
+	if (match_stat_data(&base, &st)) {
+		fputc('\n', stderr);
+		fprintf_ln(stderr, _("directory stat info changes after "
+				     "adding a file inside subdirectory"));
+		goto done;
+	}
+	fputc('.', stderr);
+
+	avoid_racy();
+	xunlink("newfile");
+	xstat_mtime_dir(&st);
+	if (!match_stat_data(&base, &st)) {
+		fputc('\n', stderr);
+		fprintf_ln(stderr, _("directory stat info does not "
+				     "change after deleting a file"));
+		goto done;
+	}
+	fill_stat_data(&base, &st);
+	fputc('.', stderr);
+
+	avoid_racy();
+	xunlink("new-dir/new");
+	xrmdir("new-dir");
+	xstat_mtime_dir(&st);
+	if (!match_stat_data(&base, &st)) {
+		fputc('\n', stderr);
+		fprintf_ln(stderr, _("directory stat info does not "
+				     "change after deleting a directory"));
+		goto done;
+	}
+
+	if (rmdir(mtime_dir.buf))
+		die_errno(_("failed to delete directory %s"), mtime_dir.buf);
+	fprintf_ln(stderr, _(" OK"));
+	ret = 1;
+
+done:
+	strbuf_release(&mtime_dir);
+	return ret;
+}
+
+static int mark_ce_flags(const char *path, int flag, int mark)
+{
+	int namelen = strlen(path);
+	int pos = cache_name_pos(path, namelen);
+	if (0 <= pos) {
+		mark_fsmonitor_invalid(&the_index, active_cache[pos]);
+		if (mark)
+			active_cache[pos]->ce_flags |= flag;
+		else
+			active_cache[pos]->ce_flags &= ~flag;
+		active_cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
+		cache_tree_invalidate_path(&the_index, path);
+		active_cache_changed |= CE_ENTRY_CHANGED;
+		return 0;
+	}
+	return -1;
+}
+
+static int remove_one_path(const char *path)
+{
+	if (!allow_remove)
+		return error("%s: does not exist and --remove not passed", path);
+	if (remove_file_from_cache(path))
+		return error("%s: cannot remove from the index", path);
+	return 0;
+}
+
+/*
+ * Handle a path that couldn't be lstat'ed. It's either:
+ *  - missing file (ENOENT or ENOTDIR). That's ok if we're
+ *    supposed to be removing it and the removal actually
+ *    succeeds.
+ *  - permission error. That's never ok.
+ */
+static int process_lstat_error(const char *path, int err)
+{
+	if (is_missing_file_error(err))
+		return remove_one_path(path);
+	return error("lstat(\"%s\"): %s", path, strerror(err));
+}
+
+static int add_one_path(const struct cache_entry *old, const char *path, int len, struct stat *st)
+{
+	int option;
+	struct cache_entry *ce;
+
+	/* Was the old index entry already up-to-date? */
+	if (old && !ce_stage(old) && !ce_match_stat(old, st, 0))
+		return 0;
+
+	ce = make_empty_cache_entry(&the_index, len);
+	memcpy(ce->name, path, len);
+	ce->ce_flags = create_ce_flags(0);
+	ce->ce_namelen = len;
+	fill_stat_cache_info(&the_index, ce, st);
+	ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
+
+	if (index_path(&the_index, &ce->oid, path, st,
+		       info_only ? 0 : HASH_WRITE_OBJECT)) {
+		discard_cache_entry(ce);
+		return -1;
+	}
+	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
+	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
+	if (add_cache_entry(ce, option)) {
+		discard_cache_entry(ce);
+		return error("%s: cannot add to the index - missing --add option?", path);
+	}
+	return 0;
+}
+
+/*
+ * Handle a path that was a directory. Four cases:
+ *
+ *  - it's already a gitlink in the index, and we keep it that
+ *    way, and update it if we can (if we cannot find the HEAD,
+ *    we're going to keep it unchanged in the index!)
+ *
+ *  - it's a *file* in the index, in which case it should be
+ *    removed as a file if removal is allowed, since it doesn't
+ *    exist as such any more. If removal isn't allowed, it's
+ *    an error.
+ *
+ *    (NOTE! This is old and arguably fairly strange behaviour.
+ *    We might want to make this an error unconditionally, and
+ *    use "--force-remove" if you actually want to force removal).
+ *
+ *  - it used to exist as a subdirectory (ie multiple files with
+ *    this particular prefix) in the index, in which case it's wrong
+ *    to try to update it as a directory.
+ *
+ *  - it doesn't exist at all in the index, but it is a valid
+ *    git directory, and it should be *added* as a gitlink.
+ */
+static int process_directory(const char *path, int len, struct stat *st)
+{
+	struct object_id oid;
+	int pos = cache_name_pos(path, len);
+
+	/* Exact match: file or existing gitlink */
+	if (pos >= 0) {
+		const struct cache_entry *ce = active_cache[pos];
+		if (S_ISGITLINK(ce->ce_mode)) {
+
+			/* Do nothing to the index if there is no HEAD! */
+			if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+				return 0;
+
+			return add_one_path(ce, path, len, st);
+		}
+		/* Should this be an unconditional error? */
+		return remove_one_path(path);
+	}
+
+	/* Inexact match: is there perhaps a subdirectory match? */
+	pos = -pos-1;
+	while (pos < active_nr) {
+		const struct cache_entry *ce = active_cache[pos++];
+
+		if (strncmp(ce->name, path, len))
+			break;
+		if (ce->name[len] > '/')
+			break;
+		if (ce->name[len] < '/')
+			continue;
+
+		/* Subdirectory match - error out */
+		return error("%s: is a directory - add individual files instead", path);
+	}
+
+	/* No match - should we add it as a gitlink? */
+	if (!resolve_gitlink_ref(path, "HEAD", &oid))
+		return add_one_path(NULL, path, len, st);
+
+	/* Error out. */
+	return error("%s: is a directory - add files inside instead", path);
+}
+
+static int process_path(const char *path, struct stat *st, int stat_errno)
+{
+	int pos, len;
+	const struct cache_entry *ce;
+
+	len = strlen(path);
+	if (has_symlink_leading_path(path, len))
+		return error("'%s' is beyond a symbolic link", path);
+
+	pos = cache_name_pos(path, len);
+	ce = pos < 0 ? NULL : active_cache[pos];
+	if (ce && ce_skip_worktree(ce)) {
+		/*
+		 * working directory version is assumed "good"
+		 * so updating it does not make sense.
+		 * On the other hand, removing it from index should work
+		 */
+		if (allow_remove && remove_file_from_cache(path))
+			return error("%s: cannot remove from the index", path);
+		return 0;
+	}
+
+	/*
+	 * First things first: get the stat information, to decide
+	 * what to do about the pathname!
+	 */
+	if (stat_errno)
+		return process_lstat_error(path, stat_errno);
+
+	if (S_ISDIR(st->st_mode))
+		return process_directory(path, len, st);
+
+	return add_one_path(ce, path, len, st);
+}
+
+static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
+			 const char *path, int stage)
+{
+	int len, option;
+	struct cache_entry *ce;
+
+	if (!verify_path(path, mode))
+		return error("Invalid path '%s'", path);
+
+	len = strlen(path);
+	ce = make_empty_cache_entry(&the_index, len);
+
+	oidcpy(&ce->oid, oid);
+	memcpy(ce->name, path, len);
+	ce->ce_flags = create_ce_flags(stage);
+	ce->ce_namelen = len;
+	ce->ce_mode = create_ce_mode(mode);
+	if (assume_unchanged)
+		ce->ce_flags |= CE_VALID;
+	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
+	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
+	if (add_cache_entry(ce, option))
+		return error("%s: cannot add to the index - missing --add option?",
+			     path);
+	report("add '%s'", path);
+	return 0;
+}
+
+static void chmod_path(char flip, const char *path)
+{
+	int pos;
+	struct cache_entry *ce;
+
+	pos = cache_name_pos(path, strlen(path));
+	if (pos < 0)
+		goto fail;
+	ce = active_cache[pos];
+	if (chmod_cache_entry(ce, flip) < 0)
+		goto fail;
+
+	report("chmod %cx '%s'", flip, path);
+	return;
+ fail:
+	die("git update-index: cannot chmod %cx '%s'", flip, path);
+}
+
+static void update_one(const char *path)
+{
+	int stat_errno = 0;
+	struct stat st;
+
+	if (mark_valid_only || mark_skip_worktree_only || force_remove ||
+	    mark_fsmonitor_only)
+		st.st_mode = 0;
+	else if (lstat(path, &st) < 0) {
+		st.st_mode = 0;
+		stat_errno = errno;
+	} /* else stat is valid */
+
+	if (!verify_path(path, st.st_mode)) {
+		fprintf(stderr, "Ignoring path %s\n", path);
+		return;
+	}
+	if (mark_valid_only) {
+		if (mark_ce_flags(path, CE_VALID, mark_valid_only == MARK_FLAG))
+			die("Unable to mark file %s", path);
+		return;
+	}
+	if (mark_skip_worktree_only) {
+		if (mark_ce_flags(path, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG))
+			die("Unable to mark file %s", path);
+		return;
+	}
+	if (mark_fsmonitor_only) {
+		if (mark_ce_flags(path, CE_FSMONITOR_VALID, mark_fsmonitor_only == MARK_FLAG))
+			die("Unable to mark file %s", path);
+		return;
+	}
+
+	if (force_remove) {
+		if (remove_file_from_cache(path))
+			die("git update-index: unable to remove %s", path);
+		report("remove '%s'", path);
+		return;
+	}
+	if (process_path(path, &st, stat_errno))
+		die("Unable to process path %s", path);
+	report("add '%s'", path);
+}
+
+static void read_index_info(int nul_term_line)
+{
+	const int hexsz = the_hash_algo->hexsz;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf uq = STRBUF_INIT;
+	strbuf_getline_fn getline_fn;
+
+	getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
+	while (getline_fn(&buf, stdin) != EOF) {
+		char *ptr, *tab;
+		char *path_name;
+		struct object_id oid;
+		unsigned int mode;
+		unsigned long ul;
+		int stage;
+
+		/* This reads lines formatted in one of three formats:
+		 *
+		 * (1) mode         SP sha1          TAB path
+		 * The first format is what "git apply --index-info"
+		 * reports, and used to reconstruct a partial tree
+		 * that is used for phony merge base tree when falling
+		 * back on 3-way merge.
+		 *
+		 * (2) mode SP type SP sha1          TAB path
+		 * The second format is to stuff "git ls-tree" output
+		 * into the index file.
+		 *
+		 * (3) mode         SP sha1 SP stage TAB path
+		 * This format is to put higher order stages into the
+		 * index file and matches "git ls-files --stage" output.
+		 */
+		errno = 0;
+		ul = strtoul(buf.buf, &ptr, 8);
+		if (ptr == buf.buf || *ptr != ' '
+		    || errno || (unsigned int) ul != ul)
+			goto bad_line;
+		mode = ul;
+
+		tab = strchr(ptr, '\t');
+		if (!tab || tab - ptr < hexsz + 1)
+			goto bad_line;
+
+		if (tab[-2] == ' ' && '0' <= tab[-1] && tab[-1] <= '3') {
+			stage = tab[-1] - '0';
+			ptr = tab + 1; /* point at the head of path */
+			tab = tab - 2; /* point at tail of sha1 */
+		}
+		else {
+			stage = 0;
+			ptr = tab + 1; /* point at the head of path */
+		}
+
+		if (get_oid_hex(tab - hexsz, &oid) ||
+			tab[-(hexsz + 1)] != ' ')
+			goto bad_line;
+
+		path_name = ptr;
+		if (!nul_term_line && path_name[0] == '"') {
+			strbuf_reset(&uq);
+			if (unquote_c_style(&uq, path_name, NULL)) {
+				die("git update-index: bad quoting of path name");
+			}
+			path_name = uq.buf;
+		}
+
+		if (!verify_path(path_name, mode)) {
+			fprintf(stderr, "Ignoring path %s\n", path_name);
+			continue;
+		}
+
+		if (!mode) {
+			/* mode == 0 means there is no such path -- remove */
+			if (remove_file_from_cache(path_name))
+				die("git update-index: unable to remove %s",
+				    ptr);
+		}
+		else {
+			/* mode ' ' sha1 '\t' name
+			 * ptr[-1] points at tab,
+			 * ptr[-41] is at the beginning of sha1
+			 */
+			ptr[-(hexsz + 2)] = ptr[-1] = 0;
+			if (add_cacheinfo(mode, &oid, path_name, stage))
+				die("git update-index: unable to update %s",
+				    path_name);
+		}
+		continue;
+
+	bad_line:
+		die("malformed index info %s", buf.buf);
+	}
+	strbuf_release(&buf);
+	strbuf_release(&uq);
+}
+
+static const char * const update_index_usage[] = {
+	N_("git update-index [<options>] [--] [<file>...]"),
+	NULL
+};
+
+static struct object_id head_oid;
+static struct object_id merge_head_oid;
+
+static struct cache_entry *read_one_ent(const char *which,
+					struct object_id *ent, const char *path,
+					int namelen, int stage)
+{
+	unsigned short mode;
+	struct object_id oid;
+	struct cache_entry *ce;
+
+	if (get_tree_entry(the_repository, ent, path, &oid, &mode)) {
+		if (which)
+			error("%s: not in %s branch.", path, which);
+		return NULL;
+	}
+	if (mode == S_IFDIR) {
+		if (which)
+			error("%s: not a blob in %s branch.", path, which);
+		return NULL;
+	}
+	ce = make_empty_cache_entry(&the_index, namelen);
+
+	oidcpy(&ce->oid, &oid);
+	memcpy(ce->name, path, namelen);
+	ce->ce_flags = create_ce_flags(stage);
+	ce->ce_namelen = namelen;
+	ce->ce_mode = create_ce_mode(mode);
+	return ce;
+}
+
+static int unresolve_one(const char *path)
+{
+	int namelen = strlen(path);
+	int pos;
+	int ret = 0;
+	struct cache_entry *ce_2 = NULL, *ce_3 = NULL;
+
+	/* See if there is such entry in the index. */
+	pos = cache_name_pos(path, namelen);
+	if (0 <= pos) {
+		/* already merged */
+		pos = unmerge_cache_entry_at(pos);
+		if (pos < active_nr) {
+			const struct cache_entry *ce = active_cache[pos];
+			if (ce_stage(ce) &&
+			    ce_namelen(ce) == namelen &&
+			    !memcmp(ce->name, path, namelen))
+				return 0;
+		}
+		/* no resolve-undo information; fall back */
+	} else {
+		/* If there isn't, either it is unmerged, or
+		 * resolved as "removed" by mistake.  We do not
+		 * want to do anything in the former case.
+		 */
+		pos = -pos-1;
+		if (pos < active_nr) {
+			const struct cache_entry *ce = active_cache[pos];
+			if (ce_namelen(ce) == namelen &&
+			    !memcmp(ce->name, path, namelen)) {
+				fprintf(stderr,
+					"%s: skipping still unmerged path.\n",
+					path);
+				goto free_return;
+			}
+		}
+	}
+
+	/* Grab blobs from given path from HEAD and MERGE_HEAD,
+	 * stuff HEAD version in stage #2,
+	 * stuff MERGE_HEAD version in stage #3.
+	 */
+	ce_2 = read_one_ent("our", &head_oid, path, namelen, 2);
+	ce_3 = read_one_ent("their", &merge_head_oid, path, namelen, 3);
+
+	if (!ce_2 || !ce_3) {
+		ret = -1;
+		goto free_return;
+	}
+	if (oideq(&ce_2->oid, &ce_3->oid) &&
+	    ce_2->ce_mode == ce_3->ce_mode) {
+		fprintf(stderr, "%s: identical in both, skipping.\n",
+			path);
+		goto free_return;
+	}
+
+	remove_file_from_cache(path);
+	if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
+		error("%s: cannot add our version to the index.", path);
+		ret = -1;
+		goto free_return;
+	}
+	if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD))
+		return 0;
+	error("%s: cannot add their version to the index.", path);
+	ret = -1;
+ free_return:
+	discard_cache_entry(ce_2);
+	discard_cache_entry(ce_3);
+	return ret;
+}
+
+static void read_head_pointers(void)
+{
+	if (read_ref("HEAD", &head_oid))
+		die("No HEAD -- no initial commit yet?");
+	if (read_ref("MERGE_HEAD", &merge_head_oid)) {
+		fprintf(stderr, "Not in the middle of a merge.\n");
+		exit(0);
+	}
+}
+
+static int do_unresolve(int ac, const char **av,
+			const char *prefix, int prefix_length)
+{
+	int i;
+	int err = 0;
+
+	/* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we
+	 * are not doing a merge, so exit with success status.
+	 */
+	read_head_pointers();
+
+	for (i = 1; i < ac; i++) {
+		const char *arg = av[i];
+		char *p = prefix_path(prefix, prefix_length, arg);
+		err |= unresolve_one(p);
+		free(p);
+	}
+	return err;
+}
+
+static int do_reupdate(int ac, const char **av,
+		       const char *prefix)
+{
+	/* Read HEAD and run update-index on paths that are
+	 * merged and already different between index and HEAD.
+	 */
+	int pos;
+	int has_head = 1;
+	struct pathspec pathspec;
+
+	parse_pathspec(&pathspec, 0,
+		       PATHSPEC_PREFER_CWD,
+		       prefix, av + 1);
+
+	if (read_ref("HEAD", &head_oid))
+		/* If there is no HEAD, that means it is an initial
+		 * commit.  Update everything in the index.
+		 */
+		has_head = 0;
+ redo:
+	for (pos = 0; pos < active_nr; pos++) {
+		const struct cache_entry *ce = active_cache[pos];
+		struct cache_entry *old = NULL;
+		int save_nr;
+		char *path;
+
+		if (ce_stage(ce) || !ce_path_match(&the_index, ce, &pathspec, NULL))
+			continue;
+		if (has_head)
+			old = read_one_ent(NULL, &head_oid,
+					   ce->name, ce_namelen(ce), 0);
+		if (old && ce->ce_mode == old->ce_mode &&
+		    oideq(&ce->oid, &old->oid)) {
+			discard_cache_entry(old);
+			continue; /* unchanged */
+		}
+		/* Be careful.  The working tree may not have the
+		 * path anymore, in which case, under 'allow_remove',
+		 * or worse yet 'allow_replace', active_nr may decrease.
+		 */
+		save_nr = active_nr;
+		path = xstrdup(ce->name);
+		update_one(path);
+		free(path);
+		discard_cache_entry(old);
+		if (save_nr != active_nr)
+			goto redo;
+	}
+	clear_pathspec(&pathspec);
+	return 0;
+}
+
+struct refresh_params {
+	unsigned int flags;
+	int *has_errors;
+};
+
+static int refresh(struct refresh_params *o, unsigned int flag)
+{
+	setup_work_tree();
+	read_cache();
+	*o->has_errors |= refresh_cache(o->flags | flag);
+	return 0;
+}
+
+static int refresh_callback(const struct option *opt,
+				const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+	return refresh(opt->value, 0);
+}
+
+static int really_refresh_callback(const struct option *opt,
+				const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+	return refresh(opt->value, REFRESH_REALLY);
+}
+
+static int chmod_callback(const struct option *opt,
+				const char *arg, int unset)
+{
+	char *flip = opt->value;
+	BUG_ON_OPT_NEG(unset);
+	if ((arg[0] != '-' && arg[0] != '+') || arg[1] != 'x' || arg[2])
+		return error("option 'chmod' expects \"+x\" or \"-x\"");
+	*flip = arg[0];
+	return 0;
+}
+
+static int resolve_undo_clear_callback(const struct option *opt,
+				const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+	resolve_undo_clear();
+	return 0;
+}
+
+static int parse_new_style_cacheinfo(const char *arg,
+				     unsigned int *mode,
+				     struct object_id *oid,
+				     const char **path)
+{
+	unsigned long ul;
+	char *endp;
+	const char *p;
+
+	if (!arg)
+		return -1;
+
+	errno = 0;
+	ul = strtoul(arg, &endp, 8);
+	if (errno || endp == arg || *endp != ',' || (unsigned int) ul != ul)
+		return -1; /* not a new-style cacheinfo */
+	*mode = ul;
+	endp++;
+	if (parse_oid_hex(endp, oid, &p) || *p != ',')
+		return -1;
+	*path = p + 1;
+	return 0;
+}
+
+static enum parse_opt_result cacheinfo_callback(
+	struct parse_opt_ctx_t *ctx, const struct option *opt,
+	const char *arg, int unset)
+{
+	struct object_id oid;
+	unsigned int mode;
+	const char *path;
+
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+
+	if (!parse_new_style_cacheinfo(ctx->argv[1], &mode, &oid, &path)) {
+		if (add_cacheinfo(mode, &oid, path, 0))
+			die("git update-index: --cacheinfo cannot add %s", path);
+		ctx->argv++;
+		ctx->argc--;
+		return 0;
+	}
+	if (ctx->argc <= 3)
+		return error("option 'cacheinfo' expects <mode>,<sha1>,<path>");
+	if (strtoul_ui(*++ctx->argv, 8, &mode) ||
+	    get_oid_hex(*++ctx->argv, &oid) ||
+	    add_cacheinfo(mode, &oid, *++ctx->argv, 0))
+		die("git update-index: --cacheinfo cannot add %s", *ctx->argv);
+	ctx->argc -= 3;
+	return 0;
+}
+
+static enum parse_opt_result stdin_cacheinfo_callback(
+	struct parse_opt_ctx_t *ctx, const struct option *opt,
+	const char *arg, int unset)
+{
+	int *nul_term_line = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+
+	if (ctx->argc != 1)
+		return error("option '%s' must be the last argument", opt->long_name);
+	allow_add = allow_replace = allow_remove = 1;
+	read_index_info(*nul_term_line);
+	return 0;
+}
+
+static enum parse_opt_result stdin_callback(
+	struct parse_opt_ctx_t *ctx, const struct option *opt,
+	const char *arg, int unset)
+{
+	int *read_from_stdin = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+
+	if (ctx->argc != 1)
+		return error("option '%s' must be the last argument", opt->long_name);
+	*read_from_stdin = 1;
+	return 0;
+}
+
+static enum parse_opt_result unresolve_callback(
+	struct parse_opt_ctx_t *ctx, const struct option *opt,
+	const char *arg, int unset)
+{
+	int *has_errors = opt->value;
+	const char *prefix = startup_info->prefix;
+
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+
+	/* consume remaining arguments. */
+	*has_errors = do_unresolve(ctx->argc, ctx->argv,
+				prefix, prefix ? strlen(prefix) : 0);
+	if (*has_errors)
+		active_cache_changed = 0;
+
+	ctx->argv += ctx->argc - 1;
+	ctx->argc = 1;
+	return 0;
+}
+
+static enum parse_opt_result reupdate_callback(
+	struct parse_opt_ctx_t *ctx, const struct option *opt,
+	const char *arg, int unset)
+{
+	int *has_errors = opt->value;
+	const char *prefix = startup_info->prefix;
+
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+
+	/* consume remaining arguments. */
+	setup_work_tree();
+	*has_errors = do_reupdate(ctx->argc, ctx->argv, prefix);
+	if (*has_errors)
+		active_cache_changed = 0;
+
+	ctx->argv += ctx->argc - 1;
+	ctx->argc = 1;
+	return 0;
+}
+
+int cmd_update_index(int argc, const char **argv, const char *prefix)
+{
+	int newfd, entries, has_errors = 0, nul_term_line = 0;
+	enum uc_mode untracked_cache = UC_UNSPECIFIED;
+	int read_from_stdin = 0;
+	int prefix_length = prefix ? strlen(prefix) : 0;
+	int preferred_index_format = 0;
+	char set_executable_bit = 0;
+	struct refresh_params refresh_args = {0, &has_errors};
+	int lock_error = 0;
+	int split_index = -1;
+	int force_write = 0;
+	int fsmonitor = -1;
+	struct lock_file lock_file = LOCK_INIT;
+	struct parse_opt_ctx_t ctx;
+	strbuf_getline_fn getline_fn;
+	int parseopt_state = PARSE_OPT_UNKNOWN;
+	struct option options[] = {
+		OPT_BIT('q', NULL, &refresh_args.flags,
+			N_("continue refresh even when index needs update"),
+			REFRESH_QUIET),
+		OPT_BIT(0, "ignore-submodules", &refresh_args.flags,
+			N_("refresh: ignore submodules"),
+			REFRESH_IGNORE_SUBMODULES),
+		OPT_SET_INT(0, "add", &allow_add,
+			N_("do not ignore new files"), 1),
+		OPT_SET_INT(0, "replace", &allow_replace,
+			N_("let files replace directories and vice-versa"), 1),
+		OPT_SET_INT(0, "remove", &allow_remove,
+			N_("notice files missing from worktree"), 1),
+		OPT_BIT(0, "unmerged", &refresh_args.flags,
+			N_("refresh even if index contains unmerged entries"),
+			REFRESH_UNMERGED),
+		{OPTION_CALLBACK, 0, "refresh", &refresh_args, NULL,
+			N_("refresh stat information"),
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+			refresh_callback},
+		{OPTION_CALLBACK, 0, "really-refresh", &refresh_args, NULL,
+			N_("like --refresh, but ignore assume-unchanged setting"),
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+			really_refresh_callback},
+		{OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL,
+			N_("<mode>,<object>,<path>"),
+			N_("add the specified entry to the index"),
+			PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */
+			PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
+			NULL, 0,
+			cacheinfo_callback},
+		{OPTION_CALLBACK, 0, "chmod", &set_executable_bit, "(+|-)x",
+			N_("override the executable bit of the listed files"),
+			PARSE_OPT_NONEG,
+			chmod_callback},
+		{OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL,
+			N_("mark files as \"not changing\""),
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+		{OPTION_SET_INT, 0, "no-assume-unchanged", &mark_valid_only, NULL,
+			N_("clear assumed-unchanged bit"),
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
+		{OPTION_SET_INT, 0, "skip-worktree", &mark_skip_worktree_only, NULL,
+			N_("mark files as \"index-only\""),
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+		{OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL,
+			N_("clear skip-worktree bit"),
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
+		OPT_SET_INT(0, "info-only", &info_only,
+			N_("add to index only; do not add content to object database"), 1),
+		OPT_SET_INT(0, "force-remove", &force_remove,
+			N_("remove named paths even if present in worktree"), 1),
+		OPT_BOOL('z', NULL, &nul_term_line,
+			 N_("with --stdin: input lines are terminated by null bytes")),
+		{OPTION_LOWLEVEL_CALLBACK, 0, "stdin", &read_from_stdin, NULL,
+			N_("read list of paths to be updated from standard input"),
+			PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+			NULL, 0, stdin_callback},
+		{OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &nul_term_line, NULL,
+			N_("add entries from standard input to the index"),
+			PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+			NULL, 0, stdin_cacheinfo_callback},
+		{OPTION_LOWLEVEL_CALLBACK, 0, "unresolve", &has_errors, NULL,
+			N_("repopulate stages #2 and #3 for the listed paths"),
+			PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+			NULL, 0, unresolve_callback},
+		{OPTION_LOWLEVEL_CALLBACK, 'g', "again", &has_errors, NULL,
+			N_("only update entries that differ from HEAD"),
+			PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+			NULL, 0, reupdate_callback},
+		OPT_BIT(0, "ignore-missing", &refresh_args.flags,
+			N_("ignore files missing from worktree"),
+			REFRESH_IGNORE_MISSING),
+		OPT_SET_INT(0, "verbose", &verbose,
+			N_("report actions to standard output"), 1),
+		{OPTION_CALLBACK, 0, "clear-resolve-undo", NULL, NULL,
+			N_("(for porcelains) forget saved unresolved conflicts"),
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+			resolve_undo_clear_callback},
+		OPT_INTEGER(0, "index-version", &preferred_index_format,
+			N_("write index in this format")),
+		OPT_BOOL(0, "split-index", &split_index,
+			N_("enable or disable split index")),
+		OPT_BOOL(0, "untracked-cache", &untracked_cache,
+			N_("enable/disable untracked cache")),
+		OPT_SET_INT(0, "test-untracked-cache", &untracked_cache,
+			    N_("test if the filesystem supports untracked cache"), UC_TEST),
+		OPT_SET_INT(0, "force-untracked-cache", &untracked_cache,
+			    N_("enable untracked cache without testing the filesystem"), UC_FORCE),
+		OPT_SET_INT(0, "force-write-index", &force_write,
+			N_("write out the index even if is not flagged as changed"), 1),
+		OPT_BOOL(0, "fsmonitor", &fsmonitor,
+			N_("enable or disable file system monitor")),
+		{OPTION_SET_INT, 0, "fsmonitor-valid", &mark_fsmonitor_only, NULL,
+			N_("mark files as fsmonitor valid"),
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+		{OPTION_SET_INT, 0, "no-fsmonitor-valid", &mark_fsmonitor_only, NULL,
+			N_("clear fsmonitor valid bit"),
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
+		OPT_END()
+	};
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(update_index_usage, options);
+
+	git_config(git_default_config, NULL);
+
+	/* we will diagnose later if it turns out that we need to update it */
+	newfd = hold_locked_index(&lock_file, 0);
+	if (newfd < 0)
+		lock_error = errno;
+
+	entries = read_cache();
+	if (entries < 0)
+		die("cache corrupted");
+
+	the_index.updated_skipworktree = 1;
+
+	/*
+	 * Custom copy of parse_options() because we want to handle
+	 * filename arguments as they come.
+	 */
+	parse_options_start(&ctx, argc, argv, prefix,
+			    options, PARSE_OPT_STOP_AT_NON_OPTION);
+	while (ctx.argc) {
+		if (parseopt_state != PARSE_OPT_DONE)
+			parseopt_state = parse_options_step(&ctx, options,
+							    update_index_usage);
+		if (!ctx.argc)
+			break;
+		switch (parseopt_state) {
+		case PARSE_OPT_HELP:
+		case PARSE_OPT_ERROR:
+			exit(129);
+		case PARSE_OPT_COMPLETE:
+			exit(0);
+		case PARSE_OPT_NON_OPTION:
+		case PARSE_OPT_DONE:
+		{
+			const char *path = ctx.argv[0];
+			char *p;
+
+			setup_work_tree();
+			p = prefix_path(prefix, prefix_length, path);
+			update_one(p);
+			if (set_executable_bit)
+				chmod_path(set_executable_bit, p);
+			free(p);
+			ctx.argc--;
+			ctx.argv++;
+			break;
+		}
+		case PARSE_OPT_UNKNOWN:
+			if (ctx.argv[0][1] == '-')
+				error("unknown option '%s'", ctx.argv[0] + 2);
+			else
+				error("unknown switch '%c'", *ctx.opt);
+			usage_with_options(update_index_usage, options);
+		}
+	}
+	argc = parse_options_end(&ctx);
+
+	getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
+	if (preferred_index_format) {
+		if (preferred_index_format < INDEX_FORMAT_LB ||
+		    INDEX_FORMAT_UB < preferred_index_format)
+			die("index-version %d not in range: %d..%d",
+			    preferred_index_format,
+			    INDEX_FORMAT_LB, INDEX_FORMAT_UB);
+
+		if (the_index.version != preferred_index_format)
+			active_cache_changed |= SOMETHING_CHANGED;
+		the_index.version = preferred_index_format;
+	}
+
+	if (read_from_stdin) {
+		struct strbuf buf = STRBUF_INIT;
+		struct strbuf unquoted = STRBUF_INIT;
+
+		setup_work_tree();
+		while (getline_fn(&buf, stdin) != EOF) {
+			char *p;
+			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);
+			}
+			p = prefix_path(prefix, prefix_length, buf.buf);
+			update_one(p);
+			if (set_executable_bit)
+				chmod_path(set_executable_bit, p);
+			free(p);
+		}
+		strbuf_release(&unquoted);
+		strbuf_release(&buf);
+	}
+
+	if (split_index > 0) {
+		if (git_config_get_split_index() == 0)
+			warning(_("core.splitIndex is set to false; "
+				  "remove or change it, if you really want to "
+				  "enable split index"));
+		if (the_index.split_index)
+			the_index.cache_changed |= SPLIT_INDEX_ORDERED;
+		else
+			add_split_index(&the_index);
+	} else if (!split_index) {
+		if (git_config_get_split_index() == 1)
+			warning(_("core.splitIndex is set to true; "
+				  "remove or change it, if you really want to "
+				  "disable split index"));
+		remove_split_index(&the_index);
+	}
+
+	switch (untracked_cache) {
+	case UC_UNSPECIFIED:
+		break;
+	case UC_DISABLE:
+		if (git_config_get_untracked_cache() == 1)
+			warning(_("core.untrackedCache is set to true; "
+				  "remove or change it, if you really want to "
+				  "disable the untracked cache"));
+		remove_untracked_cache(&the_index);
+		report(_("Untracked cache disabled"));
+		break;
+	case UC_TEST:
+		setup_work_tree();
+		return !test_if_untracked_cache_is_supported();
+	case UC_ENABLE:
+	case UC_FORCE:
+		if (git_config_get_untracked_cache() == 0)
+			warning(_("core.untrackedCache is set to false; "
+				  "remove or change it, if you really want to "
+				  "enable the untracked cache"));
+		add_untracked_cache(&the_index);
+		report(_("Untracked cache enabled for '%s'"), get_git_work_tree());
+		break;
+	default:
+		BUG("bad untracked_cache value: %d", untracked_cache);
+	}
+
+	if (fsmonitor > 0) {
+		if (git_config_get_fsmonitor() == 0)
+			warning(_("core.fsmonitor is unset; "
+				"set it if you really want to "
+				"enable fsmonitor"));
+		add_fsmonitor(&the_index);
+		report(_("fsmonitor enabled"));
+	} else if (!fsmonitor) {
+		if (git_config_get_fsmonitor() == 1)
+			warning(_("core.fsmonitor is set; "
+				"remove it if you really want to "
+				"disable fsmonitor"));
+		remove_fsmonitor(&the_index);
+		report(_("fsmonitor disabled"));
+	}
+
+	if (active_cache_changed || force_write) {
+		if (newfd < 0) {
+			if (refresh_args.flags & REFRESH_QUIET)
+				exit(128);
+			unable_to_lock_die(get_index_file(), lock_error);
+		}
+		if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+			die("Unable to write new index file");
+	}
+
+	rollback_lock_file(&lock_file);
+
+	return has_errors ? 1 : 0;
+}
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
new file mode 100644
index 0000000000..2d8f7f0578
--- /dev/null
+++ b/builtin/update-ref.c
@@ -0,0 +1,447 @@
+#include "cache.h"
+#include "config.h"
+#include "refs.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "quote.h"
+#include "argv-array.h"
+
+static const char * const git_update_ref_usage[] = {
+	N_("git update-ref [<options>] -d <refname> [<old-val>]"),
+	N_("git update-ref [<options>]    <refname> <new-val> [<old-val>]"),
+	N_("git update-ref [<options>] --stdin [-z]"),
+	NULL
+};
+
+static char line_termination = '\n';
+static unsigned int update_flags;
+static unsigned int default_flags;
+static unsigned create_reflog_flag;
+static const char *msg;
+
+/*
+ * Parse one whitespace- or NUL-terminated, possibly C-quoted argument
+ * and append the result to arg.  Return a pointer to the terminator.
+ * Die if there is an error in how the argument is C-quoted.  This
+ * function is only used if not -z.
+ */
+static const char *parse_arg(const char *next, struct strbuf *arg)
+{
+	if (*next == '"') {
+		const char *orig = next;
+
+		if (unquote_c_style(arg, next, &next))
+			die("badly quoted argument: %s", orig);
+		if (*next && !isspace(*next))
+			die("unexpected character after quoted argument: %s", orig);
+	} else {
+		while (*next && !isspace(*next))
+			strbuf_addch(arg, *next++);
+	}
+
+	return next;
+}
+
+/*
+ * Parse the reference name immediately after "command SP".  If not
+ * -z, then handle C-quoting.  Return a pointer to a newly allocated
+ * string containing the name of the reference, or NULL if there was
+ * an error.  Update *next to point at the character that terminates
+ * the argument.  Die if C-quoting is malformed or the reference name
+ * is invalid.
+ */
+static char *parse_refname(struct strbuf *input, const char **next)
+{
+	struct strbuf ref = STRBUF_INIT;
+
+	if (line_termination) {
+		/* Without -z, use the next argument */
+		*next = parse_arg(*next, &ref);
+	} else {
+		/* With -z, use everything up to the next NUL */
+		strbuf_addstr(&ref, *next);
+		*next += ref.len;
+	}
+
+	if (!ref.len) {
+		strbuf_release(&ref);
+		return NULL;
+	}
+
+	if (check_refname_format(ref.buf, REFNAME_ALLOW_ONELEVEL))
+		die("invalid ref format: %s", ref.buf);
+
+	return strbuf_detach(&ref, NULL);
+}
+
+/*
+ * The value being parsed is <oldvalue> (as opposed to <newvalue>; the
+ * difference affects which error messages are generated):
+ */
+#define PARSE_SHA1_OLD 0x01
+
+/*
+ * For backwards compatibility, accept an empty string for update's
+ * <newvalue> in binary mode to be equivalent to specifying zeros.
+ */
+#define PARSE_SHA1_ALLOW_EMPTY 0x02
+
+/*
+ * Parse an argument separator followed by the next argument, if any.
+ * If there is an argument, convert it to a SHA-1, write it to sha1,
+ * set *next to point at the character terminating the argument, and
+ * return 0.  If there is no argument at all (not even the empty
+ * string), return 1 and leave *next unchanged.  If the value is
+ * provided but cannot be converted to a SHA-1, die.  flags can
+ * include PARSE_SHA1_OLD and/or PARSE_SHA1_ALLOW_EMPTY.
+ */
+static int parse_next_oid(struct strbuf *input, const char **next,
+			  struct object_id *oid,
+			  const char *command, const char *refname,
+			  int flags)
+{
+	struct strbuf arg = STRBUF_INIT;
+	int ret = 0;
+
+	if (*next == input->buf + input->len)
+		goto eof;
+
+	if (line_termination) {
+		/* Without -z, consume SP and use next argument */
+		if (!**next || **next == line_termination)
+			return 1;
+		if (**next != ' ')
+			die("%s %s: expected SP but got: %s",
+			    command, refname, *next);
+		(*next)++;
+		*next = parse_arg(*next, &arg);
+		if (arg.len) {
+			if (get_oid(arg.buf, oid))
+				goto invalid;
+		} else {
+			/* Without -z, an empty value means all zeros: */
+			oidclr(oid);
+		}
+	} else {
+		/* With -z, read the next NUL-terminated line */
+		if (**next)
+			die("%s %s: expected NUL but got: %s",
+			    command, refname, *next);
+		(*next)++;
+		if (*next == input->buf + input->len)
+			goto eof;
+		strbuf_addstr(&arg, *next);
+		*next += arg.len;
+
+		if (arg.len) {
+			if (get_oid(arg.buf, oid))
+				goto invalid;
+		} else if (flags & PARSE_SHA1_ALLOW_EMPTY) {
+			/* With -z, treat an empty value as all zeros: */
+			warning("%s %s: missing <newvalue>, treating as zero",
+				command, refname);
+			oidclr(oid);
+		} else {
+			/*
+			 * With -z, an empty non-required value means
+			 * unspecified:
+			 */
+			ret = 1;
+		}
+	}
+
+	strbuf_release(&arg);
+
+	return ret;
+
+ invalid:
+	die(flags & PARSE_SHA1_OLD ?
+	    "%s %s: invalid <oldvalue>: %s" :
+	    "%s %s: invalid <newvalue>: %s",
+	    command, refname, arg.buf);
+
+ eof:
+	die(flags & PARSE_SHA1_OLD ?
+	    "%s %s: unexpected end of input when reading <oldvalue>" :
+	    "%s %s: unexpected end of input when reading <newvalue>",
+	    command, refname);
+}
+
+
+/*
+ * The following five parse_cmd_*() functions parse the corresponding
+ * command.  In each case, next points at the character following the
+ * command name and the following space.  They each return a pointer
+ * to the character terminating the command, and die with an
+ * explanatory message if there are any parsing problems.  All of
+ * these functions handle either text or binary format input,
+ * depending on how line_termination is set.
+ */
+
+static const char *parse_cmd_update(struct ref_transaction *transaction,
+				    struct strbuf *input, const char *next)
+{
+	struct strbuf err = STRBUF_INIT;
+	char *refname;
+	struct object_id new_oid, old_oid;
+	int have_old;
+
+	refname = parse_refname(input, &next);
+	if (!refname)
+		die("update: missing <ref>");
+
+	if (parse_next_oid(input, &next, &new_oid, "update", refname,
+			   PARSE_SHA1_ALLOW_EMPTY))
+		die("update %s: missing <newvalue>", refname);
+
+	have_old = !parse_next_oid(input, &next, &old_oid, "update", refname,
+				   PARSE_SHA1_OLD);
+
+	if (*next != line_termination)
+		die("update %s: extra input: %s", refname, next);
+
+	if (ref_transaction_update(transaction, refname,
+				   &new_oid, have_old ? &old_oid : NULL,
+				   update_flags | create_reflog_flag,
+				   msg, &err))
+		die("%s", err.buf);
+
+	update_flags = default_flags;
+	free(refname);
+	strbuf_release(&err);
+
+	return next;
+}
+
+static const char *parse_cmd_create(struct ref_transaction *transaction,
+				    struct strbuf *input, const char *next)
+{
+	struct strbuf err = STRBUF_INIT;
+	char *refname;
+	struct object_id new_oid;
+
+	refname = parse_refname(input, &next);
+	if (!refname)
+		die("create: missing <ref>");
+
+	if (parse_next_oid(input, &next, &new_oid, "create", refname, 0))
+		die("create %s: missing <newvalue>", refname);
+
+	if (is_null_oid(&new_oid))
+		die("create %s: zero <newvalue>", refname);
+
+	if (*next != line_termination)
+		die("create %s: extra input: %s", refname, next);
+
+	if (ref_transaction_create(transaction, refname, &new_oid,
+				   update_flags | create_reflog_flag,
+				   msg, &err))
+		die("%s", err.buf);
+
+	update_flags = default_flags;
+	free(refname);
+	strbuf_release(&err);
+
+	return next;
+}
+
+static const char *parse_cmd_delete(struct ref_transaction *transaction,
+				    struct strbuf *input, const char *next)
+{
+	struct strbuf err = STRBUF_INIT;
+	char *refname;
+	struct object_id old_oid;
+	int have_old;
+
+	refname = parse_refname(input, &next);
+	if (!refname)
+		die("delete: missing <ref>");
+
+	if (parse_next_oid(input, &next, &old_oid, "delete", refname,
+			   PARSE_SHA1_OLD)) {
+		have_old = 0;
+	} else {
+		if (is_null_oid(&old_oid))
+			die("delete %s: zero <oldvalue>", refname);
+		have_old = 1;
+	}
+
+	if (*next != line_termination)
+		die("delete %s: extra input: %s", refname, next);
+
+	if (ref_transaction_delete(transaction, refname,
+				   have_old ? &old_oid : NULL,
+				   update_flags, msg, &err))
+		die("%s", err.buf);
+
+	update_flags = default_flags;
+	free(refname);
+	strbuf_release(&err);
+
+	return next;
+}
+
+static const char *parse_cmd_verify(struct ref_transaction *transaction,
+				    struct strbuf *input, const char *next)
+{
+	struct strbuf err = STRBUF_INIT;
+	char *refname;
+	struct object_id old_oid;
+
+	refname = parse_refname(input, &next);
+	if (!refname)
+		die("verify: missing <ref>");
+
+	if (parse_next_oid(input, &next, &old_oid, "verify", refname,
+			   PARSE_SHA1_OLD))
+		oidclr(&old_oid);
+
+	if (*next != line_termination)
+		die("verify %s: extra input: %s", refname, next);
+
+	if (ref_transaction_verify(transaction, refname, &old_oid,
+				   update_flags, &err))
+		die("%s", err.buf);
+
+	update_flags = default_flags;
+	free(refname);
+	strbuf_release(&err);
+
+	return next;
+}
+
+static const char *parse_cmd_option(struct strbuf *input, const char *next)
+{
+	const char *rest;
+	if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination)
+		update_flags |= REF_NO_DEREF;
+	else
+		die("option unknown: %s", next);
+	return rest;
+}
+
+static void update_refs_stdin(struct ref_transaction *transaction)
+{
+	struct strbuf input = STRBUF_INIT;
+	const char *next;
+
+	if (strbuf_read(&input, 0, 1000) < 0)
+		die_errno("could not read from stdin");
+	next = input.buf;
+	/* Read each line dispatch its command */
+	while (next < input.buf + input.len) {
+		if (*next == line_termination)
+			die("empty command in input");
+		else if (isspace(*next))
+			die("whitespace before command: %s", next);
+		else if (skip_prefix(next, "update ", &next))
+			next = parse_cmd_update(transaction, &input, next);
+		else if (skip_prefix(next, "create ", &next))
+			next = parse_cmd_create(transaction, &input, next);
+		else if (skip_prefix(next, "delete ", &next))
+			next = parse_cmd_delete(transaction, &input, next);
+		else if (skip_prefix(next, "verify ", &next))
+			next = parse_cmd_verify(transaction, &input, next);
+		else if (skip_prefix(next, "option ", &next))
+			next = parse_cmd_option(&input, next);
+		else
+			die("unknown command: %s", next);
+
+		next++;
+	}
+
+	strbuf_release(&input);
+}
+
+int cmd_update_ref(int argc, const char **argv, const char *prefix)
+{
+	const char *refname, *oldval;
+	struct object_id oid, oldoid;
+	int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0;
+	int create_reflog = 0;
+	struct option options[] = {
+		OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")),
+		OPT_BOOL('d', NULL, &delete, N_("delete the reference")),
+		OPT_BOOL( 0 , "no-deref", &no_deref,
+					N_("update <refname> not the one it points to")),
+		OPT_BOOL('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")),
+		OPT_BOOL( 0 , "stdin", &read_stdin, N_("read updates from stdin")),
+		OPT_BOOL( 0 , "create-reflog", &create_reflog, N_("create a reflog")),
+		OPT_END(),
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, options, git_update_ref_usage,
+			     0);
+	if (msg && !*msg)
+		die("Refusing to perform update with empty message.");
+
+	create_reflog_flag = create_reflog ? REF_FORCE_CREATE_REFLOG : 0;
+
+	if (no_deref) {
+		default_flags = REF_NO_DEREF;
+		update_flags = default_flags;
+	}
+
+	if (read_stdin) {
+		struct strbuf err = STRBUF_INIT;
+		struct ref_transaction *transaction;
+
+		transaction = ref_transaction_begin(&err);
+		if (!transaction)
+			die("%s", err.buf);
+		if (delete || argc > 0)
+			usage_with_options(git_update_ref_usage, options);
+		if (end_null)
+			line_termination = '\0';
+		update_refs_stdin(transaction);
+		if (ref_transaction_commit(transaction, &err))
+			die("%s", err.buf);
+		ref_transaction_free(transaction);
+		strbuf_release(&err);
+		return 0;
+	}
+
+	if (end_null)
+		usage_with_options(git_update_ref_usage, options);
+
+	if (delete) {
+		if (argc < 1 || argc > 2)
+			usage_with_options(git_update_ref_usage, options);
+		refname = argv[0];
+		oldval = argv[1];
+	} else {
+		const char *value;
+		if (argc < 2 || argc > 3)
+			usage_with_options(git_update_ref_usage, options);
+		refname = argv[0];
+		value = argv[1];
+		oldval = argv[2];
+		if (get_oid(value, &oid))
+			die("%s: not a valid SHA1", value);
+	}
+
+	if (oldval) {
+		if (!*oldval)
+			/*
+			 * The empty string implies that the reference
+			 * must not already exist:
+			 */
+			oidclr(&oldoid);
+		else if (get_oid(oldval, &oldoid))
+			die("%s: not a valid old SHA1", oldval);
+	}
+
+	if (delete)
+		/*
+		 * For purposes of backwards compatibility, we treat
+		 * NULL_SHA1 as "don't care" here:
+		 */
+		return delete_ref(msg, refname,
+				  (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL,
+				  default_flags);
+	else
+		return update_ref(msg, refname, &oid, oldval ? &oldoid : NULL,
+				  default_flags | create_reflog_flag,
+				  UPDATE_REFS_DIE_ON_ERR);
+}
diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c
new file mode 100644
index 0000000000..4321a34456
--- /dev/null
+++ b/builtin/update-server-info.c
@@ -0,0 +1,26 @@
+#include "cache.h"
+#include "config.h"
+#include "builtin.h"
+#include "parse-options.h"
+
+static const char * const update_server_info_usage[] = {
+	N_("git update-server-info [--force]"),
+	NULL
+};
+
+int cmd_update_server_info(int argc, const char **argv, const char *prefix)
+{
+	int force = 0;
+	struct option options[] = {
+		OPT__FORCE(&force, N_("update the info files from scratch"), 0),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, options,
+			     update_server_info_usage, 0);
+	if (argc > 0)
+		usage_with_options(update_server_info_usage, options);
+
+	return !!update_server_info(force);
+}
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
new file mode 100644
index 0000000000..018879737a
--- /dev/null
+++ b/builtin/upload-archive.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2006 Franck Bui-Huu
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "archive.h"
+#include "pkt-line.h"
+#include "sideband.h"
+#include "run-command.h"
+#include "argv-array.h"
+
+static const char upload_archive_usage[] =
+	"git upload-archive <repo>";
+
+static const char deadchild[] =
+"git upload-archive: archiver died with error";
+
+#define MAX_ARGS (64)
+
+int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
+{
+	struct argv_array sent_argv = ARGV_ARRAY_INIT;
+	const char *arg_cmd = "argument ";
+
+	if (argc != 2 || !strcmp(argv[1], "-h"))
+		usage(upload_archive_usage);
+
+	if (!enter_repo(argv[1], 0))
+		die("'%s' does not appear to be a git repository", argv[1]);
+
+	init_archivers();
+
+	/* put received options in sent_argv[] */
+	argv_array_push(&sent_argv, "git-upload-archive");
+	for (;;) {
+		char *buf = packet_read_line(0, NULL);
+		if (!buf)
+			break;	/* got a flush */
+		if (sent_argv.argc > MAX_ARGS)
+			die("Too many options (>%d)", MAX_ARGS - 1);
+
+		if (!starts_with(buf, arg_cmd))
+			die("'argument' token or flush expected");
+		argv_array_push(&sent_argv, buf + strlen(arg_cmd));
+	}
+
+	/* parse all options sent by the client */
+	return write_archive(sent_argv.argc, sent_argv.argv, prefix,
+			     the_repository, NULL, 1);
+}
+
+__attribute__((format (printf, 1, 2)))
+static void error_clnt(const char *fmt, ...)
+{
+	struct strbuf buf = STRBUF_INIT;
+	va_list params;
+
+	va_start(params, fmt);
+	strbuf_vaddf(&buf, fmt, params);
+	va_end(params);
+	send_sideband(1, 3, buf.buf, buf.len, LARGE_PACKET_MAX);
+	die("sent error to the client: %s", buf.buf);
+}
+
+static ssize_t process_input(int child_fd, int band)
+{
+	char buf[16384];
+	ssize_t sz = read(child_fd, buf, sizeof(buf));
+	if (sz < 0) {
+		if (errno != EAGAIN && errno != EINTR)
+			error_clnt("read error: %s\n", strerror(errno));
+		return sz;
+	}
+	send_sideband(1, band, buf, sz, LARGE_PACKET_MAX);
+	return sz;
+}
+
+int cmd_upload_archive(int argc, const char **argv, const char *prefix)
+{
+	struct child_process writer = { argv };
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(upload_archive_usage);
+
+	/*
+	 * Set up sideband subprocess.
+	 *
+	 * We (parent) monitor and read from child, sending its fd#1 and fd#2
+	 * multiplexed out to our fd#1.  If the child dies, we tell the other
+	 * end over channel #3.
+	 */
+	argv[0] = "upload-archive--writer";
+	writer.out = writer.err = -1;
+	writer.git_cmd = 1;
+	if (start_command(&writer)) {
+		int err = errno;
+		packet_write_fmt(1, "NACK unable to spawn subprocess\n");
+		die("upload-archive: %s", strerror(err));
+	}
+
+	packet_write_fmt(1, "ACK\n");
+	packet_flush(1);
+
+	while (1) {
+		struct pollfd pfd[2];
+
+		pfd[0].fd = writer.out;
+		pfd[0].events = POLLIN;
+		pfd[1].fd = writer.err;
+		pfd[1].events = POLLIN;
+		if (poll(pfd, 2, -1) < 0) {
+			if (errno != EINTR) {
+				error_errno("poll failed resuming");
+				sleep(1);
+			}
+			continue;
+		}
+		if (pfd[1].revents & POLLIN)
+			/* Status stream ready */
+			if (process_input(pfd[1].fd, 2))
+				continue;
+		if (pfd[0].revents & POLLIN)
+			/* Data stream ready */
+			if (process_input(pfd[0].fd, 1))
+				continue;
+
+		if (finish_command(&writer))
+			error_clnt("%s", deadchild);
+		packet_flush(1);
+		break;
+	}
+	return 0;
+}
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
new file mode 100644
index 0000000000..6da8fa2607
--- /dev/null
+++ b/builtin/upload-pack.c
@@ -0,0 +1,74 @@
+#include "cache.h"
+#include "builtin.h"
+#include "exec-cmd.h"
+#include "pkt-line.h"
+#include "parse-options.h"
+#include "protocol.h"
+#include "upload-pack.h"
+#include "serve.h"
+
+static const char * const upload_pack_usage[] = {
+	N_("git upload-pack [<options>] <dir>"),
+	NULL
+};
+
+int cmd_upload_pack(int argc, const char **argv, const char *prefix)
+{
+	const char *dir;
+	int strict = 0;
+	struct upload_pack_options opts = { 0 };
+	struct serve_options serve_opts = SERVE_OPTIONS_INIT;
+	struct option options[] = {
+		OPT_BOOL(0, "stateless-rpc", &opts.stateless_rpc,
+			 N_("quit after a single request/response exchange")),
+		OPT_BOOL(0, "advertise-refs", &opts.advertise_refs,
+			 N_("exit immediately after initial ref advertisement")),
+		OPT_BOOL(0, "strict", &strict,
+			 N_("do not try <directory>/.git/ if <directory> is no Git directory")),
+		OPT_INTEGER(0, "timeout", &opts.timeout,
+			    N_("interrupt transfer after <n> seconds of inactivity")),
+		OPT_END()
+	};
+
+	packet_trace_identity("upload-pack");
+	read_replace_refs = 0;
+
+	argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
+
+	if (argc != 1)
+		usage_with_options(upload_pack_usage, options);
+
+	if (opts.timeout)
+		opts.daemon_mode = 1;
+
+	setup_path();
+
+	dir = argv[0];
+
+	if (!enter_repo(dir, strict))
+		die("'%s' does not appear to be a git repository", dir);
+
+	switch (determine_protocol_version_server()) {
+	case protocol_v2:
+		serve_opts.advertise_capabilities = opts.advertise_refs;
+		serve_opts.stateless_rpc = opts.stateless_rpc;
+		serve(&serve_opts);
+		break;
+	case protocol_v1:
+		/*
+		 * v1 is just the original protocol with a version string,
+		 * so just fall through after writing the version string.
+		 */
+		if (opts.advertise_refs || !opts.stateless_rpc)
+			packet_write_fmt(1, "version 1\n");
+
+		/* fallthrough */
+	case protocol_v0:
+		upload_pack(&opts);
+		break;
+	case protocol_unknown_version:
+		BUG("unknown protocol version");
+	}
+
+	return 0;
+}
diff --git a/builtin/var.c b/builtin/var.c
new file mode 100644
index 0000000000..6c6f46b4ae
--- /dev/null
+++ b/builtin/var.c
@@ -0,0 +1,94 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Eric Biederman, 2005
+ */
+#include "builtin.h"
+#include "config.h"
+
+static const char var_usage[] = "git var (-l | <variable>)";
+
+static const char *editor(int flag)
+{
+	const char *pgm = git_editor();
+
+	if (!pgm && flag & IDENT_STRICT)
+		die("Terminal is dumb, but EDITOR unset");
+
+	return pgm;
+}
+
+static const char *pager(int flag)
+{
+	const char *pgm = git_pager(1);
+
+	if (!pgm)
+		pgm = "cat";
+	return pgm;
+}
+
+struct git_var {
+	const char *name;
+	const char *(*read)(int);
+};
+static struct git_var git_vars[] = {
+	{ "GIT_COMMITTER_IDENT", git_committer_info },
+	{ "GIT_AUTHOR_IDENT",   git_author_info },
+	{ "GIT_EDITOR", editor },
+	{ "GIT_PAGER", pager },
+	{ "", NULL },
+};
+
+static void list_vars(void)
+{
+	struct git_var *ptr;
+	const char *val;
+
+	for (ptr = git_vars; ptr->read; ptr++)
+		if ((val = ptr->read(0)))
+			printf("%s=%s\n", ptr->name, val);
+}
+
+static const char *read_var(const char *var)
+{
+	struct git_var *ptr;
+	const char *val;
+	val = NULL;
+	for (ptr = git_vars; ptr->read; ptr++) {
+		if (strcmp(var, ptr->name) == 0) {
+			val = ptr->read(IDENT_STRICT);
+			break;
+		}
+	}
+	return val;
+}
+
+static int show_config(const char *var, const char *value, void *cb)
+{
+	if (value)
+		printf("%s=%s\n", var, value);
+	else
+		printf("%s\n", var);
+	return git_default_config(var, value, cb);
+}
+
+int cmd_var(int argc, const char **argv, const char *prefix)
+{
+	const char *val = NULL;
+	if (argc != 2)
+		usage(var_usage);
+
+	if (strcmp(argv[1], "-l") == 0) {
+		git_config(show_config, NULL);
+		list_vars();
+		return 0;
+	}
+	git_config(git_default_config, NULL);
+	val = read_var(argv[1]);
+	if (!val)
+		usage(var_usage);
+
+	printf("%s\n", val);
+
+	return 0;
+}
diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c
new file mode 100644
index 0000000000..40c69a0bed
--- /dev/null
+++ b/builtin/verify-commit.c
@@ -0,0 +1,90 @@
+/*
+ * Builtin "git commit-commit"
+ *
+ * Copyright (c) 2014 Michael J Gruber <git@drmicha.warpmail.net>
+ *
+ * Based on git-verify-tag
+ */
+#include "cache.h"
+#include "config.h"
+#include "builtin.h"
+#include "object-store.h"
+#include "repository.h"
+#include "commit.h"
+#include "run-command.h"
+#include "parse-options.h"
+#include "gpg-interface.h"
+
+static const char * const verify_commit_usage[] = {
+		N_("git verify-commit [-v | --verbose] <commit>..."),
+		NULL
+};
+
+static int run_gpg_verify(struct commit *commit, unsigned flags)
+{
+	struct signature_check signature_check;
+	int ret;
+
+	memset(&signature_check, 0, sizeof(signature_check));
+
+	ret = check_commit_signature(commit, &signature_check);
+	print_signature_buffer(&signature_check, flags);
+
+	signature_check_clear(&signature_check);
+	return ret;
+}
+
+static int verify_commit(const char *name, unsigned flags)
+{
+	struct object_id oid;
+	struct object *obj;
+
+	if (get_oid(name, &oid))
+		return error("commit '%s' not found.", name);
+
+	obj = parse_object(the_repository, &oid);
+	if (!obj)
+		return error("%s: unable to read file.", name);
+	if (obj->type != OBJ_COMMIT)
+		return error("%s: cannot verify a non-commit object of type %s.",
+				name, type_name(obj->type));
+
+	return run_gpg_verify((struct commit *)obj, flags);
+}
+
+static int git_verify_commit_config(const char *var, const char *value, void *cb)
+{
+	int status = git_gpg_config(var, value, cb);
+	if (status)
+		return status;
+	return git_default_config(var, value, cb);
+}
+
+int cmd_verify_commit(int argc, const char **argv, const char *prefix)
+{
+	int i = 1, verbose = 0, had_error = 0;
+	unsigned flags = 0;
+	const struct option verify_commit_options[] = {
+		OPT__VERBOSE(&verbose, N_("print commit contents")),
+		OPT_BIT(0, "raw", &flags, N_("print raw gpg status output"), GPG_VERIFY_RAW),
+		OPT_END()
+	};
+
+	git_config(git_verify_commit_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, verify_commit_options,
+			     verify_commit_usage, PARSE_OPT_KEEP_ARGV0);
+	if (argc <= i)
+		usage_with_options(verify_commit_usage, verify_commit_options);
+
+	if (verbose)
+		flags |= GPG_VERIFY_VERBOSE;
+
+	/* sometimes the program was terminated because this signal
+	 * was received in the process of writing the gpg input: */
+	signal(SIGPIPE, SIG_IGN);
+	while (i < argc)
+		if (verify_commit(argv[i++], flags))
+			had_error = 1;
+	return had_error;
+}
diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c
new file mode 100644
index 0000000000..c2a1a5c504
--- /dev/null
+++ b/builtin/verify-pack.c
@@ -0,0 +1,83 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "run-command.h"
+#include "parse-options.h"
+
+#define VERIFY_PACK_VERBOSE 01
+#define VERIFY_PACK_STAT_ONLY 02
+
+static int verify_one_pack(const char *path, unsigned int flags)
+{
+	struct child_process index_pack = CHILD_PROCESS_INIT;
+	const char *argv[] = {"index-pack", NULL, NULL, NULL };
+	struct strbuf arg = STRBUF_INIT;
+	int verbose = flags & VERIFY_PACK_VERBOSE;
+	int stat_only = flags & VERIFY_PACK_STAT_ONLY;
+	int err;
+
+	if (stat_only)
+		argv[1] = "--verify-stat-only";
+	else if (verbose)
+		argv[1] = "--verify-stat";
+	else
+		argv[1] = "--verify";
+
+	/*
+	 * In addition to "foo.pack" we accept "foo.idx" and "foo";
+	 * normalize these forms to "foo.pack" for "index-pack --verify".
+	 */
+	strbuf_addstr(&arg, path);
+	if (strbuf_strip_suffix(&arg, ".idx") ||
+	    !ends_with(arg.buf, ".pack"))
+		strbuf_addstr(&arg, ".pack");
+	argv[2] = arg.buf;
+
+	index_pack.argv = argv;
+	index_pack.git_cmd = 1;
+
+	err = run_command(&index_pack);
+
+	if (verbose || stat_only) {
+		if (err)
+			printf("%s: bad\n", arg.buf);
+		else {
+			if (!stat_only)
+				printf("%s: ok\n", arg.buf);
+		}
+	}
+	strbuf_release(&arg);
+
+	return err;
+}
+
+static const char * const verify_pack_usage[] = {
+	N_("git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."),
+	NULL
+};
+
+int cmd_verify_pack(int argc, const char **argv, const char *prefix)
+{
+	int err = 0;
+	unsigned int flags = 0;
+	int i;
+	const struct option verify_pack_options[] = {
+		OPT_BIT('v', "verbose", &flags, N_("verbose"),
+			VERIFY_PACK_VERBOSE),
+		OPT_BIT('s', "stat-only", &flags, N_("show statistics only"),
+			VERIFY_PACK_STAT_ONLY),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, verify_pack_options,
+			     verify_pack_usage, 0);
+	if (argc < 1)
+		usage_with_options(verify_pack_usage, verify_pack_options);
+	for (i = 0; i < argc; i++) {
+		if (verify_one_pack(argv[i], flags))
+			err = 1;
+	}
+
+	return err;
+}
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
new file mode 100644
index 0000000000..f45136a06b
--- /dev/null
+++ b/builtin/verify-tag.c
@@ -0,0 +1,77 @@
+/*
+ * Builtin "git verify-tag"
+ *
+ * Copyright (c) 2007 Carlos Rica <jasampler@gmail.com>
+ *
+ * Based on git-verify-tag.sh
+ */
+#include "cache.h"
+#include "config.h"
+#include "builtin.h"
+#include "tag.h"
+#include "run-command.h"
+#include "parse-options.h"
+#include "gpg-interface.h"
+#include "ref-filter.h"
+
+static const char * const verify_tag_usage[] = {
+		N_("git verify-tag [-v | --verbose] [--format=<format>] <tag>..."),
+		NULL
+};
+
+static int git_verify_tag_config(const char *var, const char *value, void *cb)
+{
+	int status = git_gpg_config(var, value, cb);
+	if (status)
+		return status;
+	return git_default_config(var, value, cb);
+}
+
+int cmd_verify_tag(int argc, const char **argv, const char *prefix)
+{
+	int i = 1, verbose = 0, had_error = 0;
+	unsigned flags = 0;
+	struct ref_format format = REF_FORMAT_INIT;
+	const struct option verify_tag_options[] = {
+		OPT__VERBOSE(&verbose, N_("print tag contents")),
+		OPT_BIT(0, "raw", &flags, N_("print raw gpg status output"), GPG_VERIFY_RAW),
+		OPT_STRING(0, "format", &format.format, N_("format"), N_("format to use for the output")),
+		OPT_END()
+	};
+
+	git_config(git_verify_tag_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, verify_tag_options,
+			     verify_tag_usage, PARSE_OPT_KEEP_ARGV0);
+	if (argc <= i)
+		usage_with_options(verify_tag_usage, verify_tag_options);
+
+	if (verbose)
+		flags |= GPG_VERIFY_VERBOSE;
+
+	if (format.format) {
+		if (verify_ref_format(&format))
+			usage_with_options(verify_tag_usage,
+					   verify_tag_options);
+		flags |= GPG_VERIFY_OMIT_STATUS;
+	}
+
+	while (i < argc) {
+		struct object_id oid;
+		const char *name = argv[i++];
+
+		if (get_oid(name, &oid)) {
+			had_error = !!error("tag '%s' not found.", name);
+			continue;
+		}
+
+		if (gpg_verify_tag(&oid, name, flags)) {
+			had_error = 1;
+			continue;
+		}
+
+		if (format.format)
+			pretty_print_ref(name, &oid, &format);
+	}
+	return had_error;
+}
diff --git a/builtin/worktree.c b/builtin/worktree.c
new file mode 100644
index 0000000000..a5bb02b207
--- /dev/null
+++ b/builtin/worktree.c
@@ -0,0 +1,987 @@
+#include "cache.h"
+#include "checkout.h"
+#include "config.h"
+#include "builtin.h"
+#include "dir.h"
+#include "parse-options.h"
+#include "argv-array.h"
+#include "branch.h"
+#include "refs.h"
+#include "run-command.h"
+#include "sigchain.h"
+#include "submodule.h"
+#include "refs.h"
+#include "utf8.h"
+#include "worktree.h"
+
+static const char * const worktree_usage[] = {
+	N_("git worktree add [<options>] <path> [<commit-ish>]"),
+	N_("git worktree list [<options>]"),
+	N_("git worktree lock [<options>] <path>"),
+	N_("git worktree move <worktree> <new-path>"),
+	N_("git worktree prune [<options>]"),
+	N_("git worktree remove [<options>] <worktree>"),
+	N_("git worktree unlock <path>"),
+	NULL
+};
+
+struct add_opts {
+	int force;
+	int detach;
+	int quiet;
+	int checkout;
+	int keep_locked;
+};
+
+static int show_only;
+static int verbose;
+static int guess_remote;
+static timestamp_t expire;
+
+static int git_worktree_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "worktree.guessremote")) {
+		guess_remote = git_config_bool(var, value);
+		return 0;
+	}
+
+	return git_default_config(var, value, cb);
+}
+
+static int delete_git_dir(const char *id)
+{
+	struct strbuf sb = STRBUF_INIT;
+	int ret;
+
+	strbuf_addstr(&sb, git_common_path("worktrees/%s", id));
+	ret = remove_dir_recursively(&sb, 0);
+	if (ret < 0 && errno == ENOTDIR)
+		ret = unlink(sb.buf);
+	if (ret)
+		error_errno(_("failed to delete '%s'"), sb.buf);
+	strbuf_release(&sb);
+	return ret;
+}
+
+static void delete_worktrees_dir_if_empty(void)
+{
+	rmdir(git_path("worktrees")); /* ignore failed removal */
+}
+
+static int prune_worktree(const char *id, struct strbuf *reason)
+{
+	struct stat st;
+	char *path;
+	int fd;
+	size_t len;
+	ssize_t read_result;
+
+	if (!is_directory(git_path("worktrees/%s", id))) {
+		strbuf_addf(reason, _("Removing worktrees/%s: not a valid directory"), id);
+		return 1;
+	}
+	if (file_exists(git_path("worktrees/%s/locked", id)))
+		return 0;
+	if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
+		strbuf_addf(reason, _("Removing worktrees/%s: gitdir file does not exist"), id);
+		return 1;
+	}
+	fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
+	if (fd < 0) {
+		strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"),
+			    id, strerror(errno));
+		return 1;
+	}
+	len = xsize_t(st.st_size);
+	path = xmallocz(len);
+
+	read_result = read_in_full(fd, path, len);
+	if (read_result < 0) {
+		strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"),
+			    id, strerror(errno));
+		close(fd);
+		free(path);
+		return 1;
+	}
+	close(fd);
+
+	if (read_result != len) {
+		strbuf_addf(reason,
+			    _("Removing worktrees/%s: short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"),
+			    id, (uintmax_t)len, (uintmax_t)read_result);
+		free(path);
+		return 1;
+	}
+	while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
+		len--;
+	if (!len) {
+		strbuf_addf(reason, _("Removing worktrees/%s: invalid gitdir file"), id);
+		free(path);
+		return 1;
+	}
+	path[len] = '\0';
+	if (!file_exists(path)) {
+		free(path);
+		if (stat(git_path("worktrees/%s/index", id), &st) ||
+		    st.st_mtime <= expire) {
+			strbuf_addf(reason, _("Removing worktrees/%s: gitdir file points to non-existent location"), id);
+			return 1;
+		} else {
+			return 0;
+		}
+	}
+	free(path);
+	return 0;
+}
+
+static void prune_worktrees(void)
+{
+	struct strbuf reason = STRBUF_INIT;
+	DIR *dir = opendir(git_path("worktrees"));
+	struct dirent *d;
+	if (!dir)
+		return;
+	while ((d = readdir(dir)) != NULL) {
+		if (is_dot_or_dotdot(d->d_name))
+			continue;
+		strbuf_reset(&reason);
+		if (!prune_worktree(d->d_name, &reason))
+			continue;
+		if (show_only || verbose)
+			printf("%s\n", reason.buf);
+		if (show_only)
+			continue;
+		delete_git_dir(d->d_name);
+	}
+	closedir(dir);
+	if (!show_only)
+		delete_worktrees_dir_if_empty();
+	strbuf_release(&reason);
+}
+
+static int prune(int ac, const char **av, const char *prefix)
+{
+	struct option options[] = {
+		OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
+		OPT__VERBOSE(&verbose, N_("report pruned working trees")),
+		OPT_EXPIRY_DATE(0, "expire", &expire,
+				N_("expire working trees older than <time>")),
+		OPT_END()
+	};
+
+	expire = TIME_MAX;
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac)
+		usage_with_options(worktree_usage, options);
+	prune_worktrees();
+	return 0;
+}
+
+static char *junk_work_tree;
+static char *junk_git_dir;
+static int is_junk;
+static pid_t junk_pid;
+
+static void remove_junk(void)
+{
+	struct strbuf sb = STRBUF_INIT;
+	if (!is_junk || getpid() != junk_pid)
+		return;
+	if (junk_git_dir) {
+		strbuf_addstr(&sb, junk_git_dir);
+		remove_dir_recursively(&sb, 0);
+		strbuf_reset(&sb);
+	}
+	if (junk_work_tree) {
+		strbuf_addstr(&sb, junk_work_tree);
+		remove_dir_recursively(&sb, 0);
+	}
+	strbuf_release(&sb);
+}
+
+static void remove_junk_on_signal(int signo)
+{
+	remove_junk();
+	sigchain_pop(signo);
+	raise(signo);
+}
+
+static const char *worktree_basename(const char *path, int *olen)
+{
+	const char *name;
+	int len;
+
+	len = strlen(path);
+	while (len && is_dir_sep(path[len - 1]))
+		len--;
+
+	for (name = path + len - 1; name > path; name--)
+		if (is_dir_sep(*name)) {
+			name++;
+			break;
+		}
+
+	*olen = len;
+	return name;
+}
+
+static void validate_worktree_add(const char *path, const struct add_opts *opts)
+{
+	struct worktree **worktrees;
+	struct worktree *wt;
+	int locked;
+
+	if (file_exists(path) && !is_empty_dir(path))
+		die(_("'%s' already exists"), path);
+
+	worktrees = get_worktrees(0);
+	/*
+	 * find_worktree()'s suffix matching may undesirably find the main
+	 * rather than a linked worktree (for instance, when the basenames
+	 * of the main worktree and the one being created are the same).
+	 * We're only interested in linked worktrees, so skip the main
+	 * worktree with +1.
+	 */
+	wt = find_worktree(worktrees + 1, NULL, path);
+	if (!wt)
+		goto done;
+
+	locked = !!worktree_lock_reason(wt);
+	if ((!locked && opts->force) || (locked && opts->force > 1)) {
+		if (delete_git_dir(wt->id))
+		    die(_("unable to re-add worktree '%s'"), path);
+		goto done;
+	}
+
+	if (locked)
+		die(_("'%s' is a missing but locked worktree;\nuse 'add -f -f' to override, or 'unlock' and 'prune' or 'remove' to clear"), path);
+	else
+		die(_("'%s' is a missing but already registered worktree;\nuse 'add -f' to override, or 'prune' or 'remove' to clear"), path);
+
+done:
+	free_worktrees(worktrees);
+}
+
+static int add_worktree(const char *path, const char *refname,
+			const struct add_opts *opts)
+{
+	struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
+	struct strbuf sb = STRBUF_INIT;
+	const char *name;
+	struct child_process cp = CHILD_PROCESS_INIT;
+	struct argv_array child_env = ARGV_ARRAY_INIT;
+	unsigned int counter = 0;
+	int len, ret;
+	struct strbuf symref = STRBUF_INIT;
+	struct commit *commit = NULL;
+	int is_branch = 0;
+	struct strbuf sb_name = STRBUF_INIT;
+
+	validate_worktree_add(path, opts);
+
+	/* is 'refname' a branch or commit? */
+	if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
+	    ref_exists(symref.buf)) {
+		is_branch = 1;
+		if (!opts->force)
+			die_if_checked_out(symref.buf, 0);
+	}
+	commit = lookup_commit_reference_by_name(refname);
+	if (!commit)
+		die(_("invalid reference: %s"), refname);
+
+	name = worktree_basename(path, &len);
+	strbuf_add(&sb, name, path + len - name);
+	sanitize_refname_component(sb.buf, &sb_name);
+	if (!sb_name.len)
+		BUG("How come '%s' becomes empty after sanitization?", sb.buf);
+	strbuf_reset(&sb);
+	name = sb_name.buf;
+	git_path_buf(&sb_repo, "worktrees/%s", name);
+	len = sb_repo.len;
+	if (safe_create_leading_directories_const(sb_repo.buf))
+		die_errno(_("could not create leading directories of '%s'"),
+			  sb_repo.buf);
+
+	while (mkdir(sb_repo.buf, 0777)) {
+		counter++;
+		if ((errno != EEXIST) || !counter /* overflow */)
+			die_errno(_("could not create directory of '%s'"),
+				  sb_repo.buf);
+		strbuf_setlen(&sb_repo, len);
+		strbuf_addf(&sb_repo, "%d", counter);
+	}
+	name = strrchr(sb_repo.buf, '/') + 1;
+
+	junk_pid = getpid();
+	atexit(remove_junk);
+	sigchain_push_common(remove_junk_on_signal);
+
+	junk_git_dir = xstrdup(sb_repo.buf);
+	is_junk = 1;
+
+	/*
+	 * lock the incomplete repo so prune won't delete it, unlock
+	 * after the preparation is over.
+	 */
+	strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+	if (!opts->keep_locked)
+		write_file(sb.buf, "initializing");
+	else
+		write_file(sb.buf, "added with --lock");
+
+	strbuf_addf(&sb_git, "%s/.git", path);
+	if (safe_create_leading_directories_const(sb_git.buf))
+		die_errno(_("could not create leading directories of '%s'"),
+			  sb_git.buf);
+	junk_work_tree = xstrdup(path);
+
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
+	write_file(sb.buf, "%s", real_path(sb_git.buf));
+	write_file(sb_git.buf, "gitdir: %s/worktrees/%s",
+		   real_path(get_git_common_dir()), name);
+	/*
+	 * This is to keep resolve_ref() happy. We need a valid HEAD
+	 * or is_git_directory() will reject the directory. Any value which
+	 * looks like an object ID will do since it will be immediately
+	 * replaced by the symbolic-ref or update-ref invocation in the new
+	 * worktree.
+	 */
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
+	write_file(sb.buf, "%s", sha1_to_hex(null_sha1));
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
+	write_file(sb.buf, "../..");
+
+	argv_array_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
+	argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
+	cp.git_cmd = 1;
+
+	if (!is_branch)
+		argv_array_pushl(&cp.args, "update-ref", "HEAD",
+				 oid_to_hex(&commit->object.oid), NULL);
+	else {
+		argv_array_pushl(&cp.args, "symbolic-ref", "HEAD",
+				 symref.buf, NULL);
+		if (opts->quiet)
+			argv_array_push(&cp.args, "--quiet");
+	}
+
+	cp.env = child_env.argv;
+	ret = run_command(&cp);
+	if (ret)
+		goto done;
+
+	if (opts->checkout) {
+		cp.argv = NULL;
+		argv_array_clear(&cp.args);
+		argv_array_pushl(&cp.args, "reset", "--hard", NULL);
+		if (opts->quiet)
+			argv_array_push(&cp.args, "--quiet");
+		cp.env = child_env.argv;
+		ret = run_command(&cp);
+		if (ret)
+			goto done;
+	}
+
+	is_junk = 0;
+	FREE_AND_NULL(junk_work_tree);
+	FREE_AND_NULL(junk_git_dir);
+
+done:
+	if (ret || !opts->keep_locked) {
+		strbuf_reset(&sb);
+		strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+		unlink_or_warn(sb.buf);
+	}
+
+	/*
+	 * Hook failure does not warrant worktree deletion, so run hook after
+	 * is_junk is cleared, but do return appropriate code when hook fails.
+	 */
+	if (!ret && opts->checkout) {
+		const char *hook = find_hook("post-checkout");
+		if (hook) {
+			const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL };
+			cp.git_cmd = 0;
+			cp.no_stdin = 1;
+			cp.stdout_to_stderr = 1;
+			cp.dir = path;
+			cp.env = env;
+			cp.argv = NULL;
+			cp.trace2_hook_name = "post-checkout";
+			argv_array_pushl(&cp.args, absolute_path(hook),
+					 oid_to_hex(&null_oid),
+					 oid_to_hex(&commit->object.oid),
+					 "1", NULL);
+			ret = run_command(&cp);
+		}
+	}
+
+	argv_array_clear(&child_env);
+	strbuf_release(&sb);
+	strbuf_release(&symref);
+	strbuf_release(&sb_repo);
+	strbuf_release(&sb_git);
+	strbuf_release(&sb_name);
+	return ret;
+}
+
+static void print_preparing_worktree_line(int detach,
+					  const char *branch,
+					  const char *new_branch,
+					  int force_new_branch)
+{
+	if (force_new_branch) {
+		struct commit *commit = lookup_commit_reference_by_name(new_branch);
+		if (!commit)
+			printf_ln(_("Preparing worktree (new branch '%s')"), new_branch);
+		else
+			printf_ln(_("Preparing worktree (resetting branch '%s'; was at %s)"),
+				  new_branch,
+				  find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+	} else if (new_branch) {
+		printf_ln(_("Preparing worktree (new branch '%s')"), new_branch);
+	} else {
+		struct strbuf s = STRBUF_INIT;
+		if (!detach && !strbuf_check_branch_ref(&s, branch) &&
+		    ref_exists(s.buf))
+			printf_ln(_("Preparing worktree (checking out '%s')"),
+				  branch);
+		else {
+			struct commit *commit = lookup_commit_reference_by_name(branch);
+			if (!commit)
+				die(_("invalid reference: %s"), branch);
+			printf_ln(_("Preparing worktree (detached HEAD %s)"),
+				  find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+		}
+		strbuf_release(&s);
+	}
+}
+
+static const char *dwim_branch(const char *path, const char **new_branch)
+{
+	int n;
+	const char *s = worktree_basename(path, &n);
+	const char *branchname = xstrndup(s, n);
+	struct strbuf ref = STRBUF_INIT;
+
+	UNLEAK(branchname);
+	if (!strbuf_check_branch_ref(&ref, branchname) &&
+	    ref_exists(ref.buf)) {
+		strbuf_release(&ref);
+		return branchname;
+	}
+
+	*new_branch = branchname;
+	if (guess_remote) {
+		struct object_id oid;
+		const char *remote =
+			unique_tracking_name(*new_branch, &oid, NULL);
+		return remote;
+	}
+	return NULL;
+}
+
+static int add(int ac, const char **av, const char *prefix)
+{
+	struct add_opts opts;
+	const char *new_branch_force = NULL;
+	char *path;
+	const char *branch;
+	const char *new_branch = NULL;
+	const char *opt_track = NULL;
+	struct option options[] = {
+		OPT__FORCE(&opts.force,
+			   N_("checkout <branch> even if already checked out in other worktree"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_STRING('b', NULL, &new_branch, N_("branch"),
+			   N_("create a new branch")),
+		OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
+			   N_("create or reset a branch")),
+		OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
+		OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
+		OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")),
+		OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
+		OPT_PASSTHRU(0, "track", &opt_track, NULL,
+			     N_("set up tracking mode (see git-branch(1))"),
+			     PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
+		OPT_BOOL(0, "guess-remote", &guess_remote,
+			 N_("try to match the new branch name with a remote-tracking branch")),
+		OPT_END()
+	};
+
+	memset(&opts, 0, sizeof(opts));
+	opts.checkout = 1;
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
+		die(_("-b, -B, and --detach are mutually exclusive"));
+	if (ac < 1 || ac > 2)
+		usage_with_options(worktree_usage, options);
+
+	path = prefix_filename(prefix, av[0]);
+	branch = ac < 2 ? "HEAD" : av[1];
+
+	if (!strcmp(branch, "-"))
+		branch = "@{-1}";
+
+	if (new_branch_force) {
+		struct strbuf symref = STRBUF_INIT;
+
+		new_branch = new_branch_force;
+
+		if (!opts.force &&
+		    !strbuf_check_branch_ref(&symref, new_branch) &&
+		    ref_exists(symref.buf))
+			die_if_checked_out(symref.buf, 0);
+		strbuf_release(&symref);
+	}
+
+	if (ac < 2 && !new_branch && !opts.detach) {
+		const char *s = dwim_branch(path, &new_branch);
+		if (s)
+			branch = s;
+	}
+
+	if (ac == 2 && !new_branch && !opts.detach) {
+		struct object_id oid;
+		struct commit *commit;
+		const char *remote;
+
+		commit = lookup_commit_reference_by_name(branch);
+		if (!commit) {
+			remote = unique_tracking_name(branch, &oid, NULL);
+			if (remote) {
+				new_branch = branch;
+				branch = remote;
+			}
+		}
+	}
+	if (!opts.quiet)
+		print_preparing_worktree_line(opts.detach, branch, new_branch, !!new_branch_force);
+
+	if (new_branch) {
+		struct child_process cp = CHILD_PROCESS_INIT;
+		cp.git_cmd = 1;
+		argv_array_push(&cp.args, "branch");
+		if (new_branch_force)
+			argv_array_push(&cp.args, "--force");
+		if (opts.quiet)
+			argv_array_push(&cp.args, "--quiet");
+		argv_array_push(&cp.args, new_branch);
+		argv_array_push(&cp.args, branch);
+		if (opt_track)
+			argv_array_push(&cp.args, opt_track);
+		if (run_command(&cp))
+			return -1;
+		branch = new_branch;
+	} else if (opt_track) {
+		die(_("--[no-]track can only be used if a new branch is created"));
+	}
+
+	UNLEAK(path);
+	UNLEAK(opts);
+	return add_worktree(path, branch, &opts);
+}
+
+static void show_worktree_porcelain(struct worktree *wt)
+{
+	printf("worktree %s\n", wt->path);
+	if (wt->is_bare)
+		printf("bare\n");
+	else {
+		printf("HEAD %s\n", oid_to_hex(&wt->head_oid));
+		if (wt->is_detached)
+			printf("detached\n");
+		else if (wt->head_ref)
+			printf("branch %s\n", wt->head_ref);
+	}
+	printf("\n");
+}
+
+static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
+{
+	struct strbuf sb = STRBUF_INIT;
+	int cur_path_len = strlen(wt->path);
+	int path_adj = cur_path_len - utf8_strwidth(wt->path);
+
+	strbuf_addf(&sb, "%-*s ", 1 + path_maxlen + path_adj, wt->path);
+	if (wt->is_bare)
+		strbuf_addstr(&sb, "(bare)");
+	else {
+		strbuf_addf(&sb, "%-*s ", abbrev_len,
+				find_unique_abbrev(&wt->head_oid, DEFAULT_ABBREV));
+		if (wt->is_detached)
+			strbuf_addstr(&sb, "(detached HEAD)");
+		else if (wt->head_ref) {
+			char *ref = shorten_unambiguous_ref(wt->head_ref, 0);
+			strbuf_addf(&sb, "[%s]", ref);
+			free(ref);
+		} else
+			strbuf_addstr(&sb, "(error)");
+	}
+	printf("%s\n", sb.buf);
+
+	strbuf_release(&sb);
+}
+
+static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen)
+{
+	int i;
+
+	for (i = 0; wt[i]; i++) {
+		int sha1_len;
+		int path_len = strlen(wt[i]->path);
+
+		if (path_len > *maxlen)
+			*maxlen = path_len;
+		sha1_len = strlen(find_unique_abbrev(&wt[i]->head_oid, *abbrev));
+		if (sha1_len > *abbrev)
+			*abbrev = sha1_len;
+	}
+}
+
+static int list(int ac, const char **av, const char *prefix)
+{
+	int porcelain = 0;
+
+	struct option options[] = {
+		OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
+		OPT_END()
+	};
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac)
+		usage_with_options(worktree_usage, options);
+	else {
+		struct worktree **worktrees = get_worktrees(GWT_SORT_LINKED);
+		int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
+
+		if (!porcelain)
+			measure_widths(worktrees, &abbrev, &path_maxlen);
+
+		for (i = 0; worktrees[i]; i++) {
+			if (porcelain)
+				show_worktree_porcelain(worktrees[i]);
+			else
+				show_worktree(worktrees[i], path_maxlen, abbrev);
+		}
+		free_worktrees(worktrees);
+	}
+	return 0;
+}
+
+static int lock_worktree(int ac, const char **av, const char *prefix)
+{
+	const char *reason = "", *old_reason;
+	struct option options[] = {
+		OPT_STRING(0, "reason", &reason, N_("string"),
+			   N_("reason for locking")),
+		OPT_END()
+	};
+	struct worktree **worktrees, *wt;
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac != 1)
+		usage_with_options(worktree_usage, options);
+
+	worktrees = get_worktrees(0);
+	wt = find_worktree(worktrees, prefix, av[0]);
+	if (!wt)
+		die(_("'%s' is not a working tree"), av[0]);
+	if (is_main_worktree(wt))
+		die(_("The main working tree cannot be locked or unlocked"));
+
+	old_reason = worktree_lock_reason(wt);
+	if (old_reason) {
+		if (*old_reason)
+			die(_("'%s' is already locked, reason: %s"),
+			    av[0], old_reason);
+		die(_("'%s' is already locked"), av[0]);
+	}
+
+	write_file(git_common_path("worktrees/%s/locked", wt->id),
+		   "%s", reason);
+	free_worktrees(worktrees);
+	return 0;
+}
+
+static int unlock_worktree(int ac, const char **av, const char *prefix)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+	struct worktree **worktrees, *wt;
+	int ret;
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac != 1)
+		usage_with_options(worktree_usage, options);
+
+	worktrees = get_worktrees(0);
+	wt = find_worktree(worktrees, prefix, av[0]);
+	if (!wt)
+		die(_("'%s' is not a working tree"), av[0]);
+	if (is_main_worktree(wt))
+		die(_("The main working tree cannot be locked or unlocked"));
+	if (!worktree_lock_reason(wt))
+		die(_("'%s' is not locked"), av[0]);
+	ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
+	free_worktrees(worktrees);
+	return ret;
+}
+
+static void validate_no_submodules(const struct worktree *wt)
+{
+	struct index_state istate = { NULL };
+	struct strbuf path = STRBUF_INIT;
+	int i, found_submodules = 0;
+
+	if (is_directory(worktree_git_path(wt, "modules"))) {
+		/*
+		 * There could be false positives, e.g. the "modules"
+		 * directory exists but is empty. But it's a rare case and
+		 * this simpler check is probably good enough for now.
+		 */
+		found_submodules = 1;
+	} else if (read_index_from(&istate, worktree_git_path(wt, "index"),
+				   get_worktree_git_dir(wt)) > 0) {
+		for (i = 0; i < istate.cache_nr; i++) {
+			struct cache_entry *ce = istate.cache[i];
+			int err;
+
+			if (!S_ISGITLINK(ce->ce_mode))
+				continue;
+
+			strbuf_reset(&path);
+			strbuf_addf(&path, "%s/%s", wt->path, ce->name);
+			if (!is_submodule_populated_gently(path.buf, &err))
+				continue;
+
+			found_submodules = 1;
+			break;
+		}
+	}
+	discard_index(&istate);
+	strbuf_release(&path);
+
+	if (found_submodules)
+		die(_("working trees containing submodules cannot be moved or removed"));
+}
+
+static int move_worktree(int ac, const char **av, const char *prefix)
+{
+	int force = 0;
+	struct option options[] = {
+		OPT__FORCE(&force,
+			 N_("force move even if worktree is dirty or locked"),
+			 PARSE_OPT_NOCOMPLETE),
+		OPT_END()
+	};
+	struct worktree **worktrees, *wt;
+	struct strbuf dst = STRBUF_INIT;
+	struct strbuf errmsg = STRBUF_INIT;
+	const char *reason = NULL;
+	char *path;
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac != 2)
+		usage_with_options(worktree_usage, options);
+
+	path = prefix_filename(prefix, av[1]);
+	strbuf_addstr(&dst, path);
+	free(path);
+
+	worktrees = get_worktrees(0);
+	wt = find_worktree(worktrees, prefix, av[0]);
+	if (!wt)
+		die(_("'%s' is not a working tree"), av[0]);
+	if (is_main_worktree(wt))
+		die(_("'%s' is a main working tree"), av[0]);
+	if (is_directory(dst.buf)) {
+		const char *sep = find_last_dir_sep(wt->path);
+
+		if (!sep)
+			die(_("could not figure out destination name from '%s'"),
+			    wt->path);
+		strbuf_trim_trailing_dir_sep(&dst);
+		strbuf_addstr(&dst, sep);
+	}
+	if (file_exists(dst.buf))
+		die(_("target '%s' already exists"), dst.buf);
+
+	validate_no_submodules(wt);
+
+	if (force < 2)
+		reason = worktree_lock_reason(wt);
+	if (reason) {
+		if (*reason)
+			die(_("cannot move a locked working tree, lock reason: %s\nuse 'move -f -f' to override or unlock first"),
+			    reason);
+		die(_("cannot move a locked working tree;\nuse 'move -f -f' to override or unlock first"));
+	}
+	if (validate_worktree(wt, &errmsg, 0))
+		die(_("validation failed, cannot move working tree: %s"),
+		    errmsg.buf);
+	strbuf_release(&errmsg);
+
+	if (rename(wt->path, dst.buf) == -1)
+		die_errno(_("failed to move '%s' to '%s'"), wt->path, dst.buf);
+
+	update_worktree_location(wt, dst.buf);
+
+	strbuf_release(&dst);
+	free_worktrees(worktrees);
+	return 0;
+}
+
+/*
+ * Note, "git status --porcelain" is used to determine if it's safe to
+ * delete a whole worktree. "git status" does not ignore user
+ * configuration, so if a normal "git status" shows "clean" for the
+ * user, then it's ok to remove it.
+ *
+ * This assumption may be a bad one. We may want to ignore
+ * (potentially bad) user settings and only delete a worktree when
+ * it's absolutely safe to do so from _our_ point of view because we
+ * know better.
+ */
+static void check_clean_worktree(struct worktree *wt,
+				 const char *original_path)
+{
+	struct argv_array child_env = ARGV_ARRAY_INIT;
+	struct child_process cp;
+	char buf[1];
+	int ret;
+
+	/*
+	 * Until we sort this out, all submodules are "dirty" and
+	 * will abort this function.
+	 */
+	validate_no_submodules(wt);
+
+	argv_array_pushf(&child_env, "%s=%s/.git",
+			 GIT_DIR_ENVIRONMENT, wt->path);
+	argv_array_pushf(&child_env, "%s=%s",
+			 GIT_WORK_TREE_ENVIRONMENT, wt->path);
+	memset(&cp, 0, sizeof(cp));
+	argv_array_pushl(&cp.args, "status",
+			 "--porcelain", "--ignore-submodules=none",
+			 NULL);
+	cp.env = child_env.argv;
+	cp.git_cmd = 1;
+	cp.dir = wt->path;
+	cp.out = -1;
+	ret = start_command(&cp);
+	if (ret)
+		die_errno(_("failed to run 'git status' on '%s'"),
+			  original_path);
+	ret = xread(cp.out, buf, sizeof(buf));
+	if (ret)
+		die(_("'%s' is dirty, use --force to delete it"),
+		    original_path);
+	close(cp.out);
+	ret = finish_command(&cp);
+	if (ret)
+		die_errno(_("failed to run 'git status' on '%s', code %d"),
+			  original_path, ret);
+}
+
+static int delete_git_work_tree(struct worktree *wt)
+{
+	struct strbuf sb = STRBUF_INIT;
+	int ret = 0;
+
+	strbuf_addstr(&sb, wt->path);
+	if (remove_dir_recursively(&sb, 0)) {
+		error_errno(_("failed to delete '%s'"), sb.buf);
+		ret = -1;
+	}
+	strbuf_release(&sb);
+	return ret;
+}
+
+static int remove_worktree(int ac, const char **av, const char *prefix)
+{
+	int force = 0;
+	struct option options[] = {
+		OPT__FORCE(&force,
+			 N_("force removal even if worktree is dirty or locked"),
+			 PARSE_OPT_NOCOMPLETE),
+		OPT_END()
+	};
+	struct worktree **worktrees, *wt;
+	struct strbuf errmsg = STRBUF_INIT;
+	const char *reason = NULL;
+	int ret = 0;
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac != 1)
+		usage_with_options(worktree_usage, options);
+
+	worktrees = get_worktrees(0);
+	wt = find_worktree(worktrees, prefix, av[0]);
+	if (!wt)
+		die(_("'%s' is not a working tree"), av[0]);
+	if (is_main_worktree(wt))
+		die(_("'%s' is a main working tree"), av[0]);
+	if (force < 2)
+		reason = worktree_lock_reason(wt);
+	if (reason) {
+		if (*reason)
+			die(_("cannot remove a locked working tree, lock reason: %s\nuse 'remove -f -f' to override or unlock first"),
+			    reason);
+		die(_("cannot remove a locked working tree;\nuse 'remove -f -f' to override or unlock first"));
+	}
+	if (validate_worktree(wt, &errmsg, WT_VALIDATE_WORKTREE_MISSING_OK))
+		die(_("validation failed, cannot remove working tree: %s"),
+		    errmsg.buf);
+	strbuf_release(&errmsg);
+
+	if (file_exists(wt->path)) {
+		if (!force)
+			check_clean_worktree(wt, av[0]);
+
+		ret |= delete_git_work_tree(wt);
+	}
+	/*
+	 * continue on even if ret is non-zero, there's no going back
+	 * from here.
+	 */
+	ret |= delete_git_dir(wt->id);
+	delete_worktrees_dir_if_empty();
+
+	free_worktrees(worktrees);
+	return ret;
+}
+
+int cmd_worktree(int ac, const char **av, const char *prefix)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+
+	git_config(git_worktree_config, NULL);
+
+	if (ac < 2)
+		usage_with_options(worktree_usage, options);
+	if (!prefix)
+		prefix = "";
+	if (!strcmp(av[1], "add"))
+		return add(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "prune"))
+		return prune(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "list"))
+		return list(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "lock"))
+		return lock_worktree(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "unlock"))
+		return unlock_worktree(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "move"))
+		return move_worktree(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "remove"))
+		return remove_worktree(ac - 1, av + 1, prefix);
+	usage_with_options(worktree_usage, options);
+}
diff --git a/builtin/write-tree.c b/builtin/write-tree.c
new file mode 100644
index 0000000000..45d61707e7
--- /dev/null
+++ b/builtin/write-tree.c
@@ -0,0 +1,57 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "tree.h"
+#include "cache-tree.h"
+#include "parse-options.h"
+
+static const char * const write_tree_usage[] = {
+	N_("git write-tree [--missing-ok] [--prefix=<prefix>/]"),
+	NULL
+};
+
+int cmd_write_tree(int argc, const char **argv, const char *cmd_prefix)
+{
+	int flags = 0, ret;
+	const char *tree_prefix = NULL;
+	struct object_id oid;
+	const char *me = "git-write-tree";
+	struct option write_tree_options[] = {
+		OPT_BIT(0, "missing-ok", &flags, N_("allow missing objects"),
+			WRITE_TREE_MISSING_OK),
+		OPT_STRING(0, "prefix", &tree_prefix, N_("<prefix>/"),
+			   N_("write tree object for a subdirectory <prefix>")),
+		{ OPTION_BIT, 0, "ignore-cache-tree", &flags, NULL,
+		  N_("only useful for debugging"),
+		  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, NULL,
+		  WRITE_TREE_IGNORE_CACHE_TREE },
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, cmd_prefix, write_tree_options,
+			     write_tree_usage, 0);
+
+	ret = write_cache_as_tree(&oid, flags, tree_prefix);
+	switch (ret) {
+	case 0:
+		printf("%s\n", oid_to_hex(&oid));
+		break;
+	case WRITE_TREE_UNREADABLE_INDEX:
+		die("%s: error reading the index", me);
+		break;
+	case WRITE_TREE_UNMERGED_INDEX:
+		die("%s: error building trees", me);
+		break;
+	case WRITE_TREE_PREFIX_ERROR:
+		die("%s: prefix %s not found", me, tree_prefix);
+		break;
+	}
+	return ret;
+}