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