builtin / rerere.con commit Merge branch 'ss/cherry-pick-x-doc' into maint (0f51322)
   1#include "builtin.h"
   2#include "cache.h"
   3#include "dir.h"
   4#include "parse-options.h"
   5#include "string-list.h"
   6#include "rerere.h"
   7#include "xdiff/xdiff.h"
   8#include "xdiff-interface.h"
   9
  10static const char * const rerere_usage[] = {
  11        "git rerere [clear | forget path... | status | remaining | diff | gc]",
  12        NULL,
  13};
  14
  15/* these values are days */
  16static int cutoff_noresolve = 15;
  17static int cutoff_resolve = 60;
  18
  19static time_t rerere_created_at(const char *name)
  20{
  21        struct stat st;
  22        return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
  23}
  24
  25static time_t rerere_last_used_at(const char *name)
  26{
  27        struct stat st;
  28        return stat(rerere_path(name, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
  29}
  30
  31static void unlink_rr_item(const char *name)
  32{
  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));
  37}
  38
  39static int git_rerere_gc_config(const char *var, const char *value, void *cb)
  40{
  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);
  45        else
  46                return git_default_config(var, value, cb);
  47        return 0;
  48}
  49
  50static void garbage_collect(struct string_list *rr)
  51{
  52        struct string_list to_remove = STRING_LIST_INIT_DUP;
  53        DIR *dir;
  54        struct dirent *e;
  55        int i, cutoff;
  56        time_t now = time(NULL), then;
  57
  58        git_config(git_rerere_gc_config, NULL);
  59        dir = opendir(git_path("rr-cache"));
  60        if (!dir)
  61                die_errno("unable to open rr-cache directory");
  62        while ((e = readdir(dir))) {
  63                if (is_dot_or_dotdot(e->d_name))
  64                        continue;
  65
  66                then = rerere_last_used_at(e->d_name);
  67                if (then) {
  68                        cutoff = cutoff_resolve;
  69                } else {
  70                        then = rerere_created_at(e->d_name);
  71                        if (!then)
  72                                continue;
  73                        cutoff = cutoff_noresolve;
  74                }
  75                if (then < now - cutoff * 86400)
  76                        string_list_append(&to_remove, e->d_name);
  77        }
  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);
  81}
  82
  83static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
  84{
  85        int i;
  86        for (i = 0; i < nbuf; i++)
  87                if (write_in_full(1, ptr[i].ptr, ptr[i].size) != ptr[i].size)
  88                        return -1;
  89        return 0;
  90}
  91
  92static int diff_two(const char *file1, const char *label1,
  93                const char *file2, const char *label2)
  94{
  95        xpparam_t xpp;
  96        xdemitconf_t xecfg;
  97        xdemitcb_t ecb;
  98        mmfile_t minus, plus;
  99
 100        if (read_mmfile(&minus, file1) || read_mmfile(&plus, file2))
 101                return 1;
 102
 103        printf("--- a/%s\n+++ b/%s\n", label1, label2);
 104        fflush(stdout);
 105        memset(&xpp, 0, sizeof(xpp));
 106        xpp.flags = 0;
 107        memset(&xecfg, 0, sizeof(xecfg));
 108        xecfg.ctxlen = 3;
 109        ecb.outf = outf;
 110        xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
 111
 112        free(minus.ptr);
 113        free(plus.ptr);
 114        return 0;
 115}
 116
 117int cmd_rerere(int argc, const char **argv, const char *prefix)
 118{
 119        struct string_list merge_rr = STRING_LIST_INIT_DUP;
 120        int i, fd, autoupdate = -1, flags = 0;
 121
 122        struct option options[] = {
 123                OPT_SET_INT(0, "rerere-autoupdate", &autoupdate,
 124                        "register clean resolutions in index", 1),
 125                OPT_END(),
 126        };
 127
 128        argc = parse_options(argc, argv, prefix, options, rerere_usage, 0);
 129
 130        if (autoupdate == 1)
 131                flags = RERERE_AUTOUPDATE;
 132        if (autoupdate == 0)
 133                flags = RERERE_NOAUTOUPDATE;
 134
 135        if (argc < 1)
 136                return rerere(flags);
 137
 138        if (!strcmp(argv[0], "forget")) {
 139                const char **pathspec;
 140                if (argc < 2)
 141                        warning("'git rerere forget' without paths is deprecated");
 142                pathspec = get_pathspec(prefix, argv + 1);
 143                return rerere_forget(pathspec);
 144        }
 145
 146        fd = setup_rerere(&merge_rr, flags);
 147        if (fd < 0)
 148                return 0;
 149
 150        if (!strcmp(argv[0], "clear")) {
 151                for (i = 0; i < merge_rr.nr; i++) {
 152                        const char *name = (const char *)merge_rr.items[i].util;
 153                        if (!has_rerere_resolution(name))
 154                                unlink_rr_item(name);
 155                }
 156                unlink_or_warn(git_path("MERGE_RR"));
 157        } else if (!strcmp(argv[0], "gc"))
 158                garbage_collect(&merge_rr);
 159        else if (!strcmp(argv[0], "status"))
 160                for (i = 0; i < merge_rr.nr; i++)
 161                        printf("%s\n", merge_rr.items[i].string);
 162        else if (!strcmp(argv[0], "remaining")) {
 163                rerere_remaining(&merge_rr);
 164                for (i = 0; i < merge_rr.nr; i++) {
 165                        if (merge_rr.items[i].util != RERERE_RESOLVED)
 166                                printf("%s\n", merge_rr.items[i].string);
 167                        else
 168                                /* prepare for later call to
 169                                 * string_list_clear() */
 170                                merge_rr.items[i].util = NULL;
 171                }
 172        } else if (!strcmp(argv[0], "diff"))
 173                for (i = 0; i < merge_rr.nr; i++) {
 174                        const char *path = merge_rr.items[i].string;
 175                        const char *name = (const char *)merge_rr.items[i].util;
 176                        diff_two(rerere_path(name, "preimage"), path, path, path);
 177                }
 178        else
 179                usage_with_options(rerere_usage, options);
 180
 181        string_list_clear(&merge_rr, 1);
 182        return 0;
 183}