]> git.ipfire.org Git - thirdparty/git.git/blame - builtin/reflog.c
The eighth batch
[thirdparty/git.git] / builtin / reflog.c
CommitLineData
03eae9af 1#define USE_THE_REPOSITORY_VARIABLE
41f43b82 2
4264dc15 3#include "builtin.h"
b2141fc1 4#include "config.h"
f394e093 5#include "gettext.h"
7aa619c3 6#include "hex.h"
c3c8b691 7#include "odb.h"
1389d9dd
JH
8#include "revision.h"
9#include "reachable.h"
dd77d587 10#include "wildmatch.h"
c9ef0d95 11#include "worktree.h"
7d3d226e 12#include "reflog.h"
d699d15c 13#include "refs.h"
49fd5511 14#include "parse-options.h"
1389d9dd 15
fbc15b13
ÆAB
16#define BUILTIN_REFLOG_SHOW_USAGE \
17 N_("git reflog [show] [<log-options>] [<ref>]")
18
d699d15c
PS
19#define BUILTIN_REFLOG_LIST_USAGE \
20 N_("git reflog list")
21
649c7bb7
PS
22#define BUILTIN_REFLOG_EXISTS_USAGE \
23 N_("git reflog exists <ref>")
1e91d3fa 24
7aa619c3
PS
25#define BUILTIN_REFLOG_WRITE_USAGE \
26 N_("git reflog write <ref> <old-oid> <new-oid> <message>")
1e91d3fa
ÆAB
27
28#define BUILTIN_REFLOG_DELETE_USAGE \
cbe48529
ÆAB
29 N_("git reflog delete [--rewrite] [--updateref]\n" \
30 " [--dry-run | -n] [--verbose] <ref>@{<specifier>}...")
1e91d3fa 31
d1270689
KN
32#define BUILTIN_REFLOG_DROP_USAGE \
33 N_("git reflog drop [--all [--single-worktree] | <refs>...]")
34
649c7bb7
PS
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
fbc15b13
ÆAB
40static const char *const reflog_show_usage[] = {
41 BUILTIN_REFLOG_SHOW_USAGE,
42 NULL,
43};
44
d699d15c
PS
45static const char *const reflog_list_usage[] = {
46 BUILTIN_REFLOG_LIST_USAGE,
47 NULL,
48};
49
649c7bb7
PS
50static const char *const reflog_exists_usage[] = {
51 BUILTIN_REFLOG_EXISTS_USAGE,
52 NULL,
1e91d3fa
ÆAB
53};
54
7aa619c3
PS
55static const char *const reflog_write_usage[] = {
56 BUILTIN_REFLOG_WRITE_USAGE,
57 NULL,
1e91d3fa
ÆAB
58};
59
60static const char *const reflog_delete_usage[] = {
61 BUILTIN_REFLOG_DELETE_USAGE,
62 NULL
63};
64
d1270689
KN
65static const char *const reflog_drop_usage[] = {
66 BUILTIN_REFLOG_DROP_USAGE,
67 NULL,
68};
69
649c7bb7
PS
70static const char *const reflog_expire_usage[] = {
71 BUILTIN_REFLOG_EXPIRE_USAGE,
72 NULL
73};
74
e3c36758 75static const char *const reflog_usage[] = {
fbc15b13 76 BUILTIN_REFLOG_SHOW_USAGE,
d699d15c 77 BUILTIN_REFLOG_LIST_USAGE,
649c7bb7 78 BUILTIN_REFLOG_EXISTS_USAGE,
7aa619c3 79 BUILTIN_REFLOG_WRITE_USAGE,
e3c36758 80 BUILTIN_REFLOG_DELETE_USAGE,
d1270689 81 BUILTIN_REFLOG_DROP_USAGE,
649c7bb7 82 BUILTIN_REFLOG_EXPIRE_USAGE,
e3c36758
ÆAB
83 NULL
84};
85
f2919bae
ÆAB
86struct worktree_reflogs {
87 struct worktree *worktree;
88 struct string_list reflogs;
bda3a31c
JH
89};
90
31f89839 91static int collect_reflog(const char *ref, void *cb_data)
bda3a31c 92{
f2919bae
ÆAB
93 struct worktree_reflogs *cb = cb_data;
94 struct worktree *worktree = cb->worktree;
c9ef0d95
NTND
95 struct strbuf newref = STRBUF_INIT;
96
97 /*
98 * Avoid collecting the same shared ref multiple times because
99 * they are available via all worktrees.
100 */
71e54734
HWN
101 if (!worktree->is_current &&
102 parse_worktree_ref(ref, NULL, NULL, NULL) == REF_WORKTREE_SHARED)
c9ef0d95
NTND
103 return 0;
104
f2919bae
ÆAB
105 strbuf_worktree_ref(worktree, &newref, ref);
106 string_list_append_nodup(&cb->reflogs, strbuf_detach(&newref, NULL));
bda3a31c 107
bda3a31c
JH
108 return 0;
109}
110
33d7bdd6
JC
111static int expire_unreachable_callback(const struct option *opt,
112 const char *arg,
113 int unset)
114{
2ed80083 115 struct reflog_expire_options *opts = opt->value;
33d7bdd6 116
ee610f00
JK
117 BUG_ON_OPT_NEG(unset);
118
2ed80083 119 if (parse_expiry_date(arg, &opts->expire_unreachable))
33d7bdd6
JC
120 die(_("invalid timestamp '%s' given to '--%s'"),
121 arg, opt->long_name);
122
d20fc193 123 opts->explicit_expiry |= REFLOG_EXPIRE_UNREACH;
33d7bdd6
JC
124 return 0;
125}
126
127static int expire_total_callback(const struct option *opt,
128 const char *arg,
129 int unset)
130{
2ed80083 131 struct reflog_expire_options *opts = opt->value;
33d7bdd6 132
ee610f00
JK
133 BUG_ON_OPT_NEG(unset);
134
2ed80083 135 if (parse_expiry_date(arg, &opts->expire_total))
33d7bdd6
JC
136 die(_("invalid timestamp '%s' given to '--%s'"),
137 arg, opt->long_name);
138
d20fc193 139 opts->explicit_expiry |= REFLOG_EXPIRE_TOTAL;
33d7bdd6
JC
140 return 0;
141}
142
6f33d8e2
KN
143static int cmd_reflog_show(int argc, const char **argv, const char *prefix,
144 struct repository *repo UNUSED)
fbc15b13
ÆAB
145{
146 struct option options[] = {
147 OPT_END()
148 };
149
150 parse_options(argc, argv, prefix, options, reflog_show_usage,
151 PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 |
99d86d60 152 PARSE_OPT_KEEP_UNKNOWN_OPT);
fbc15b13 153
9b1cb507 154 return cmd_log_reflog(argc, argv, prefix, the_repository);
fbc15b13
ÆAB
155}
156
d699d15c
PS
157static int show_reflog(const char *refname, void *cb_data UNUSED)
158{
159 printf("%s\n", refname);
160 return 0;
161}
162
6f33d8e2
KN
163static int cmd_reflog_list(int argc, const char **argv, const char *prefix,
164 struct repository *repo UNUSED)
d699d15c
PS
165{
166 struct option options[] = {
167 OPT_END()
168 };
169 struct ref_store *ref_store;
170
171 argc = parse_options(argc, argv, prefix, options, reflog_list_usage, 0);
172 if (argc)
173 return error(_("%s does not accept arguments: '%s'"),
174 "list", argv[0]);
175
176 ref_store = get_main_ref_store(the_repository);
177
178 return refs_for_each_reflog(ref_store, show_reflog, NULL);
179}
180
6f33d8e2
KN
181static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
182 struct repository *repo UNUSED)
4264dc15 183{
dddbad72 184 timestamp_t now = time(NULL);
85658275 185 struct reflog_expire_options opts = REFLOG_EXPIRE_OPTIONS_INIT(now);
26d4c51d 186 int i, status, do_all, single_worktree = 0;
aba56c89 187 unsigned int flags = 0;
fcd2c3d9
ÆAB
188 int verbose = 0;
189 reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
33d7bdd6 190 const struct option options[] = {
cbf498eb 191 OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"),
33d7bdd6
JC
192 EXPIRE_REFLOGS_DRY_RUN),
193 OPT_BIT(0, "rewrite", &flags,
194 N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
195 EXPIRE_REFLOGS_REWRITE),
196 OPT_BIT(0, "updateref", &flags,
197 N_("update the reference to the value of the top reflog entry"),
198 EXPIRE_REFLOGS_UPDATE_REF),
9e1f22c8 199 OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")),
2ed80083 200 OPT_CALLBACK_F(0, "expire", &opts, N_("timestamp"),
33d7bdd6
JC
201 N_("prune entries older than the specified time"),
202 PARSE_OPT_NONEG,
203 expire_total_callback),
2ed80083 204 OPT_CALLBACK_F(0, "expire-unreachable", &opts, N_("timestamp"),
33d7bdd6
JC
205 N_("prune entries older than <time> that are not reachable from the current tip of the branch"),
206 PARSE_OPT_NONEG,
207 expire_unreachable_callback),
2ed80083 208 OPT_BOOL(0, "stale-fix", &opts.stalefix,
33d7bdd6
JC
209 N_("prune any reflog entries that point to broken commits")),
210 OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")),
26d4c51d 211 OPT_BOOL(0, "single-worktree", &single_worktree,
9e1f22c8 212 N_("limits processing to reflogs from the current worktree only")),
33d7bdd6
JC
213 OPT_END()
214 };
4264dc15 215
9ce196e8 216 repo_config(the_repository, reflog_expire_config, &opts);
4aec56d1 217
4264dc15
JH
218 save_commit_buffer = 0;
219 do_all = status = 0;
4aec56d1 220
33d7bdd6 221 argc = parse_options(argc, argv, prefix, options, reflog_expire_usage, 0);
3cb22b8e 222
fcd2c3d9
ÆAB
223 if (verbose)
224 should_prune_fn = should_expire_reflog_ent_verbose;
225
3cb22b8e
JH
226 /*
227 * We can trust the commits and objects reachable from refs
228 * even in older repository. We cannot trust what's reachable
229 * from reflog if the repository was pruned with older git.
230 */
2ed80083 231 if (opts.stalefix) {
994b328f
ÆAB
232 struct rev_info revs;
233
234 repo_init_revisions(the_repository, &revs, prefix);
ca556f47 235 revs.do_not_die_on_missing_objects = 1;
994b328f
ÆAB
236 revs.ignore_missing = 1;
237 revs.ignore_missing_links = 1;
fcd2c3d9 238 if (verbose)
dd509db3 239 printf(_("Marking reachable objects..."));
994b328f 240 mark_reachable_objects(&revs, 0, 0, NULL);
2108fe4a 241 release_revisions(&revs);
fcd2c3d9 242 if (verbose)
1389d9dd
JH
243 putchar('\n');
244 }
245
bda3a31c 246 if (do_all) {
f2919bae
ÆAB
247 struct worktree_reflogs collected = {
248 .reflogs = STRING_LIST_INIT_DUP,
249 };
250 struct string_list_item *item;
c9ef0d95 251 struct worktree **worktrees, **p;
bda3a31c 252
03f2465b 253 worktrees = get_worktrees();
c9ef0d95 254 for (p = worktrees; *p; p++) {
26d4c51d 255 if (single_worktree && !(*p)->is_current)
c9ef0d95 256 continue;
f2919bae 257 collected.worktree = *p;
c9ef0d95
NTND
258 refs_for_each_reflog(get_worktree_ref_store(*p),
259 collect_reflog, &collected);
260 }
261 free_worktrees(worktrees);
f2919bae
ÆAB
262
263 for_each_string_list_item(item, &collected.reflogs) {
fcd2c3d9 264 struct expire_reflog_policy_cb cb = {
2ed80083 265 .opts = opts,
fcd2c3d9
ÆAB
266 .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
267 };
ae35e16c 268
d20fc193 269 reflog_expire_options_set_refname(&cb.opts, item->string);
2e5c4758
PS
270 status |= refs_reflog_expire(get_main_ref_store(the_repository),
271 item->string, flags,
272 reflog_expiry_prepare,
273 should_prune_fn,
274 reflog_expiry_cleanup,
275 &cb);
bda3a31c 276 }
f2919bae 277 string_list_clear(&collected.reflogs, 0);
bda3a31c
JH
278 }
279
33d7bdd6 280 for (i = 0; i < argc; i++) {
90fb46ec 281 char *ref;
2ed80083 282 struct expire_reflog_policy_cb cb = { .opts = opts };
46fbe418 283
2bb444b1 284 if (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) {
52f2dfb0 285 status |= error(_("reflog could not be found: '%s'"), argv[i]);
4264dc15
JH
286 continue;
287 }
d20fc193 288 reflog_expire_options_set_refname(&cb.opts, ref);
2e5c4758
PS
289 status |= refs_reflog_expire(get_main_ref_store(the_repository),
290 ref, flags,
291 reflog_expiry_prepare,
292 should_prune_fn,
293 reflog_expiry_cleanup,
294 &cb);
3c815049 295 free(ref);
4264dc15 296 }
26552cb6
JK
297
298 reflog_clear_expire_config(&opts);
299
4264dc15
JH
300 return status;
301}
302
6f33d8e2
KN
303static int cmd_reflog_delete(int argc, const char **argv, const char *prefix,
304 struct repository *repo UNUSED)
552cecc2 305{
552cecc2 306 int i, status = 0;
aba56c89 307 unsigned int flags = 0;
fcd2c3d9 308 int verbose = 0;
7d3d226e 309
33d7bdd6 310 const struct option options[] = {
cbf498eb 311 OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"),
33d7bdd6
JC
312 EXPIRE_REFLOGS_DRY_RUN),
313 OPT_BIT(0, "rewrite", &flags,
314 N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
315 EXPIRE_REFLOGS_REWRITE),
316 OPT_BIT(0, "updateref", &flags,
317 N_("update the reference to the value of the top reflog entry"),
318 EXPIRE_REFLOGS_UPDATE_REF),
9e1f22c8 319 OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")),
33d7bdd6
JC
320 OPT_END()
321 };
322
323 argc = parse_options(argc, argv, prefix, options, reflog_delete_usage, 0);
3c386aa3 324
33d7bdd6 325 if (argc < 1)
dd509db3 326 return error(_("no reflog specified to delete"));
3c386aa3 327
7d3d226e
JC
328 for (i = 0; i < argc; i++)
329 status |= reflog_delete(argv[i], flags, verbose);
552cecc2 330
552cecc2
JS
331 return status;
332}
333
6f33d8e2
KN
334static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,
335 struct repository *repo UNUSED)
afcb2e7a 336{
a34393f5
ÆAB
337 struct option options[] = {
338 OPT_END()
339 };
340 const char *refname;
afcb2e7a 341
a34393f5
ÆAB
342 argc = parse_options(argc, argv, prefix, options, reflog_exists_usage,
343 0);
344 if (!argc)
345 usage_with_options(reflog_exists_usage, options);
afcb2e7a 346
a34393f5
ÆAB
347 refname = argv[0];
348 if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
349 die(_("invalid ref format: %s"), refname);
2e5c4758
PS
350 return !refs_reflog_exists(get_main_ref_store(the_repository),
351 refname);
afcb2e7a
DT
352}
353
d1270689
KN
354static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,
355 struct repository *repo)
356{
357 int ret = 0, do_all = 0, single_worktree = 0;
358 const struct option options[] = {
359 OPT_BOOL(0, "all", &do_all, N_("drop the reflogs of all references")),
360 OPT_BOOL(0, "single-worktree", &single_worktree,
361 N_("drop reflogs from the current worktree only")),
362 OPT_END()
363 };
364
365 argc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);
366
367 if (argc && do_all)
368 usage(_("references specified along with --all"));
369
370 if (do_all) {
371 struct worktree_reflogs collected = {
372 .reflogs = STRING_LIST_INIT_DUP,
373 };
374 struct string_list_item *item;
375 struct worktree **worktrees, **p;
376
377 worktrees = get_worktrees();
378 for (p = worktrees; *p; p++) {
379 if (single_worktree && !(*p)->is_current)
380 continue;
381 collected.worktree = *p;
382 refs_for_each_reflog(get_worktree_ref_store(*p),
383 collect_reflog, &collected);
384 }
385 free_worktrees(worktrees);
386
387 for_each_string_list_item(item, &collected.reflogs)
388 ret |= refs_delete_reflog(get_main_ref_store(repo),
389 item->string);
390 string_list_clear(&collected.reflogs, 0);
391
392 return ret;
393 }
394
395 for (int i = 0; i < argc; i++) {
396 char *ref;
397 if (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {
398 ret |= error(_("reflog could not be found: '%s'"), argv[i]);
399 continue;
400 }
401
402 ret |= refs_delete_reflog(get_main_ref_store(repo), ref);
403 free(ref);
404 }
405
406 return ret;
407}
408
7aa619c3
PS
409static int cmd_reflog_write(int argc, const char **argv, const char *prefix,
410 struct repository *repo)
411{
412 const struct option options[] = {
413 OPT_END()
414 };
415 struct object_id old_oid, new_oid;
416 struct strbuf err = STRBUF_INIT;
417 struct ref_transaction *tx;
418 const char *ref, *message;
419 int ret;
420
421 argc = parse_options(argc, argv, prefix, options, reflog_write_usage, 0);
422 if (argc != 4)
423 usage_with_options(reflog_write_usage, options);
424
425 ref = argv[0];
426 if (!is_root_ref(ref) && check_refname_format(ref, 0))
427 die(_("invalid reference name: %s"), ref);
428
429 ret = get_oid_hex_algop(argv[1], &old_oid, repo->hash_algo);
430 if (ret)
431 die(_("invalid old object ID: '%s'"), argv[1]);
c3c8b691 432 if (!is_null_oid(&old_oid) && !odb_has_object(repo->objects, &old_oid, 0))
7aa619c3
PS
433 die(_("old object '%s' does not exist"), argv[1]);
434
435 ret = get_oid_hex_algop(argv[2], &new_oid, repo->hash_algo);
436 if (ret)
437 die(_("invalid new object ID: '%s'"), argv[2]);
c3c8b691 438 if (!is_null_oid(&new_oid) && !odb_has_object(repo->objects, &new_oid, 0))
7aa619c3
PS
439 die(_("new object '%s' does not exist"), argv[2]);
440
441 message = argv[3];
442
443 tx = ref_store_transaction_begin(get_main_ref_store(repo), 0, &err);
444 if (!tx)
445 die(_("cannot start transaction: %s"), err.buf);
446
447 ret = ref_transaction_update_reflog(tx, ref, &new_oid, &old_oid,
448 git_committer_info(0),
449 message, 0, &err);
450 if (ret)
451 die(_("cannot queue reflog update: %s"), err.buf);
452
453 ret = ref_transaction_commit(tx, &err);
454 if (ret)
455 die(_("cannot commit reflog update: %s"), err.buf);
456
457 ref_transaction_free(tx);
458 strbuf_release(&err);
459 return 0;
460}
461
1389d9dd
JH
462/*
463 * main "reflog"
464 */
9b1cb507
JC
465int cmd_reflog(int argc,
466 const char **argv,
467 const char *prefix,
468 struct repository *repository)
4264dc15 469{
729b9733 470 parse_opt_subcommand_fn *fn = NULL;
e3c36758 471 struct option options[] = {
729b9733 472 OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
d699d15c 473 OPT_SUBCOMMAND("list", &fn, cmd_reflog_list),
729b9733 474 OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
7aa619c3 475 OPT_SUBCOMMAND("write", &fn, cmd_reflog_write),
649c7bb7 476 OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
d1270689 477 OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop),
649c7bb7 478 OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
e3c36758
ÆAB
479 OPT_END()
480 };
481
482 argc = parse_options(argc, argv, prefix, options, reflog_usage,
729b9733 483 PARSE_OPT_SUBCOMMAND_OPTIONAL |
e3c36758 484 PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 |
729b9733
SG
485 PARSE_OPT_KEEP_UNKNOWN_OPT);
486 if (fn)
6f33d8e2 487 return fn(argc - 1, argv + 1, prefix, repository);
729b9733 488 else
9b1cb507 489 return cmd_log_reflog(argc, argv, prefix, repository);
4264dc15 490}