]> git.ipfire.org Git - thirdparty/git.git/blame_incremental - builtin/reflog.c
The eighth batch
[thirdparty/git.git] / builtin / reflog.c
... / ...
CommitLineData
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
35static const char *const reflog_show_usage[] = {
36 BUILTIN_REFLOG_SHOW_USAGE,
37 NULL,
38};
39
40static const char *const reflog_list_usage[] = {
41 BUILTIN_REFLOG_LIST_USAGE,
42 NULL,
43};
44
45static const char *const reflog_expire_usage[] = {
46 BUILTIN_REFLOG_EXPIRE_USAGE,
47 NULL
48};
49
50static const char *const reflog_delete_usage[] = {
51 BUILTIN_REFLOG_DELETE_USAGE,
52 NULL
53};
54
55static const char *const reflog_exists_usage[] = {
56 BUILTIN_REFLOG_EXISTS_USAGE,
57 NULL,
58};
59
60static const char *const reflog_drop_usage[] = {
61 BUILTIN_REFLOG_DROP_USAGE,
62 NULL,
63};
64
65static 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
75struct worktree_reflogs {
76 struct worktree *worktree;
77 struct string_list reflogs;
78};
79
80static 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
100static 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
116static 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
132static 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
146static int show_reflog(const char *refname, void *cb_data UNUSED)
147{
148 printf("%s\n", refname);
149 return 0;
150}
151
152static 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
170static 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
289static 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
320static 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
340static 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 */
398int 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}