diff-cache.con commit [PATCH] Implement git-checkout-cache -u to update stat information in the cache. (415e96c)
   1#include "cache.h"
   2#include "diff.h"
   3
   4static int cached_only = 0;
   5static int generate_patch = 0;
   6static int match_nonexisting = 0;
   7static int line_termination = '\n';
   8static int detect_rename = 0;
   9
  10/* A file entry went away or appeared */
  11static void show_file(const char *prefix, struct cache_entry *ce, unsigned char *sha1, unsigned int mode)
  12{
  13        if (generate_patch)
  14                diff_addremove(prefix[0], ntohl(mode), sha1, ce->name, NULL);
  15        else
  16                printf("%s%06o\tblob\t%s\t%s%c", prefix, ntohl(mode),
  17                       sha1_to_hex(sha1), ce->name, line_termination);
  18}
  19
  20static int get_stat_data(struct cache_entry *ce, unsigned char **sha1p, unsigned int *modep)
  21{
  22        unsigned char *sha1 = ce->sha1;
  23        unsigned int mode = ce->ce_mode;
  24
  25        if (!cached_only) {
  26                static unsigned char no_sha1[20];
  27                int changed;
  28                struct stat st;
  29                if (lstat(ce->name, &st) < 0) {
  30                        if (errno == ENOENT && match_nonexisting) {
  31                                *sha1p = sha1;
  32                                *modep = mode;
  33                                return 0;
  34                        }
  35                        return -1;
  36                }
  37                changed = ce_match_stat(ce, &st);
  38                if (changed) {
  39                        mode = create_ce_mode(st.st_mode);
  40                        sha1 = no_sha1;
  41                }
  42        }
  43
  44        *sha1p = sha1;
  45        *modep = mode;
  46        return 0;
  47}
  48
  49static void show_new_file(struct cache_entry *new)
  50{
  51        unsigned char *sha1;
  52        unsigned int mode;
  53
  54        /* New file in the index: it might actually be different in the working copy */
  55        if (get_stat_data(new, &sha1, &mode) < 0)
  56                return;
  57
  58        show_file("+", new, sha1, mode);
  59}
  60
  61static int show_modified(struct cache_entry *old,
  62                         struct cache_entry *new,
  63                         int report_missing)
  64{
  65        unsigned int mode, oldmode;
  66        unsigned char *sha1;
  67        char old_sha1_hex[60];
  68
  69        if (get_stat_data(new, &sha1, &mode) < 0) {
  70                if (report_missing)
  71                        show_file("-", old, old->sha1, old->ce_mode);
  72                return -1;
  73        }
  74
  75        oldmode = old->ce_mode;
  76        if (mode == oldmode && !memcmp(sha1, old->sha1, 20))
  77                return 0;
  78
  79        mode = ntohl(mode);
  80        oldmode = ntohl(oldmode);
  81
  82        if (generate_patch)
  83                diff_change(oldmode, mode,
  84                            old->sha1, sha1, old->name, NULL);
  85        else {
  86                strcpy(old_sha1_hex, sha1_to_hex(old->sha1));
  87                printf("*%06o->%06o\tblob\t%s->%s\t%s%c", oldmode, mode,
  88                       old_sha1_hex, sha1_to_hex(sha1),
  89                       old->name, line_termination);
  90        }
  91        return 0;
  92}
  93
  94static int diff_cache(struct cache_entry **ac, int entries)
  95{
  96        while (entries) {
  97                struct cache_entry *ce = *ac;
  98                int same = (entries > 1) && ce_same_name(ce, ac[1]);
  99
 100                switch (ce_stage(ce)) {
 101                case 0:
 102                        /* No stage 1 entry? That means it's a new file */
 103                        if (!same) {
 104                                show_new_file(ce);
 105                                break;
 106                        }
 107                        /* Show difference between old and new */
 108                        show_modified(ac[1], ce, 1);
 109                        break;
 110                case 1:
 111                        /* No stage 3 (merge) entry? That means it's been deleted */
 112                        if (!same) {
 113                                show_file("-", ce, ce->sha1, ce->ce_mode);
 114                                break;
 115                        }
 116                        /* We come here with ce pointing at stage 1
 117                         * (original tree) and ac[1] pointing at stage
 118                         * 3 (unmerged).  show-modified with
 119                         * report-mising set to false does not say the
 120                         * file is deleted but reports true if work
 121                         * tree does not have it, in which case we
 122                         * fall through to report the unmerged state.
 123                         * Otherwise, we show the differences between
 124                         * the original tree and the work tree.
 125                         */
 126                        if (!cached_only && !show_modified(ce, ac[1], 0))
 127                                break;
 128                        /* fallthru */
 129                case 3:
 130                        if (generate_patch)
 131                                diff_unmerge(ce->name);
 132                        else
 133                                printf("U %s%c", ce->name, line_termination);
 134                        break;
 135
 136                default:
 137                        die("impossible cache entry stage");
 138                }
 139
 140                /*
 141                 * Ignore all the different stages for this file,
 142                 * we've handled the relevant cases now.
 143                 */
 144                do {
 145                        ac++;
 146                        entries--;
 147                } while (entries && ce_same_name(ce, ac[0]));
 148        }
 149        return 0;
 150}
 151
 152/*
 153 * This turns all merge entries into "stage 3". That guarantees that
 154 * when we read in the new tree (into "stage 1"), we won't lose sight
 155 * of the fact that we had unmerged entries.
 156 */
 157static void mark_merge_entries(void)
 158{
 159        int i;
 160        for (i = 0; i < active_nr; i++) {
 161                struct cache_entry *ce = active_cache[i];
 162                if (!ce_stage(ce))
 163                        continue;
 164                ce->ce_flags |= htons(CE_STAGEMASK);
 165        }
 166}
 167
 168static char *diff_cache_usage =
 169"git-diff-cache [-p] [-r] [-z] [-m] [-M] [--cached] <tree-ish>";
 170
 171int main(int argc, char **argv)
 172{
 173        unsigned char tree_sha1[20];
 174        void *tree;
 175        unsigned long size;
 176        int ret;
 177
 178        read_cache();
 179        while (argc > 2) {
 180                char *arg = argv[1];
 181                argv++;
 182                argc--;
 183                if (!strcmp(arg, "-r")) {
 184                        /* We accept the -r flag just to look like diff-tree */
 185                        continue;
 186                }
 187                if (!strcmp(arg, "-p")) {
 188                        generate_patch = 1;
 189                        continue;
 190                }
 191                if (!strcmp(arg, "-M")) {
 192                        generate_patch = detect_rename = 1;
 193                        continue;
 194                }
 195                if (!strcmp(arg, "-z")) {
 196                        line_termination = '\0';
 197                        continue;
 198                }
 199                if (!strcmp(arg, "-m")) {
 200                        match_nonexisting = 1;
 201                        continue;
 202                }
 203                if (!strcmp(arg, "--cached")) {
 204                        cached_only = 1;
 205                        continue;
 206                }
 207                usage(diff_cache_usage);
 208        }
 209
 210        if (argc != 2 || get_sha1(argv[1], tree_sha1))
 211                usage(diff_cache_usage);
 212
 213        if (generate_patch)
 214                diff_setup(detect_rename, 0, 0, 0, 0);
 215
 216        mark_merge_entries();
 217
 218        tree = read_object_with_reference(tree_sha1, "tree", &size, 0);
 219        if (!tree)
 220                die("bad tree object %s", argv[1]);
 221        if (read_tree(tree, size, 1))
 222                die("unable to read tree object %s", argv[1]);
 223
 224        ret = diff_cache(active_cache, active_nr);
 225        if (generate_patch)
 226                diff_flush();
 227        return ret;
 228}