Skip to content

Commit cf03815

Browse files
committed
Merge branch 'ps/reflog-migrate-fixes' into ps/remote-rename-fix
* 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 type
2 parents e4ef048 + 465eff8 commit cf03815

File tree

12 files changed

+413
-117
lines changed

12 files changed

+413
-117
lines changed

Documentation/git-reflog.adoc

Lines changed: 52 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@ git-reflog - Manage reflog information
88

99
SYNOPSIS
1010
--------
11-
[verse]
12-
'git reflog' [show] [<log-options>] [<ref>]
13-
'git reflog list'
14-
'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]
11+
[synopsis]
12+
git reflog [show] [<log-options>] [<ref>]
13+
git reflog list
14+
git reflog exists <ref>
15+
git reflog write <ref> <old-oid> <new-oid> <message>
16+
git reflog delete [--rewrite] [--updateref]
17+
[--dry-run | -n] [--verbose] <ref>@{<specifier>}...
18+
git reflog drop [--all [--single-worktree] | <refs>...]
19+
git reflog expire [--expire=<time>] [--expire-unreachable=<time>]
1520
[--rewrite] [--updateref] [--stale-fix]
1621
[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
17-
'git reflog delete' [--rewrite] [--updateref]
18-
[--dry-run | -n] [--verbose] <ref>@{<specifier>}...
19-
'git reflog drop' [--all [--single-worktree] | <refs>...]
20-
'git reflog exists' <ref>
2122

2223
DESCRIPTION
2324
-----------
@@ -43,11 +44,15 @@ actions, and in addition the `HEAD` reflog records branch switching.
4344

4445
The "list" subcommand lists all refs which have a corresponding reflog.
4546

46-
The "expire" subcommand prunes older reflog entries. Entries older
47-
than `expire` time, or entries older than `expire-unreachable` time
48-
and not reachable from the current tip, are removed from the reflog.
49-
This is typically not used directly by end users -- instead, see
50-
linkgit:git-gc[1].
47+
The "exists" subcommand checks whether a ref has a reflog. It exits
48+
with zero status if the reflog exists, and non-zero status if it does
49+
not.
50+
51+
The "write" subcommand writes a single entry to the reflog of a given
52+
reference. This new entry is appended to the reflog and will thus become
53+
the most recent entry. The reference name must be fully qualified. Both the old
54+
and new object IDs must not be abbreviated and must point to existing objects.
55+
The reflog message gets normalized.
5156

5257
The "delete" subcommand deletes single entries from the reflog, but
5358
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
5863
references. This is in contrast to "expire" and "delete", both of which
5964
can be used to delete reflog entries, but not the reflog itself.
6065

61-
The "exists" subcommand checks whether a ref has a reflog. It exits
62-
with zero status if the reflog exists, and non-zero status if it does
63-
not.
66+
The "expire" subcommand prunes older reflog entries. Entries older
67+
than `expire` time, or entries older than `expire-unreachable` time
68+
and not reachable from the current tip, are removed from the reflog.
69+
This is typically not used directly by end users -- instead, see
70+
linkgit:git-gc[1].
6471

6572
OPTIONS
6673
-------
@@ -71,26 +78,45 @@ Options for `show`
7178
`git reflog show` accepts any of the options accepted by `git log`.
7279

7380

81+
Options for `delete`
82+
~~~~~~~~~~~~~~~~~~~~
83+
84+
`git reflog delete` accepts options `--updateref`, `--rewrite`, `-n`,
85+
`--dry-run`, and `--verbose`, with the same meanings as when they are
86+
used with `expire`.
87+
88+
Options for `drop`
89+
~~~~~~~~~~~~~~~~~~
90+
91+
`--all`::
92+
Drop the reflogs of all references from all worktrees.
93+
94+
`--single-worktree`::
95+
By default when `--all` is specified, reflogs from all working
96+
trees are dropped. This option limits the processing to reflogs
97+
from the current working tree only.
98+
99+
74100
Options for `expire`
75101
~~~~~~~~~~~~~~~~~~~~
76102

77-
--all::
103+
`--all`::
78104
Process the reflogs of all references.
79105

80-
--single-worktree::
106+
`--single-worktree`::
81107
By default when `--all` is specified, reflogs from all working
82108
trees are processed. This option limits the processing to reflogs
83109
from the current working tree only.
84110

85-
--expire=<time>::
111+
`--expire=<time>`::
86112
Prune entries older than the specified time. If this option is
87113
not specified, the expiration time is taken from the
88114
configuration setting `gc.reflogExpire`, which in turn
89115
defaults to 90 days. `--expire=all` prunes entries regardless
90116
of their age; `--expire=never` turns off pruning of reachable
91117
entries (but see `--expire-unreachable`).
92118

93-
--expire-unreachable=<time>::
119+
`--expire-unreachable=<time>`::
94120
Prune entries older than `<time>` that are not reachable from
95121
the current tip of the branch. If this option is not
96122
specified, the expiration time is taken from the configuration
@@ -100,17 +126,17 @@ Options for `expire`
100126
turns off early pruning of unreachable entries (but see
101127
`--expire`).
102128

103-
--updateref::
129+
`--updateref`::
104130
Update the reference to the value of the top reflog entry (i.e.
105131
<ref>@\{0\}) if the previous top entry was pruned. (This
106132
option is ignored for symbolic references.)
107133

108-
--rewrite::
134+
`--rewrite`::
109135
If a reflog entry's predecessor is pruned, adjust its "old"
110136
SHA-1 to be equal to the "new" SHA-1 field of the entry that
111137
now precedes it.
112138

113-
--stale-fix::
139+
`--stale-fix`::
114140
Prune any reflog entries that point to "broken commits". A
115141
broken commit is a commit that is not reachable from any of
116142
the reference tips and that refers, directly or indirectly, to
@@ -121,33 +147,15 @@ has the same cost as 'git prune'. It is primarily intended to fix
121147
corruption caused by garbage collecting using older versions of Git,
122148
which didn't protect objects referred to by reflogs.
123149

124-
-n::
125-
--dry-run::
150+
`-n`::
151+
`--dry-run`::
126152
Do not actually prune any entries; just show what would have
127153
been pruned.
128154

129-
--verbose::
155+
`--verbose`::
130156
Print extra information on screen.
131157

132158

133-
Options for `delete`
134-
~~~~~~~~~~~~~~~~~~~~
135-
136-
`git reflog delete` accepts options `--updateref`, `--rewrite`, `-n`,
137-
`--dry-run`, and `--verbose`, with the same meanings as when they are
138-
used with `expire`.
139-
140-
Options for `drop`
141-
~~~~~~~~~~~~~~~~~~
142-
143-
--all::
144-
Drop the reflogs of all references from all worktrees.
145-
146-
--single-worktree::
147-
By default when `--all` is specified, reflogs from all working
148-
trees are dropped. This option limits the processing to reflogs
149-
from the current working tree only.
150-
151159
GIT
152160
---
153161
Part of the linkgit:git[1] suite

builtin/reflog.c

Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include "builtin.h"
44
#include "config.h"
55
#include "gettext.h"
6+
#include "hex.h"
7+
#include "odb.h"
68
#include "revision.h"
79
#include "reachable.h"
810
#include "wildmatch.h"
@@ -17,21 +19,24 @@
1719
#define BUILTIN_REFLOG_LIST_USAGE \
1820
N_("git reflog list")
1921

20-
#define BUILTIN_REFLOG_EXPIRE_USAGE \
21-
N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \
22-
" [--rewrite] [--updateref] [--stale-fix]\n" \
23-
" [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]")
22+
#define BUILTIN_REFLOG_EXISTS_USAGE \
23+
N_("git reflog exists <ref>")
24+
25+
#define BUILTIN_REFLOG_WRITE_USAGE \
26+
N_("git reflog write <ref> <old-oid> <new-oid> <message>")
2427

