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