summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/checkout-index.c1
-rw-r--r--builtin/checkout.c9
-rw-r--r--builtin/clone.c14
-rw-r--r--builtin/commit.c122
-rw-r--r--builtin/credential-cache--daemon.c3
-rw-r--r--builtin/credential-cache.c2
-rw-r--r--builtin/difftool.c1
-rw-r--r--builtin/init-db.c32
-rw-r--r--builtin/log.c33
-rw-r--r--builtin/ls-files.c75
-rw-r--r--builtin/ls-remote.c4
-rw-r--r--builtin/ls-tree.c6
-rw-r--r--builtin/pack-objects.c334
-rw-r--r--builtin/rebase.c11
-rw-r--r--builtin/remote.c8
-rw-r--r--builtin/repack.c207
-rw-r--r--builtin/reset.c2
-rw-r--r--builtin/stash.c2
-rw-r--r--builtin/symbolic-ref.c4
-rw-r--r--builtin/worktree.c10
20 files changed, 739 insertions, 141 deletions
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 023e49e271..c0bf4ac1b2 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -11,6 +11,7 @@
#include "quote.h"
#include "cache-tree.h"
#include "parse-options.h"
+#include "entry.h"
#define CHECKOUT_ALL 4
static int nul_term_line;
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 2d6550bc3c..4c696ef480 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -26,6 +26,7 @@
#include "unpack-trees.h"
#include "wt-status.h"
#include "xdiff-interface.h"
+#include "entry.h"
static const char * const checkout_usage[] = {
N_("git checkout [<options>] <branch>"),
@@ -114,7 +115,7 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm
}
static int update_some(const struct object_id *oid, struct strbuf *base,
- const char *pathname, unsigned mode, int stage, void *context)
+ const char *pathname, unsigned mode, void *context)
{
int len;
struct cache_entry *ce;
@@ -155,8 +156,8 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
{
- read_tree_recursive(the_repository, tree, "", 0, 0,
- pathspec, update_some, NULL);
+ read_tree(the_repository, tree,
+ pathspec, update_some, NULL);
/* update the index with the given tree's info
* for all args, expanding wildcards, and exit
@@ -322,7 +323,7 @@ static void mark_ce_for_checkout_overlay(struct cache_entry *ce,
* 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
+ * need ps_matched and read_tree (and
* eventually tree_entry_interesting) cannot fill
* ps_matched yet. Once it can, we can avoid calling
* match_pathspec() for _all_ entries when
diff --git a/builtin/clone.c b/builtin/clone.c
index 51e844a2de..952fe3d8fc 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -964,10 +964,10 @@ 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, *display_repo = NULL;
+ char *path = NULL, *dir, *display_repo = NULL;
int dest_exists, real_dest_exists = 0;
const struct ref *refs, *remote_head;
- const struct ref *remote_head_points_at;
+ struct ref *remote_head_points_at = NULL;
const struct ref *our_head_points_at;
struct ref *mapped_refs;
const struct ref *ref;
@@ -1017,9 +1017,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
repo_name = argv[0];
path = get_repo_path(repo_name, &is_bundle);
- if (path)
+ if (path) {
+ FREE_AND_NULL(path);
repo = absolute_pathdup(repo_name);
- else if (strchr(repo_name, ':')) {
+ } else if (strchr(repo_name, ':')) {
repo = repo_name;
display_repo = transport_anonymize_url(repo);
} else
@@ -1393,6 +1394,11 @@ cleanup:
strbuf_release(&reflog_msg);
strbuf_release(&branch_top);
strbuf_release(&key);
+ free_refs(mapped_refs);
+ free_refs(remote_head_points_at);
+ free(dir);
+ free(path);
+ UNLEAK(repo);
junk_mode = JUNK_LEAVE_ALL;
strvec_clear(&transport_ls_refs_options.ref_prefixes);
diff --git a/builtin/commit.c b/builtin/commit.c
index 4b06672bd0..55d50a8891 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -105,7 +105,8 @@ static const char *template_file;
*/
static const char *author_message, *author_message_buffer;
static char *edit_message, *use_message;
-static char *fixup_message, *squash_message;
+static char *fixup_message, *fixup_commit, *squash_message;
+static const char *fixup_prefix;
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;
@@ -366,7 +367,8 @@ static const char *prepare_index(const char **argv, const char *prefix,
die(_("--pathspec-file-nul requires --pathspec-from-file"));
}
- if (!pathspec.nr && (also || (only && !amend && !allow_empty)))
+ if (!pathspec.nr && (also || (only && !allow_empty &&
+ (!amend || (fixup_message && strcmp(fixup_prefix, "amend"))))))
die(_("No paths with --include/--only does not make sense."));
if (read_cache_preload(&pathspec) < 0)
@@ -690,6 +692,22 @@ static void adjust_comment_line_char(const struct strbuf *sb)
comment_line_char = *p;
}
+static void prepare_amend_commit(struct commit *commit, struct strbuf *sb,
+ struct pretty_print_context *ctx)
+{
+ const char *buffer, *subject, *fmt;
+
+ buffer = get_commit_buffer(commit, NULL);
+ find_commit_subject(buffer, &subject);
+ /*
+ * If we amend the 'amend!' commit then we don't want to
+ * duplicate the subject line.
+ */
+ fmt = starts_with(subject, "amend!") ? "%b" : "%B";
+ format_commit_message(commit, fmt, sb, ctx);
+ unuse_commit_buffer(commit, buffer);
+}
+
static int prepare_to_commit(const char *index_file, const char *prefix,
struct commit *current_head,
struct wt_status *s,
@@ -754,15 +772,33 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
} else if (fixup_message) {
struct pretty_print_context ctx = {0};
struct commit *commit;
- commit = lookup_commit_reference_by_name(fixup_message);
+ char *fmt;
+ commit = lookup_commit_reference_by_name(fixup_commit);
if (!commit)
- die(_("could not lookup commit %s"), fixup_message);
+ die(_("could not lookup commit %s"), fixup_commit);
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);
+ fmt = xstrfmt("%s! %%s\n\n", fixup_prefix);
+ format_commit_message(commit, fmt, &sb, &ctx);
+ free(fmt);
hook_arg1 = "message";
+
+ /*
+ * Only `-m` commit message option is checked here, as
+ * it supports `--fixup` to append the commit message.
+ *
+ * The other commit message options `-c`/`-C`/`-F` are
+ * incompatible with all the forms of `--fixup` and
+ * have already errored out while parsing the `git commit`
+ * options.
+ */
+ if (have_option_m && !strcmp(fixup_prefix, "fixup"))
+ strbuf_addbuf(&sb, &message);
+
+ if (!strcmp(fixup_prefix, "amend")) {
+ if (have_option_m)
+ die(_("cannot combine -m with --fixup:%s"), fixup_message);
+ prepare_amend_commit(commit, &sb, &ctx);
+ }
} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
size_t merge_msg_start;
@@ -1173,6 +1209,19 @@ static void finalize_deferred_config(struct wt_status *s)
s->ahead_behind_flags = AHEAD_BEHIND_FULL;
}
+static void check_fixup_reword_options(int argc, const char *argv[]) {
+ if (whence != FROM_COMMIT) {
+ if (whence == FROM_MERGE)
+ die(_("You are in the middle of a merge -- cannot reword."));
+ else if (is_from_cherry_pick(whence))
+ die(_("You are in the middle of a cherry-pick -- cannot reword."));
+ }
+ if (argc)
+ die(_("cannot combine reword option of --fixup with path '%s'"), *argv);
+ if (patch_interactive || interactive || all || also || only)
+ die(_("reword option of --fixup is mutually exclusive with --patch/--interactive/--all/--include/--only"));
+}
+
static int parse_and_validate_options(int argc, const char *argv[],
const struct option *options,
const char * const usage[],
@@ -1191,7 +1240,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
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)
+ if (logfile || have_option_m || use_message)
use_editor = 0;
if (0 <= edit_flag)
use_editor = edit_flag;
@@ -1248,6 +1297,42 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (also + only + all + interactive > 1)
die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
+
+ if (fixup_message) {
+ /*
+ * We limit --fixup's suboptions to only alpha characters.
+ * If the first character after a run of alpha is colon,
+ * then the part before the colon may be a known suboption
+ * name like `amend` or `reword`, or a misspelt suboption
+ * name. In either case, we treat it as
+ * --fixup=<suboption>:<arg>.
+ *
+ * Otherwise, we are dealing with --fixup=<commit>.
+ */
+ char *p = fixup_message;
+ while (isalpha(*p))
+ p++;
+ if (p > fixup_message && *p == ':') {
+ *p = '\0';
+ fixup_commit = p + 1;
+ if (!strcmp("amend", fixup_message) ||
+ !strcmp("reword", fixup_message)) {
+ fixup_prefix = "amend";
+ allow_empty = 1;
+ if (*fixup_message == 'r') {
+ check_fixup_reword_options(argc, argv);
+ only = 1;
+ }
+ } else {
+ die(_("unknown option: --fixup=%s:%s"), fixup_message, fixup_commit);
+ }
+ } else {
+ fixup_commit = fixup_message;
+ fixup_prefix = "fixup";
+ use_editor = 0;
+ }
+ }
+
cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
handle_untracked_files_arg(s);
@@ -1525,7 +1610,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
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")),
+ /*
+ * TRANSLATORS: Leave "[(amend|reword):]" as-is,
+ * and only translate <commit>.
+ */
+ OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
@@ -1685,6 +1774,19 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
exit(1);
}
+ if (fixup_message && starts_with(sb.buf, "amend! ") &&
+ !allow_empty_message) {
+ struct strbuf body = STRBUF_INIT;
+ size_t len = commit_subject_length(sb.buf);
+ strbuf_addstr(&body, sb.buf + len);
+ if (message_is_empty(&body, cleanup_mode)) {
+ rollback_index_files();
+ fprintf(stderr, _("Aborting commit due to empty commit message body.\n"));
+ exit(1);
+ }
+ strbuf_release(&body);
+ }
+
if (amend) {
const char *exclude_gpgsig[3] = { "gpgsig", "gpgsig-sha256", NULL };
extra = read_commit_extra_headers(current_head, exclude_gpgsig);
diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c
index c61f123a3b..4c6c89ab0d 100644
--- a/builtin/credential-cache--daemon.c
+++ b/builtin/credential-cache--daemon.c
@@ -203,9 +203,10 @@ static int serve_cache_loop(int fd)
static void serve_cache(const char *socket_path, int debug)
{
+ struct unix_stream_listen_opts opts = UNIX_STREAM_LISTEN_OPTS_INIT;
int fd;
- fd = unix_stream_listen(socket_path);
+ fd = unix_stream_listen(socket_path, &opts);
if (fd < 0)
die_errno("unable to bind to '%s'", socket_path);
diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c
index 9b3f709905..76a6ba3722 100644
--- a/builtin/credential-cache.c
+++ b/builtin/credential-cache.c
@@ -14,7 +14,7 @@
static int send_request(const char *socket, const struct strbuf *out)
{
int got_data = 0;
- int fd = unix_stream_connect(socket);
+ int fd = unix_stream_connect(socket, 0);
if (fd < 0)
return -1;
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 6e18e623fd..ef25729d49 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -23,6 +23,7 @@
#include "lockfile.h"
#include "object-store.h"
#include "dir.h"
+#include "entry.h"
static int trust_exit_code;
diff --git a/builtin/init-db.c b/builtin/init-db.c
index f82efe4aff..c19b35f1e6 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -25,7 +25,6 @@
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)
@@ -94,7 +93,7 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
}
}
-static void copy_templates(const char *template_dir)
+static void copy_templates(const char *template_dir, const char *init_template_dir)
{
struct strbuf path = STRBUF_INIT;
struct strbuf template_path = STRBUF_INIT;
@@ -107,7 +106,7 @@ static void copy_templates(const char *template_dir)
if (!template_dir)
template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
if (!template_dir)
- template_dir = init_db_template_dir;
+ template_dir = init_template_dir;
if (!template_dir)
template_dir = to_free = system_path(DEFAULT_GIT_TEMPLATE_DIR);
if (!template_dir[0]) {
@@ -154,17 +153,6 @@ free_return:
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.
@@ -212,12 +200,9 @@ static int create_default_files(const char *template_path,
int reinit;
int filemode;
struct strbuf err = STRBUF_INIT;
+ const char *init_template_dir = NULL;
const char *work_tree = get_git_work_tree();
- /* 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
@@ -227,7 +212,8 @@ static int create_default_files(const char *template_path,
* values (since we've just potentially changed what's available on
* disk).
*/
- copy_templates(template_path);
+ git_config_get_value("init.templatedir", &init_template_dir);
+ copy_templates(template_path, init_template_dir);
git_config_clear();
reset_shared_repository();
git_config(git_default_config, NULL);
@@ -422,8 +408,8 @@ int init_db(const char *git_dir, const char *real_git_dir,
}
startup_info->have_repository = 1;
- /* Just look for `core.hidedotfiles` */
- git_config(git_init_db_config, NULL);
+ /* Ensure `core.hidedotfiles` is processed */
+ git_config(platform_core_config, NULL);
safe_create_dir(git_dir, 0);
@@ -575,8 +561,10 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
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))
+ if (template_dir && *template_dir && !is_absolute_path(template_dir)) {
template_dir = absolute_pathdup(template_dir);
+ UNLEAK(template_dir);
+ }
if (argc == 1) {
int mkdir_tried = 0;
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80e..8acd285daf 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -599,7 +599,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)
static int show_tree_object(const struct object_id *oid,
struct strbuf *base,
- const char *pathname, unsigned mode, int stage, void *context)
+ const char *pathname, unsigned mode, void *context)
{
FILE *file = context;
fprintf(file, "%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
@@ -681,9 +681,9 @@ int cmd_show(int argc, const char **argv, const char *prefix)
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);
+ read_tree(the_repository, (struct tree *)o,
+ &match_all, show_tree_object,
+ rev.diffopt.file);
rev.shown_one = 1;
break;
case OBJ_COMMIT:
@@ -1662,13 +1662,19 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
oidclr(&bases->base_commit);
}
-static const char *diff_title(struct strbuf *sb, int reroll_count,
- const char *generic, const char *rerolled)
+static const char *diff_title(struct strbuf *sb,
+ const char *reroll_count,
+ const char *generic,
+ const char *rerolled)
{
- if (reroll_count <= 0)
+ int v;
+
+ /* RFC may be v0, so allow -v1 to diff against v0 */
+ if (reroll_count && !strtol_i(reroll_count, 10, &v) &&
+ v >= 1)
+ strbuf_addf(sb, rerolled, v - 1);
+ else
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;
}
@@ -1717,7 +1723,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
struct strbuf buf = STRBUF_INIT;
int use_patch_format = 0;
int quiet = 0;
- int reroll_count = -1;
+ const char *reroll_count = NULL;
char *cover_from_description_arg = NULL;
char *branch_name = NULL;
char *base_commit = NULL;
@@ -1751,7 +1757,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
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,
+ OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
N_("mark the series as Nth re-roll")),
OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
N_("max length of output filename")),
@@ -1862,9 +1868,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (cover_from_description_arg)
cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
- if (0 < reroll_count) {
+ if (reroll_count) {
struct strbuf sprefix = STRBUF_INIT;
- strbuf_addf(&sprefix, "%s v%d",
+
+ strbuf_addf(&sprefix, "%s v%s",
rev.subject_prefix, reroll_count);
rev.reroll_count = reroll_count;
rev.subject_prefix = strbuf_detach(&sprefix, NULL);
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index f6f9e483b2..60a2913a01 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -12,6 +12,7 @@
#include "dir.h"
#include "builtin.h"
#include "tree.h"
+#include "cache-tree.h"
#include "parse-options.h"
#include "resolve-undo.h"
#include "string-list.h"
@@ -420,6 +421,53 @@ static int get_common_prefix_len(const char *common_prefix)
return common_prefix_len;
}
+static int read_one_entry_opt(struct index_state *istate,
+ const struct object_id *oid,
+ struct strbuf *base,
+ const char *pathname,
+ unsigned mode, int opt)
+{
+ int len;
+ struct cache_entry *ce;
+
+ if (S_ISDIR(mode))
+ return READ_TREE_RECURSIVE;
+
+ len = strlen(pathname);
+ ce = make_empty_cache_entry(istate, base->len + len);
+
+ ce->ce_mode = create_ce_mode(mode);
+ ce->ce_flags = create_ce_flags(1);
+ ce->ce_namelen = base->len + len;
+ memcpy(ce->name, base->buf, base->len);
+ memcpy(ce->name + base->len, pathname, len+1);
+ oidcpy(&ce->oid, oid);
+ return add_index_entry(istate, ce, opt);
+}
+
+static int read_one_entry(const struct object_id *oid, struct strbuf *base,
+ const char *pathname, unsigned mode,
+ void *context)
+{
+ struct index_state *istate = context;
+ return read_one_entry_opt(istate, oid, base, pathname,
+ mode,
+ ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
+}
+
+/*
+ * This is used when the caller knows there is no existing entries at
+ * the stage that will conflict with the entry being added.
+ */
+static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,
+ const char *pathname, unsigned mode,
+ void *context)
+{
+ struct index_state *istate = context;
+ return read_one_entry_opt(istate, oid, base, pathname,
+ mode, ADD_CACHE_JUST_APPEND);
+}
+
/*
* Read the tree specified with --with-tree option
* (typically, HEAD) into stage #1 and then
@@ -436,6 +484,8 @@ void overlay_tree_on_index(struct index_state *istate,
struct pathspec pathspec;
struct cache_entry *last_stage0 = NULL;
int i;
+ read_tree_fn_t fn = NULL;
+ int err;
if (get_oid(tree_name, &oid))
die("tree-ish %s not found.", tree_name);
@@ -458,9 +508,32 @@ void overlay_tree_on_index(struct index_state *istate,
PATHSPEC_PREFER_CWD, prefix, matchbuf);
} else
memset(&pathspec, 0, sizeof(pathspec));
- if (read_tree(the_repository, tree, 1, &pathspec, istate))
+
+ /*
+ * See if we have cache entry at the stage. If so,
+ * do it the original slow way, otherwise, append and then
+ * sort at the end.
+ */
+ for (i = 0; !fn && i < istate->cache_nr; i++) {
+ const struct cache_entry *ce = istate->cache[i];
+ if (ce_stage(ce) == 1)
+ fn = read_one_entry;
+ }
+
+ if (!fn)
+ fn = read_one_entry_quick;
+ err = read_tree(the_repository, tree, &pathspec, fn, istate);
+ if (err)
die("unable to read tree entries %s", tree_name);
+ /*
+ * Sort the cache entry -- we need to nuke the cache tree, though.
+ */
+ if (fn == read_one_entry_quick) {
+ cache_tree_free(&istate->cache_tree);
+ QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);
+ }
+
for (i = 0; i < istate->cache_nr; i++) {
struct cache_entry *ce = istate->cache[i];
switch (ce_stage(ce)) {
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index abfa984737..1794548c71 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -124,8 +124,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
repo_set_hash_algo(the_repository, hash_algo);
}
- if (transport_disconnect(transport))
- return 1;
if (!dest && !quiet)
fprintf(stderr, "From %s\n", *remote->url);
@@ -151,5 +149,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
}
ref_array_clear(&ref_array);
+ if (transport_disconnect(transport))
+ return 1;
return status;
}
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index 7cad3f24eb..3a442631c7 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -62,7 +62,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)
}
static int show_tree(const struct object_id *oid, struct strbuf *base,
- const char *pathname, unsigned mode, int stage, void *context)
+ const char *pathname, unsigned mode, void *context)
{
int retval = 0;
int baselen;
@@ -185,6 +185,6 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
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);
+ return !!read_tree(the_repository, tree,
+ &pathspec, show_tree, NULL);
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 4bb602688c..525c2d8552 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -815,8 +815,8 @@ static struct reused_chunk {
/* The offset of the first object of this chunk in the original
* packfile. */
off_t original;
- /* The offset of the first object of this chunk in the generated
- * packfile minus "original". */
+ /* The difference for "original" minus the offset of the first object of
+ * this chunk in the generated packfile. */
off_t difference;
} *reused_chunks;
static int reused_chunks_nr;
@@ -1188,7 +1188,8 @@ static int have_duplicate_entry(const struct object_id *oid,
return 1;
}
-static int want_found_object(int exclude, struct packed_git *p)
+static int want_found_object(const struct object_id *oid, int exclude,
+ struct packed_git *p)
{
if (exclude)
return 1;
@@ -1204,27 +1205,82 @@ static int want_found_object(int exclude, struct packed_git *p)
* 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;
+ * We can however first check whether these options can possibly 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;
+ /*
+ * Objects in packs borrowed from elsewhere are discarded regardless of
+ * if they appear in other packs that weren't borrowed.
+ */
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;
+
+ /*
+ * Then handle .keep first, as we have a fast(er) path there.
+ */
+ if (ignore_packed_keep_on_disk || ignore_packed_keep_in_core) {
+ /*
+ * Set the flags for the kept-pack cache to be the ones we want
+ * to ignore.
+ *
+ * That is, if we are ignoring objects in on-disk keep packs,
+ * then we want to search through the on-disk keep and ignore
+ * the in-core ones.
+ */
+ unsigned flags = 0;
+ if (ignore_packed_keep_on_disk)
+ flags |= ON_DISK_KEEP_PACKS;
+ if (ignore_packed_keep_in_core)
+ flags |= IN_CORE_KEEP_PACKS;
+
+ if (ignore_packed_keep_on_disk && p->pack_keep)
+ return 0;
+ if (ignore_packed_keep_in_core && p->pack_keep_in_core)
+ return 0;
+ if (has_object_kept_pack(oid, flags))
+ return 0;
+ }
+
+ /*
+ * At this point we know definitively that either we don't care about
+ * keep-packs, or the object is not in one. Keep checking other
+ * conditions...
+ */
+ if (!local || !have_non_local_packs)
+ return 1;
/* we don't know yet; keep looking for more packs */
return -1;
}
+static int want_object_in_pack_one(struct packed_git *p,
+ const struct object_id *oid,
+ int exclude,
+ struct packed_git **found_pack,
+ off_t *found_offset)
+{
+ 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))
+ return -1;
+ *found_offset = offset;
+ *found_pack = p;
+ }
+ return want_found_object(oid, exclude, p);
+ }
+ 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).
@@ -1252,7 +1308,7 @@ static int want_object_in_pack(const struct object_id *oid,
* are present we will determine the answer right now.
*/
if (*found_pack) {
- want = want_found_object(exclude, *found_pack);
+ want = want_found_object(oid, exclude, *found_pack);
if (want != -1)
return want;
}
@@ -1260,51 +1316,20 @@ static int want_object_in_pack(const struct object_id *oid,
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;
- }
+ want = want_object_in_pack_one(e.p, oid, exclude, found_pack, found_offset);
+ 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;
- }
+ want = want_object_in_pack_one(p, oid, exclude, found_pack, found_offset);
+ if (!exclude && want > 0)
+ list_move(&p->mru,
+ get_packed_git_mru(the_repository));
+ if (want != -1)
+ return want;
}
if (uri_protocols.nr) {
@@ -2986,6 +3011,191 @@ static int git_pack_config(const char *k, const char *v, void *cb)
return git_default_config(k, v, cb);
}
+/* Counters for trace2 output when in --stdin-packs mode. */
+static int stdin_packs_found_nr;
+static int stdin_packs_hints_nr;
+
+static int add_object_entry_from_pack(const struct object_id *oid,
+ struct packed_git *p,
+ uint32_t pos,
+ void *_data)
+{
+ struct rev_info *revs = _data;
+ struct object_info oi = OBJECT_INFO_INIT;
+ off_t ofs;
+ enum object_type type;
+
+ display_progress(progress_state, ++nr_seen);
+
+ if (have_duplicate_entry(oid, 0))
+ return 0;
+
+ ofs = nth_packed_object_offset(p, pos);
+ if (!want_object_in_pack(oid, 0, &p, &ofs))
+ return 0;
+
+ oi.typep = &type;
+ if (packed_object_info(the_repository, p, ofs, &oi) < 0)
+ die(_("could not get type of object %s in pack %s"),
+ oid_to_hex(oid), p->pack_name);
+ else if (type == OBJ_COMMIT) {
+ /*
+ * commits in included packs are used as starting points for the
+ * subsequent revision walk
+ */
+ add_pending_oid(revs, NULL, oid, 0);
+ }
+
+ stdin_packs_found_nr++;
+
+ create_object_entry(oid, type, 0, 0, 0, p, ofs);
+
+ return 0;
+}
+
+static void show_commit_pack_hint(struct commit *commit, void *_data)
+{
+ /* nothing to do; commits don't have a namehash */
+}
+
+static void show_object_pack_hint(struct object *object, const char *name,
+ void *_data)
+{
+ struct object_entry *oe = packlist_find(&to_pack, &object->oid);
+ if (!oe)
+ return;
+
+ /*
+ * Our 'to_pack' list was constructed by iterating all objects packed in
+ * included packs, and so doesn't have a non-zero hash field that you
+ * would typically pick up during a reachability traversal.
+ *
+ * Make a best-effort attempt to fill in the ->hash and ->no_try_delta
+ * here using a now in order to perhaps improve the delta selection
+ * process.
+ */
+ oe->hash = pack_name_hash(name);
+ oe->no_try_delta = name && no_try_delta(name);
+
+ stdin_packs_hints_nr++;
+}
+
+static int pack_mtime_cmp(const void *_a, const void *_b)
+{
+ struct packed_git *a = ((const struct string_list_item*)_a)->util;
+ struct packed_git *b = ((const struct string_list_item*)_b)->util;
+
+ /*
+ * order packs by descending mtime so that objects are laid out
+ * roughly as newest-to-oldest
+ */
+ if (a->mtime < b->mtime)
+ return 1;
+ else if (b->mtime < a->mtime)
+ return -1;
+ else
+ return 0;
+}
+
+static void read_packs_list_from_stdin(void)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct string_list include_packs = STRING_LIST_INIT_DUP;
+ struct string_list exclude_packs = STRING_LIST_INIT_DUP;
+ struct string_list_item *item = NULL;
+
+ struct packed_git *p;
+ struct rev_info revs;
+
+ repo_init_revisions(the_repository, &revs, NULL);
+ /*
+ * Use a revision walk to fill in the namehash of objects in the include
+ * packs. To save time, we'll avoid traversing through objects that are
+ * in excluded packs.
+ *
+ * That may cause us to avoid populating all of the namehash fields of
+ * all included objects, but our goal is best-effort, since this is only
+ * an optimization during delta selection.
+ */
+ revs.no_kept_objects = 1;
+ revs.keep_pack_cache_flags |= IN_CORE_KEEP_PACKS;
+ revs.blob_objects = 1;
+ revs.tree_objects = 1;
+ revs.tag_objects = 1;
+ revs.ignore_missing_links = 1;
+
+ while (strbuf_getline(&buf, stdin) != EOF) {
+ if (!buf.len)
+ continue;
+
+ if (*buf.buf == '^')
+ string_list_append(&exclude_packs, buf.buf + 1);
+ else
+ string_list_append(&include_packs, buf.buf);
+
+ strbuf_reset(&buf);
+ }
+
+ string_list_sort(&include_packs);
+ string_list_sort(&exclude_packs);
+
+ for (p = get_all_packs(the_repository); p; p = p->next) {
+ const char *pack_name = pack_basename(p);
+
+ item = string_list_lookup(&include_packs, pack_name);
+ if (!item)
+ item = string_list_lookup(&exclude_packs, pack_name);
+
+ if (item)
+ item->util = p;
+ }
+
+ /*
+ * First handle all of the excluded packs, marking them as kept in-core
+ * so that later calls to add_object_entry() discards any objects that
+ * are also found in excluded packs.
+ */
+ for_each_string_list_item(item, &exclude_packs) {
+ struct packed_git *p = item->util;
+ if (!p)
+ die(_("could not find pack '%s'"), item->string);
+ p->pack_keep_in_core = 1;
+ }
+
+ /*
+ * Order packs by ascending mtime; use QSORT directly to access the
+ * string_list_item's ->util pointer, which string_list_sort() does not
+ * provide.
+ */
+ QSORT(include_packs.items, include_packs.nr, pack_mtime_cmp);
+
+ for_each_string_list_item(item, &include_packs) {
+ struct packed_git *p = item->util;
+ if (!p)
+ die(_("could not find pack '%s'"), item->string);
+ for_each_object_in_pack(p,
+ add_object_entry_from_pack,
+ &revs,
+ FOR_EACH_OBJECT_PACK_ORDER);
+ }
+
+ if (prepare_revision_walk(&revs))
+ die(_("revision walk setup failed"));
+ traverse_commit_list(&revs,
+ show_commit_pack_hint,
+ show_object_pack_hint,
+ NULL);
+
+ trace2_data_intmax("pack-objects", the_repository, "stdin_packs_found",
+ stdin_packs_found_nr);
+ trace2_data_intmax("pack-objects", the_repository, "stdin_packs_hints",
+ stdin_packs_hints_nr);
+
+ strbuf_release(&buf);
+ string_list_clear(&include_packs, 0);
+ string_list_clear(&exclude_packs, 0);
+}
+
static void read_object_list_from_stdin(void)
{
char line[GIT_MAX_HEXSZ + 1 + PATH_MAX + 2];
@@ -3489,6 +3699,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
struct strvec rp = STRVEC_INIT;
int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0;
int rev_list_index = 0;
+ int stdin_packs = 0;
struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
struct option pack_objects_options[] = {
OPT_SET_INT('q', "quiet", &progress,
@@ -3539,6 +3750,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
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, "stdin-packs", &stdin_packs,
+ N_("read packs from stdin")),
OPT_BOOL(0, "stdout", &pack_to_stdout,
N_("output pack to stdout")),
OPT_BOOL(0, "include-tag", &include_tag,
@@ -3645,7 +3858,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
use_internal_rev_list = 1;
strvec_push(&rp, "--indexed-objects");
}
- if (rev_list_unpacked) {
+ if (rev_list_unpacked && !stdin_packs) {
use_internal_rev_list = 1;
strvec_push(&rp, "--unpacked");
}
@@ -3690,8 +3903,13 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (filter_options.choice) {
if (!pack_to_stdout)
die(_("cannot use --filter without --stdout"));
+ if (stdin_packs)
+ die(_("cannot use --filter with --stdin-packs"));
}
+ if (stdin_packs && use_internal_rev_list)
+ die(_("cannot use internal rev list with --stdin-packs"));
+
/*
* "soft" reasons not to use bitmaps - for on-disk repack by default we want
*
@@ -3750,7 +3968,13 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (progress)
progress_state = start_progress(_("Enumerating objects"), 0);
- if (!use_internal_rev_list)
+ if (stdin_packs) {
+ /* avoids adding objects in excluded packs */
+ ignore_packed_keep_in_core = 1;
+ read_packs_list_from_stdin();
+ if (rev_list_unpacked)
+ add_unreachable_loose_objects();
+ } else if (!use_internal_rev_list)
read_object_list_from_stdin();
else {
get_object_list(rp.nr, rp.v);
diff --git a/builtin/rebase.c b/builtin/rebase.c
index de400f9a19..783b526f6e 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -100,7 +100,6 @@ struct rebase_options {
char *strategy, *strategy_opts;
struct strbuf git_format_patch_opt;
int reschedule_failed_exec;
- int use_legacy_rebase;
int reapply_cherry_picks;
int fork_point;
};
@@ -1102,11 +1101,6 @@ static int rebase_config(const char *var, const char *value, void *data)
return 0;
}
- if (!strcmp(var, "rebase.usebuiltin")) {
- opts->use_legacy_rebase = !git_config_bool(var, value);
- return 0;
- }
-
if (!strcmp(var, "rebase.backend")) {
return git_config_string(&opts->default_backend, var, value);
}
@@ -1441,11 +1435,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
gpg_sign = options.gpg_sign_opt ? "" : NULL;
FREE_AND_NULL(options.gpg_sign_opt);
- 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))
diff --git a/builtin/remote.c b/builtin/remote.c
index 717b662d45..7f88e6ce9d 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -938,9 +938,6 @@ 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);
@@ -948,10 +945,12 @@ static int get_remote_ref_states(const char *name,
read_branches();
if (query) {
+ struct transport *transport;
+ const struct ref *remote_refs;
+
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)
@@ -960,6 +959,7 @@ static int get_remote_ref_states(const char *name,
get_head_names(remote_refs, states);
if (query & GET_PUSH_REF_STATES)
get_push_ref_states(remote_refs, states);
+ transport_disconnect(transport);
} else {
for_each_ref(append_ref_to_tracked_list, states);
string_list_sort(&states->tracked);
diff --git a/builtin/repack.c b/builtin/repack.c
index 01440de2d5..6ce2556c9e 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -297,6 +297,142 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
#define ALL_INTO_ONE 1
#define LOOSEN_UNREACHABLE 2
+struct pack_geometry {
+ struct packed_git **pack;
+ uint32_t pack_nr, pack_alloc;
+ uint32_t split;
+};
+
+static uint32_t geometry_pack_weight(struct packed_git *p)
+{
+ if (open_pack_index(p))
+ die(_("cannot open index for %s"), p->pack_name);
+ return p->num_objects;
+}
+
+static int geometry_cmp(const void *va, const void *vb)
+{
+ uint32_t aw = geometry_pack_weight(*(struct packed_git **)va),
+ bw = geometry_pack_weight(*(struct packed_git **)vb);
+
+ if (aw < bw)
+ return -1;
+ if (aw > bw)
+ return 1;
+ return 0;
+}
+
+static void init_pack_geometry(struct pack_geometry **geometry_p)
+{
+ struct packed_git *p;
+ struct pack_geometry *geometry;
+
+ *geometry_p = xcalloc(1, sizeof(struct pack_geometry));
+ geometry = *geometry_p;
+
+ for (p = get_all_packs(the_repository); p; p = p->next) {
+ if (!pack_kept_objects && p->pack_keep)
+ continue;
+
+ ALLOC_GROW(geometry->pack,
+ geometry->pack_nr + 1,
+ geometry->pack_alloc);
+
+ geometry->pack[geometry->pack_nr] = p;
+ geometry->pack_nr++;
+ }
+
+ QSORT(geometry->pack, geometry->pack_nr, geometry_cmp);
+}
+
+static void split_pack_geometry(struct pack_geometry *geometry, int factor)
+{
+ uint32_t i;
+ uint32_t split;
+ off_t total_size = 0;
+
+ if (!geometry->pack_nr) {
+ geometry->split = geometry->pack_nr;
+ return;
+ }
+
+ /*
+ * First, count the number of packs (in descending order of size) which
+ * already form a geometric progression.
+ */
+ for (i = geometry->pack_nr - 1; i > 0; i--) {
+ struct packed_git *ours = geometry->pack[i];
+ struct packed_git *prev = geometry->pack[i - 1];
+
+ if (unsigned_mult_overflows(factor, geometry_pack_weight(prev)))
+ die(_("pack %s too large to consider in geometric "
+ "progression"),
+ prev->pack_name);
+
+ if (geometry_pack_weight(ours) < factor * geometry_pack_weight(prev))
+ break;
+ }
+
+ split = i;
+
+ if (split) {
+ /*
+ * Move the split one to the right, since the top element in the
+ * last-compared pair can't be in the progression. Only do this
+ * when we split in the middle of the array (otherwise if we got
+ * to the end, then the split is in the right place).
+ */
+ split++;
+ }
+
+ /*
+ * Then, anything to the left of 'split' must be in a new pack. But,
+ * creating that new pack may cause packs in the heavy half to no longer
+ * form a geometric progression.
+ *
+ * Compute an expected size of the new pack, and then determine how many
+ * packs in the heavy half need to be joined into it (if any) to restore
+ * the geometric progression.
+ */
+ for (i = 0; i < split; i++) {
+ struct packed_git *p = geometry->pack[i];
+
+ if (unsigned_add_overflows(total_size, geometry_pack_weight(p)))
+ die(_("pack %s too large to roll up"), p->pack_name);
+ total_size += geometry_pack_weight(p);
+ }
+ for (i = split; i < geometry->pack_nr; i++) {
+ struct packed_git *ours = geometry->pack[i];
+
+ if (unsigned_mult_overflows(factor, total_size))
+ die(_("pack %s too large to roll up"), ours->pack_name);
+
+ if (geometry_pack_weight(ours) < factor * total_size) {
+ if (unsigned_add_overflows(total_size,
+ geometry_pack_weight(ours)))
+ die(_("pack %s too large to roll up"),
+ ours->pack_name);
+
+ split++;
+ total_size += geometry_pack_weight(ours);
+ } else
+ break;
+ }
+
+ geometry->split = split;
+}
+
+static void clear_pack_geometry(struct pack_geometry *geometry)
+{
+ if (!geometry)
+ return;
+
+ free(geometry->pack);
+ geometry->pack_nr = 0;
+ geometry->pack_alloc = 0;
+ geometry->split = 0;
+}
+
int cmd_repack(int argc, const char **argv, const char *prefix)
{
struct child_process cmd = CHILD_PROCESS_INIT;
@@ -304,6 +440,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
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 pack_geometry *geometry = NULL;
struct strbuf line = STRBUF_INIT;
int i, ext, ret;
FILE *out;
@@ -316,6 +453,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
int no_update_server_info = 0;
struct pack_objects_args po_args = {NULL};
+ int geometric_factor = 0;
struct option builtin_repack_options[] = {
OPT_BIT('a', NULL, &pack_everything,
@@ -356,6 +494,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
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_INTEGER('g', "geometric", &geometric_factor,
+ N_("find a geometric progression with factor <N>")),
OPT_END()
};
@@ -382,6 +522,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (write_bitmaps && !(pack_everything & ALL_INTO_ONE))
die(_(incremental_bitmap_conflict_error));
+ if (geometric_factor) {
+ if (pack_everything)
+ die(_("--geometric is incompatible with -A, -a"));
+ init_pack_geometry(&geometry);
+ split_pack_geometry(geometry, geometric_factor);
+ }
+
packdir = mkpathdup("%s/pack", get_object_directory());
packtmp = mkpathdup("%s/.tmp-%d-pack", packdir, (int)getpid());
@@ -396,9 +543,21 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
strvec_pushf(&cmd.args, "--keep-pack=%s",
keep_pack_list.items[i].string);
strvec_push(&cmd.args, "--non-empty");
- strvec_push(&cmd.args, "--all");
- strvec_push(&cmd.args, "--reflog");
- strvec_push(&cmd.args, "--indexed-objects");
+ if (!geometry) {
+ /*
+ * We need to grab all reachable objects, including those that
+ * are reachable from reflogs and the index.
+ *
+ * When repacking into a geometric progression of packs,
+ * however, we ask 'git pack-objects --stdin-packs', and it is
+ * not about packing objects based on reachability but about
+ * repacking all the objects in specified packs and loose ones
+ * (indeed, --stdin-packs is incompatible with these options).
+ */
+ strvec_push(&cmd.args, "--all");
+ strvec_push(&cmd.args, "--reflog");
+ strvec_push(&cmd.args, "--indexed-objects");
+ }
if (has_promisor_remote())
strvec_push(&cmd.args, "--exclude-promisor-objects");
if (write_bitmaps > 0)
@@ -429,17 +588,37 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
}
}
+ } else if (geometry) {
+ strvec_push(&cmd.args, "--stdin-packs");
+ strvec_push(&cmd.args, "--unpacked");
} else {
strvec_push(&cmd.args, "--unpacked");
strvec_push(&cmd.args, "--incremental");
}
- cmd.no_stdin = 1;
+ if (geometry)
+ cmd.in = -1;
+ else
+ cmd.no_stdin = 1;
ret = start_command(&cmd);
if (ret)
return ret;
+ if (geometry) {
+ FILE *in = xfdopen(cmd.in, "w");
+ /*
+ * The resulting pack should contain all objects in packs that
+ * are going to be rolled up, but exclude objects in packs which
+ * are being left alone.
+ */
+ for (i = 0; i < geometry->split; i++)
+ fprintf(in, "%s\n", pack_basename(geometry->pack[i]));
+ for (i = geometry->split; i < geometry->pack_nr; i++)
+ fprintf(in, "^%s\n", pack_basename(geometry->pack[i]));
+ fclose(in);
+ }
+
out = xfdopen(cmd.out, "r");
while (strbuf_getline_lf(&line, out) != EOF) {
if (line.len != the_hash_algo->hexsz)
@@ -507,6 +686,25 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (!string_list_has_string(&names, sha1))
remove_redundant_pack(packdir, item->string);
}
+
+ if (geometry) {
+ struct strbuf buf = STRBUF_INIT;
+
+ uint32_t i;
+ for (i = 0; i < geometry->split; i++) {
+ struct packed_git *p = geometry->pack[i];
+ if (string_list_has_string(&names,
+ hash_to_hex(p->hash)))
+ continue;
+
+ strbuf_reset(&buf);
+ strbuf_addstr(&buf, pack_basename(p));
+ strbuf_strip_suffix(&buf, ".pack");
+
+ remove_redundant_pack(packdir, buf.buf);
+ }
+ strbuf_release(&buf);
+ }
if (!po_args.quiet && isatty(2))
opts |= PRUNE_PACKED_VERBOSE;
prune_packed_objects(opts);
@@ -528,6 +726,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
string_list_clear(&names, 0);
string_list_clear(&rollback, 0);
string_list_clear(&existing_packs, 0);
+ clear_pack_geometry(geometry);
strbuf_release(&line);
return 0;
diff --git a/builtin/reset.c b/builtin/reset.c
index c635b062c3..43e855cb88 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -425,7 +425,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
dwim_ref(rev, strlen(rev), &dummy, &ref, 0);
if (ref && !starts_with(ref, "refs/"))
- ref = NULL;
+ FREE_AND_NULL(ref);
err = reset_index(ref, &oid, reset_type, quiet);
if (reset_type == KEEP && !err)
diff --git a/builtin/stash.c b/builtin/stash.c
index 3477e940e3..c56fed3354 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -10,11 +10,13 @@
#include "strvec.h"
#include "run-command.h"
#include "dir.h"
+#include "entry.h"
#include "rerere.h"
#include "revision.h"
#include "log-tree.h"
#include "diffcore.h"
#include "exec-cmd.h"
+#include "entry.h"
#define INCLUDE_ALL_FILES 2
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index 80237f0df1..e547a08d6c 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -24,9 +24,11 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int print)
return 1;
}
if (print) {
+ char *to_free = NULL;
if (shorten)
- refname = shorten_unambiguous_ref(refname, 0);
+ refname = to_free = shorten_unambiguous_ref(refname, 0);
puts(refname);
+ free(to_free);
}
return 0;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 1cd5c2016e..8771453493 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -446,16 +446,18 @@ static void print_preparing_worktree_line(int detach,
static const char *dwim_branch(const char *path, const char **new_branch)
{
int n;
+ int branch_exists;
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);
+
+ branch_exists = !strbuf_check_branch_ref(&ref, branchname) &&
+ ref_exists(ref.buf);
+ strbuf_release(&ref);
+ if (branch_exists)
return branchname;
- }
*new_branch = branchname;
if (guess_remote) {