2528
#define BUILTIN_REFLOG_DELETE_USAGE \
2629
N_("git reflog delete [--rewrite] [--updateref]\n" \
2730
" [--dry-run | -n] [--verbose] <ref>@{<specifier>}...")
2831

29-
#define BUILTIN_REFLOG_EXISTS_USAGE \
30-
N_("git reflog exists <ref>")
31-
3232
#define BUILTIN_REFLOG_DROP_USAGE \
3333
N_("git reflog drop [--all [--single-worktree] | <refs>...]")
3434

35+
#define BUILTIN_REFLOG_EXPIRE_USAGE \
36+
N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \
37+
" [--rewrite] [--updateref] [--stale-fix]\n" \
38+
" [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]")
39+
3540
static const char *const reflog_show_usage[] = {
3641
BUILTIN_REFLOG_SHOW_USAGE,
3742
NULL,
@@ -42,33 +47,39 @@ static const char *const reflog_list_usage[] = {
4247
NULL,
4348
};
4449

45-
static const char *const reflog_expire_usage[] = {
46-
BUILTIN_REFLOG_EXPIRE_USAGE,
47-
NULL
50+
static const char *const reflog_exists_usage[] = {
51+
BUILTIN_REFLOG_EXISTS_USAGE,
52+
NULL,
53+
};
54+
55+
static const char *const reflog_write_usage[] = {
56+
BUILTIN_REFLOG_WRITE_USAGE,
57+
NULL,
4858
};
4959

5060
static const char *const reflog_delete_usage[] = {
5161
BUILTIN_REFLOG_DELETE_USAGE,
5262
NULL
5363
};
5464

55-
static const char *const reflog_exists_usage[] = {
56-
BUILTIN_REFLOG_EXISTS_USAGE,
57-
NULL,
58-
};
59-
6065
static const char *const reflog_drop_usage[] = {
6166
BUILTIN_REFLOG_DROP_USAGE,
6267
NULL,
6368
};
6469

70+
static const char *const reflog_expire_usage[] = {
71+
BUILTIN_REFLOG_EXPIRE_USAGE,
72+
NULL
73+
};
74+
6575
static const char *const reflog_usage[] = {
6676
BUILTIN_REFLOG_SHOW_USAGE,
6777
BUILTIN_REFLOG_LIST_USAGE,
68-
BUILTIN_REFLOG_EXPIRE_USAGE,
78+
BUILTIN_REFLOG_EXISTS_USAGE,
79+
BUILTIN_REFLOG_WRITE_USAGE,
6980
BUILTIN_REFLOG_DELETE_USAGE,
7081
BUILTIN_REFLOG_DROP_USAGE,
71-
BUILTIN_REFLOG_EXISTS_USAGE,
82+
BUILTIN_REFLOG_EXPIRE_USAGE,
7283
NULL
7384
};
7485

@@ -392,6 +403,59 @@ static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,
392403
return ret;
393404
}
394405

406+
static int cmd_reflog_write(int argc, const char **argv, const char *prefix,
407+
struct repository *repo)
408+
{
409+
const struct option options[] = {
410+
OPT_END()
411+
};
412+
struct object_id old_oid, new_oid;
413+
struct strbuf err = STRBUF_INIT;
414+
struct ref_transaction *tx;
415+
const char *ref, *message;
416+
int ret;
417+
418+
argc = parse_options(argc, argv, prefix, options, reflog_write_usage, 0);
419+
if (argc != 4)
420+
usage_with_options(reflog_write_usage, options);
421+
422+
ref = argv[0];
423+
if (!is_root_ref(ref) && check_refname_format(ref, 0))
424+
die(_("invalid reference name: %s"), ref);
425+
426+
ret = get_oid_hex_algop(argv[1], &old_oid, repo->hash_algo);
427+
if (ret)
428+
die(_("invalid old object ID: '%s'"), argv[1]);
429+
if (!is_null_oid(&old_oid) && !has_object(the_repository, &old_oid, 0))
430+
die(_("old object '%s' does not exist"), argv[1]);
431+
432+
ret = get_oid_hex_algop(argv[2], &new_oid, repo->hash_algo);
433+
if (ret)
434+
die(_("invalid new object ID: '%s'"), argv[2]);
435+
if (!is_null_oid(&new_oid) && !has_object(the_repository, &new_oid, 0))
436+
die(_("new object '%s' does not exist"), argv[2]);
437+
438+
message = argv[3];
439+
440+
tx = ref_store_transaction_begin(get_main_ref_store(repo), 0, &err);
441+
if (!tx)
442+
die(_("cannot start transaction: %s"), err.buf);
443+
444+
ret = ref_transaction_update_reflog(tx, ref, &new_oid, &old_oid,
445+
git_committer_info(0),
446+
message, 0, &err);
447+
if (ret)
448+
die(_("cannot queue reflog update: %s"), err.buf);
449+
450+
ret = ref_transaction_commit(tx, &err);
451+
if (ret)
452+
die(_("cannot commit reflog update: %s"), err.buf);
453+
454+
ref_transaction_free(tx);
455+
strbuf_release(&err);
456+
return 0;
457+
}
458+
395459
/*
396460
* main "reflog"
397461
*/
@@ -404,10 +468,11 @@ int cmd_reflog(int argc,
404468
struct option options[] = {
405469
OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
406470
OPT_SUBCOMMAND("list", &fn, cmd_reflog_list),
407-
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
408-
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
409471
OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
472+
OPT_SUBCOMMAND("write", &fn, cmd_reflog_write),
473+
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
410474
OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop),
475+
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
411476
OPT_END()
412477
};
413478

ident.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ static void strbuf_addstr_without_crud(struct strbuf *sb, const char *src)
272272
* can still be NULL if the input line only has the name/email part
273273
* (e.g. reading from a reflog entry).
274274
*/
275-
int split_ident_line(struct ident_split *split, const char *line, int len)
275+
int split_ident_line(struct ident_split *split, const char *line, size_t len)
276276
{
277277
const char *cp;
278278
size_t span;

ident.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ void reset_ident_date(void);
3535
* Signals an success with 0, but time part of the result may be NULL
3636
* if the input lacks timestamp and zone
3737
*/
38-
int split_ident_line(struct ident_split *, const char *, int);
38+
int split_ident_line(struct ident_split *, const char *, size_t);
3939

4040
/*
4141
* Given a commit or tag object buffer and the commit or tag headers, replaces

0 commit comments

Comments
 (0)