diff-lib.con commit git-svn: enable --minimize to simplify the config and connections (47e39c5)
   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 = ce_mode_from_stat(ce, st.st_mode);
 174                        sha1 = no_sha1;
 175                }
 176        }
 177
 178        *sha1p = sha1;
 179        *modep = mode;
 180        return 0;
 181}
 182
 183static void show_new_file(struct rev_info *revs,
 184                          struct cache_entry *new,
 185                          int cached, int match_missing)
 186{
 187        unsigned char *sha1;
 188        unsigned int mode;
 189
 190        /* New file in the index: it might actually be different in
 191         * the working copy.
 192         */
 193        if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0)
 194                return;
 195
 196        diff_index_show_file(revs, "+", new, sha1, mode);
 197}
 198
 199static int show_modified(struct rev_info *revs,
 200                         struct cache_entry *old,
 201                         struct cache_entry *new,
 202                         int report_missing,
 203                         int cached, int match_missing)
 204{
 205        unsigned int mode, oldmode;
 206        unsigned char *sha1;
 207
 208        if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) {
 209                if (report_missing)
 210                        diff_index_show_file(revs, "-", old,
 211                                             old->sha1, old->ce_mode);
 212                return -1;
 213        }
 214
 215        if (revs->combine_merges && !cached &&
 216            (hashcmp(sha1, old->sha1) || hashcmp(old->sha1, new->sha1))) {
 217                struct combine_diff_path *p;
 218                int pathlen = ce_namelen(new);
 219
 220                p = xmalloc(combine_diff_path_size(2, pathlen));
 221                p->path = (char *) &p->parent[2];
 222                p->next = NULL;
 223                p->len = pathlen;
 224                memcpy(p->path, new->name, pathlen);
 225                p->path[pathlen] = 0;
 226                p->mode = ntohl(mode);
 227                hashclr(p->sha1);
 228                memset(p->parent, 0, 2 * sizeof(struct combine_diff_parent));
 229                p->parent[0].status = DIFF_STATUS_MODIFIED;
 230                p->parent[0].mode = ntohl(new->ce_mode);
 231                hashcpy(p->parent[0].sha1, new->sha1);
 232                p->parent[1].status = DIFF_STATUS_MODIFIED;
 233                p->parent[1].mode = ntohl(old->ce_mode);
 234                hashcpy(p->parent[1].sha1, old->sha1);
 235                show_combined_diff(p, 2, revs->dense_combined_merges, revs);
 236                free(p);
 237                return 0;
 238        }
 239
 240        oldmode = old->ce_mode;
 241        if (mode == oldmode && !hashcmp(sha1, old->sha1) &&
 242            !revs->diffopt.find_copies_harder)
 243                return 0;
 244
 245        mode = ntohl(mode);
 246        oldmode = ntohl(oldmode);
 247
 248        diff_change(&revs->diffopt, oldmode, mode,
 249                    old->sha1, sha1, old->name, NULL);
 250        return 0;
 251}
 252
 253static int diff_cache(struct rev_info *revs,
 254                      struct cache_entry **ac, int entries,
 255                      const char **pathspec,
 256                      int cached, int match_missing)
 257{
 258        while (entries) {
 259                struct cache_entry *ce = *ac;
 260                int same = (entries > 1) && ce_same_name(ce, ac[1]);
 261
 262                if (!ce_path_match(ce, pathspec))
 263                        goto skip_entry;
 264
 265                switch (ce_stage(ce)) {
 266                case 0:
 267                        /* No stage 1 entry? That means it's a new file */
 268                        if (!same) {
 269                                show_new_file(revs, ce, cached, match_missing);
 270                                break;
 271                        }
 272                        /* Show difference between old and new */
 273                        show_modified(revs, ac[1], ce, 1,
 274                                      cached, match_missing);
 275                        break;
 276                case 1:
 277                        /* No stage 3 (merge) entry?
 278                         * That means it's been deleted.
 279                         */
 280                        if (!same) {
 281                                diff_index_show_file(revs, "-", ce,
 282                                                     ce->sha1, ce->ce_mode);
 283                                break;
 284                        }
 285                        /* We come here with ce pointing at stage 1
 286                         * (original tree) and ac[1] pointing at stage
 287                         * 3 (unmerged).  show-modified with
 288                         * report-missing set to false does not say the
 289                         * file is deleted but reports true if work
 290                         * tree does not have it, in which case we
 291                         * fall through to report the unmerged state.
 292                         * Otherwise, we show the differences between
 293                         * the original tree and the work tree.
 294                         */
 295                        if (!cached &&
 296                            !show_modified(revs, ce, ac[1], 0,
 297                                           cached, match_missing))
 298                                break;
 299                        diff_unmerge(&revs->diffopt, ce->name,
 300                                     ntohl(ce->ce_mode), ce->sha1);
 301                        break;
 302                case 3:
 303                        diff_unmerge(&revs->diffopt, ce->name,
 304                                     0, null_sha1);
 305                        break;
 306
 307                default:
 308                        die("impossible cache entry stage");
 309                }
 310
 311skip_entry:
 312                /*
 313                 * Ignore all the different stages for this file,
 314                 * we've handled the relevant cases now.
 315                 */
 316                do {
 317                        ac++;
 318                        entries--;
 319                } while (entries && ce_same_name(ce, ac[0]));
 320        }
 321        return 0;
 322}
 323
 324/*
 325 * This turns all merge entries into "stage 3". That guarantees that
 326 * when we read in the new tree (into "stage 1"), we won't lose sight
 327 * of the fact that we had unmerged entries.
 328 */
 329static void mark_merge_entries(void)
 330{
 331        int i;
 332        for (i = 0; i < active_nr; i++) {
 333                struct cache_entry *ce = active_cache[i];
 334                if (!ce_stage(ce))
 335                        continue;
 336                ce->ce_flags |= htons(CE_STAGEMASK);
 337        }
 338}
 339
 340int run_diff_index(struct rev_info *revs, int cached)
 341{
 342        int ret;
 343        struct object *ent;
 344        struct tree *tree;
 345        const char *tree_name;
 346        int match_missing = 0;
 347
 348        /* 
 349         * Backward compatibility wart - "diff-index -m" does
 350         * not mean "do not ignore merges", but totally different.
 351         */
 352        if (!revs->ignore_merges)
 353                match_missing = 1;
 354
 355        if (read_cache() < 0) {
 356                perror("read_cache");
 357                return -1;
 358        }
 359        mark_merge_entries();
 360
 361        ent = revs->pending.objects[0].item;
 362        tree_name = revs->pending.objects[0].name;
 363        tree = parse_tree_indirect(ent->sha1);
 364        if (!tree)
 365                return error("bad tree object %s", tree_name);
 366        if (read_tree(tree, 1, revs->prune_data))
 367                return error("unable to read tree object %s", tree_name);
 368        ret = diff_cache(revs, active_cache, active_nr, revs->prune_data,
 369                         cached, match_missing);
 370        diffcore_std(&revs->diffopt);
 371        diff_flush(&revs->diffopt);
 372        return ret;
 373}
 374
 375int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
 376{
 377        struct tree *tree;
 378        struct rev_info revs;
 379        int i;
 380        struct cache_entry **dst;
 381        struct cache_entry *last = NULL;
 382
 383        /*
 384         * This is used by git-blame to run diff-cache internally;
 385         * it potentially needs to repeatedly run this, so we will
 386         * start by removing the higher order entries the last round
 387         * left behind.
 388         */
 389        dst = active_cache;
 390        for (i = 0; i < active_nr; i++) {
 391                struct cache_entry *ce = active_cache[i];
 392                if (ce_stage(ce)) {
 393                        if (last && !strcmp(ce->name, last->name))
 394                                continue;
 395                        cache_tree_invalidate_path(active_cache_tree,
 396                                                   ce->name);
 397                        last = ce;
 398                        ce->ce_mode = 0;
 399                        ce->ce_flags &= ~htons(CE_STAGEMASK);
 400                }
 401                *dst++ = ce;
 402        }
 403        active_nr = dst - active_cache;
 404
 405        init_revisions(&revs, NULL);
 406        revs.prune_data = opt->paths;
 407        tree = parse_tree_indirect(tree_sha1);
 408        if (!tree)
 409                die("bad tree object %s", sha1_to_hex(tree_sha1));
 410        if (read_tree(tree, 1, opt->paths))
 411                return error("unable to read tree %s", sha1_to_hex(tree_sha1));
 412        return diff_cache(&revs, active_cache, active_nr, revs.prune_data,
 413                          1, 0);
 414}