Git fork
at reftables-rust 492 lines 14 kB view raw
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 40static const char *const reflog_show_usage[] = { 41 BUILTIN_REFLOG_SHOW_USAGE, 42 NULL, 43}; 44 45static const char *const reflog_list_usage[] = { 46 BUILTIN_REFLOG_LIST_USAGE, 47 NULL, 48}; 49 50static const char *const reflog_exists_usage[] = { 51 BUILTIN_REFLOG_EXISTS_USAGE, 52 NULL, 53}; 54 55static const char *const reflog_write_usage[] = { 56 BUILTIN_REFLOG_WRITE_USAGE, 57 NULL, 58}; 59 60static const char *const reflog_delete_usage[] = { 61 BUILTIN_REFLOG_DELETE_USAGE, 62 NULL 63}; 64 65static const char *const reflog_drop_usage[] = { 66 BUILTIN_REFLOG_DROP_USAGE, 67 NULL, 68}; 69 70static const char *const reflog_expire_usage[] = { 71 BUILTIN_REFLOG_EXPIRE_USAGE, 72 NULL 73}; 74 75static 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 86struct worktree_reflogs { 87 struct worktree *worktree; 88 struct string_list reflogs; 89}; 90 91static 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 111static 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 127static 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 143static 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 157static int show_reflog(const char *refname, void *cb_data UNUSED) 158{ 159 printf("%s\n", refname); 160 return 0; 161} 162 163static 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 181static 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 303static 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 334static 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 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 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 repo_config(repo, git_ident_config, NULL); 422 423 argc = parse_options(argc, argv, prefix, options, reflog_write_usage, 0); 424 if (argc != 4) 425 usage_with_options(reflog_write_usage, options); 426 427 ref = argv[0]; 428 if (!is_root_ref(ref) && check_refname_format(ref, 0)) 429 die(_("invalid reference name: %s"), ref); 430 431 ret = get_oid_hex_algop(argv[1], &old_oid, repo->hash_algo); 432 if (ret) 433 die(_("invalid old object ID: '%s'"), argv[1]); 434 if (!is_null_oid(&old_oid) && !odb_has_object(repo->objects, &old_oid, 0)) 435 die(_("old object '%s' does not exist"), argv[1]); 436 437 ret = get_oid_hex_algop(argv[2], &new_oid, repo->hash_algo); 438 if (ret) 439 die(_("invalid new object ID: '%s'"), argv[2]); 440 if (!is_null_oid(&new_oid) && !odb_has_object(repo->objects, &new_oid, 0)) 441 die(_("new object '%s' does not exist"), argv[2]); 442 443 message = argv[3]; 444 445 tx = ref_store_transaction_begin(get_main_ref_store(repo), 0, &err); 446 if (!tx) 447 die(_("cannot start transaction: %s"), err.buf); 448 449 ret = ref_transaction_update_reflog(tx, ref, &new_oid, &old_oid, 450 git_committer_info(0), 451 message, 0, &err); 452 if (ret) 453 die(_("cannot queue reflog update: %s"), err.buf); 454 455 ret = ref_transaction_commit(tx, &err); 456 if (ret) 457 die(_("cannot commit reflog update: %s"), err.buf); 458 459 ref_transaction_free(tx); 460 strbuf_release(&err); 461 return 0; 462} 463 464/* 465 * main "reflog" 466 */ 467int cmd_reflog(int argc, 468 const char **argv, 469 const char *prefix, 470 struct repository *repository) 471{ 472 parse_opt_subcommand_fn *fn = NULL; 473 struct option options[] = { 474 OPT_SUBCOMMAND("show", &fn, cmd_reflog_show), 475 OPT_SUBCOMMAND("list", &fn, cmd_reflog_list), 476 OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists), 477 OPT_SUBCOMMAND("write", &fn, cmd_reflog_write), 478 OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete), 479 OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop), 480 OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire), 481 OPT_END() 482 }; 483 484 argc = parse_options(argc, argv, prefix, options, reflog_usage, 485 PARSE_OPT_SUBCOMMAND_OPTIONAL | 486 PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 | 487 PARSE_OPT_KEEP_UNKNOWN_OPT); 488 if (fn) 489 return fn(argc - 1, argv + 1, prefix, repository); 490 else 491 return cmd_log_reflog(argc, argv, prefix, repository); 492}