builtin/repack: add guards for --drop-filtered
--drop-filtered removes local promisor blobs. That is only safe when the repository is not mid-operation and when the blobs are not actively in use, so add two guards, both skipped for bare repositories which have neither a worktree nor an index. First, refuse to run while a merge, rebase, am, cherry-pick, revert, or bisect is in progress. During these operations the working tree and index are in an intermediate state, and rewriting packs and deleting objects underneath a half-finished operation is unsafe. Second, refuse to drop a blob that the current index references. Such a blob is needed by the working tree, so dropping it would only cause the next command that touches the worktree to lazy-fetch it straight back, reclaiming nothing. The offending path is reported so the user can see why the drop was refused. Mentored-by: Christian Couder <christian.couder@gmail.com> Mentored-by: Siddharth Asthana <siddharthasthana31@gmail.com> Signed-off-by: Siddharth Shrimali <r.siddharth.shrimali@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>main
parent
0c4142a25b
commit
c6fed8b7a6
|
|
@ -204,6 +204,15 @@ 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
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
#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
|
||||
|
|
@ -317,6 +319,33 @@ int cmd_repack(int argc,
|
|||
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;
|
||||
|
||||
/*
|
||||
|
|
@ -332,6 +361,29 @@ int cmd_repack(int argc,
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -147,4 +147,39 @@ test_expect_success '--drop-filtered removes the promisor blob locally' '
|
|||
test_grep "$SMALL" present
|
||||
'
|
||||
|
||||
test_expect_success '--drop-filtered refuses when a merge is in progress' '
|
||||
test_when_finished "git -C repo merge --abort || :" &&
|
||||
|
||||
# Create a conflicting merge so wt_status reports it.
|
||||
git -C repo checkout -B mergebase base &&
|
||||
echo one >repo/conflict.txt &&
|
||||
git -C repo add conflict.txt &&
|
||||
git -C repo commit -m one &&
|
||||
|
||||
git -C repo checkout -B mergeother base &&
|
||||
echo two >repo/conflict.txt &&
|
||||
git -C repo add conflict.txt &&
|
||||
git -C repo commit -m two &&
|
||||
|
||||
test_must_fail git -C repo merge mergebase &&
|
||||
|
||||
test_must_fail git -C repo -c repack.writeBitmaps=false \
|
||||
repack --drop-filtered --filter=blob:limit=1k --dry-run -a 2>err &&
|
||||
test_grep "in progress" err
|
||||
'
|
||||
|
||||
test_expect_success '--drop-filtered refuses to drop an index-referenced blob' '
|
||||
# Create a large blob, add it to the index and make it a promisor object
|
||||
# so the index references it and enumeration picks it up.
|
||||
test-tool genrandom idx 4096 >repo/tracked-big.bin &&
|
||||
git -C repo add tracked-big.bin &&
|
||||
OID=$(git -C repo rev-parse :tracked-big.bin) &&
|
||||
printf "%s\n" "$OID" | pack_as_from_promisor >/dev/null &&
|
||||
delete_object repo "$OID" &&
|
||||
|
||||
test_must_fail git -C repo -c repack.writeBitmaps=false \
|
||||
repack --drop-filtered --filter=blob:limit=1k --dry-run -a 2>err &&
|
||||
test_grep "referenced by the current index" err
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
|||
Loading…
Reference in New Issue