]>
Commit | Line | Data |
---|---|---|
1 | #define USE_THE_REPOSITORY_VARIABLE | |
2 | ||
3 | #include "builtin.h" | |
4 | #include "config.h" | |
5 | #include "gettext.h" | |
6 | #include "revision.h" | |
7 | #include "reachable.h" | |
8 | #include "wildmatch.h" | |
9 | #include "worktree.h" | |
10 | #include "reflog.h" | |
11 | #include "refs.h" | |
12 | #include "parse-options.h" | |
13 | ||
14 | #define BUILTIN_REFLOG_SHOW_USAGE \ | |
15 | N_("git reflog [show] [<log-options>] [<ref>]") | |
16 | ||
17 | #define BUILTIN_REFLOG_LIST_USAGE \ | |
18 | N_("git reflog list") | |
19 | ||
20 | #define BUILTIN_REFLOG_EXPIRE_USAGE \ | |
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>...]") | |
24 | ||
25 | #define BUILTIN_REFLOG_DELETE_USAGE \ | |
26 | N_("git reflog delete [--rewrite] [--updateref]\n" \ | |
27 | " [--dry-run | -n] [--verbose] <ref>@{<specifier>}...") | |
28 | ||
29 | #define BUILTIN_REFLOG_EXISTS_USAGE \ | |
30 | N_("git reflog exists <ref>") | |
31 | ||
32 | #define BUILTIN_REFLOG_DROP_USAGE \ | |
33 | N_("git reflog drop [--all [--single-worktree] | <refs>...]") | |
34 | ||
35 | static const char *const reflog_show_usage[] = { | |
36 | BUILTIN_REFLOG_SHOW_USAGE, | |
37 | NULL, | |
38 | }; | |
39 | ||
40 | static const char *const reflog_list_usage[] = { | |
41 | BUILTIN_REFLOG_LIST_USAGE, | |
42 | NULL, | |
43 | }; | |
44 | ||
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 | ||
55 | static const char *const reflog_exists_usage[] = { | |
56 | BUILTIN_REFLOG_EXISTS_USAGE, | |
57 | NULL, | |
58 | }; | |
59 | ||
60 | static const char *const reflog_drop_usage[] = { | |
61 | BUILTIN_REFLOG_DROP_USAGE, | |
62 | NULL, | |
63 | }; | |
64 | ||
65 | static const char *const reflog_usage[] = { | |
66 | BUILTIN_REFLOG_SHOW_USAGE, | |
67 | BUILTIN_REFLOG_LIST_USAGE, | |
68 | BUILTIN_REFLOG_EXPIRE_USAGE, | |
69 | BUILTIN_REFLOG_DELETE_USAGE, | |
70 | BUILTIN_REFLOG_DROP_USAGE, | |
71 | BUILTIN_REFLOG_EXISTS_USAGE, | |
72 | NULL | |
73 | }; | |
74 | ||
75 | struct worktree_reflogs { | |
76 | struct worktree *worktree; | |
77 | struct string_list reflogs; | |
78 | }; | |
79 | ||
80 | static int collect_reflog(const char *ref, void *cb_data) | |
81 | { | |
82 | struct worktree_reflogs *cb = cb_data; | |
83 | struct worktree *worktree = cb->worktree; | |
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 | */ | |
90 | if (!worktree->is_current && | |
91 | parse_worktree_ref(ref, NULL, NULL, NULL) == REF_WORKTREE_SHARED) | |
92 | return 0; | |
93 | ||
94 | strbuf_worktree_ref(worktree, &newref, ref); | |
95 | string_list_append_nodup(&cb->reflogs, strbuf_detach(&newref, NULL)); | |
96 | ||
97 | return 0; | |
98 | } | |
99 | ||
100 | static int expire_unreachable_callback(const struct option *opt, | |
101 | const char *arg, | |
102 | int unset) | |
103 | { | |
104 | struct reflog_expire_options *opts = opt->value; | |
105 | ||
106 | BUG_ON_OPT_NEG(unset); | |
107 | ||
108 | if (parse_expiry_date(arg, &opts->expire_unreachable)) | |
109 | die(_("invalid timestamp '%s' given to '--%s'"), | |
110 | arg, opt->long_name); | |
111 | ||
112 | opts->explicit_expiry |= REFLOG_EXPIRE_UNREACH; | |
113 | return 0; | |
114 | } | |
115 | ||
116 | static int expire_total_callback(const struct option *opt, | |
117 | const char *arg, | |
118 | int unset) | |
119 | { | |
120 | struct reflog_expire_options *opts = opt->value; | |
121 | ||
122 | BUG_ON_OPT_NEG(unset); | |
123 | ||
124 | if (parse_expiry_date(arg, &opts->expire_total)) | |
125 | die(_("invalid timestamp '%s' given to '--%s'"), | |
126 | arg, opt->long_name); | |
127 | ||
128 | opts->explicit_expiry |= REFLOG_EXPIRE_TOTAL; | |
129 | return 0; | |
130 | } | |
131 | ||
132 | static int cmd_reflog_show(int argc, const char **argv, const char *prefix, | |
133 | struct repository *repo UNUSED) | |
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 | | |
141 | PARSE_OPT_KEEP_UNKNOWN_OPT); | |
142 | ||
143 | return cmd_log_reflog(argc, argv, prefix, the_repository); | |
144 | } | |
145 | ||
146 | static int show_reflog(const char *refname, void *cb_data UNUSED) | |
147 | { | |
148 | printf("%s\n", refname); | |
149 | return 0; | |
150 | } | |
151 | ||
152 | static int cmd_reflog_list(int argc, const char **argv, const char *prefix, | |
153 | struct repository *repo UNUSED) | |
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 | ||
170 | static int cmd_reflog_expire(int argc, const char **argv, const char *prefix, | |
171 | struct repository *repo UNUSED) | |
172 | { | |
173 | timestamp_t now = time(NULL); | |
174 | struct reflog_expire_options opts = REFLOG_EXPIRE_OPTIONS_INIT(now); | |
175 | int i, status, do_all, single_worktree = 0; | |
176 | unsigned int flags = 0; | |
177 | int verbose = 0; | |
178 | reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent; | |
179 | const struct option options[] = { | |
180 | OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"), | |
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), | |
188 | OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")), | |
189 | OPT_CALLBACK_F(0, "expire", &opts, N_("timestamp"), | |
190 | N_("prune entries older than the specified time"), | |
191 | PARSE_OPT_NONEG, | |
192 | expire_total_callback), | |
193 | OPT_CALLBACK_F(0, "expire-unreachable", &opts, N_("timestamp"), | |
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), | |
197 | OPT_BOOL(0, "stale-fix", &opts.stalefix, | |
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")), | |
200 | OPT_BOOL(0, "single-worktree", &single_worktree, | |
201 | N_("limits processing to reflogs from the current worktree only")), | |
202 | OPT_END() | |
203 | }; | |
204 | ||
205 | git_config(reflog_expire_config, &opts); | |
206 | ||
207 | save_commit_buffer = 0; | |
208 | do_all = status = 0; | |
209 | ||
210 | argc = parse_options(argc, argv, prefix, options, reflog_expire_usage, 0); | |
211 | ||
212 | if (verbose) | |
213 | should_prune_fn = should_expire_reflog_ent_verbose; | |
214 | ||
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 | */ | |
220 | if (opts.stalefix) { | |
221 | struct rev_info revs; | |
222 | ||
223 | repo_init_revisions(the_repository, &revs, prefix); | |
224 | revs.do_not_die_on_missing_objects = 1; | |
225 | revs.ignore_missing = 1; | |
226 | revs.ignore_missing_links = 1; | |
227 | if (verbose) | |
228 | printf(_("Marking reachable objects...")); | |
229 | mark_reachable_objects(&revs, 0, 0, NULL); | |
230 | release_revisions(&revs); | |
231 | if (verbose) | |
232 | putchar('\n'); | |
233 | } | |
234 | ||
235 | if (do_all) { | |
236 | struct worktree_reflogs collected = { | |
237 | .reflogs = STRING_LIST_INIT_DUP, | |
238 | }; | |
239 | struct string_list_item *item; | |
240 | struct worktree **worktrees, **p; | |
241 | ||
242 | worktrees = get_worktrees(); | |
243 | for (p = worktrees; *p; p++) { | |
244 | if (single_worktree && !(*p)->is_current) | |
245 | continue; | |
246 | collected.worktree = *p; | |
247 | refs_for_each_reflog(get_worktree_ref_store(*p), | |
248 | collect_reflog, &collected); | |
249 | } | |
250 | free_worktrees(worktrees); | |
251 | ||
252 | for_each_string_list_item(item, &collected.reflogs) { | |
253 | struct expire_reflog_policy_cb cb = { | |
254 | .opts = opts, | |
255 | .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN), | |
256 | }; | |
257 | ||
258 | reflog_expire_options_set_refname(&cb.opts, item->string); | |
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); | |
265 | } | |
266 | string_list_clear(&collected.reflogs, 0); | |
267 | } | |
268 | ||
269 | for (i = 0; i < argc; i++) { | |
270 | char *ref; | |
271 | struct expire_reflog_policy_cb cb = { .opts = opts }; | |
272 | ||
273 | if (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) { | |
274 | status |= error(_("reflog could not be found: '%s'"), argv[i]); | |
275 | continue; | |
276 | } | |
277 | reflog_expire_options_set_refname(&cb.opts, ref); | |
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); | |
284 | free(ref); | |
285 | } | |
286 | return status; | |
287 | } | |
288 | ||
289 | static int cmd_reflog_delete(int argc, const char **argv, const char *prefix, | |
290 | struct repository *repo UNUSED) | |
291 | { | |
292 | int i, status = 0; | |
293 | unsigned int flags = 0; | |
294 | int verbose = 0; | |
295 | ||
296 | const struct option options[] = { | |
297 | OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"), | |
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), | |
305 | OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")), | |
306 | OPT_END() | |
307 | }; | |
308 | ||
309 | argc = parse_options(argc, argv, prefix, options, reflog_delete_usage, 0); | |
310 | ||
311 | if (argc < 1) | |
312 | return error(_("no reflog specified to delete")); | |
313 | ||
314 | for (i = 0; i < argc; i++) | |
315 | status |= reflog_delete(argv[i], flags, verbose); | |
316 | ||
317 | return status; | |
318 | } | |
319 | ||
320 | static int cmd_reflog_exists(int argc, const char **argv, const char *prefix, | |
321 | struct repository *repo UNUSED) | |
322 | { | |
323 | struct option options[] = { | |
324 | OPT_END() | |
325 | }; | |
326 | const char *refname; | |
327 | ||
328 | argc = parse_options(argc, argv, prefix, options, reflog_exists_usage, | |
329 | 0); | |
330 | if (!argc) | |
331 | usage_with_options(reflog_exists_usage, options); | |
332 | ||
333 | refname = argv[0]; | |
334 | if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) | |
335 | die(_("invalid ref format: %s"), refname); | |
336 | return !refs_reflog_exists(get_main_ref_store(the_repository), | |
337 | refname); | |
338 | } | |
339 | ||
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 | ||
395 | /* | |
396 | * main "reflog" | |
397 | */ | |
398 | int cmd_reflog(int argc, | |
399 | const char **argv, | |
400 | const char *prefix, | |
401 | struct repository *repository) | |
402 | { | |
403 | parse_opt_subcommand_fn *fn = NULL; | |
404 | struct option options[] = { | |
405 | OPT_SUBCOMMAND("show", &fn, cmd_reflog_show), | |
406 | OPT_SUBCOMMAND("list", &fn, cmd_reflog_list), | |
407 | OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire), | |
408 | OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete), | |
409 | OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists), | |
410 | OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop), | |
411 | OPT_END() | |
412 | }; | |
413 | ||
414 | argc = parse_options(argc, argv, prefix, options, reflog_usage, | |
415 | PARSE_OPT_SUBCOMMAND_OPTIONAL | | |
416 | PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 | | |
417 | PARSE_OPT_KEEP_UNKNOWN_OPT); | |
418 | if (fn) | |
419 | return fn(argc - 1, argv + 1, prefix, repository); | |
420 | else | |
421 | return cmd_log_reflog(argc, argv, prefix, repository); | |
422 | } |