object: add object_array initializer helper function
[git/gitster.git] / builtin / reflog.c
bloba1fa0c855f4ae74188e5e258c8a7b0f9ba57ed19
1 #include "builtin.h"
2 #include "config.h"
3 #include "gettext.h"
4 #include "revision.h"
5 #include "reachable.h"
6 #include "worktree.h"
7 #include "reflog.h"
8 #include "parse-options.h"
10 #define BUILTIN_REFLOG_SHOW_USAGE \
11 N_("git reflog [show] [<log-options>] [<ref>]")
13 #define BUILTIN_REFLOG_EXPIRE_USAGE \
14 N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \
15 " [--rewrite] [--updateref] [--stale-fix]\n" \
16 " [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]")
18 #define BUILTIN_REFLOG_DELETE_USAGE \
19 N_("git reflog delete [--rewrite] [--updateref]\n" \
20 " [--dry-run | -n] [--verbose] <ref>@{<specifier>}...")
22 #define BUILTIN_REFLOG_EXISTS_USAGE \
23 N_("git reflog exists <ref>")
25 static const char *const reflog_show_usage[] = {
26 BUILTIN_REFLOG_SHOW_USAGE,
27 NULL,
30 static const char *const reflog_expire_usage[] = {
31 BUILTIN_REFLOG_EXPIRE_USAGE,
32 NULL
35 static const char *const reflog_delete_usage[] = {
36 BUILTIN_REFLOG_DELETE_USAGE,
37 NULL
40 static const char *const reflog_exists_usage[] = {
41 BUILTIN_REFLOG_EXISTS_USAGE,
42 NULL,
45 static const char *const reflog_usage[] = {
46 BUILTIN_REFLOG_SHOW_USAGE,
47 BUILTIN_REFLOG_EXPIRE_USAGE,
48 BUILTIN_REFLOG_DELETE_USAGE,
49 BUILTIN_REFLOG_EXISTS_USAGE,
50 NULL
53 static timestamp_t default_reflog_expire;
54 static timestamp_t default_reflog_expire_unreachable;
56 struct worktree_reflogs {
57 struct worktree *worktree;
58 struct string_list reflogs;
61 static int collect_reflog(const char *ref, const struct object_id *oid UNUSED,
62 int flags UNUSED, void *cb_data)
64 struct worktree_reflogs *cb = cb_data;
65 struct worktree *worktree = cb->worktree;
66 struct strbuf newref = STRBUF_INIT;
69 * Avoid collecting the same shared ref multiple times because
70 * they are available via all worktrees.
72 if (!worktree->is_current &&
73 parse_worktree_ref(ref, NULL, NULL, NULL) == REF_WORKTREE_SHARED)
74 return 0;
76 strbuf_worktree_ref(worktree, &newref, ref);
77 string_list_append_nodup(&cb->reflogs, strbuf_detach(&newref, NULL));
79 return 0;
82 static struct reflog_expire_cfg {
83 struct reflog_expire_cfg *next;
84 timestamp_t expire_total;
85 timestamp_t expire_unreachable;
86 char pattern[FLEX_ARRAY];
87 } *reflog_expire_cfg, **reflog_expire_cfg_tail;
89 static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
91 struct reflog_expire_cfg *ent;
93 if (!reflog_expire_cfg_tail)
94 reflog_expire_cfg_tail = &reflog_expire_cfg;
96 for (ent = reflog_expire_cfg; ent; ent = ent->next)
97 if (!strncmp(ent->pattern, pattern, len) &&
98 ent->pattern[len] == '\0')
99 return ent;
101 FLEX_ALLOC_MEM(ent, pattern, pattern, len);
102 *reflog_expire_cfg_tail = ent;
103 reflog_expire_cfg_tail = &(ent->next);
104 return ent;
107 /* expiry timer slot */
108 #define EXPIRE_TOTAL 01
109 #define EXPIRE_UNREACH 02
111 static int reflog_expire_config(const char *var, const char *value, void *cb)
113 const char *pattern, *key;
114 size_t pattern_len;
115 timestamp_t expire;
116 int slot;
117 struct reflog_expire_cfg *ent;
119 if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
120 return git_default_config(var, value, cb);
122 if (!strcmp(key, "reflogexpire")) {
123 slot = EXPIRE_TOTAL;
124 if (git_config_expiry_date(&expire, var, value))
125 return -1;
126 } else if (!strcmp(key, "reflogexpireunreachable")) {
127 slot = EXPIRE_UNREACH;
128 if (git_config_expiry_date(&expire, var, value))
129 return -1;
130 } else
131 return git_default_config(var, value, cb);
133 if (!pattern) {
134 switch (slot) {
135 case EXPIRE_TOTAL:
136 default_reflog_expire = expire;
137 break;
138 case EXPIRE_UNREACH:
139 default_reflog_expire_unreachable = expire;
140 break;
142 return 0;
145 ent = find_cfg_ent(pattern, pattern_len);
146 if (!ent)
147 return -1;
148 switch (slot) {
149 case EXPIRE_TOTAL:
150 ent->expire_total = expire;
151 break;
152 case EXPIRE_UNREACH:
153 ent->expire_unreachable = expire;
154 break;
156 return 0;
159 static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, const char *ref)
161 struct reflog_expire_cfg *ent;
163 if (cb->explicit_expiry == (EXPIRE_TOTAL|EXPIRE_UNREACH))
164 return; /* both given explicitly -- nothing to tweak */
166 for (ent = reflog_expire_cfg; ent; ent = ent->next) {
167 if (!wildmatch(ent->pattern, ref, 0)) {
168 if (!(cb->explicit_expiry & EXPIRE_TOTAL))
169 cb->expire_total = ent->expire_total;
170 if (!(cb->explicit_expiry & EXPIRE_UNREACH))
171 cb->expire_unreachable = ent->expire_unreachable;
172 return;
177 * If unconfigured, make stash never expire
179 if (!strcmp(ref, "refs/stash")) {
180 if (!(cb->explicit_expiry & EXPIRE_TOTAL))
181 cb->expire_total = 0;
182 if (!(cb->explicit_expiry & EXPIRE_UNREACH))
183 cb->expire_unreachable = 0;
184 return;
187 /* Nothing matched -- use the default value */
188 if (!(cb->explicit_expiry & EXPIRE_TOTAL))
189 cb->expire_total = default_reflog_expire;
190 if (!(cb->explicit_expiry & EXPIRE_UNREACH))
191 cb->expire_unreachable = default_reflog_expire_unreachable;
194 static int expire_unreachable_callback(const struct option *opt,
195 const char *arg,
196 int unset)
198 struct cmd_reflog_expire_cb *cmd = opt->value;
200 BUG_ON_OPT_NEG(unset);
202 if (parse_expiry_date(arg, &cmd->expire_unreachable))
203 die(_("invalid timestamp '%s' given to '--%s'"),
204 arg, opt->long_name);
206 cmd->explicit_expiry |= EXPIRE_UNREACH;
207 return 0;
210 static int expire_total_callback(const struct option *opt,
211 const char *arg,
212 int unset)
214 struct cmd_reflog_expire_cb *cmd = opt->value;
216 BUG_ON_OPT_NEG(unset);
218 if (parse_expiry_date(arg, &cmd->expire_total))
219 die(_("invalid timestamp '%s' given to '--%s'"),
220 arg, opt->long_name);
222 cmd->explicit_expiry |= EXPIRE_TOTAL;
223 return 0;
226 static int cmd_reflog_show(int argc, const char **argv, const char *prefix)
228 struct option options[] = {
229 OPT_END()
232 parse_options(argc, argv, prefix, options, reflog_show_usage,
233 PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 |
234 PARSE_OPT_KEEP_UNKNOWN_OPT);
236 return cmd_log_reflog(argc, argv, prefix);
239 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
241 struct cmd_reflog_expire_cb cmd = { 0 };
242 timestamp_t now = time(NULL);
243 int i, status, do_all, all_worktrees = 1;
244 unsigned int flags = 0;
245 int verbose = 0;
246 reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
247 const struct option options[] = {
248 OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
249 EXPIRE_REFLOGS_DRY_RUN),
250 OPT_BIT(0, "rewrite", &flags,
251 N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
252 EXPIRE_REFLOGS_REWRITE),
253 OPT_BIT(0, "updateref", &flags,
254 N_("update the reference to the value of the top reflog entry"),
255 EXPIRE_REFLOGS_UPDATE_REF),
256 OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")),
257 OPT_CALLBACK_F(0, "expire", &cmd, N_("timestamp"),
258 N_("prune entries older than the specified time"),
259 PARSE_OPT_NONEG,
260 expire_total_callback),
261 OPT_CALLBACK_F(0, "expire-unreachable", &cmd, N_("timestamp"),
262 N_("prune entries older than <time> that are not reachable from the current tip of the branch"),
263 PARSE_OPT_NONEG,
264 expire_unreachable_callback),
265 OPT_BOOL(0, "stale-fix", &cmd.stalefix,
266 N_("prune any reflog entries that point to broken commits")),
267 OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")),
268 OPT_BOOL(1, "single-worktree", &all_worktrees,
269 N_("limits processing to reflogs from the current worktree only")),
270 OPT_END()
273 default_reflog_expire_unreachable = now - 30 * 24 * 3600;
274 default_reflog_expire = now - 90 * 24 * 3600;
275 git_config(reflog_expire_config, NULL);
277 save_commit_buffer = 0;
278 do_all = status = 0;
280 cmd.explicit_expiry = 0;
281 cmd.expire_total = default_reflog_expire;
282 cmd.expire_unreachable = default_reflog_expire_unreachable;
284 argc = parse_options(argc, argv, prefix, options, reflog_expire_usage, 0);
286 if (verbose)
287 should_prune_fn = should_expire_reflog_ent_verbose;
290 * We can trust the commits and objects reachable from refs
291 * even in older repository. We cannot trust what's reachable
292 * from reflog if the repository was pruned with older git.
294 if (cmd.stalefix) {
295 struct rev_info revs;
297 repo_init_revisions(the_repository, &revs, prefix);
298 revs.do_not_die_on_missing_tree = 1;
299 revs.ignore_missing = 1;
300 revs.ignore_missing_links = 1;
301 if (verbose)
302 printf(_("Marking reachable objects..."));
303 mark_reachable_objects(&revs, 0, 0, NULL);
304 release_revisions(&revs);
305 if (verbose)
306 putchar('\n');
309 if (do_all) {
310 struct worktree_reflogs collected = {
311 .reflogs = STRING_LIST_INIT_DUP,
313 struct string_list_item *item;
314 struct worktree **worktrees, **p;
316 worktrees = get_worktrees();
317 for (p = worktrees; *p; p++) {
318 if (!all_worktrees && !(*p)->is_current)
319 continue;
320 collected.worktree = *p;
321 refs_for_each_reflog(get_worktree_ref_store(*p),
322 collect_reflog, &collected);
324 free_worktrees(worktrees);
326 for_each_string_list_item(item, &collected.reflogs) {
327 struct expire_reflog_policy_cb cb = {
328 .cmd = cmd,
329 .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
332 set_reflog_expiry_param(&cb.cmd, item->string);
333 status |= reflog_expire(item->string, flags,
334 reflog_expiry_prepare,
335 should_prune_fn,
336 reflog_expiry_cleanup,
337 &cb);
339 string_list_clear(&collected.reflogs, 0);
342 for (i = 0; i < argc; i++) {
343 char *ref;
344 struct expire_reflog_policy_cb cb = { .cmd = cmd };
346 if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) {
347 status |= error(_("%s points nowhere!"), argv[i]);
348 continue;
350 set_reflog_expiry_param(&cb.cmd, ref);
351 status |= reflog_expire(ref, flags,
352 reflog_expiry_prepare,
353 should_prune_fn,
354 reflog_expiry_cleanup,
355 &cb);
356 free(ref);
358 return status;
361 static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
363 int i, status = 0;
364 unsigned int flags = 0;
365 int verbose = 0;
367 const struct option options[] = {
368 OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
369 EXPIRE_REFLOGS_DRY_RUN),
370 OPT_BIT(0, "rewrite", &flags,
371 N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
372 EXPIRE_REFLOGS_REWRITE),
373 OPT_BIT(0, "updateref", &flags,
374 N_("update the reference to the value of the top reflog entry"),
375 EXPIRE_REFLOGS_UPDATE_REF),
376 OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")),
377 OPT_END()
380 argc = parse_options(argc, argv, prefix, options, reflog_delete_usage, 0);
382 if (argc < 1)
383 return error(_("no reflog specified to delete"));
385 for (i = 0; i < argc; i++)
386 status |= reflog_delete(argv[i], flags, verbose);
388 return status;
391 static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)
393 struct option options[] = {
394 OPT_END()
396 const char *refname;
398 argc = parse_options(argc, argv, prefix, options, reflog_exists_usage,
400 if (!argc)
401 usage_with_options(reflog_exists_usage, options);
403 refname = argv[0];
404 if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
405 die(_("invalid ref format: %s"), refname);
406 return !reflog_exists(refname);
410 * main "reflog"
413 int cmd_reflog(int argc, const char **argv, const char *prefix)
415 parse_opt_subcommand_fn *fn = NULL;
416 struct option options[] = {
417 OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
418 OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
419 OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
420 OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
421 OPT_END()
424 argc = parse_options(argc, argv, prefix, options, reflog_usage,
425 PARSE_OPT_SUBCOMMAND_OPTIONAL |
426 PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 |
427 PARSE_OPT_KEEP_UNKNOWN_OPT);
428 if (fn)
429 return fn(argc - 1, argv + 1, prefix);
430 else
431 return cmd_log_reflog(argc, argv, prefix);