Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a039ee6
completion: no-op refactoring of diff completion
gitster Aug 12, 2026
4b1b7a4
completion: complete tracked paths for 'git diff'
gitster Aug 12, 2026
354d1bf
completion: 'git diff' completes untracked paths as a last resort
gitster Aug 12, 2026
32f1896
Merge branch 'jc/complete-diff-tracked-paths' into jc/complete-checkout
gitster Aug 12, 2026
e4621a0
packfile: fix perf regression with many packs
dscho Aug 13, 2026
7355146
completion: no-op refactoring of checkout completion
gitster Aug 13, 2026
3fe9209
completion: complete tracked paths for "git checkout"
gitster Aug 13, 2026
05e2ab1
completion: 'git checkout' completes untracked paths as a last resort
gitster Aug 13, 2026
1e746b0
builtin/repack: add --drop-filtered and --dry-run options
SiddharthShrimali Aug 13, 2026
401c308
list-objects-filter: add list_objects_filter__filter_oidset()
SiddharthShrimali Aug 13, 2026
85531bb
repack-promisor: allow excluding objects from the rebuilt promisor pack
SiddharthShrimali Aug 13, 2026
8bb2a3f
builtin/repack: enumerate promisor blobs for --drop-filtered
SiddharthShrimali Aug 13, 2026
0c4142a
builtin/repack: actually drop filtered promisor blobs
SiddharthShrimali Aug 13, 2026
c6fed8b
builtin/repack: add guards for --drop-filtered
SiddharthShrimali Aug 13, 2026
0266361
doc: fix typo in submitting patches
theSwapnilSaste Aug 14, 2026
f17d211
chdir-notify.h: Removed unused param 'name'
Cloinville7 Aug 14, 2026
6730f07
Merge branch 'ss/repack-drop-filtered'
gitster Aug 25, 2026
6d7be5f
Merge branch 'ss/submittingpatches-typofix'
gitster Aug 25, 2026
b767751
Merge branch 'js/packfile-fast-append'
gitster Aug 25, 2026
3caf98a
Merge branch 'ch/chdir-notify-drop-name'
gitster Aug 25, 2026
4e5713a
Merge branch 'jc/complete-diff-tracked-paths'
gitster Aug 25, 2026
fee541a
Merge branch 'jc/complete-checkout'
gitster Aug 25, 2026
f78ce2f
The 19th batch
gitster Aug 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions Documentation/RelNotes/2.56.0.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ UI, Workflows & Features
code did not check the presence of a value and instead segfaulted
without one, which has been corrected.

* 'git repack' has been taught '--drop-filtered' to delete local
promisor blobs exceeding a limit (currently 'blob:limit=') in partial
clones, reclaiming space. Guards prevent running during other
operations or if referenced by the index.


Performance, Internal Implementation, Development Support etc.
--------------------------------------------------------------
Expand Down Expand Up @@ -419,6 +424,16 @@ Performance, Internal Implementation, Development Support etc.
* The setting of a now-unused member '.pretty_given' in the sequencer
machinery has been removed.

* The performance of adding numerous new packfiles has been improved
by introducing a fast path for known-new packfiles to skip an
unnecessary traversal in packfile_list_append(), avoiding a
quadratic complexity regression on load.

* The unused name parameter in 'struct chdir_notify_entry' has been
removed from chdir_notify_register(), chdir_notify_unregister(), and
related callback signatures across several subsystems, simplifying the
API now that trace output no longer uses it.


Fixes since v2.55
-----------------
Expand Down Expand Up @@ -669,3 +684,14 @@ Fixes since v2.55

* The help text for the '-l' option of 'git diff' has been updated.
(merge 764243bdf4 en/diff-l-opt-help later to maint).

* 'git -C <dir> diff fi<TAB>' did not complete 'file', which has
been corrected.
(merge 354d1bf3a0 jc/complete-diff-tracked-paths later to maint).

* 'git -C <dir> checkout fi<TAB>' did not complete 'file', which has
been corrected.
(merge 05e2ab1f31 jc/complete-checkout later to maint).

