diff-lib.con commit Update GIT_TRACE documentation. (2886bdb)
   1/*
   2 * Copyright (C) 2005 Junio C Hamano
   3 */
   4#include "cache.h"
   5#include "quote.h"
   6#include "commit.h"
   7#include "diff.h"
   8#include "diffcore.h"
   9#include "revision.h"
  10
  11/*
  12 * diff-files
  13 */
  14
  15int run_diff_files(struct rev_info *revs, int silent_on_removed)
  16{
  17        int entries, i;
  18        int diff_unmerged_stage = revs->max_count;
  19
  20        if (diff_unmerged_stage < 0)
  21                diff_unmerged_stage = 2;
  22        entries = read_cache();
  23        if (entries < 0) {
  24                perror("read_cache");
  25                return -1;
  26        }
  27        for (i = 0; i < entries; i++) {
  28                struct stat st;
  29                unsigned int oldmode, newmode;
  30                struct cache_entry *ce = active_cache[i];
  31                int changed;
  32
  33                if (!ce_path_match(ce, revs->prune_data))
  34                        continue;
  35
  36                if (ce_stage(ce)) {
  37                        struct combine_diff_path *dpath;
  38                        int num_compare_stages = 0;
  39                        size_t path_len;
  40
  41                        path_len = ce_namelen(ce);
  42
  43                        dpath = xmalloc (combine_diff_path_size (5, path_len));
  44                        dpath->path = (char *) &(dpath->parent[5]);
  45
  46                        dpath->next = NULL;
  47                        dpath->len = path_len;
  48                        memcpy(dpath->path, ce->name, path_len);
  49                        dpath->path[path_len] = '\0';
  50                        dpath->mode = 0;
  51                        hashclr(dpath->sha1);
  52                        memset(&(dpath->parent[0]), 0,
  53                                        sizeof(struct combine_diff_parent)*5);
  54
  55                        while (i < entries) {
  56                                struct cache_entry *nce = active_cache[i];
  57                                int stage;
  58
  59                                if (strcmp(ce->name, nce->name))
  60                                        break;
  61
  62                                /* Stage #2 (ours) is the first parent,
  63                                 * stage #3 (theirs) is the second.
  64                                 */
  65                                stage = ce_stage(nce);
  66                                if (2 <= stage) {
  67                                        int mode = ntohl(nce->ce_mode);
  68                                        num_compare_stages++;
  69                                        hashcpy(dpath->parent[stage-2].sha1, nce->sha1);
  70                                        dpath->parent[stage-2].mode =
  71                                                canon_mode(mode);
  72                                        dpath->parent[stage-2].status =
  73                                                DIFF_STATUS_MODIFIED;
  74                                }
  75
  76                                /* diff against the proper unmerged stage */
  77                                if (stage == diff_unmerged_stage)
  78                                        ce = nce;
  79                                i++;
  80                        }
  81                        /*
  82                         * Compensate for loop update
  83                         */
  84                        i--;
  85
  86                        if (revs->combine_merges && num_compare_stages == 2) {
  87                                show_combined_diff(dpath, 2,
  88                                                   revs->dense_combined_merges,
  89                                                   revs);
  90                                free(dpath);
  91                                continue;
  92                        }
  93                        free(dpath);
  94                        dpath = NULL;
  95
  96                        /*
  97                         * Show the diff for the 'ce' if we found the one
  98                         * from the desired stage.
  99                         */
 100                        diff_unmerge(&revs->diffopt, ce->name);
 101                        if (ce_stage(ce) != diff_unmerged_stage)
 102                                continue;
 103                }
 104
 105                if (lstat(ce->name, &st) < 0) {
 106                        if (errno != ENOENT && errno != ENOTDIR) {
 107                                perror(ce->name);
 108                                continue;
 109                        }
 110                        if (silent_on_removed)
 111                                continue;
 112                        diff_addremove(&revs->diffopt, '-', ntohl(ce->ce_mode),
 113                                       ce->sha1, ce->name, NULL);
 114                        continue;
 115                }
 116                changed = ce_match_stat(ce, &st, 0);
 117                if (!changed && !revs->diffopt.find_copies_harder)
 118                        continue;
 119                oldmode = ntohl(ce->ce_mode);
 120
 121                newmode = canon_mode(st.st_mode);
 122                if (!trust_executable_bit &&
 123                    S_ISREG(newmode) && S_ISREG(oldmode) &&
 124                    ((newmode ^ oldmode) == 0111))
 125                        newmode = oldmode;
 126                diff_change(&revs->diffopt, oldmode, newmode,
 127                            ce->sha1, (changed ? null_sha1 : ce->sha1),
 128                            ce->name, NULL);
 129
 130        }
 131        diffcore_std(&revs->diffopt);
 132        diff_flush(&revs->diffopt);
 133        return 0;
 134}
 135
 136/*
 137 * diff-index
 138 */
 139
 140/* A file entry went away or appeared */
 141static void diff_index_show_file(struct rev_info *revs,
 142                                 const char *prefix,
 143                                 struct cache_entry *ce,
 144                                 unsigned char *sha1, unsigned int mode)
 145{
 146        diff_addremove(&revs->diffopt, prefix[0], ntohl(mode),
 147                       sha1, ce->name, NULL);
 148}
 149
 150static int get_stat_data(struct cache_entry *ce,
 151                         unsigned char **sha1p,
 152                         unsigned int *modep,
 153                         int cached, int match_missing)
 154{
 155        unsigned char *sha1 = ce->sha1;
 156        unsigned int mode = ce->ce_mode;
 157
 158        if (!cached) {
 159                static unsigned char no_sha1[20];
 160                int changed;
 161                struct stat st;
 162                if (lstat(ce->name, &st) < 0) {
 163                        if (errno == ENOENT && match_missing) {
 164                                *sha1p = sha1;
 165                                *modep = mode;
 166                                return 0;
 167                        }
 168                        return -1;
 169                }
 170                changed = ce_match_stat(ce, &st, 0);
 171                if (changed) {
 172                        mode = create_ce_mode(st.st_mode);
 173                        if (!trust_executable_bit && S_ISREG(st.st_mode))
 174                                mode = ce->ce_mode;
 175                        sha1 = no_sha1;
 176                }
 177        }
 178
 179        *sha1p = sha1;
 180        *modep = mode;
 181        return 0;
 182}
 183
 184static void show_new_file(struct rev_info *revs,
 185                          struct cache_entry *new,
 186                          int cached, int match_missing)
 187{
 188        unsigned char *sha1;
 189        unsigned int mode;
 190
 191        /* New file in the index: it might actually be different in
 192         * the working copy.
 193         */
 194        if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0)
 195                return;
 196
 197        diff_index_show_file(revs, "+", new, sha1, mode);
 198}
 199
 200static int show_modified(struct rev_info *revs,
 201                         struct cache_entry *old,
 202                         struct cache_entry *new,
 203                         int report_missing,
 204                         int cached, int match_missing)
 205{
 206        unsigned int mode, oldmode;
 207        unsigned char *sha1;
 208
 209        if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) {
 210                if (report_missing)
 211                        diff_index_show_file(revs, "-", old,
 212                                             old->sha1, old->ce_mode);
 213                return -1;
 214        }
 215
 216        oldmode = old->ce_mode;
 217        if (mode == oldmode && !hashcmp(sha1, old->sha1) &&
 218            !revs->diffopt.find_copies_harder)
 219                return 0;
 220
 221        mode = ntohl(mode);
 222        oldmode = ntohl(oldmode);
 223
 224        diff_change(&revs->diffopt, oldmode, mode,
 225                    old->sha1, sha1, old->name, NULL);
 226        return 0;
 227}
 228
 229static int diff_cache(struct rev_info *revs,
 230                      struct cache_entry **ac, int entries,
 231                      const char **pathspec,
 232                      int cached, int match_missing)
 233{
 234        while (entries) {
 235                struct cache_entry *ce = *ac;
 236                int same = (entries > 1) && ce_same_name(ce, ac[1]);
 237
 238                if (!ce_path_match(ce, pathspec))
 239                        goto skip_entry;
 240
 241                switch (ce_stage(ce)) {
 242                case 0:
 243                        /* No stage 1 entry? That means it's a new file */
 244                        if (!same) {
 245                                show_new_file(revs, ce, cached, match_missing);
 246                                break;
 247                        }
 248                        /* Show difference between old and new */
 249                        show_modified(revs,ac[1], ce, 1,
 250                                      cached, match_missing);
 251                        break;
 252                case 1:
 253                        /* No stage 3 (merge) entry?
 254                         * That means it's been deleted.
 255                         */
 256                        if (!same) {
 257                                diff_index_show_file(revs, "-", ce,
 258                                                     ce->sha1, ce->ce_mode);
 259                                break;
 260                        }
 261                        /* We come here with ce pointing at stage 1
 262                         * (original tree) and ac[1] pointing at stage
 263                         * 3 (unmerged).  show-modified with
 264                         * report-missing set to false does not say the
 265                         * file is deleted but reports true if work
 266                         * tree does not have it, in which case we
 267                         * fall through to report the unmerged state.
 268                         * Otherwise, we show the differences between
 269                         * the original tree and the work tree.
 270                         */
 271                        if (!cached &&
 272                            !show_modified(revs, ce, ac[1], 0,
 273                                           cached, match_missing))
 274                                break;
 275                        /* fallthru */
 276                case 3:
 277                        diff_unmerge(&revs->diffopt, ce->name);
 278                        break;
 279
 280                default:
 281                        die("impossible cache entry stage");
 282                }
 283
 284skip_entry:
 285                /*
 286                 * Ignore all the different stages for this file,
 287                 * we've handled the relevant cases now.
 288                 */
 289                do {
 290                        ac++;
 291                        entries--;
 292                } while (entries && ce_same_name(ce, ac[0]));
 293        }
 294        return 0;
 295}
 296
 297/*
 298 * This turns all merge entries into "stage 3". That guarantees that
 299 * when we read in the new tree (into "stage 1"), we won't lose sight
 300 * of the fact that we had unmerged entries.
 301 */
 302static void mark_merge_entries(void)
 303{
 304        int i;
 305        for (i = 0; i < active_nr; i++) {
 306                struct cache_entry *ce = active_cache[i];
 307                if (!ce_stage(ce))
 308                        continue;
 309                ce->ce_flags |= htons(CE_STAGEMASK);
 310        }
 311}
 312
 313int run_diff_index(struct rev_info *revs, int cached)
 314{
 315        int ret;
 316        struct object *ent;
 317        struct tree *tree;
 318        const char *tree_name;
 319        int match_missing = 0;
 320
 321        /* 
 322         * Backward compatibility wart - "diff-index -m" does
 323         * not mean "do not ignore merges", but totally different.
 324         */
 325        if (!revs->ignore_merges)
 326                match_missing = 1;
 327
 328        if (read_cache() < 0) {
 329                perror("read_cache");
 330                return -1;
 331        }
 332        mark_merge_entries();
 333
 334        ent = revs->pending.objects[0].item;
 335        tree_name = revs->pending.objects[0].name;
 336        tree = parse_tree_indirect(ent->sha1);
 337        if (!tree)
 338                return error("bad tree object %s", tree_name);
 339        if (read_tree(tree, 1, revs->prune_data))
 340                return error("unable to read tree object %s", tree_name);
 341        ret = diff_cache(revs, active_cache, active_nr, revs->prune_data,
 342                         cached, match_missing);
 343        diffcore_std(&revs->diffopt);
 344        diff_flush(&revs->diffopt);
 345        return ret;
 346}