]> git.ipfire.org Git - thirdparty/git.git/blame - builtin/reflog.c
Merge branch 'tz/lib-gpg-prereq-fix'
[thirdparty/git.git] / builtin / reflog.c
CommitLineData
4264dc15 1#include "builtin.h"
b2141fc1 2#include "config.h"
f394e093 3#include "gettext.h"
1389d9dd
JH
4#include "revision.h"
5#include "reachable.h"
c9ef0d95 6#include "worktree.h"
7d3d226e 7#include "reflog.h"
49fd5511 8#include "parse-options.h"
1389d9dd 9
fbc15b13
ÆAB
10#define BUILTIN_REFLOG_SHOW_USAGE \
11 N_("git reflog [show] [<log-options>] [<ref>]")
12
1e91d3fa 13#define BUILTIN_REFLOG_EXPIRE_USAGE \
cbe48529
ÆAB
14 N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \
15 " [--rewrite] [--updateref] [--stale-fix]\n" \
16 " [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]")
1e91d3fa
ÆAB
17
18#define BUILTIN_REFLOG_DELETE_USAGE \
cbe48529
ÆAB
19 N_("git reflog delete [--rewrite] [--updateref]\n" \
20 " [--dry-run | -n] [--verbose] <ref>@{<specifier>}...")
1e91d3fa
ÆAB
21
22#define BUILTIN_REFLOG_EXISTS_USAGE \
23 N_("git reflog exists <ref>")
24
fbc15b13
ÆAB
25static const char *const reflog_show_usage[] = {
26 BUILTIN_REFLOG_SHOW_USAGE,
27 NULL,
28};
29
1e91d3fa
ÆAB
30static const char *const reflog_expire_usage[] = {
31 BUILTIN_REFLOG_EXPIRE_USAGE,
32 NULL
33};
34
35static const char *const reflog_delete_usage[] = {
36 BUILTIN_REFLOG_DELETE_USAGE,
37 NULL
38};
39
a34393f5
ÆAB
40static const char *const reflog_exists_usage[] = {
41 BUILTIN_REFLOG_EXISTS_USAGE,
42 NULL,
43};
4264dc15 44
e3c36758 45static const char *const reflog_usage[] = {
fbc15b13 46 BUILTIN_REFLOG_SHOW_USAGE,
e3c36758
ÆAB
47 BUILTIN_REFLOG_EXPIRE_USAGE,
48 BUILTIN_REFLOG_DELETE_USAGE,
49 BUILTIN_REFLOG_EXISTS_USAGE,
50 NULL
51};
52
dddbad72
JS
53static timestamp_t default_reflog_expire;
54static timestamp_t default_reflog_expire_unreachable;
4aec56d1 55
f2919bae
ÆAB
56struct worktree_reflogs {
57 struct worktree *worktree;
58 struct string_list reflogs;
bda3a31c
JH
59};
60
5cf88fd8
ÆAB
61static int collect_reflog(const char *ref, const struct object_id *oid UNUSED,
62 int flags UNUSED, void *cb_data)
bda3a31c 63{
f2919bae
ÆAB
64 struct worktree_reflogs *cb = cb_data;
65 struct worktree *worktree = cb->worktree;
c9ef0d95
NTND
66 struct strbuf newref = STRBUF_INIT;
67
68 /*
69 * Avoid collecting the same shared ref multiple times because
70 * they are available via all worktrees.
71 */
71e54734
HWN
72 if (!worktree->is_current &&
73 parse_worktree_ref(ref, NULL, NULL, NULL) == REF_WORKTREE_SHARED)
c9ef0d95
NTND
74 return 0;
75
f2919bae
ÆAB
76 strbuf_worktree_ref(worktree, &newref, ref);
77 string_list_append_nodup(&cb->reflogs, strbuf_detach(&newref, NULL));
bda3a31c 78
bda3a31c
JH
79 return 0;
80}
81
3cb22b8e
JH
82static struct reflog_expire_cfg {
83 struct reflog_expire_cfg *next;
dddbad72
JS
84 timestamp_t expire_total;
85 timestamp_t expire_unreachable;
3cb22b8e
JH
86 char pattern[FLEX_ARRAY];
87} *reflog_expire_cfg, **reflog_expire_cfg_tail;
88
89static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
4aec56d1 90{
3cb22b8e
JH
91 struct reflog_expire_cfg *ent;
92
93 if (!reflog_expire_cfg_tail)
94 reflog_expire_cfg_tail = &reflog_expire_cfg;
95
96 for (ent = reflog_expire_cfg; ent; ent = ent->next)
c3a700fb
JK
97 if (!strncmp(ent->pattern, pattern, len) &&
98 ent->pattern[len] == '\0')
3cb22b8e
JH
99 return ent;
100
96ffc06f 101 FLEX_ALLOC_MEM(ent, pattern, pattern, len);
3cb22b8e
JH
102 *reflog_expire_cfg_tail = ent;
103 reflog_expire_cfg_tail = &(ent->next);
104 return ent;
105}
106
3cb22b8e
JH
107/* expiry timer slot */
108#define EXPIRE_TOTAL 01
109#define EXPIRE_UNREACH 02
110
111static int reflog_expire_config(const char *var, const char *value, void *cb)
112{
b3873c33 113 const char *pattern, *key;
f5914f4b 114 size_t pattern_len;
dddbad72 115 timestamp_t expire;
3cb22b8e
JH
116 int slot;
117 struct reflog_expire_cfg *ent;
118
b3873c33 119 if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
3cb22b8e
JH
120 return git_default_config(var, value, cb);
121
b3873c33 122 if (!strcmp(key, "reflogexpire")) {
3cb22b8e 123 slot = EXPIRE_TOTAL;
5f967424 124 if (git_config_expiry_date(&expire, var, value))
3cb22b8e 125 return -1;
b3873c33 126 } else if (!strcmp(key, "reflogexpireunreachable")) {
3cb22b8e 127 slot = EXPIRE_UNREACH;
5f967424 128 if (git_config_expiry_date(&expire, var, value))
3cb22b8e
JH
129 return -1;
130 } else
131 return git_default_config(var, value, cb);
132
b3873c33 133 if (!pattern) {
3cb22b8e
JH
134 switch (slot) {
135 case EXPIRE_TOTAL:
136 default_reflog_expire = expire;
137 break;
138 case EXPIRE_UNREACH:
139 default_reflog_expire_unreachable = expire;
140 break;
141 }
4f342b96
JH
142 return 0;
143 }
3cb22b8e 144
b3873c33 145 ent = find_cfg_ent(pattern, pattern_len);
3cb22b8e
JH
146 if (!ent)
147 return -1;
148 switch (slot) {
149 case EXPIRE_TOTAL:
150 ent->expire_total = expire;
151 break;
152 case EXPIRE_UNREACH:
153 ent->expire_unreachable = expire;
154 break;
155 }
156 return 0;
157}
158
33d7bdd6 159static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, const char *ref)
3cb22b8e
JH
160{
161 struct reflog_expire_cfg *ent;
162
33d7bdd6 163 if (cb->explicit_expiry == (EXPIRE_TOTAL|EXPIRE_UNREACH))
3cb22b8e
JH
164 return; /* both given explicitly -- nothing to tweak */
165
166 for (ent = reflog_expire_cfg; ent; ent = ent->next) {
55d34269 167 if (!wildmatch(ent->pattern, ref, 0)) {
33d7bdd6 168 if (!(cb->explicit_expiry & EXPIRE_TOTAL))
3cb22b8e 169 cb->expire_total = ent->expire_total;
33d7bdd6 170 if (!(cb->explicit_expiry & EXPIRE_UNREACH))
3cb22b8e
JH
171 cb->expire_unreachable = ent->expire_unreachable;
172 return;
173 }
174 }
175
60bce2bb
JH
176 /*
177 * If unconfigured, make stash never expire
178 */
179 if (!strcmp(ref, "refs/stash")) {
33d7bdd6 180 if (!(cb->explicit_expiry & EXPIRE_TOTAL))
60bce2bb 181 cb->expire_total = 0;
33d7bdd6 182 if (!(cb->explicit_expiry & EXPIRE_UNREACH))
60bce2bb
JH
183 cb->expire_unreachable = 0;
184 return;
185 }
186
3cb22b8e 187 /* Nothing matched -- use the default value */
33d7bdd6 188 if (!(cb->explicit_expiry & EXPIRE_TOTAL))
3cb22b8e 189 cb->expire_total = default_reflog_expire;
33d7bdd6 190 if (!(cb->explicit_expiry & EXPIRE_UNREACH))
3cb22b8e 191 cb->expire_unreachable = default_reflog_expire_unreachable;
4aec56d1
JH
192}
193
33d7bdd6
JC
194static int expire_unreachable_callback(const struct option *opt,
195 const char *arg,
196 int unset)
197{
198 struct cmd_reflog_expire_cb *cmd = opt->value;
199
ee610f00
JK
200 BUG_ON_OPT_NEG(unset);
201
33d7bdd6
JC
202 if (parse_expiry_date(arg, &cmd->expire_unreachable))
203 die(_("invalid timestamp '%s' given to '--%s'"),
204 arg, opt->long_name);
205
206 cmd->explicit_expiry |= EXPIRE_UNREACH;
207 return 0;
208}
209
210static int expire_total_callback(const struct option *opt,
211 const char *arg,
212 int unset)
213{
214 struct cmd_reflog_expire_cb *cmd = opt->value;
215
ee610f00
JK
216 BUG_ON_OPT_NEG(unset);
217
33d7bdd6
JC
218 if (parse_expiry_date(arg, &cmd->expire_total))
219 die(_("invalid timestamp '%s' given to '--%s'"),
220 arg, opt->long_name);
221
222 cmd->explicit_expiry |= EXPIRE_TOTAL;
223 return 0;
224}
225
fbc15b13
ÆAB
226static int cmd_reflog_show(int argc, const char **argv, const char *prefix)
227{
228 struct option options[] = {
229 OPT_END()
230 };
231
232 parse_options(argc, argv, prefix, options, reflog_show_usage,
233 PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 |
99d86d60 234 PARSE_OPT_KEEP_UNKNOWN_OPT);
fbc15b13 235
840344db 236 return cmd_log_reflog(argc, argv, prefix);
fbc15b13
ÆAB
237}
238
4264dc15
JH
239static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
240{
46fbe418 241 struct cmd_reflog_expire_cb cmd = { 0 };
dddbad72 242 timestamp_t now = time(NULL);
c9ef0d95 243 int i, status, do_all, all_worktrees = 1;
aba56c89 244 unsigned int flags = 0;
fcd2c3d9
ÆAB
245 int verbose = 0;
246 reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
33d7bdd6
JC
247 const struct option options[] = {
248 OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
249 EXPIRE_REFLOGS_DRY_RUN),
250 OPT_BIT(0, "rewrite", &flags,
251 N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
252 EXPIRE_REFLOGS_REWRITE),
253 OPT_BIT(0, "updateref", &flags,
254 N_("update the reference to the value of the top reflog entry"),
255 EXPIRE_REFLOGS_UPDATE_REF),
9e1f22c8 256 OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")),
33d7bdd6
JC
257 OPT_CALLBACK_F(0, "expire", &cmd, N_("timestamp"),
258 N_("prune entries older than the specified time"),
259 PARSE_OPT_NONEG,
260 expire_total_callback),
261 OPT_CALLBACK_F(0, "expire-unreachable", &cmd, N_("timestamp"),
262 N_("prune entries older than <time> that are not reachable from the current tip of the branch"),
263 PARSE_OPT_NONEG,
264 expire_unreachable_callback),
265 OPT_BOOL(0, "stale-fix", &cmd.stalefix,
266 N_("prune any reflog entries that point to broken commits")),
267 OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")),
268 OPT_BOOL(1, "single-worktree", &all_worktrees,
9e1f22c8 269 N_("limits processing to reflogs from the current worktree only")),
33d7bdd6
JC
270 OPT_END()
271 };
4264dc15 272
4a9f4394
AS
273 default_reflog_expire_unreachable = now - 30 * 24 * 3600;
274 default_reflog_expire = now - 90 * 24 * 3600;
ef90d6d4 275 git_config(reflog_expire_config, NULL);
4aec56d1 276
4264dc15
JH
277 save_commit_buffer = 0;
278 do_all = status = 0;
4aec56d1 279
33d7bdd6 280 cmd.explicit_expiry = 0;
46fbe418
ÆAB
281 cmd.expire_total = default_reflog_expire;
282 cmd.expire_unreachable = default_reflog_expire_unreachable;
4264dc15 283
33d7bdd6 284 argc = parse_options(argc, argv, prefix, options, reflog_expire_usage, 0);
3cb22b8e 285
fcd2c3d9
ÆAB
286 if (verbose)
287 should_prune_fn = should_expire_reflog_ent_verbose;
288
3cb22b8e
JH
289 /*
290 * We can trust the commits and objects reachable from refs
291 * even in older repository. We cannot trust what's reachable
292 * from reflog if the repository was pruned with older git.
293 */
46fbe418 294 if (cmd.stalefix) {
994b328f
ÆAB
295 struct rev_info revs;
296
297 repo_init_revisions(the_repository, &revs, prefix);
298 revs.do_not_die_on_missing_tree = 1;
299 revs.ignore_missing = 1;
300 revs.ignore_missing_links = 1;
fcd2c3d9 301 if (verbose)
dd509db3 302 printf(_("Marking reachable objects..."));
994b328f 303 mark_reachable_objects(&revs, 0, 0, NULL);
2108fe4a 304 release_revisions(&revs);
fcd2c3d9 305 if (verbose)
1389d9dd
JH
306 putchar('\n');
307 }
308
bda3a31c 309 if (do_all) {
f2919bae
ÆAB
310 struct worktree_reflogs collected = {
311 .reflogs = STRING_LIST_INIT_DUP,
312 };
313 struct string_list_item *item;
c9ef0d95 314 struct worktree **worktrees, **p;
bda3a31c 315
03f2465b 316 worktrees = get_worktrees();
c9ef0d95
NTND
317 for (p = worktrees; *p; p++) {
318 if (!all_worktrees && !(*p)->is_current)
319 continue;
f2919bae 320 collected.worktree = *p;
c9ef0d95
NTND
321 refs_for_each_reflog(get_worktree_ref_store(*p),
322 collect_reflog, &collected);
323 }
324 free_worktrees(worktrees);
f2919bae
ÆAB
325
326 for_each_string_list_item(item, &collected.reflogs) {
fcd2c3d9
ÆAB
327 struct expire_reflog_policy_cb cb = {
328 .cmd = cmd,
329 .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
330 };
ae35e16c 331
33d7bdd6 332 set_reflog_expiry_param(&cb.cmd, item->string);
f2919bae 333 status |= reflog_expire(item->string, flags,
fa5b1830 334 reflog_expiry_prepare,
fcd2c3d9 335 should_prune_fn,
fa5b1830
MH
336 reflog_expiry_cleanup,
337 &cb);
bda3a31c 338 }
f2919bae 339 string_list_clear(&collected.reflogs, 0);
bda3a31c
JH
340 }
341
33d7bdd6 342 for (i = 0; i < argc; i++) {
90fb46ec 343 char *ref;
46fbe418
ÆAB
344 struct expire_reflog_policy_cb cb = { .cmd = cmd };
345
ae35e16c 346 if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) {
dd509db3 347 status |= error(_("%s points nowhere!"), argv[i]);
4264dc15
JH
348 continue;
349 }
33d7bdd6 350 set_reflog_expiry_param(&cb.cmd, ref);
cc40b5ce 351 status |= reflog_expire(ref, flags,
fa5b1830 352 reflog_expiry_prepare,
fcd2c3d9 353 should_prune_fn,
fa5b1830
MH
354 reflog_expiry_cleanup,
355 &cb);
3c815049 356 free(ref);
4264dc15
JH
357 }
358 return status;
359}
360
552cecc2
JS
361static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
362{
552cecc2 363 int i, status = 0;
aba56c89 364 unsigned int flags = 0;
fcd2c3d9 365 int verbose = 0;
7d3d226e 366
33d7bdd6
JC
367 const struct option options[] = {
368 OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
369 EXPIRE_REFLOGS_DRY_RUN),
370 OPT_BIT(0, "rewrite", &flags,
371 N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
372 EXPIRE_REFLOGS_REWRITE),
373 OPT_BIT(0, "updateref", &flags,
374 N_("update the reference to the value of the top reflog entry"),
375 EXPIRE_REFLOGS_UPDATE_REF),
9e1f22c8 376 OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")),
33d7bdd6
JC
377 OPT_END()
378 };
379
380 argc = parse_options(argc, argv, prefix, options, reflog_delete_usage, 0);
3c386aa3 381
33d7bdd6 382 if (argc < 1)
dd509db3 383 return error(_("no reflog specified to delete"));
3c386aa3 384
7d3d226e
JC
385 for (i = 0; i < argc; i++)
386 status |= reflog_delete(argv[i], flags, verbose);
552cecc2 387
552cecc2
JS
388 return status;
389}
390
afcb2e7a
DT
391static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)
392{
a34393f5
ÆAB
393 struct option options[] = {
394 OPT_END()
395 };
396 const char *refname;
afcb2e7a 397
a34393f5
ÆAB
398 argc = parse_options(argc, argv, prefix, options, reflog_exists_usage,
399 0);
400 if (!argc)
401 usage_with_options(reflog_exists_usage, options);
afcb2e7a 402
a34393f5
ÆAB
403 refname = argv[0];
404 if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
405 die(_("invalid ref format: %s"), refname);
406 return !reflog_exists(refname);
afcb2e7a
DT
407}
408
1389d9dd
JH
409/*
410 * main "reflog"
411 */
412
4264dc15
JH
413int cmd_reflog(int argc, const char **argv, const char *prefix)
414{
729b9733 415 parse_opt_subcommand_fn *fn = NULL;
e3c36758 416 struct option options[] = {
729b9733
SG
417 OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
418 OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
419 OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
420 OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
e3c36758
ÆAB
421 OPT_END()
422 };
423
424 argc = parse_options(argc, argv, prefix, options, reflog_usage,
729b9733 425 PARSE_OPT_SUBCOMMAND_OPTIONAL |
e3c36758 426 PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 |
729b9733
SG
427 PARSE_OPT_KEEP_UNKNOWN_OPT);
428 if (fn)
429 return fn(argc - 1, argv + 1, prefix);
430 else
431 return cmd_log_reflog(argc, argv, prefix);
4264dc15 432}