* Other code cleanup, docfix, build fix, etc.
(merge 026636128f ss/submittingpatches-typofix later to maint).
2 changes: 1 addition & 1 deletion Documentation/SubmittingPatches
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ of review.
can still continue to further improve them by adding more patches on
top, but by the time a topic gets merged to 'next', it is expected
that everybody agrees that the scope and the basic direction of the
topic are appropriate, so such an incremental updates are limited to
topic are appropriate, so such incremental updates are limited to
small corrections and polishing. After a topic cooks for some time
(like 7 calendar days) in 'next' without needing further tweaks on
top, it gets merged to the 'master' branch and waits to become part
Expand Down
37 changes: 37 additions & 0 deletions Documentation/git-repack.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ SYNOPSIS
'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]
[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]
[--write-midx[=<mode>]] [--name-hash-version=<n>] [--path-walk]
[--filter=<filter-spec>] [--drop-filtered [--dry-run]]

DESCRIPTION
-----------
Expand Down Expand Up @@ -182,6 +183,42 @@ depth is 4095.
`objects` and `objects/info/alternates` sections of
linkgit:gitrepository-layout[5].

--drop-filtered::
Delete the local objects that match the `--filter` specification
instead of keeping them in a separate packfile, reclaiming the
disk space they occupy. This is intended for partial clones,
where the filtered objects are promisor objects that remain
recoverable from the promisor remote and are lazily re-fetched
on demand when they are next needed.
+
Only large blobs are supported for now, so `--filter=blob:limit=<n>`
is currently the only accepted filter. Because dropped objects must be
recoverable, this option requires a promisor remote to be configured
and refuses to run otherwise.
+
This option requires `-a`, and implies `-d`: the objects are dropped by
rebuilding the promisor pack without them and then removing the now
redundant old packs, so the redundant packs must be deleted for the
space to actually be reclaimed. It is incompatible with `--filter-to`
and with bitmap writing (`-b`/`--write-bitmap-index`), since filtering
breaks the single-pack closure that bitmaps require. A bitmap setting
coming from configuration is silently disabled for the duration of the
command.
+
As a convenience, since dropped objects remain recoverable by lazy fetch,
`--drop-filtered` refuses to run while another operation
(merge, rebase, am, cherry-pick, revert, or bisect) is in progress, to
avoid a surprising network fetch mid-operation, and refuses to drop any
blob that the current index references, since such a blob would only be
lazily re-fetched by the next command that inspects the working tree.
These checks are skipped in bare repositories, which have neither a
working tree nor an index.

--dry-run::
Only meaningful with `--drop-filtered`. List the objects that
would be dropped, one object ID per line, without rebuilding any
pack or deleting anything.

-b::
--write-bitmap-index::
Write a reachability bitmap index as part of the repack. This
Expand Down
154 changes: 149 additions & 5 deletions builtin/repack.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
#include "promisor-remote.h"
#include "repack.h"
#include "shallow.h"
#include "list-objects-filter-options.h"
#include "oidset.h"
#include "hex.h"
#include "wt-status.h"
#include "read-cache-ll.h"

#define ALL_INTO_ONE 1
#define LOOSEN_UNREACHABLE 2
Expand All @@ -28,11 +33,15 @@ static int use_delta_islands;
static int run_update_server_info = 1;
static char *packdir, *packtmp_name, *packtmp;
static int midx_must_contain_cruft = 1;
static int drop_filtered;
static int dry_run;
static int write_bitmaps_given;

static const char *const git_repack_usage[] = {
N_("git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n"
"[--write-midx[=<mode>]] [--name-hash-version=<n>] [--path-walk]"),
"[--write-midx[=<mode>]] [--name-hash-version=<n>] [--path-walk]\n"
"[--filter=<filter-spec>] [--drop-filtered [--dry-run]]"),
NULL
};

Expand Down Expand Up @@ -111,6 +120,21 @@ static int repack_config(const char *var, const char *value,
return git_default_config(var, value, ctx, cb);
}

static int option_parse_write_bitmaps(const struct option *opt, const char *arg,
int unset)
{
int *value = opt->value;

BUG_ON_OPT_ARG(arg);
if (unset)
*value = 0;
else
*value = 1;

write_bitmaps_given = 1;
return 0;
}

