Merge branch 'ps/reflog-migrate-fixes' into maint-2.51
"git refs migrate" to migrate the reflog entries from a refs backend to another had a handful of bugs squashed. * ps/reflog-migrate-fixes: refs: fix invalid old object IDs when migrating reflogs refs: stop unsetting REF_HAVE_OLD for log-only updates refs/files: detect race when generating reflog entry for HEAD refs: fix identity for migrated reflogs ident: fix type of string length parameter builtin/reflog: implement subcommand to write new entries refs: export `ref_transaction_update_reflog()` builtin/reflog: improve grouping of subcommands Documentation/git-reflog: convert to use synopsis typemaint
commit
e04c0aded3
|
|
@ -8,16 +8,17 @@ git-reflog - Manage reflog information
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
[verse]
|
[synopsis]
|
||||||
'git reflog' [show] [<log-options>] [<ref>]
|
git reflog [show] [<log-options>] [<ref>]
|
||||||
'git reflog list'
|
git reflog list
|
||||||
'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]
|
git reflog exists <ref>
|
||||||
|
git reflog write <ref> <old-oid> <new-oid> <message>
|
||||||
|
git reflog delete [--rewrite] [--updateref]
|
||||||
|
[--dry-run | -n] [--verbose] <ref>@{<specifier>}...
|
||||||
|
git reflog drop [--all [--single-worktree] | <refs>...]
|
||||||
|
git reflog expire [--expire=<time>] [--expire-unreachable=<time>]
|
||||||
[--rewrite] [--updateref] [--stale-fix]
|
[--rewrite] [--updateref] [--stale-fix]
|
||||||
[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
|
[--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
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
|
@ -43,11 +44,15 @@ actions, and in addition the `HEAD` reflog records branch switching.
|
||||||
|
|
||||||
The "list" subcommand lists all refs which have a corresponding reflog.
|
The "list" subcommand lists all refs which have a corresponding reflog.
|
||||||
|
|
||||||
The "expire" subcommand prunes older reflog entries. Entries older
|
The "exists" subcommand checks whether a ref has a reflog. It exits
|
||||||
than `expire` time, or entries older than `expire-unreachable` time
|
with zero status if the reflog exists, and non-zero status if it does
|
||||||
and not reachable from the current tip, are removed from the reflog.
|
not.
|
||||||
This is typically not used directly by end users -- instead, see
|
|
||||||
linkgit:git-gc[1].
|
The "write" subcommand writes a single entry to the reflog of a given
|
||||||
|
reference. This new entry is appended to the reflog and will thus become
|
||||||
|
the most recent entry. The reference name must be fully qualified. Both the old
|
||||||
|
and new object IDs must not be abbreviated and must point to existing objects.
|
||||||
|
The reflog message gets normalized.
|
||||||
|
|
||||||
The "delete" subcommand deletes single entries from the reflog, but
|
The "delete" subcommand deletes single entries from the reflog, but
|
||||||
not the reflog itself. Its argument must be an _exact_ entry (e.g. "`git
|
not the reflog itself. Its argument must be an _exact_ entry (e.g. "`git
|
||||||
|
|
@ -58,9 +63,11 @@ The "drop" subcommand completely removes the reflog for the specified
|
||||||
references. This is in contrast to "expire" and "delete", both of which
|
references. This is in contrast to "expire" and "delete", both of which
|
||||||
can be used to delete reflog entries, but not the reflog itself.
|
can be used to delete reflog entries, but not the reflog itself.
|
||||||
|
|
||||||
The "exists" subcommand checks whether a ref has a reflog. It exits
|
The "expire" subcommand prunes older reflog entries. Entries older
|
||||||
with zero status if the reflog exists, and non-zero status if it does
|
than `expire` time, or entries older than `expire-unreachable` time
|
||||||
not.
|
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].
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
|
|
@ -71,65 +78,6 @@ Options for `show`
|
||||||
`git reflog show` accepts any of the options accepted by `git log`.
|
`git reflog show` accepts any of the options accepted by `git log`.
|
||||||
|
|
||||||
|
|
||||||
Options for `expire`
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
--all::
|
|
||||||
Process the reflogs of all references.
|
|
||||||
|
|
||||||
--single-worktree::
|
|
||||||
By default when `--all` is specified, reflogs from all working
|
|
||||||
trees are processed. This option limits the processing to reflogs
|
|
||||||
from the current working tree only.
|
|
||||||
|
|
||||||
--expire=<time>::
|
|
||||||
Prune entries older than the specified time. If this option is
|
|
||||||
not specified, the expiration time is taken from the
|
|
||||||
configuration setting `gc.reflogExpire`, which in turn
|
|
||||||
defaults to 90 days. `--expire=all` prunes entries regardless
|
|
||||||
of their age; `--expire=never` turns off pruning of reachable
|
|
||||||
entries (but see `--expire-unreachable`).
|
|
||||||
|
|
||||||
--expire-unreachable=<time>::
|
|
||||||
Prune entries older than `<time>` that are not reachable from
|
|
||||||
the current tip of the branch. If this option is not
|
|
||||||
specified, the expiration time is taken from the configuration
|
|
||||||
setting `gc.reflogExpireUnreachable`, which in turn defaults
|
|
||||||
to 30 days. `--expire-unreachable=all` prunes unreachable
|
|
||||||
entries regardless of their age; `--expire-unreachable=never`
|
|
||||||
turns off early pruning of unreachable entries (but see
|
|
||||||
`--expire`).
|
|
||||||
|
|
||||||
--updateref::
|
|
||||||
Update the reference to the value of the top reflog entry (i.e.
|
|
||||||
<ref>@\{0\}) if the previous top entry was pruned. (This
|
|
||||||
option is ignored for symbolic references.)
|
|
||||||
|
|
||||||
--rewrite::
|
|
||||||
If a reflog entry's predecessor is pruned, adjust its "old"
|
|
||||||
SHA-1 to be equal to the "new" SHA-1 field of the entry that
|
|
||||||
now precedes it.
|
|
||||||
|
|
||||||
--stale-fix::
|
|
||||||
Prune any reflog entries that point to "broken commits". A
|
|
||||||
broken commit is a commit that is not reachable from any of
|
|
||||||
the reference tips and that refers, directly or indirectly, to
|
|
||||||
a missing commit, tree, or blob object.
|
|
||||||
+
|
|
||||||
This computation involves traversing all the reachable objects, i.e. it
|
|
||||||
has the same cost as 'git prune'. It is primarily intended to fix
|
|
||||||
corruption caused by garbage collecting using older versions of Git,
|
|
||||||
which didn't protect objects referred to by reflogs.
|
|
||||||
|
|
||||||
-n::
|
|
||||||
--dry-run::
|
|
||||||
Do not actually prune any entries; just show what would have
|
|
||||||
been pruned.
|
|
||||||
|
|
||||||
--verbose::
|
|
||||||
Print extra information on screen.
|
|
||||||
|
|
||||||
|
|
||||||
Options for `delete`
|
Options for `delete`
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
@ -140,14 +88,74 @@ used with `expire`.
|
||||||
Options for `drop`
|
Options for `drop`
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
--all::
|
`--all`::
|
||||||
Drop the reflogs of all references from all worktrees.
|
Drop the reflogs of all references from all worktrees.
|
||||||
|
|
||||||
--single-worktree::
|
`--single-worktree`::
|
||||||
By default when `--all` is specified, reflogs from all working
|
By default when `--all` is specified, reflogs from all working
|
||||||
trees are dropped. This option limits the processing to reflogs
|
trees are dropped. This option limits the processing to reflogs
|
||||||
from the current working tree only.
|
from the current working tree only.
|
||||||
|
|
||||||
|
|
||||||
|
Options for `expire`
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
`--all`::
|
||||||
|
Process the reflogs of all references.
|
||||||
|
|
||||||
|
`--single-worktree`::
|
||||||
|
By default when `--all` is specified, reflogs from all working
|
||||||
|
trees are processed. This option limits the processing to reflogs
|
||||||
|
from the current working tree only.
|
||||||
|
|
||||||
|
`--expire=<time>`::
|
||||||
|
Prune entries older than the specified time. If this option is
|
||||||
|
not specified, the expiration time is taken from the
|
||||||
|
configuration setting `gc.reflogExpire`, which in turn
|
||||||
|
defaults to 90 days. `--expire=all` prunes entries regardless
|
||||||
|
of their age; `--expire=never` turns off pruning of reachable
|
||||||
|
entries (but see `--expire-unreachable`).
|
||||||
|
|
||||||
|
`--expire-unreachable=<time>`::
|
||||||
|
Prune entries older than `<time>` that are not reachable from
|
||||||
|
the current tip of the branch. If this option is not
|
||||||
|
specified, the expiration time is taken from the configuration
|
||||||
|
setting `gc.reflogExpireUnreachable`, which in turn defaults
|
||||||
|
to 30 days. `--expire-unreachable=all` prunes unreachable
|
||||||
|
entries regardless of their age; `--expire-unreachable=never`
|
||||||
|
turns off early pruning of unreachable entries (but see
|
||||||
|
`--expire`).
|
||||||
|
|
||||||
|
`--updateref`::
|
||||||
|
Update the reference to the value of the top reflog entry (i.e.
|
||||||
|
<ref>@\{0\}) if the previous top entry was pruned. (This
|
||||||
|
option is ignored for symbolic references.)
|
||||||
|
|
||||||
|
`--rewrite`::
|
||||||
|
If a reflog entry's predecessor is pruned, adjust its "old"
|
||||||
|
SHA-1 to be equal to the "new" SHA-1 field of the entry that
|
||||||
|
now precedes it.
|
||||||
|
|
||||||
|
`--stale-fix`::
|
||||||
|
Prune any reflog entries that point to "broken commits". A
|
||||||
|
broken commit is a commit that is not reachable from any of
|
||||||
|
the reference tips and that refers, directly or indirectly, to
|
||||||
|
a missing commit, tree, or blob object.
|
||||||
|
+
|
||||||
|
This computation involves traversing all the reachable objects, i.e. it
|
||||||
|
has the same cost as 'git prune'. It is primarily intended to fix
|
||||||
|
corruption caused by garbage collecting using older versions of Git,
|
||||||
|
which didn't protect objects referred to by reflogs.
|
||||||
|
|
||||||
|
`-n`::
|
||||||
|
`--dry-run`::
|
||||||
|
Do not actually prune any entries; just show what would have
|
||||||
|
been pruned.
|
||||||
|
|
||||||
|
`--verbose`::
|
||||||
|
Print extra information on screen.
|
||||||
|
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
---
|
---
|
||||||
Part of the linkgit:git[1] suite
|
Part of the linkgit:git[1] suite
|
||||||
|
|
|
||||||
103
builtin/reflog.c
103
builtin/reflog.c
|
|
@ -3,6 +3,8 @@
|
||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "gettext.h"
|
#include "gettext.h"
|
||||||
|
#include "hex.h"
|
||||||
|
#include "odb.h"
|
||||||
#include "revision.h"
|
#include "revision.h"
|
||||||
#include "reachable.h"
|
#include "reachable.h"
|
||||||
#include "wildmatch.h"
|
#include "wildmatch.h"
|
||||||
|
|
@ -17,21 +19,24 @@
|
||||||
#define BUILTIN_REFLOG_LIST_USAGE \
|
#define BUILTIN_REFLOG_LIST_USAGE \
|
||||||
N_("git reflog list")
|
N_("git reflog list")
|
||||||
|
|
||||||
#define BUILTIN_REFLOG_EXPIRE_USAGE \
|
#define BUILTIN_REFLOG_EXISTS_USAGE \
|
||||||
N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \
|
N_("git reflog exists <ref>")
|
||||||
" [--rewrite] [--updateref] [--stale-fix]\n" \
|
|
||||||
" [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]")
|
#define BUILTIN_REFLOG_WRITE_USAGE \
|
||||||
|
N_("git reflog write <ref> <old-oid> <new-oid> <message>")
|
||||||
|
|
||||||
#define BUILTIN_REFLOG_DELETE_USAGE \
|
#define BUILTIN_REFLOG_DELETE_USAGE \
|
||||||
N_("git reflog delete [--rewrite] [--updateref]\n" \
|
N_("git reflog delete [--rewrite] [--updateref]\n" \
|
||||||
" [--dry-run | -n] [--verbose] <ref>@{<specifier>}...")
|
" [--dry-run | -n] [--verbose] <ref>@{<specifier>}...")
|
||||||
|
|
||||||
#define BUILTIN_REFLOG_EXISTS_USAGE \
|
|
||||||
N_("git reflog exists <ref>")
|
|
||||||
|
|
||||||
#define BUILTIN_REFLOG_DROP_USAGE \
|
#define BUILTIN_REFLOG_DROP_USAGE \
|
||||||
N_("git reflog drop [--all [--single-worktree] | <refs>...]")
|
N_("git reflog drop [--all [--single-worktree] | <refs>...]")
|
||||||
|
|
||||||
|
#define BUILTIN_REFLOG_EXPIRE_USAGE \
|
||||||
|
N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \
|
||||||
|
" [--rewrite] [--updateref] [--stale-fix]\n" \
|
||||||
|
" [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]")
|
||||||
|
|
||||||
static const char *const reflog_show_usage[] = {
|
static const char *const reflog_show_usage[] = {
|
||||||
BUILTIN_REFLOG_SHOW_USAGE,
|
BUILTIN_REFLOG_SHOW_USAGE,
|
||||||
NULL,
|
NULL,
|
||||||
|
|
@ -42,9 +47,14 @@ static const char *const reflog_list_usage[] = {
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *const reflog_expire_usage[] = {
|
static const char *const reflog_exists_usage[] = {
|
||||||
BUILTIN_REFLOG_EXPIRE_USAGE,
|
BUILTIN_REFLOG_EXISTS_USAGE,
|
||||||
NULL
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const reflog_write_usage[] = {
|
||||||
|
BUILTIN_REFLOG_WRITE_USAGE,
|
||||||
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *const reflog_delete_usage[] = {
|
static const char *const reflog_delete_usage[] = {
|
||||||
|
|
@ -52,23 +62,24 @@ static const char *const reflog_delete_usage[] = {
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *const reflog_exists_usage[] = {
|
|
||||||
BUILTIN_REFLOG_EXISTS_USAGE,
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *const reflog_drop_usage[] = {
|
static const char *const reflog_drop_usage[] = {
|
||||||
BUILTIN_REFLOG_DROP_USAGE,
|
BUILTIN_REFLOG_DROP_USAGE,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char *const reflog_expire_usage[] = {
|
||||||
|
BUILTIN_REFLOG_EXPIRE_USAGE,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
static const char *const reflog_usage[] = {
|
static const char *const reflog_usage[] = {
|
||||||
BUILTIN_REFLOG_SHOW_USAGE,
|
BUILTIN_REFLOG_SHOW_USAGE,
|
||||||
BUILTIN_REFLOG_LIST_USAGE,
|
BUILTIN_REFLOG_LIST_USAGE,
|
||||||
BUILTIN_REFLOG_EXPIRE_USAGE,
|
BUILTIN_REFLOG_EXISTS_USAGE,
|
||||||
|
BUILTIN_REFLOG_WRITE_USAGE,
|
||||||
BUILTIN_REFLOG_DELETE_USAGE,
|
BUILTIN_REFLOG_DELETE_USAGE,
|
||||||
BUILTIN_REFLOG_DROP_USAGE,
|
BUILTIN_REFLOG_DROP_USAGE,
|
||||||
BUILTIN_REFLOG_EXISTS_USAGE,
|
BUILTIN_REFLOG_EXPIRE_USAGE,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -395,6 +406,59 @@ static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cmd_reflog_write(int argc, const char **argv, const char *prefix,
|
||||||
|
struct repository *repo)
|
||||||
|
{
|
||||||
|
const struct option options[] = {
|
||||||
|
OPT_END()
|
||||||
|
};
|
||||||
|
struct object_id old_oid, new_oid;
|
||||||
|
struct strbuf err = STRBUF_INIT;
|
||||||
|
struct ref_transaction *tx;
|
||||||
|
const char *ref, *message;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
argc = parse_options(argc, argv, prefix, options, reflog_write_usage, 0);
|
||||||
|
if (argc != 4)
|
||||||
|
usage_with_options(reflog_write_usage, options);
|
||||||
|
|
||||||
|
ref = argv[0];
|
||||||
|
if (!is_root_ref(ref) && check_refname_format(ref, 0))
|
||||||
|
die(_("invalid reference name: %s"), ref);
|
||||||
|
|
||||||
|
ret = get_oid_hex_algop(argv[1], &old_oid, repo->hash_algo);
|
||||||
|
if (ret)
|
||||||
|
die(_("invalid old object ID: '%s'"), argv[1]);
|
||||||
|
if (!is_null_oid(&old_oid) && !odb_has_object(repo->objects, &old_oid, 0))
|
||||||
|
die(_("old object '%s' does not exist"), argv[1]);
|
||||||
|
|
||||||
|
ret = get_oid_hex_algop(argv[2], &new_oid, repo->hash_algo);
|
||||||
|
if (ret)
|
||||||
|
die(_("invalid new object ID: '%s'"), argv[2]);
|
||||||
|
if (!is_null_oid(&new_oid) && !odb_has_object(repo->objects, &new_oid, 0))
|
||||||
|
die(_("new object '%s' does not exist"), argv[2]);
|
||||||
|
|
||||||
|
message = argv[3];
|
||||||
|
|
||||||
|
tx = ref_store_transaction_begin(get_main_ref_store(repo), 0, &err);
|
||||||
|
if (!tx)
|
||||||
|
die(_("cannot start transaction: %s"), err.buf);
|
||||||
|
|
||||||
|
ret = ref_transaction_update_reflog(tx, ref, &new_oid, &old_oid,
|
||||||
|
git_committer_info(0),
|
||||||
|
message, 0, &err);
|
||||||
|
if (ret)
|
||||||
|
die(_("cannot queue reflog update: %s"), err.buf);
|
||||||
|
|
||||||
|
ret = ref_transaction_commit(tx, &err);
|
||||||
|
if (ret)
|
||||||
|
die(_("cannot commit reflog update: %s"), err.buf);
|
||||||
|
|
||||||
|
ref_transaction_free(tx);
|
||||||
|
strbuf_release(&err);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* main "reflog"
|
* main "reflog"
|
||||||
*/
|
*/
|
||||||
|
|
@ -407,10 +471,11 @@ int cmd_reflog(int argc,
|
||||||
struct option options[] = {
|
struct option options[] = {
|
||||||
OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
|
OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
|
||||||
OPT_SUBCOMMAND("list", &fn, cmd_reflog_list),
|
OPT_SUBCOMMAND("list", &fn, cmd_reflog_list),
|
||||||
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
|
|
||||||
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
|
|
||||||
OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
|
OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
|
||||||
|
OPT_SUBCOMMAND("write", &fn, cmd_reflog_write),
|
||||||
|
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
|
||||||
OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop),
|
OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop),
|
||||||
|
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
2
ident.c
2
ident.c
|
|
@ -272,7 +272,7 @@ static void strbuf_addstr_without_crud(struct strbuf *sb, const char *src)
|
||||||
* can still be NULL if the input line only has the name/email part
|
* can still be NULL if the input line only has the name/email part
|
||||||
* (e.g. reading from a reflog entry).
|
* (e.g. reading from a reflog entry).
|
||||||
*/
|
*/
|
||||||
int split_ident_line(struct ident_split *split, const char *line, int len)
|
int split_ident_line(struct ident_split *split, const char *line, size_t len)
|
||||||
{
|
{
|
||||||
const char *cp;
|
const char *cp;
|
||||||
size_t span;
|
size_t span;
|
||||||
|
|
|
||||||
2
ident.h
2
ident.h
|
|
@ -35,7 +35,7 @@ void reset_ident_date(void);
|
||||||
* Signals an success with 0, but time part of the result may be NULL
|
* Signals an success with 0, but time part of the result may be NULL
|
||||||
* if the input lacks timestamp and zone
|
* if the input lacks timestamp and zone
|
||||||
*/
|
*/
|
||||||
int split_ident_line(struct ident_split *, const char *, int);
|
int split_ident_line(struct ident_split *, const char *, size_t);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a commit or tag object buffer and the commit or tag headers, replaces
|
* Given a commit or tag object buffer and the commit or tag headers, replaces
|
||||||
|
|
|
||||||
60
refs.c
60
refs.c
|
|
@ -1362,27 +1362,22 @@ int ref_transaction_update(struct ref_transaction *transaction,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
int ref_transaction_update_reflog(struct ref_transaction *transaction,
|
||||||
* Similar to`ref_transaction_update`, but this function is only for adding
|
const char *refname,
|
||||||
* a reflog update. Supports providing custom committer information. The index
|
const struct object_id *new_oid,
|
||||||
* field can be utiltized to order updates as desired. When not used, the
|
const struct object_id *old_oid,
|
||||||
* updates default to being ordered by refname.
|
const char *committer_info,
|
||||||
*/
|
const char *msg,
|
||||||
static int ref_transaction_update_reflog(struct ref_transaction *transaction,
|
uint64_t index,
|
||||||
const char *refname,
|
struct strbuf *err)
|
||||||
const struct object_id *new_oid,
|
|
||||||
const struct object_id *old_oid,
|
|
||||||
const char *committer_info,
|
|
||||||
unsigned int flags,
|
|
||||||
const char *msg,
|
|
||||||
uint64_t index,
|
|
||||||
struct strbuf *err)
|
|
||||||
{
|
{
|
||||||
struct ref_update *update;
|
struct ref_update *update;
|
||||||
|
unsigned int flags;
|
||||||
|
|
||||||
assert(err);
|
assert(err);
|
||||||
|
|
||||||
flags |= REF_LOG_ONLY | REF_FORCE_CREATE_REFLOG | REF_NO_DEREF;
|
flags = REF_HAVE_OLD | REF_HAVE_NEW | REF_LOG_ONLY | REF_FORCE_CREATE_REFLOG | REF_NO_DEREF |
|
||||||
|
REF_LOG_USE_PROVIDED_OIDS;
|
||||||
|
|
||||||
if (!transaction_refname_valid(refname, new_oid, flags, err))
|
if (!transaction_refname_valid(refname, new_oid, flags, err))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -1390,11 +1385,6 @@ static int ref_transaction_update_reflog(struct ref_transaction *transaction,
|
||||||
update = ref_transaction_add_update(transaction, refname, flags,
|
update = ref_transaction_add_update(transaction, refname, flags,
|
||||||
new_oid, old_oid, NULL, NULL,
|
new_oid, old_oid, NULL, NULL,
|
||||||
committer_info, msg);
|
committer_info, msg);
|
||||||
/*
|
|
||||||
* While we do set the old_oid value, we unset the flag to skip
|
|
||||||
* old_oid verification which only makes sense for refs.
|
|
||||||
*/
|
|
||||||
update->flags &= ~REF_HAVE_OLD;
|
|
||||||
update->index = index;
|
update->index = index;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -2951,7 +2941,7 @@ struct migration_data {
|
||||||
struct ref_store *old_refs;
|
struct ref_store *old_refs;
|
||||||
struct ref_transaction *transaction;
|
struct ref_transaction *transaction;
|
||||||
struct strbuf *errbuf;
|
struct strbuf *errbuf;
|
||||||
struct strbuf sb;
|
struct strbuf sb, name, mail;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
|
static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
|
||||||
|
|
@ -2990,7 +2980,7 @@ struct reflog_migration_data {
|
||||||
struct ref_store *old_refs;
|
struct ref_store *old_refs;
|
||||||
struct ref_transaction *transaction;
|
struct ref_transaction *transaction;
|
||||||
struct strbuf *errbuf;
|
struct strbuf *errbuf;
|
||||||
struct strbuf *sb;
|
struct strbuf *sb, *name, *mail;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int migrate_one_reflog_entry(struct object_id *old_oid,
|
static int migrate_one_reflog_entry(struct object_id *old_oid,
|
||||||
|
|
@ -3000,18 +2990,25 @@ static int migrate_one_reflog_entry(struct object_id *old_oid,
|
||||||
const char *msg, void *cb_data)
|
const char *msg, void *cb_data)
|
||||||
{
|
{
|
||||||
struct reflog_migration_data *data = cb_data;
|
struct reflog_migration_data *data = cb_data;
|
||||||
|
struct ident_split ident;
|
||||||
const char *date;
|
const char *date;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (split_ident_line(&ident, committer, strlen(committer)) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
strbuf_reset(data->name);
|
||||||
|
strbuf_add(data->name, ident.name_begin, ident.name_end - ident.name_begin);
|
||||||
|
strbuf_reset(data->mail);
|
||||||
|
strbuf_add(data->mail, ident.mail_begin, ident.mail_end - ident.mail_begin);
|
||||||
|
|
||||||
date = show_date(timestamp, tz, DATE_MODE(NORMAL));
|
date = show_date(timestamp, tz, DATE_MODE(NORMAL));
|
||||||
strbuf_reset(data->sb);
|
strbuf_reset(data->sb);
|
||||||
/* committer contains name and email */
|
strbuf_addstr(data->sb, fmt_ident(data->name->buf, data->mail->buf, WANT_BLANK_IDENT, date, 0));
|
||||||
strbuf_addstr(data->sb, fmt_ident("", committer, WANT_BLANK_IDENT, date, 0));
|
|
||||||
|
|
||||||
ret = ref_transaction_update_reflog(data->transaction, data->refname,
|
ret = ref_transaction_update_reflog(data->transaction, data->refname,
|
||||||
new_oid, old_oid, data->sb->buf,
|
new_oid, old_oid, data->sb->buf,
|
||||||
REF_HAVE_NEW | REF_HAVE_OLD, msg,
|
msg, data->index++, data->errbuf);
|
||||||
data->index++, data->errbuf);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3024,6 +3021,8 @@ static int migrate_one_reflog(const char *refname, void *cb_data)
|
||||||
.transaction = migration_data->transaction,
|
.transaction = migration_data->transaction,
|
||||||
.errbuf = migration_data->errbuf,
|
.errbuf = migration_data->errbuf,
|
||||||
.sb = &migration_data->sb,
|
.sb = &migration_data->sb,
|
||||||
|
.name = &migration_data->name,
|
||||||
|
.mail = &migration_data->mail,
|
||||||
};
|
};
|
||||||
|
|
||||||
return refs_for_each_reflog_ent(migration_data->old_refs, refname,
|
return refs_for_each_reflog_ent(migration_data->old_refs, refname,
|
||||||
|
|
@ -3122,6 +3121,8 @@ int repo_migrate_ref_storage_format(struct repository *repo,
|
||||||
struct strbuf new_gitdir = STRBUF_INIT;
|
struct strbuf new_gitdir = STRBUF_INIT;
|
||||||
struct migration_data data = {
|
struct migration_data data = {
|
||||||
.sb = STRBUF_INIT,
|
.sb = STRBUF_INIT,
|
||||||
|
.name = STRBUF_INIT,
|
||||||
|
.mail = STRBUF_INIT,
|
||||||
};
|
};
|
||||||
int did_migrate_refs = 0;
|
int did_migrate_refs = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
@ -3297,11 +3298,16 @@ done:
|
||||||
ref_transaction_free(transaction);
|
ref_transaction_free(transaction);
|
||||||
strbuf_release(&new_gitdir);
|
strbuf_release(&new_gitdir);
|
||||||
strbuf_release(&data.sb);
|
strbuf_release(&data.sb);
|
||||||
|
strbuf_release(&data.name);
|
||||||
|
strbuf_release(&data.mail);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ref_update_expects_existing_old_ref(struct ref_update *update)
|
int ref_update_expects_existing_old_ref(struct ref_update *update)
|
||||||
{
|
{
|
||||||
|
if (update->flags & REF_LOG_ONLY)
|
||||||
|
return 0;
|
||||||
|
|
||||||
return (update->flags & REF_HAVE_OLD) &&
|
return (update->flags & REF_HAVE_OLD) &&
|
||||||
(!is_null_oid(&update->old_oid) || update->old_target);
|
(!is_null_oid(&update->old_oid) || update->old_target);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
refs.h
24
refs.h
|
|
@ -759,13 +759,20 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
|
||||||
*/
|
*/
|
||||||
#define REF_SKIP_CREATE_REFLOG (1 << 12)
|
#define REF_SKIP_CREATE_REFLOG (1 << 12)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When writing a REF_LOG_ONLY record, use the old and new object IDs provided
|
||||||
|
* in the update instead of resolving the old object ID. The caller must also
|
||||||
|
* set both REF_HAVE_OLD and REF_HAVE_NEW.
|
||||||
|
*/
|
||||||
|
#define REF_LOG_USE_PROVIDED_OIDS (1 << 13)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bitmask of all of the flags that are allowed to be passed in to
|
* Bitmask of all of the flags that are allowed to be passed in to
|
||||||
* ref_transaction_update() and friends:
|
* ref_transaction_update() and friends:
|
||||||
*/
|
*/
|
||||||
#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \
|
#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \
|
||||||
(REF_NO_DEREF | REF_FORCE_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION | \
|
(REF_NO_DEREF | REF_FORCE_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION | \
|
||||||
REF_SKIP_REFNAME_VERIFICATION | REF_SKIP_CREATE_REFLOG)
|
REF_SKIP_REFNAME_VERIFICATION | REF_SKIP_CREATE_REFLOG | REF_LOG_USE_PROVIDED_OIDS)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a reference update to transaction. `new_oid` is the value that
|
* Add a reference update to transaction. `new_oid` is the value that
|
||||||
|
|
@ -794,6 +801,21 @@ int ref_transaction_update(struct ref_transaction *transaction,
|
||||||
unsigned int flags, const char *msg,
|
unsigned int flags, const char *msg,
|
||||||
struct strbuf *err);
|
struct strbuf *err);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Similar to `ref_transaction_update`, but this function is only for adding
|
||||||
|
* a reflog update. Supports providing custom committer information. The index
|
||||||
|
* field can be utiltized to order updates as desired. When set to zero, the
|
||||||
|
* updates default to being ordered by refname.
|
||||||
|
*/
|
||||||
|
int ref_transaction_update_reflog(struct ref_transaction *transaction,
|
||||||
|
const char *refname,
|
||||||
|
const struct object_id *new_oid,
|
||||||
|
const struct object_id *old_oid,
|
||||||
|
const char *committer_info,
|
||||||
|
const char *msg,
|
||||||
|
uint64_t index,
|
||||||
|
struct strbuf *err);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a reference creation to transaction. new_oid is the value that
|
* Add a reference creation to transaction. new_oid is the value that
|
||||||
* the reference should have after the update; it must not be
|
* the reference should have after the update; it must not be
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,12 @@
|
||||||
*/
|
*/
|
||||||
#define REF_DELETED_RMDIR (1 << 9)
|
#define REF_DELETED_RMDIR (1 << 9)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used to indicate that the reflog-only update has been created via
|
||||||
|
* `split_head_update()`.
|
||||||
|
*/
|
||||||
|
#define REF_LOG_VIA_SPLIT (1 << 14)
|
||||||
|
|
||||||
struct ref_lock {
|
struct ref_lock {
|
||||||
char *ref_name;
|
char *ref_name;
|
||||||
struct lock_file lk;
|
struct lock_file lk;
|
||||||
|
|
@ -2421,9 +2427,10 @@ static enum ref_transaction_error split_head_update(struct ref_update *update,
|
||||||
|
|
||||||
new_update = ref_transaction_add_update(
|
new_update = ref_transaction_add_update(
|
||||||
transaction, "HEAD",
|
transaction, "HEAD",
|
||||||
update->flags | REF_LOG_ONLY | REF_NO_DEREF,
|
update->flags | REF_LOG_ONLY | REF_NO_DEREF | REF_LOG_VIA_SPLIT,
|
||||||
&update->new_oid, &update->old_oid,
|
&update->new_oid, &update->old_oid,
|
||||||
NULL, NULL, update->committer_info, update->msg);
|
NULL, NULL, update->committer_info, update->msg);
|
||||||
|
new_update->parent_update = update;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add "HEAD". This insertion is O(N) in the transaction
|
* Add "HEAD". This insertion is O(N) in the transaction
|
||||||
|
|
@ -2494,7 +2501,6 @@ static enum ref_transaction_error split_symref_update(struct ref_update *update,
|
||||||
* done when new_update is processed.
|
* done when new_update is processed.
|
||||||
*/
|
*/
|
||||||
update->flags |= REF_LOG_ONLY | REF_NO_DEREF;
|
update->flags |= REF_LOG_ONLY | REF_NO_DEREF;
|
||||||
update->flags &= ~REF_HAVE_OLD;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -2509,8 +2515,9 @@ static enum ref_transaction_error check_old_oid(struct ref_update *update,
|
||||||
struct object_id *oid,
|
struct object_id *oid,
|
||||||
struct strbuf *err)
|
struct strbuf *err)
|
||||||
{
|
{
|
||||||
if (!(update->flags & REF_HAVE_OLD) ||
|
if (update->flags & REF_LOG_ONLY ||
|
||||||
oideq(oid, &update->old_oid))
|
!(update->flags & REF_HAVE_OLD) ||
|
||||||
|
oideq(oid, &update->old_oid))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (is_null_oid(&update->old_oid)) {
|
if (is_null_oid(&update->old_oid)) {
|
||||||
|
|
@ -2601,7 +2608,36 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re
|
||||||
|
|
||||||
update->backend_data = lock;
|
update->backend_data = lock;
|
||||||
|
|
||||||
if (update->type & REF_ISSYMREF) {
|
if (update->flags & REF_LOG_VIA_SPLIT) {
|
||||||
|
struct ref_lock *parent_lock;
|
||||||
|
|
||||||
|
if (!update->parent_update)
|
||||||
|
BUG("split update without a parent");
|
||||||
|
|
||||||
|
parent_lock = update->parent_update->backend_data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check that "HEAD" didn't racily change since we have looked
|
||||||
|
* it up. If it did we must refuse to write the reflog entry.
|
||||||
|
*
|
||||||
|
* Note that this does not catch all races: if "HEAD" was
|
||||||
|
* racily changed to point to one of the refs part of the
|
||||||
|
* transaction then we would miss writing the split reflog
|
||||||
|
* entry for "HEAD".
|
||||||
|
*/
|
||||||
|
if (!(update->type & REF_ISSYMREF) ||
|
||||||
|
strcmp(update->parent_update->refname, referent.buf)) {
|
||||||
|
strbuf_addstr(err, "HEAD has been racily updated");
|
||||||
|
ret = REF_TRANSACTION_ERROR_GENERIC;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update->flags & REF_HAVE_OLD) {
|
||||||
|
oidcpy(&lock->old_oid, &update->old_oid);
|
||||||
|
} else {
|
||||||
|
oidcpy(&lock->old_oid, &parent_lock->old_oid);
|
||||||
|
}
|
||||||
|
} else if (update->type & REF_ISSYMREF) {
|
||||||
if (update->flags & REF_NO_DEREF) {
|
if (update->flags & REF_NO_DEREF) {
|
||||||
/*
|
/*
|
||||||
* We won't be reading the referent as part of
|
* We won't be reading the referent as part of
|
||||||
|
|
@ -2977,6 +3013,20 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
|
||||||
struct ref_lock *lock,
|
struct ref_lock *lock,
|
||||||
struct strbuf *err)
|
struct strbuf *err)
|
||||||
{
|
{
|
||||||
|
struct object_id *old_oid = &lock->old_oid;
|
||||||
|
|
||||||
|
if (update->flags & REF_LOG_USE_PROVIDED_OIDS) {
|
||||||
|
if (!(update->flags & REF_HAVE_OLD) ||
|
||||||
|
!(update->flags & REF_HAVE_NEW) ||
|
||||||
|
!(update->flags & REF_LOG_ONLY)) {
|
||||||
|
strbuf_addf(err, _("trying to write reflog for '%s'"
|
||||||
|
"with incomplete values"), update->refname);
|
||||||
|
return REF_TRANSACTION_ERROR_GENERIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_oid = &update->old_oid;
|
||||||
|
}
|
||||||
|
|
||||||
if (update->new_target) {
|
if (update->new_target) {
|
||||||
/*
|
/*
|
||||||
* We want to get the resolved OID for the target, to ensure
|
* We want to get the resolved OID for the target, to ensure
|
||||||
|
|
@ -2994,7 +3044,7 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid,
|
if (files_log_ref_write(refs, lock->ref_name, old_oid,
|
||||||
&update->new_oid, update->committer_info,
|
&update->new_oid, update->committer_info,
|
||||||
update->msg, update->flags, err)) {
|
update->msg, update->flags, err)) {
|
||||||
char *old_msg = strbuf_detach(err, NULL);
|
char *old_msg = strbuf_detach(err, NULL);
|
||||||
|
|
@ -3062,7 +3112,8 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
|
||||||
for (i = 0; i < transaction->nr; i++) {
|
for (i = 0; i < transaction->nr; i++) {
|
||||||
struct ref_update *update = transaction->updates[i];
|
struct ref_update *update = transaction->updates[i];
|
||||||
|
|
||||||
if ((update->flags & REF_HAVE_OLD) &&
|
if (!(update->flags & REF_LOG_ONLY) &&
|
||||||
|
(update->flags & REF_HAVE_OLD) &&
|
||||||
!is_null_oid(&update->old_oid))
|
!is_null_oid(&update->old_oid))
|
||||||
BUG("initial ref transaction with old_sha1 set");
|
BUG("initial ref transaction with old_sha1 set");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -662,7 +662,8 @@ enum ref_transaction_error ref_update_check_old_target(const char *referent,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the ref must exist, this means that the old_oid or
|
* Check if the ref must exist, this means that the old_oid or
|
||||||
* old_target is non NULL.
|
* old_target is non NULL. Log-only updates never require the old state to
|
||||||
|
* match.
|
||||||
*/
|
*/
|
||||||
int ref_update_expects_existing_old_ref(struct ref_update *update);
|
int ref_update_expects_existing_old_ref(struct ref_update *update);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1102,6 +1102,20 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
|
||||||
if (ret)
|
if (ret)
|
||||||
return REF_TRANSACTION_ERROR_GENERIC;
|
return REF_TRANSACTION_ERROR_GENERIC;
|
||||||
|
|
||||||
|
if (u->flags & REF_LOG_USE_PROVIDED_OIDS) {
|
||||||
|
if (!(u->flags & REF_HAVE_OLD) ||
|
||||||
|
!(u->flags & REF_HAVE_NEW) ||
|
||||||
|
!(u->flags & REF_LOG_ONLY)) {
|
||||||
|
strbuf_addf(err, _("trying to write reflog for '%s'"
|
||||||
|
"with incomplete values"), u->refname);
|
||||||
|
return REF_TRANSACTION_ERROR_GENERIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queue_transaction_update(refs, tx_data, u, &u->old_oid, err))
|
||||||
|
return REF_TRANSACTION_ERROR_GENERIC;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Verify that the new object ID is valid. */
|
/* Verify that the new object ID is valid. */
|
||||||
if ((u->flags & REF_HAVE_NEW) && !is_null_oid(&u->new_oid) &&
|
if ((u->flags & REF_HAVE_NEW) && !is_null_oid(&u->new_oid) &&
|
||||||
!(u->flags & REF_SKIP_OID_VERIFICATION) &&
|
!(u->flags & REF_SKIP_OID_VERIFICATION) &&
|
||||||
|
|
@ -1186,8 +1200,6 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
/* The reference does not exist, but we expected it to. */
|
/* The reference does not exist, but we expected it to. */
|
||||||
strbuf_addf(err, _("cannot lock ref '%s': "
|
strbuf_addf(err, _("cannot lock ref '%s': "
|
||||||
|
|
||||||
|
|
||||||
"unable to resolve reference '%s'"),
|
"unable to resolve reference '%s'"),
|
||||||
ref_update_original_update_refname(u), u->refname);
|
ref_update_original_update_refname(u), u->refname);
|
||||||
return REF_TRANSACTION_ERROR_NONEXISTENT_REF;
|
return REF_TRANSACTION_ERROR_NONEXISTENT_REF;
|
||||||
|
|
@ -1241,13 +1253,8 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
|
||||||
|
|
||||||
new_update->parent_update = u;
|
new_update->parent_update = u;
|
||||||
|
|
||||||
/*
|
/* Change the symbolic ref update to log only. */
|
||||||
* Change the symbolic ref update to log only. Also, it
|
|
||||||
* doesn't need to check its old OID value, as that will be
|
|
||||||
* done when new_update is processed.
|
|
||||||
*/
|
|
||||||
u->flags |= REF_LOG_ONLY | REF_NO_DEREF;
|
u->flags |= REF_LOG_ONLY | REF_NO_DEREF;
|
||||||
u->flags &= ~REF_HAVE_OLD;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1271,7 +1278,8 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
|
||||||
ret = ref_update_check_old_target(referent->buf, u, err);
|
ret = ref_update_check_old_target(referent->buf, u, err);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
} else if ((u->flags & REF_HAVE_OLD) && !oideq(¤t_oid, &u->old_oid)) {
|
} else if ((u->flags & (REF_LOG_ONLY | REF_HAVE_OLD)) == REF_HAVE_OLD &&
|
||||||
|
!oideq(¤t_oid, &u->old_oid)) {
|
||||||
if (is_null_oid(&u->old_oid)) {
|
if (is_null_oid(&u->old_oid)) {
|
||||||
strbuf_addf(err, _("cannot lock ref '%s': "
|
strbuf_addf(err, _("cannot lock ref '%s': "
|
||||||
"reference already exists"),
|
"reference already exists"),
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,7 @@ integration_tests = [
|
||||||
't1418-reflog-exists.sh',
|
't1418-reflog-exists.sh',
|
||||||
't1419-exclude-refs.sh',
|
't1419-exclude-refs.sh',
|
||||||
't1420-lost-found.sh',
|
't1420-lost-found.sh',
|
||||||
|
't1421-reflog-write.sh',
|
||||||
't1430-bad-ref-name.sh',
|
't1430-bad-ref-name.sh',
|
||||||
't1450-fsck.sh',
|
't1450-fsck.sh',
|
||||||
't1451-fsck-buffer.sh',
|
't1451-fsck-buffer.sh',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='Manually write reflog entries'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
SIGNATURE="C O Mitter <committer@example.com> 1112911993 -0700"
|
||||||
|
|
||||||
|
test_reflog_matches () {
|
||||||
|
repo="$1" &&
|
||||||
|
refname="$2" &&
|
||||||
|
cat >actual &&
|
||||||
|
test-tool -C "$repo" ref-store main for-each-reflog-ent "$refname" >expected &&
|
||||||
|
test_cmp expected actual
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success 'invalid number of arguments' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
for args in "" "1" "1 2" "1 2 3" "1 2 3 4 5"
|
||||||
|
do
|
||||||
|
test_must_fail git reflog write $args 2>err &&
|
||||||
|
test_grep "usage: git reflog write" err || return 1
|
||||||
|
done
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'invalid refname' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
test_must_fail git reflog write "refs/heads/ invalid" $ZERO_OID $ZERO_OID first 2>err &&
|
||||||
|
test_grep "invalid reference name: " err
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'unqualified refname is rejected' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
test_must_fail git reflog write unqualified $ZERO_OID $ZERO_OID first 2>err &&
|
||||||
|
test_grep "invalid reference name: " err
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'nonexistent object IDs' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
test_must_fail git reflog write refs/heads/something $(test_oid deadbeef) $ZERO_OID old-object-id 2>err &&
|
||||||
|
test_grep "old object .* does not exist" err &&
|
||||||
|
test_must_fail git reflog write refs/heads/something $ZERO_OID $(test_oid deadbeef) new-object-id 2>err &&
|
||||||
|
test_grep "new object .* does not exist" err
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'abbreviated object IDs' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
test_commit initial &&
|
||||||
|
abbreviated_oid=$(git rev-parse HEAD | test_copy_bytes 8) &&
|
||||||
|
test_must_fail git reflog write refs/heads/something $abbreviated_oid $ZERO_OID old-object-id 2>err &&
|
||||||
|
test_grep "invalid old object ID" err &&
|
||||||
|
test_must_fail git reflog write refs/heads/something $ZERO_OID $abbreviated_oid new-object-id 2>err &&
|
||||||
|
test_grep "invalid new object ID" err
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'reflog message gets normalized' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
test_commit initial &&
|
||||||
|
COMMIT_OID=$(git rev-parse HEAD) &&
|
||||||
|
git reflog write HEAD $COMMIT_OID $COMMIT_OID "$(printf "message\nwith\nnewlines")" &&
|
||||||
|
git reflog show -1 --format=%gs HEAD >actual &&
|
||||||
|
echo "message with newlines" >expected &&
|
||||||
|
test_cmp expected actual
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'simple writes' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
test_commit initial &&
|
||||||
|
COMMIT_OID=$(git rev-parse HEAD) &&
|
||||||
|
|
||||||
|
git reflog write refs/heads/something $ZERO_OID $COMMIT_OID first &&
|
||||||
|
test_reflog_matches . refs/heads/something <<-EOF &&
|
||||||
|
$ZERO_OID $COMMIT_OID $SIGNATURE first
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git reflog write refs/heads/something $COMMIT_OID $COMMIT_OID second &&
|
||||||
|
test_reflog_matches . refs/heads/something <<-EOF
|
||||||
|
$ZERO_OID $COMMIT_OID $SIGNATURE first
|
||||||
|
$COMMIT_OID $COMMIT_OID $SIGNATURE second
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'can write to root ref' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
test_commit initial &&
|
||||||
|
COMMIT_OID=$(git rev-parse HEAD) &&
|
||||||
|
|
||||||
|
git reflog write ROOT_REF_HEAD $ZERO_OID $COMMIT_OID first &&
|
||||||
|
test_reflog_matches . ROOT_REF_HEAD <<-EOF
|
||||||
|
$ZERO_OID $COMMIT_OID $SIGNATURE first
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
||||||
|
|
@ -7,6 +7,17 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
||||||
|
|
||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
print_all_reflog_entries () {
|
||||||
|
repo=$1 &&
|
||||||
|
test-tool -C "$repo" ref-store main for-each-reflog >reflogs &&
|
||||||
|
while read reflog
|
||||||
|
do
|
||||||
|
echo "REFLOG: $reflog" &&
|
||||||
|
test-tool -C "$repo" ref-store main for-each-reflog-ent "$reflog" ||
|
||||||
|
return 1
|
||||||
|
done <reflogs
|
||||||
|
}
|
||||||
|
|
||||||
# Migrate the provided repository from one format to the other and
|
# Migrate the provided repository from one format to the other and
|
||||||
# verify that the references and logs are migrated over correctly.
|
# verify that the references and logs are migrated over correctly.
|
||||||
# Usage: test_migration <repo> <format> [<skip_reflog_verify> [<options...>]]
|
# Usage: test_migration <repo> <format> [<skip_reflog_verify> [<options...>]]
|
||||||
|
|
@ -28,8 +39,7 @@ test_migration () {
|
||||||
--format='%(refname) %(objectname) %(symref)' >expect &&
|
--format='%(refname) %(objectname) %(symref)' >expect &&
|
||||||
if ! $skip_reflog_verify
|
if ! $skip_reflog_verify
|
||||||
then
|
then
|
||||||
git -C "$repo" reflog --all >expect_logs &&
|
print_all_reflog_entries "$repo" >expect_logs
|
||||||
git -C "$repo" reflog list >expect_log_list
|
|
||||||
fi &&
|
fi &&
|
||||||
|
|
||||||
git -C "$repo" refs migrate --ref-format="$format" "$@" &&
|
git -C "$repo" refs migrate --ref-format="$format" "$@" &&
|
||||||
|
|
@ -39,10 +49,8 @@ test_migration () {
|
||||||
test_cmp expect actual &&
|
test_cmp expect actual &&
|
||||||
if ! $skip_reflog_verify
|
if ! $skip_reflog_verify
|
||||||
then
|
then
|
||||||
git -C "$repo" reflog --all >actual_logs &&
|
print_all_reflog_entries "$repo" >actual_logs &&
|
||||||
git -C "$repo" reflog list >actual_log_list &&
|
test_cmp expect_logs actual_logs
|
||||||
test_cmp expect_logs actual_logs &&
|
|
||||||
test_cmp expect_log_list actual_log_list
|
|
||||||
fi &&
|
fi &&
|
||||||
|
|
||||||
git -C "$repo" rev-parse --show-ref-format >actual &&
|
git -C "$repo" rev-parse --show-ref-format >actual &&
|
||||||
|
|
@ -273,7 +281,7 @@ test_expect_success 'multiple reftable blocks with multiple entries' '
|
||||||
test_commit -C repo second &&
|
test_commit -C repo second &&
|
||||||
printf "update refs/heads/ref-%d HEAD\n" $(test_seq 3000) >stdin &&
|
printf "update refs/heads/ref-%d HEAD\n" $(test_seq 3000) >stdin &&
|
||||||
git -C repo update-ref --stdin <stdin &&
|
git -C repo update-ref --stdin <stdin &&
|
||||||
test_migration repo reftable
|
test_migration repo reftable true
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'migrating from files format deletes backend files' '
|
test_expect_success 'migrating from files format deletes backend files' '
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue