Merge branch 'kn/reflog-drop'
"git reflog" learns "drop" subcommand, that discards the entire reflog data for a ref. * kn/reflog-drop: reflog: implement subcommand to drop reflogs reflog: improve error for when reflog is not found
This commit is contained in:
@@ -16,6 +16,7 @@ SYNOPSIS
|
||||
[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
|
||||
'git reflog delete' [--rewrite] [--updateref]
|
||||
[--dry-run | -n] [--verbose] <ref>@{<specifier>}...
|
||||
'git reflog drop' [--all [--single-worktree] | <refs>...]
|
||||
'git reflog exists' <ref>
|
||||
|
||||
DESCRIPTION
|
||||
@@ -48,10 +49,14 @@ and not reachable from the current tip, are removed from the reflog.
|
||||
This is typically not used directly by end users -- instead, see
|
||||
linkgit:git-gc[1].
|
||||
|
||||
The "delete" subcommand deletes single entries from the reflog. Its
|
||||
argument must be an _exact_ entry (e.g. "`git reflog delete
|
||||
master@{2}`"). This subcommand is also typically not used directly by
|
||||
end users.
|
||||
The "delete" subcommand deletes single entries from the reflog, but
|
||||
not the reflog itself. Its argument must be an _exact_ entry (e.g. "`git
|
||||
reflog delete master@{2}`"). This subcommand is also typically not used
|
||||
directly by end users.
|
||||
|
||||
The "drop" subcommand completely removes the reflog for the specified
|
||||
references. This is in contrast to "expire" and "delete", both of which
|
||||
can be used to delete reflog entries, but not the reflog itself.
|
||||
|
||||
The "exists" subcommand checks whether a ref has a reflog. It exits
|
||||
with zero status if the reflog exists, and non-zero status if it does
|
||||
@@ -132,6 +137,16 @@ Options for `delete`
|
||||
`--dry-run`, and `--verbose`, with the same meanings as when they are
|
||||
used with `expire`.
|
||||
|
||||
Options for `drop`
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
--all::
|
||||
Drop the reflogs of all references from all worktrees.
|
||||
|
||||
--single-worktree::
|
||||
By default when `--all` is specified, reflogs from all working
|
||||
trees are dropped. This option limits the processing to reflogs
|
||||
from the current working tree only.
|
||||
|
||||
GIT
|
||||
---
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
#define BUILTIN_REFLOG_EXISTS_USAGE \
|
||||
N_("git reflog exists <ref>")
|
||||
|
||||
#define BUILTIN_REFLOG_DROP_USAGE \
|
||||
N_("git reflog drop [--all [--single-worktree] | <refs>...]")
|
||||
|
||||
static const char *const reflog_show_usage[] = {
|
||||
BUILTIN_REFLOG_SHOW_USAGE,
|
||||
NULL,
|
||||
@@ -54,11 +57,17 @@ static const char *const reflog_exists_usage[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char *const reflog_drop_usage[] = {
|
||||
BUILTIN_REFLOG_DROP_USAGE,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char *const reflog_usage[] = {
|
||||
BUILTIN_REFLOG_SHOW_USAGE,
|
||||
BUILTIN_REFLOG_LIST_USAGE,
|
||||
BUILTIN_REFLOG_EXPIRE_USAGE,
|
||||
BUILTIN_REFLOG_DELETE_USAGE,
|
||||
BUILTIN_REFLOG_DROP_USAGE,
|
||||
BUILTIN_REFLOG_EXISTS_USAGE,
|
||||
NULL
|
||||
};
|
||||
@@ -383,7 +392,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
|
||||
struct expire_reflog_policy_cb cb = { .cmd = cmd };
|
||||
|
||||
if (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) {
|
||||
status |= error(_("%s points nowhere!"), argv[i]);
|
||||
status |= error(_("reflog could not be found: '%s'"), argv[i]);
|
||||
continue;
|
||||
}
|
||||
set_reflog_expiry_param(&cb.cmd, ref);
|
||||
@@ -449,10 +458,64 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,
|
||||
refname);
|
||||
}
|
||||
|
||||
static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,
|
||||
struct repository *repo)
|
||||
{
|
||||
int ret = 0, do_all = 0, single_worktree = 0;
|
||||
const struct option options[] = {
|
||||
OPT_BOOL(0, "all", &do_all, N_("drop the reflogs of all references")),
|
||||
OPT_BOOL(0, "single-worktree", &single_worktree,
|
||||
N_("drop reflogs from the current worktree only")),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);
|
||||
|
||||
if (argc && do_all)
|
||||
usage(_("references specified along with --all"));
|
||||
|
||||
if (do_all) {
|
||||
struct worktree_reflogs collected = {
|
||||
.reflogs = STRING_LIST_INIT_DUP,
|
||||
};
|
||||
struct string_list_item *item;
|
||||
struct worktree **worktrees, **p;
|
||||
|
||||
worktrees = get_worktrees();
|
||||
for (p = worktrees; *p; p++) {
|
||||
if (single_worktree && !(*p)->is_current)
|
||||
continue;
|
||||
collected.worktree = *p;
|
||||
refs_for_each_reflog(get_worktree_ref_store(*p),
|
||||
collect_reflog, &collected);
|
||||
}
|
||||
free_worktrees(worktrees);
|
||||
|
||||
for_each_string_list_item(item, &collected.reflogs)
|
||||
ret |= refs_delete_reflog(get_main_ref_store(repo),
|
||||
item->string);
|
||||
string_list_clear(&collected.reflogs, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
char *ref;
|
||||
if (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {
|
||||
ret |= error(_("reflog could not be found: '%s'"), argv[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret |= refs_delete_reflog(get_main_ref_store(repo), ref);
|
||||
free(ref);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* main "reflog"
|
||||
*/
|
||||
|
||||
int cmd_reflog(int argc,
|
||||
const char **argv,
|
||||
const char *prefix,
|
||||
@@ -465,6 +528,7 @@ int cmd_reflog(int argc,
|
||||
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
|
||||
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
|
||||
OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
|
||||
OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
||||
@@ -315,9 +315,9 @@ test_expect_success 'git reflog expire unknown reference' '
|
||||
test_config gc.reflogexpireunreachable never &&
|
||||
|
||||
test_must_fail git reflog expire main@{123} 2>stderr &&
|
||||
test_grep "points nowhere" stderr &&
|
||||
test_grep "error: reflog could not be found: ${SQ}main@{123}${SQ}" stderr &&
|
||||
test_must_fail git reflog expire does-not-exist 2>stderr &&
|
||||
test_grep "points nowhere" stderr
|
||||
test_grep "error: reflog could not be found: ${SQ}does-not-exist${SQ}" stderr
|
||||
'
|
||||
|
||||
test_expect_success 'checkout should not delete log for packed ref' '
|
||||
@@ -551,4 +551,126 @@ test_expect_success 'reflog with invalid object ID can be listed' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'reflog drop non-existent ref' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_must_fail git reflog exists refs/heads/non-existent &&
|
||||
test_must_fail git reflog drop refs/heads/non-existent 2>stderr &&
|
||||
test_grep "error: reflog could not be found: ${SQ}refs/heads/non-existent${SQ}" stderr
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'reflog drop' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit A &&
|
||||
test_commit_bulk --ref=refs/heads/branch 1 &&
|
||||
git reflog exists refs/heads/main &&
|
||||
git reflog exists refs/heads/branch &&
|
||||
git reflog drop refs/heads/main &&
|
||||
test_must_fail git reflog exists refs/heads/main &&
|
||||
git reflog exists refs/heads/branch
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'reflog drop multiple references' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit A &&
|
||||
test_commit_bulk --ref=refs/heads/branch 1 &&
|
||||
git reflog exists refs/heads/main &&
|
||||
git reflog exists refs/heads/branch &&
|
||||
git reflog drop refs/heads/main refs/heads/branch &&
|
||||
test_must_fail git reflog exists refs/heads/main &&
|
||||
test_must_fail git reflog exists refs/heads/branch
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'reflog drop multiple references some non-existent' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit A &&
|
||||
test_commit_bulk --ref=refs/heads/branch 1 &&
|
||||
git reflog exists refs/heads/main &&
|
||||
git reflog exists refs/heads/branch &&
|
||||
test_must_fail git reflog exists refs/heads/non-existent &&
|
||||
test_must_fail git reflog drop refs/heads/main refs/heads/non-existent refs/heads/branch 2>stderr &&
|
||||
test_must_fail git reflog exists refs/heads/main &&
|
||||
test_must_fail git reflog exists refs/heads/branch &&
|
||||
test_must_fail git reflog exists refs/heads/non-existent &&
|
||||
test_grep "error: reflog could not be found: ${SQ}refs/heads/non-existent${SQ}" stderr
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'reflog drop --all' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit A &&
|
||||
test_commit_bulk --ref=refs/heads/branch 1 &&
|
||||
git reflog exists refs/heads/main &&
|
||||
git reflog exists refs/heads/branch &&
|
||||
git reflog drop --all &&
|
||||
test_must_fail git reflog exists refs/heads/main &&
|
||||
test_must_fail git reflog exists refs/heads/branch
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'reflog drop --all multiple worktrees' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
test_when_finished "rm -rf wt" &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit A &&
|
||||
git worktree add ../wt &&
|
||||
test_commit_bulk -C ../wt --ref=refs/heads/branch 1 &&
|
||||
git reflog exists refs/heads/main &&
|
||||
git reflog exists refs/heads/branch &&
|
||||
git reflog drop --all &&
|
||||
test_must_fail git reflog exists refs/heads/main &&
|
||||
test_must_fail git reflog exists refs/heads/branch
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'reflog drop --all --single-worktree' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
test_when_finished "rm -rf wt" &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit A &&
|
||||
git worktree add ../wt &&
|
||||
test_commit -C ../wt foobar &&
|
||||
git reflog exists refs/heads/main &&
|
||||
git reflog exists refs/heads/wt &&
|
||||
test-tool ref-store worktree:wt reflog-exists HEAD &&
|
||||
git reflog drop --all --single-worktree &&
|
||||
test_must_fail git reflog exists refs/heads/main &&
|
||||
test_must_fail git reflog exists refs/heads/wt &&
|
||||
test_must_fail test-tool ref-store worktree:main reflog-exists HEAD &&
|
||||
test-tool ref-store worktree:wt reflog-exists HEAD
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'reflog drop --all with reference' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit A &&
|
||||
test_must_fail git reflog drop --all refs/heads/main 2>stderr &&
|
||||
test_grep "usage: references specified along with --all" stderr
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
Reference in New Issue
Block a user