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