static int option_parse_write_midx(const struct option *opt, const char *arg,
int unset)
{
Expand Down Expand Up @@ -140,6 +164,7 @@ int cmd_repack(int argc,
struct string_list_item *item;
struct string_list names = STRING_LIST_INIT_DUP;
struct existing_packs existing = EXISTING_PACKS_INIT;
struct oidset drop_oids = OIDSET_INIT;
struct pack_geometry geometry = { 0 };
struct tempfile *refs_snapshot = NULL;
int i, ret;
Expand Down Expand Up @@ -194,8 +219,9 @@ int cmd_repack(int argc,
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_CALLBACK_F('b', "write-bitmap-index", &write_bitmaps, NULL,
N_("write bitmap index"),
PARSE_OPT_NOARG, option_parse_write_bitmaps),
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"),
Expand Down Expand Up @@ -231,6 +257,10 @@ int cmd_repack(int argc,
N_("pack prefix to store a pack containing pruned objects")),
OPT_STRING(0, "filter-to", &filter_to, N_("dir"),
N_("pack prefix to store a pack containing filtered out objects")),
OPT_BOOL(0, "drop-filtered", &drop_filtered,
N_("delete filtered out objects (requires --filter)")),
OPT_BOOL(0, "dry-run", &dry_run,
N_("only show which objects would be dropped")),
OPT_END()
};

Expand All @@ -252,6 +282,118 @@ int cmd_repack(int argc,
po_args.depth = xstrdup_or_null(opt_depth);
po_args.threads = xstrdup_or_null(opt_threads);

die_for_incompatible_opt2(drop_filtered, "--drop-filtered",
!!filter_to, "--filter-to");

if (dry_run && !drop_filtered)
die(_("--dry-run only takes effect with --drop-filtered"));

if (drop_filtered) {
if (!po_args.filter_options.choice)
die(_("--drop-filtered requires --filter"));

if (!(pack_everything & ALL_INTO_ONE))
die(_("--drop-filtered requires -a"));

/*
* Only blob:limit=<n> is supported for now. Reject other
* filter choices early, before walking the object database.
*/
if (po_args.filter_options.choice != LOFC_BLOB_LIMIT)
die(_("--drop-filtered only supports --filter=blob:limit=<n> for now"));

/*
* An explicit -b on the command line is a conflict we have to
* report; a bitmap setting from config is silently overridden
* for the duration of the command.
*/
if (write_bitmaps_given && write_bitmaps > 0)
die(_("options '%s' and '%s' cannot be used together"),
"--drop-filtered", "--write-bitmap-index");

/*
* Without a promisor remote there is nowhere to re-fetch the
* dropped objects from, so dropping them would be permanent
* data loss.
*/
if (!repo_has_promisor_remote(repo))
die(_("--drop-filtered requires a promisor remote"));

/*
* Refuse to run while another operation is in progress. A
* dropped object would just be lazily re-fetched when the
* operation resumes, but triggering a network fetch in the
* middle of a half-finished
* merge/rebase/cherry-pick/revert/bisect is a poor
* experience, so this is a UX convenience rather than a
* safety measure. Bare repositories have no such state, so
* the check is skipped there.
*/
if (!is_bare_repository(repo)) {
struct wt_status_state state = { 0 };

wt_status_get_state(repo, &state, 0);
if (state.merge_in_progress || state.revert_in_progress ||
state.rebase_in_progress || state.bisect_in_progress ||
state.cherry_pick_in_progress || state.am_in_progress ||
state.rebase_interactive_in_progress) {
wt_status_state_free_buffers(&state);
die(_("--drop-filtered cannot be used while "
"another operation (merge, rebase, am, "
"cherry-pick, revert, or bisect) is in "
"progress"));
}
wt_status_state_free_buffers(&state);
}

write_bitmaps = 0;

/*
* Dropping objects means rebuilding the promisor packs
* without them and then removing the old packs, so the
* redundant packs must be deleted. Imply -d on a real run.
*/
if (!dry_run)
delete_redundant = 1;

ret = enumerate_promisor_blobs(repo, &po_args.filter_options, &drop_oids);

if (ret)
goto cleanup;

/*
* Refuse to drop blobs that the current index references.
* Such a blob would only be lazily re-fetched by the next
* command that touches the worktree, so dropping it reclaims
* nothing. This guard just avoids that churn. Bare
* repositories have no index, so the check is skipped there.
*/
if (!is_bare_repository(repo) && oidset_size(&drop_oids)) {
struct index_state *istate = repo->index;
unsigned int i;

if (repo_read_index(repo) < 0)
die(_("could not read the index"));

for (i = 0; i < istate->cache_nr; i++) {
const struct cache_entry *ce = istate->cache[i];

if (oidset_contains(&drop_oids, &ce->oid))
die(_("cannot drop '%s' (%s): it is referenced by the current index"),
ce->name, oid_to_hex(&ce->oid));
}
}

if (dry_run) {
struct oidset_iter iter;
const struct object_id *oid;

oidset_iter_init(&drop_oids, &iter);
while ((oid = oidset_iter_next(&iter)))
printf("%s\n", oid_to_hex(oid));
}
}

if (delete_redundant && repo->repository_format_precious_objects)
die(_("cannot delete packs in a precious-objects repo"));

Expand Down Expand Up @@ -362,7 +504,8 @@ int cmd_repack(int argc,
strvec_push(&cmd.args, "--delta-islands");

if (pack_everything & ALL_INTO_ONE) {
repack_promisor_objects(repo, &po_args, &names, packtmp);
repack_promisor_objects(repo, &po_args, &names, packtmp,
(drop_filtered && !dry_run) ? &drop_oids : NULL);

if (existing_packs_has_non_kept(&existing) &&
delete_redundant &&
Expand Down Expand Up @@ -545,7 +688,7 @@ int cmd_repack(int argc,
}
}

if (po_args.filter_options.choice) {
if (po_args.filter_options.choice && !drop_filtered) {
struct write_pack_opts opts = {
.po_args = &po_args,
.destination = filter_to,
Expand Down Expand Up @@ -638,6 +781,7 @@ int cmd_repack(int argc,
cleanup:
string_list_clear(&keep_pack_list, 0);
string_list_clear(&names, 1);
oidset_clear(&drop_oids);
existing_packs_release(&existing);
pack_geometry_release(&geometry);
pack_objects_args_release(&po_args);
Expand Down
12 changes: 4 additions & 8 deletions chdir-notify.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,22 @@
#include "trace.h"

struct chdir_notify_entry {
const char *name;
chdir_notify_callback cb;
void *data;
struct list_head list;
};
static LIST_HEAD(chdir_notify_entries);

void chdir_notify_register(const char *name,
chdir_notify_callback cb,
void chdir_notify_register(chdir_notify_callback cb,
void *data)
{
struct chdir_notify_entry *e = xmalloc(sizeof(*e));
e->name = name;
e->cb = cb;
e->data = data;
list_add_tail(&e->list, &chdir_notify_entries);
}

void chdir_notify_unregister(const char *name, chdir_notify_callback cb,
void chdir_notify_unregister(chdir_notify_callback cb,
void *data)
{
struct list_head *pos, *p;
Expand All @@ -34,8 +31,7 @@ void chdir_notify_unregister(const char *name, chdir_notify_callback cb,
struct chdir_notify_entry *e =
list_entry(pos, struct chdir_notify_entry, list);

if (e->cb != cb || e->data != data || !e->name != !name ||
(e->name && strcmp(e->name, name)))
if (e->cb != cb || e->data != data)
continue;

list_del(pos);
Expand Down Expand Up @@ -64,7 +60,7 @@ int chdir_notify(const char *new_cwd)
list_for_each(pos, &chdir_notify_entries) {
struct chdir_notify_entry *e =
list_entry(pos, struct chdir_notify_entry, list);
e->cb(e->name, old_cwd.buf, new_cwd, e->data);
e->cb(old_cwd.buf, new_cwd, e->data);
}

strbuf_release(&old_cwd);
Expand Down
8 changes: 3 additions & 5 deletions chdir-notify.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,11 @@
* $GIT_TRACE_SETUP. It may be NULL, but if non-NULL should point to
* storage which lasts as long as the registration is active.
*/
typedef void (*chdir_notify_callback)(const char *name,
const char *old_cwd,
typedef void (*chdir_notify_callback)(const char *old_cwd,
const char *new_cwd,
void *data);
void chdir_notify_register(const char *name, chdir_notify_callback cb, void *data);
void chdir_notify_unregister(const char *name, chdir_notify_callback cb,
void *data);
void chdir_notify_register(chdir_notify_callback cb, void *data);
void chdir_notify_unregister(chdir_notify_callback cb, void *data);

/*
*
Expand Down
Loading