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