4 #include "parse-options.h"
5 #include "string-list.h"
7 #include "xdiff/xdiff.h"
8 #include "xdiff-interface.h"
10 static const char * const rerere_usage
[] = {
11 "git rerere [clear | status | diff | gc]",
15 /* these values are days */
16 static int cutoff_noresolve
= 15;
17 static int cutoff_resolve
= 60;
19 static time_t rerere_created_at(const char *name
)
22 return stat(rerere_path(name
, "preimage"), &st
) ? (time_t) 0 : st
.st_mtime
;
25 static time_t rerere_last_used_at(const char *name
)
28 return stat(rerere_path(name
, "postimage"), &st
) ? (time_t) 0 : st
.st_mtime
;
31 static void unlink_rr_item(const char *name
)
33 unlink(rerere_path(name
, "thisimage"));
34 unlink(rerere_path(name
, "preimage"));
35 unlink(rerere_path(name
, "postimage"));
36 rmdir(git_path("rr-cache/%s", name
));
39 static int git_rerere_gc_config(const char *var
, const char *value
, void *cb
)
41 if (!strcmp(var
, "gc.rerereresolved"))
42 cutoff_resolve
= git_config_int(var
, value
);
43 else if (!strcmp(var
, "gc.rerereunresolved"))
44 cutoff_noresolve
= git_config_int(var
, value
);
46 return git_default_config(var
, value
, cb
);
50 static void garbage_collect(struct string_list
*rr
)
52 struct string_list to_remove
= STRING_LIST_INIT_DUP
;
56 time_t now
= time(NULL
), then
;
58 git_config(git_rerere_gc_config
, NULL
);
59 dir
= opendir(git_path("rr-cache"));
61 die_errno("unable to open rr-cache directory");
62 while ((e
= readdir(dir
))) {
63 if (is_dot_or_dotdot(e
->d_name
))
66 then
= rerere_last_used_at(e
->d_name
);
68 cutoff
= cutoff_resolve
;
70 then
= rerere_created_at(e
->d_name
);
73 cutoff
= cutoff_noresolve
;
75 if (then
< now
- cutoff
* 86400)
76 string_list_append(&to_remove
, e
->d_name
);
78 for (i
= 0; i
< to_remove
.nr
; i
++)
79 unlink_rr_item(to_remove
.items
[i
].string
);
80 string_list_clear(&to_remove
, 0);
83 static int outf(void *dummy
, mmbuffer_t
*ptr
, int nbuf
)
86 for (i
= 0; i
< nbuf
; i
++)
87 if (write_in_full(1, ptr
[i
].ptr
, ptr
[i
].size
) != ptr
[i
].size
)
92 static int diff_two(const char *file1
, const char *label1
,
93 const char *file2
, const char *label2
)
100 if (read_mmfile(&minus
, file1
) || read_mmfile(&plus
, file2
))
103 printf("--- a/%s\n+++ b/%s\n", label1
, label2
);
105 memset(&xpp
, 0, sizeof(xpp
));
107 memset(&xecfg
, 0, sizeof(xecfg
));
110 xdi_diff(&minus
, &plus
, &xpp
, &xecfg
, &ecb
);
117 int cmd_rerere(int argc
, const char **argv
, const char *prefix
)
119 struct string_list merge_rr
= STRING_LIST_INIT_DUP
;
120 int i
, fd
, autoupdate
= -1, flags
= 0;
122 struct option options
[] = {
123 OPT_SET_INT(0, "rerere-autoupdate", &autoupdate
,
124 "register clean resolutions in index", 1),
128 argc
= parse_options(argc
, argv
, prefix
, options
, rerere_usage
, 0);
131 flags
= RERERE_AUTOUPDATE
;
133 flags
= RERERE_NOAUTOUPDATE
;
136 return rerere(flags
);
138 if (!strcmp(argv
[0], "forget")) {
139 const char **pathspec
= get_pathspec(prefix
, argv
+ 1);
140 return rerere_forget(pathspec
);
143 fd
= setup_rerere(&merge_rr
, flags
);
147 if (!strcmp(argv
[0], "clear")) {
148 for (i
= 0; i
< merge_rr
.nr
; i
++) {
149 const char *name
= (const char *)merge_rr
.items
[i
].util
;
150 if (!has_rerere_resolution(name
))
151 unlink_rr_item(name
);
153 unlink_or_warn(git_path("MERGE_RR"));
154 } else if (!strcmp(argv
[0], "gc"))
155 garbage_collect(&merge_rr
);
156 else if (!strcmp(argv
[0], "status"))
157 for (i
= 0; i
< merge_rr
.nr
; i
++)
158 printf("%s\n", merge_rr
.items
[i
].string
);
159 else if (!strcmp(argv
[0], "diff"))
160 for (i
= 0; i
< merge_rr
.nr
; i
++) {
161 const char *path
= merge_rr
.items
[i
].string
;
162 const char *name
= (const char *)merge_rr
.items
[i
].util
;
163 diff_two(rerere_path(name
, "preimage"), path
, path
, path
);
166 usage_with_options(rerere_usage
, options
);
168 string_list_clear(&merge_rr
, 1);