]> git.ipfire.org Git - thirdparty/git.git/blob - builtin/reflog.c
c8f6b93d60b1e811c2f3a0ce6dd40088ca127f52
[thirdparty/git.git] / builtin / reflog.c
1 #define USE_THE_REPOSITORY_VARIABLE
2
3 #include "builtin.h"
4 #include "config.h"
5 #include "gettext.h"
6 #include "hex.h"
7 #include "odb.h"
8 #include "revision.h"
9 #include "reachable.h"
10 #include "wildmatch.h"
11 #include "worktree.h"
12 #include "reflog.h"
13 #include "refs.h"
14 #include "parse-options.h"
15
16 #define BUILTIN_REFLOG_SHOW_USAGE \
17 N_("git reflog [show] [<log-options>] [<ref>]")
18
19 #define BUILTIN_REFLOG_LIST_USAGE \
20 N_("git reflog list")
21
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>")
27
28 #define BUILTIN_REFLOG_DELETE_USAGE \
29 N_("git reflog delete [--rewrite] [--updateref]\n" \
30 " [--dry-run | -n] [--verbose] <ref>@{<specifier>}...")
31
32 #define BUILTIN_REFLOG_DROP_USAGE \
33 N_("git reflog drop [--all [--single-worktree] | <refs>...]")
34
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
40 static const char *const reflog_show_usage[] = {
41 BUILTIN_REFLOG_SHOW_USAGE,
42 NULL,
43 };
44
45 static const char *const reflog_list_usage[] = {
46 BUILTIN_REFLOG_LIST_USAGE,
47 NULL,
48 };
49
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,
58 };
59
60 static const char *const reflog_delete_usage[] = {
61 BUILTIN_REFLOG_DELETE_USAGE,
62 NULL
63 };
64
65 static const char *const reflog_drop_usage[] = {
66 BUILTIN_REFLOG_DROP_USAGE,
67 NULL,
68 };
69
70 static const char *const reflog_expire_usage[] = {
71 BUILTIN_REFLOG_EXPIRE_USAGE,
72 NULL
73 };
74
75 static const char *const reflog_usage[] = {
76 BUILTIN_REFLOG_SHOW_USAGE,
77 BUILTIN_REFLOG_LIST_USAGE,
78 BUILTIN_REFLOG_EXISTS_USAGE,
79 BUILTIN_REFLOG_WRITE_USAGE,
80 BUILTIN_REFLOG_DELETE_USAGE,
81 BUILTIN_REFLOG_DROP_USAGE,
82 BUILTIN_REFLOG_EXPIRE_USAGE,
83 NULL
84 };
85
86 struct worktree_reflogs {
87 struct worktree *worktree;
88 struct string_list reflogs;
89 };
90
91 static int collect_reflog(const char *ref, void *cb_data)
92 {
93 struct worktree_reflogs *cb = cb_data;
94 struct worktree *worktree = cb->worktree;
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 */
101 if (!worktree->is_current &&
102 parse_worktree_ref(ref, NULL, NULL, NULL) == REF_WORKTREE_SHARED)
103 return 0;
104
105 strbuf_worktree_ref(worktree, &newref, ref);
106 string_list_append_nodup(&cb->reflogs, strbuf_detach(&newref, NULL));
107
108 return 0;
109 }
110
111 static int expire_unreachable_callback(const struct option *opt,
112 const char *arg,
113 int unset)
114 {
115 struct reflog_expire_options *opts = opt->value;
116
117 BUG_ON_OPT_NEG(unset);
118
119 if (parse_expiry_date(arg, &opts->expire_unreachable))
120 die(_("invalid timestamp '%s' given to '--%s'"),
121 arg, opt->long_name);
122
123 opts->explicit_expiry |= REFLOG_EXPIRE_UNREACH;
124 return 0;
125 }
126
127 static int expire_total_callback(const struct option *opt,
128 const char *arg,
129 int unset)
130 {
131 struct reflog_expire_options *opts = opt->value;
132
133 BUG_ON_OPT_NEG(unset);
134
135 if (parse_expiry_date(arg, &opts->expire_total))
136 die(_("invalid timestamp '%s' given to '--%s'"),
137 arg, opt->long_name);
138
139 opts->explicit_expiry |= REFLOG_EXPIRE_TOTAL;
140 return 0;
141 }
142
143 static int cmd_reflog_show(int argc, const char **argv, const char *prefix,
144 struct repository *repo UNUSED)
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 |
152 PARSE_OPT_KEEP_UNKNOWN_OPT);
153
154 return cmd_log_reflog(argc, argv, prefix, the_repository);
155 }
156
157 static int show_reflog(const char *refname, void *cb_data UNUSED)
158 {
159 printf("%s\n", refname);
160 return 0;
161 }
162
163 static int cmd_reflog_list(int argc, const char **argv, const char *prefix,
164 struct repository *repo UNUSED)
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
181 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
182 struct repository *repo UNUSED)
183 {
184 timestamp_t now = time(NULL);
185 struct reflog_expire_options opts = REFLOG_EXPIRE_OPTIONS_INIT(now);
186 int i, status, do_all, single_worktree = 0;
187 unsigned int flags = 0;
188 int verbose = 0;
189 reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
190 const struct option options[] = {
191 OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"),
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),
199 OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")),
200 OPT_CALLBACK_F(0, "expire", &opts, N_("timestamp"),
201 N_("prune entries older than the specified time"),
202 PARSE_OPT_NONEG,
203 expire_total_callback),
204 OPT_CALLBACK_F(0, "expire-unreachable", &opts, N_("timestamp"),
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),
208 OPT_BOOL(0, "stale-fix", &opts.stalefix,
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")),
211 OPT_BOOL(0, "single-worktree", &single_worktree,
212 N_("limits processing to reflogs from the current worktree only")),
213 OPT_END()
214 };
215
216 repo_config(the_repository, reflog_expire_config, &opts);
217
218 save_commit_buffer = 0;
219 do_all = status = 0;
220
221 argc = parse_options(argc, argv, prefix, options, reflog_expire_usage, 0);
222
223 if (verbose)
224 should_prune_fn = should_expire_reflog_ent_verbose;
225
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 */
231 if (opts.stalefix) {
232 struct rev_info revs;
233
234 repo_init_revisions(the_repository, &revs, prefix);
235 revs.do_not_die_on_missing_objects = 1;
236 revs.ignore_missing = 1;
237 revs.ignore_missing_links = 1;
238 if (verbose)
239 printf(_("Marking reachable objects..."));
240 mark_reachable_objects(&revs, 0, 0, NULL);
241 release_revisions(&revs);
242 if (verbose)
243 putchar('\n');
244 }
245
246 if (do_all) {
247 struct worktree_reflogs collected = {
248 .reflogs = STRING_LIST_INIT_DUP,
249 };
250 struct string_list_item *item;
251 struct worktree **worktrees, **p;
252
253 worktrees = get_worktrees();
254 for (p = worktrees; *p; p++) {
255 if (single_worktree && !(*p)->is_current)
256 continue;
257 collected.worktree = *p;
258 refs_for_each_reflog(get_worktree_ref_store(*p),
259 collect_reflog, &collected);
260 }
261 free_worktrees(worktrees);
262
263 for_each_string_list_item(item, &collected.reflogs) {
264 struct expire_reflog_policy_cb cb = {
265 .opts = opts,
266 .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
267 };
268
269 reflog_expire_options_set_refname(&cb.opts, item->string);
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);
276 }
277 string_list_clear(&collected.reflogs, 0);
278 }
279
280 for (i = 0; i < argc; i++) {
281 char *ref;
282 struct expire_reflog_policy_cb cb = { .opts = opts };
283
284 if (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) {
285 status |= error(_("reflog could not be found: '%s'"), argv[i]);
286 continue;
287 }
288 reflog_expire_options_set_refname(&cb.opts, ref);
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);
295 free(ref);
296 }
297
298 reflog_clear_expire_config(&opts);
299
300 return status;
301 }
302
303 static int cmd_reflog_delete(int argc, const char **argv, const char *prefix,
304 struct repository *repo UNUSED)
305 {
306 int i, status = 0;
307 unsigned int flags = 0;
308 int verbose = 0;
309
310 const struct option options[] = {
311 OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"),
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),
319 OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")),
320 OPT_END()
321 };
322
323 argc = parse_options(argc, argv, prefix, options, reflog_delete_usage, 0);
324
325 if (argc < 1)
326 return error(_("no reflog specified to delete"));
327
328 for (i = 0; i < argc; i++)
329 status |= reflog_delete(argv[i], flags, verbose);
330
331 return status;
332 }
333
334 static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,
335 struct repository *repo UNUSED)
336 {
337 struct option options[] = {
338 OPT_END()
339 };
340 const char *refname;
341
342 argc = parse_options(argc, argv, prefix, options, reflog_exists_usage,
343 0);
344 if (!argc)
345 usage_with_options(reflog_exists_usage, options);
346
347 refname = argv[0];
348 if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
349 die(_("invalid ref format: %s"), refname);
350 return !refs_reflog_exists(get_main_ref_store(the_repository),
351 refname);
352 }
353
354 static 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
409 static 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]);
432 if (!is_null_oid(&old_oid) && !odb_has_object(repo->objects, &old_oid, 0))
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]);
438 if (!is_null_oid(&new_oid) && !odb_has_object(repo->objects, &new_oid, 0))
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
462 /*
463 * main "reflog"
464 */
465 int cmd_reflog(int argc,
466 const char **argv,
467 const char *prefix,
468 struct repository *repository)
469 {
470 parse_opt_subcommand_fn *fn = NULL;
471 struct option options[] = {
472 OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
473 OPT_SUBCOMMAND("list", &fn, cmd_reflog_list),
474 OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
475 OPT_SUBCOMMAND("write", &fn, cmd_reflog_write),
476 OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
477 OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop),
478 OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
479 OPT_END()
480 };
481
482 argc = parse_options(argc, argv, prefix, options, reflog_usage,
483 PARSE_OPT_SUBCOMMAND_OPTIONAL |
484 PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 |
485 PARSE_OPT_KEEP_UNKNOWN_OPT);
486 if (fn)
487 return fn(argc - 1, argv + 1, prefix, repository);
488 else
489 return cmd_log_reflog(argc, argv, prefix, repository);
490 }