diff-lib.con commit git-mv: succeed even if source is a prefix of destination (1d6249e)
   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                        memset(dpath->sha1, 0, 20);
  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                                        memcpy(dpath->parent[stage-2].sha1,
  70                                               nce->sha1, 20);
  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);
 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        oldmode = old->ce_mode;
 218        if (mode == oldmode && !memcmp(sha1, old->sha1, 20) &&
 219            !revs->diffopt.find_copies_harder)
 220                return 0;
 221
 222        mode = ntohl(mode);
 223        oldmode = ntohl(oldmode);
 224
 225        diff_change(&revs->diffopt, oldmode, mode,
 226                    old->sha1, sha1, old->name, NULL);
 227        return 0;
 228}
 229
 230static int diff_cache(struct rev_info *revs,
 231                      struct cache_entry **ac, int entries,
 232                      const char **pathspec,
 233                      int cached, int match_missing)
 234{
 235        while (entries) {
 236                struct cache_entry *ce = *ac;
 237                int same = (entries > 1) && ce_same_name(ce, ac[1]);
 238
 239                if (!ce_path_match(ce, pathspec))
 240                        goto skip_entry;
 241
 242                switch (ce_stage(ce)) {
 243                case 0:
 244                        /* No stage 1 entry? That means it's a new file */
 245                        if (!same) {
 246                                show_new_file(revs, ce, cached, match_missing);
 247                                break;
 248                        }
 249                        /* Show difference between old and new */
 250                        show_modified(revs,ac[1], ce, 1,
 251                                      cached, match_missing);
 252                        break;
 253                case 1:
 254                        /* No stage 3 (merge) entry?
 255                         * That means it's been deleted.
 256                         */
 257                        if (!same) {
 258                                diff_index_show_file(revs, "-", ce,
 259                                                     ce->sha1, ce->ce_mode);
 260                                break;
 261                        }
 262                        /* We come here with ce pointing at stage 1
 263                         * (original tree) and ac[1] pointing at stage
 264                         * 3 (unmerged).  show-modified with
 265                         * report-missing set to false does not say the
 266                         * file is deleted but reports true if work
 267                         * tree does not have it, in which case we
 268                         * fall through to report the unmerged state.
 269                         * Otherwise, we show the differences between
 270                         * the original tree and the work tree.
 271                         */
 272                        if (!cached &&
 273                            !show_modified(revs, ce, ac[1], 0,
 274                                           cached, match_missing))
 275                                break;
 276                        /* fallthru */
 277                case 3:
 278                        diff_unmerge(&revs->diffopt, ce->name);
 279                        break;
 280
 281                default:
 282                        die("impossible cache entry stage");
 283                }
 284
 285skip_entry:
 286                /*
 287                 * Ignore all the different stages for this file,
 288                 * we've handled the relevant cases now.
 289                 */
 290                do {
 291                        ac++;
 292                        entries--;
 293                } while (entries && ce_same_name(ce, ac[0]));
 294        }
 295        return 0;
 296}
 297
 298/*
 299 * This turns all merge entries into "stage 3". That guarantees that
 300 * when we read in the new tree (into "stage 1"), we won't lose sight
 301 * of the fact that we had unmerged entries.
 302 */
 303static void mark_merge_entries(void)
 304{
 305        int i;
 306        for (i = 0; i < active_nr; i++) {
 307                struct cache_entry *ce = active_cache[i];
 308                if (!ce_stage(ce))
 309                        continue;
 310                ce->ce_flags |= htons(CE_STAGEMASK);
 311        }
 312}
 313
 314int run_diff_index(struct rev_info *revs, int cached)
 315{
 316        int ret;
 317        struct object *ent;
 318        struct tree *tree;
 319        const char *tree_name;
 320        int match_missing = 0;
 321
 322        /* 
 323         * Backward compatibility wart - "diff-index -m" does
 324         * not mean "do not ignore merges", but totally different.
 325         */
 326        if (!revs->ignore_merges)
 327                match_missing = 1;
 328
 329        if (read_cache() < 0) {
 330                perror("read_cache");
 331                return -1;
 332        }
 333        mark_merge_entries();
 334
 335        ent = revs->pending.objects[0].item;
 336        tree_name = revs->pending.objects[0].name;
 337        tree = parse_tree_indirect(ent->sha1);
 338        if (!tree)
 339                return error("bad tree object %s", tree_name);
 340        if (read_tree(tree, 1, revs->prune_data))
 341                return error("unable to read tree object %s", tree_name);
 342        ret = diff_cache(revs, active_cache, active_nr, revs->prune_data,
 343                         cached, match_missing);
 344        diffcore_std(&revs->diffopt);
 345        diff_flush(&revs->diffopt);
 346        return ret;
 347}