diff-lib.con commit Merge branch '2.16' of https://github.com/ChrisADR/git-po (daa8563)
   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#include "unpack-trees.h"
  12#include "refs.h"
  13#include "submodule.h"
  14#include "dir.h"
  15#include "fsmonitor.h"
  16
  17/*
  18 * diff-files
  19 */
  20
  21/*
  22 * Has the work tree entity been removed?
  23 *
  24 * Return 1 if it was removed from the work tree, 0 if an entity to be
  25 * compared with the cache entry ce still exists (the latter includes
  26 * the case where a directory that is not a submodule repository
  27 * exists for ce that is a submodule -- it is a submodule that is not
  28 * checked out).  Return negative for an error.
  29 */
  30static int check_removed(const struct cache_entry *ce, struct stat *st)
  31{
  32        if (lstat(ce->name, st) < 0) {
  33                if (!is_missing_file_error(errno))
  34                        return -1;
  35                return 1;
  36        }
  37        if (has_symlink_leading_path(ce->name, ce_namelen(ce)))
  38                return 1;
  39        if (S_ISDIR(st->st_mode)) {
  40                struct object_id sub;
  41
  42                /*
  43                 * If ce is already a gitlink, we can have a plain
  44                 * directory (i.e. the submodule is not checked out),
  45                 * or a checked out submodule.  Either case this is not
  46                 * a case where something was removed from the work tree,
  47                 * so we will return 0.
  48                 *
  49                 * Otherwise, if the directory is not a submodule
  50                 * repository, that means ce which was a blob turned into
  51                 * a directory --- the blob was removed!
  52                 */
  53                if (!S_ISGITLINK(ce->ce_mode) &&
  54                    resolve_gitlink_ref(ce->name, "HEAD", &sub))
  55                        return 1;
  56        }
  57        return 0;
  58}
  59
  60/*
  61 * Has a file changed or has a submodule new commits or a dirty work tree?
  62 *
  63 * Return 1 when changes are detected, 0 otherwise. If the DIRTY_SUBMODULES
  64 * option is set, the caller does not only want to know if a submodule is
  65 * modified at all but wants to know all the conditions that are met (new
  66 * commits, untracked content and/or modified content).
  67 */
  68static int match_stat_with_submodule(struct diff_options *diffopt,
  69                                     const struct cache_entry *ce,
  70                                     struct stat *st, unsigned ce_option,
  71                                     unsigned *dirty_submodule)
  72{
  73        int changed = ce_match_stat(ce, st, ce_option);
  74        if (S_ISGITLINK(ce->ce_mode)) {
  75                struct diff_flags orig_flags = diffopt->flags;
  76                if (!diffopt->flags.override_submodule_config)
  77                        set_diffopt_flags_from_submodule_config(diffopt, ce->name);
  78                if (diffopt->flags.ignore_submodules)
  79                        changed = 0;
  80                else if (!diffopt->flags.ignore_dirty_submodules &&
  81                         (!changed || diffopt->flags.dirty_submodules))
  82                        *dirty_submodule = is_submodule_modified(ce->name,
  83                                                                 diffopt->flags.ignore_untracked_in_submodules);
  84                diffopt->flags = orig_flags;
  85        }
  86        return changed;
  87}
  88
  89int run_diff_files(struct rev_info *revs, unsigned int option)
  90{
  91        int entries, i;
  92        int diff_unmerged_stage = revs->max_count;
  93        unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED)
  94                              ? CE_MATCH_RACY_IS_DIRTY : 0);
  95
  96        diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/");
  97
  98        if (diff_unmerged_stage < 0)
  99                diff_unmerged_stage = 2;
 100        entries = active_nr;
 101        for (i = 0; i < entries; i++) {
 102                unsigned int oldmode, newmode;
 103                struct cache_entry *ce = active_cache[i];
 104                int changed;
 105                unsigned dirty_submodule = 0;
 106                const struct object_id *old_oid, *new_oid;
 107
 108                if (diff_can_quit_early(&revs->diffopt))
 109                        break;
 110
 111                if (!ce_path_match(ce, &revs->prune_data, NULL))
 112                        continue;
 113
 114                if (ce_stage(ce)) {
 115                        struct combine_diff_path *dpath;
 116                        struct diff_filepair *pair;
 117                        unsigned int wt_mode = 0;
 118                        int num_compare_stages = 0;
 119                        size_t path_len;
 120                        struct stat st;
 121
 122                        path_len = ce_namelen(ce);
 123
 124                        dpath = xmalloc(combine_diff_path_size(5, path_len));
 125                        dpath->path = (char *) &(dpath->parent[5]);
 126
 127                        dpath->next = NULL;
 128                        memcpy(dpath->path, ce->name, path_len);
 129                        dpath->path[path_len] = '\0';
 130                        oidclr(&dpath->oid);
 131                        memset(&(dpath->parent[0]), 0,
 132                               sizeof(struct combine_diff_parent)*5);
 133
 134                        changed = check_removed(ce, &st);
 135                        if (!changed)
 136                                wt_mode = ce_mode_from_stat(ce, st.st_mode);
 137                        else {
 138                                if (changed < 0) {
 139                                        perror(ce->name);
 140                                        continue;
 141                                }
 142                                wt_mode = 0;
 143                        }
 144                        dpath->mode = wt_mode;
 145
 146                        while (i < entries) {
 147                                struct cache_entry *nce = active_cache[i];
 148                                int stage;
 149
 150                                if (strcmp(ce->name, nce->name))
 151                                        break;
 152
 153                                /* Stage #2 (ours) is the first parent,
 154                                 * stage #3 (theirs) is the second.
 155                                 */
 156                                stage = ce_stage(nce);
 157                                if (2 <= stage) {
 158                                        int mode = nce->ce_mode;
 159                                        num_compare_stages++;
 160                                        oidcpy(&dpath->parent[stage - 2].oid,
 161                                               &nce->oid);
 162                                        dpath->parent[stage-2].mode = ce_mode_from_stat(nce, mode);
 163                                        dpath->parent[stage-2].status =
 164                                                DIFF_STATUS_MODIFIED;
 165                                }
 166
 167                                /* diff against the proper unmerged stage */
 168                                if (stage == diff_unmerged_stage)
 169                                        ce = nce;
 170                                i++;
 171                        }
 172                        /*
 173                         * Compensate for loop update
 174                         */
 175                        i--;
 176
 177                        if (revs->combine_merges && num_compare_stages == 2) {
 178                                show_combined_diff(dpath, 2,
 179                                                   revs->dense_combined_merges,
 180                                                   revs);
 181                                free(dpath);
 182                                continue;
 183                        }
 184                        FREE_AND_NULL(dpath);
 185
 186                        /*
 187                         * Show the diff for the 'ce' if we found the one
 188                         * from the desired stage.
 189                         */
 190                        pair = diff_unmerge(&revs->diffopt, ce->name);
 191                        if (wt_mode)
 192                                pair->two->mode = wt_mode;
 193                        if (ce_stage(ce) != diff_unmerged_stage)
 194                                continue;
 195                }
 196
 197                if (ce_uptodate(ce) || ce_skip_worktree(ce))
 198                        continue;
 199
 200                /* If CE_VALID is set, don't look at workdir for file removal */
 201                if (ce->ce_flags & CE_VALID) {
 202                        changed = 0;
 203                        newmode = ce->ce_mode;
 204                } else {
 205                        struct stat st;
 206
 207                        changed = check_removed(ce, &st);
 208                        if (changed) {
 209                                if (changed < 0) {
 210                                        perror(ce->name);
 211                                        continue;
 212                                }
 213                                diff_addremove(&revs->diffopt, '-', ce->ce_mode,
 214                                               &ce->oid,
 215                                               !is_null_oid(&ce->oid),
 216                                               ce->name, 0);
 217                                continue;
 218                        } else if (revs->diffopt.ita_invisible_in_index &&
 219                                   ce_intent_to_add(ce)) {
 220                                diff_addremove(&revs->diffopt, '+', ce->ce_mode,
 221                                               the_hash_algo->empty_tree, 0,
 222                                               ce->name, 0);
 223                                continue;
 224                        }
 225
 226                        changed = match_stat_with_submodule(&revs->diffopt, ce, &st,
 227                                                            ce_option, &dirty_submodule);
 228                        newmode = ce_mode_from_stat(ce, st.st_mode);
 229                }
 230
 231                if (!changed && !dirty_submodule) {
 232                        ce_mark_uptodate(ce);
 233                        mark_fsmonitor_valid(ce);
 234                        if (!revs->diffopt.flags.find_copies_harder)
 235                                continue;
 236                }
 237                oldmode = ce->ce_mode;
 238                old_oid = &ce->oid;
 239                new_oid = changed ? &null_oid : &ce->oid;
 240                diff_change(&revs->diffopt, oldmode, newmode,
 241                            old_oid, new_oid,
 242                            !is_null_oid(old_oid),
 243                            !is_null_oid(new_oid),
 244                            ce->name, 0, dirty_submodule);
 245
 246        }
 247        diffcore_std(&revs->diffopt);
 248        diff_flush(&revs->diffopt);
 249        return 0;
 250}
 251
 252/*
 253 * diff-index
 254 */
 255
 256/* A file entry went away or appeared */
 257static void diff_index_show_file(struct rev_info *revs,
 258                                 const char *prefix,
 259                                 const struct cache_entry *ce,
 260                                 const struct object_id *oid, int oid_valid,
 261                                 unsigned int mode,
 262                                 unsigned dirty_submodule)
 263{
 264        diff_addremove(&revs->diffopt, prefix[0], mode,
 265                       oid, oid_valid, ce->name, dirty_submodule);
 266}
 267
 268static int get_stat_data(const struct cache_entry *ce,
 269                         const struct object_id **oidp,
 270                         unsigned int *modep,
 271                         int cached, int match_missing,
 272                         unsigned *dirty_submodule, struct diff_options *diffopt)
 273{
 274        const struct object_id *oid = &ce->oid;
 275        unsigned int mode = ce->ce_mode;
 276
 277        if (!cached && !ce_uptodate(ce)) {
 278                int changed;
 279                struct stat st;
 280                changed = check_removed(ce, &st);
 281                if (changed < 0)
 282                        return -1;
 283                else if (changed) {
 284                        if (match_missing) {
 285                                *oidp = oid;
 286                                *modep = mode;
 287                                return 0;
 288                        }
 289                        return -1;
 290                }
 291                changed = match_stat_with_submodule(diffopt, ce, &st,
 292                                                    0, dirty_submodule);
 293                if (changed) {
 294                        mode = ce_mode_from_stat(ce, st.st_mode);
 295                        oid = &null_oid;
 296                }
 297        }
 298
 299        *oidp = oid;
 300        *modep = mode;
 301        return 0;
 302}
 303
 304static void show_new_file(struct rev_info *revs,
 305                          const struct cache_entry *new,
 306                          int cached, int match_missing)
 307{
 308        const struct object_id *oid;
 309        unsigned int mode;
 310        unsigned dirty_submodule = 0;
 311
 312        /*
 313         * New file in the index: it might actually be different in
 314         * the working tree.
 315         */
 316        if (get_stat_data(new, &oid, &mode, cached, match_missing,
 317            &dirty_submodule, &revs->diffopt) < 0)
 318                return;
 319
 320        diff_index_show_file(revs, "+", new, oid, !is_null_oid(oid), mode, dirty_submodule);
 321}
 322
 323static int show_modified(struct rev_info *revs,
 324                         const struct cache_entry *old,
 325                         const struct cache_entry *new,
 326                         int report_missing,
 327                         int cached, int match_missing)
 328{
 329        unsigned int mode, oldmode;
 330        const struct object_id *oid;
 331        unsigned dirty_submodule = 0;
 332
 333        if (get_stat_data(new, &oid, &mode, cached, match_missing,
 334                          &dirty_submodule, &revs->diffopt) < 0) {
 335                if (report_missing)
 336                        diff_index_show_file(revs, "-", old,
 337                                             &old->oid, 1, old->ce_mode,
 338                                             0);
 339                return -1;
 340        }
 341
 342        if (revs->combine_merges && !cached &&
 343            (oidcmp(oid, &old->oid) || oidcmp(&old->oid, &new->oid))) {
 344                struct combine_diff_path *p;
 345                int pathlen = ce_namelen(new);
 346
 347                p = xmalloc(combine_diff_path_size(2, pathlen));
 348                p->path = (char *) &p->parent[2];
 349                p->next = NULL;
 350                memcpy(p->path, new->name, pathlen);
 351                p->path[pathlen] = 0;
 352                p->mode = mode;
 353                oidclr(&p->oid);
 354                memset(p->parent, 0, 2 * sizeof(struct combine_diff_parent));
 355                p->parent[0].status = DIFF_STATUS_MODIFIED;
 356                p->parent[0].mode = new->ce_mode;
 357                oidcpy(&p->parent[0].oid, &new->oid);
 358                p->parent[1].status = DIFF_STATUS_MODIFIED;
 359                p->parent[1].mode = old->ce_mode;
 360                oidcpy(&p->parent[1].oid, &old->oid);
 361                show_combined_diff(p, 2, revs->dense_combined_merges, revs);
 362                free(p);
 363                return 0;
 364        }
 365
 366        oldmode = old->ce_mode;
 367        if (mode == oldmode && !oidcmp(oid, &old->oid) && !dirty_submodule &&
 368            !revs->diffopt.flags.find_copies_harder)
 369                return 0;
 370
 371        diff_change(&revs->diffopt, oldmode, mode,
 372                    &old->oid, oid, 1, !is_null_oid(oid),
 373                    old->name, 0, dirty_submodule);
 374        return 0;
 375}
 376
 377/*
 378 * This gets a mix of an existing index and a tree, one pathname entry
 379 * at a time. The index entry may be a single stage-0 one, but it could
 380 * also be multiple unmerged entries (in which case idx_pos/idx_nr will
 381 * give you the position and number of entries in the index).
 382 */
 383static void do_oneway_diff(struct unpack_trees_options *o,
 384                           const struct cache_entry *idx,
 385                           const struct cache_entry *tree)
 386{
 387        struct rev_info *revs = o->unpack_data;
 388        int match_missing, cached;
 389
 390        /* i-t-a entries do not actually exist in the index */
 391        if (revs->diffopt.ita_invisible_in_index &&
 392            idx && ce_intent_to_add(idx)) {
 393                idx = NULL;
 394                if (!tree)
 395                        return; /* nothing to diff.. */
 396        }
 397
 398        /* if the entry is not checked out, don't examine work tree */
 399        cached = o->index_only ||
 400                (idx && ((idx->ce_flags & CE_VALID) || ce_skip_worktree(idx)));
 401        /*
 402         * Backward compatibility wart - "diff-index -m" does
 403         * not mean "do not ignore merges", but "match_missing".
 404         *
 405         * But with the revision flag parsing, that's found in
 406         * "!revs->ignore_merges".
 407         */
 408        match_missing = !revs->ignore_merges;
 409
 410        if (cached && idx && ce_stage(idx)) {
 411                struct diff_filepair *pair;
 412                pair = diff_unmerge(&revs->diffopt, idx->name);
 413                if (tree)
 414                        fill_filespec(pair->one, &tree->oid, 1,
 415                                      tree->ce_mode);
 416                return;
 417        }
 418
 419        /*
 420         * Something added to the tree?
 421         */
 422        if (!tree) {
 423                show_new_file(revs, idx, cached, match_missing);
 424                return;
 425        }
 426
 427        /*
 428         * Something removed from the tree?
 429         */
 430        if (!idx) {
 431                diff_index_show_file(revs, "-", tree, &tree->oid, 1,
 432                                     tree->ce_mode, 0);
 433                return;
 434        }
 435
 436        /* Show difference between old and new */
 437        show_modified(revs, tree, idx, 1, cached, match_missing);
 438}
 439
 440/*
 441 * The unpack_trees() interface is designed for merging, so
 442 * the different source entries are designed primarily for
 443 * the source trees, with the old index being really mainly
 444 * used for being replaced by the result.
 445 *
 446 * For diffing, the index is more important, and we only have a
 447 * single tree.
 448 *
 449 * We're supposed to advance o->pos to skip what we have already processed.
 450 *
 451 * This wrapper makes it all more readable, and takes care of all
 452 * the fairly complex unpack_trees() semantic requirements, including
 453 * the skipping, the path matching, the type conflict cases etc.
 454 */
 455static int oneway_diff(const struct cache_entry * const *src,
 456                       struct unpack_trees_options *o)
 457{
 458        const struct cache_entry *idx = src[0];
 459        const struct cache_entry *tree = src[1];
 460        struct rev_info *revs = o->unpack_data;
 461
 462        /*
 463         * Unpack-trees generates a DF/conflict entry if
 464         * there was a directory in the index and a tree
 465         * in the tree. From a diff standpoint, that's a
 466         * delete of the tree and a create of the file.
 467         */
 468        if (tree == o->df_conflict_entry)
 469                tree = NULL;
 470
 471        if (ce_path_match(idx ? idx : tree, &revs->prune_data, NULL)) {
 472                do_oneway_diff(o, idx, tree);
 473                if (diff_can_quit_early(&revs->diffopt)) {
 474                        o->exiting_early = 1;
 475                        return -1;
 476                }
 477        }
 478
 479        return 0;
 480}
 481
 482static int diff_cache(struct rev_info *revs,
 483                      const struct object_id *tree_oid,
 484                      const char *tree_name,
 485                      int cached)
 486{
 487        struct tree *tree;
 488        struct tree_desc t;
 489        struct unpack_trees_options opts;
 490
 491        tree = parse_tree_indirect(tree_oid);
 492        if (!tree)
 493                return error("bad tree object %s",
 494                             tree_name ? tree_name : oid_to_hex(tree_oid));
 495        memset(&opts, 0, sizeof(opts));
 496        opts.head_idx = 1;
 497        opts.index_only = cached;
 498        opts.diff_index_cached = (cached &&
 499                                  !revs->diffopt.flags.find_copies_harder);
 500        opts.merge = 1;
 501        opts.fn = oneway_diff;
 502        opts.unpack_data = revs;
 503        opts.src_index = &the_index;
 504        opts.dst_index = NULL;
 505        opts.pathspec = &revs->diffopt.pathspec;
 506        opts.pathspec->recursive = 1;
 507
 508        init_tree_desc(&t, tree->buffer, tree->size);
 509        return unpack_trees(1, &t, &opts);
 510}
 511
 512int run_diff_index(struct rev_info *revs, int cached)
 513{
 514        struct object_array_entry *ent;
 515
 516        ent = revs->pending.objects;
 517        if (diff_cache(revs, &ent->item->oid, ent->name, cached))
 518                exit(128);
 519
 520        diff_set_mnemonic_prefix(&revs->diffopt, "c/", cached ? "i/" : "w/");
 521        diffcore_fix_diff_index(&revs->diffopt);
 522        diffcore_std(&revs->diffopt);
 523        diff_flush(&revs->diffopt);
 524        return 0;
 525}
 526
 527int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 528{
 529        struct rev_info revs;
 530
 531        init_revisions(&revs, NULL);
 532        copy_pathspec(&revs.prune_data, &opt->pathspec);
 533        revs.diffopt = *opt;
 534
 535        if (diff_cache(&revs, tree_oid, NULL, 1))
 536                exit(128);
 537        return 0;
 538}
 539
 540int index_differs_from(const char *def, const struct diff_flags *flags,
 541                       int ita_invisible_in_index)
 542{
 543        struct rev_info rev;
 544        struct setup_revision_opt opt;
 545
 546        init_revisions(&rev, NULL);
 547        memset(&opt, 0, sizeof(opt));
 548        opt.def = def;
 549        setup_revisions(0, NULL, &rev, &opt);
 550        rev.diffopt.flags.quick = 1;
 551        rev.diffopt.flags.exit_with_status = 1;
 552        if (flags)
 553                diff_flags_or(&rev.diffopt.flags, flags);
 554        rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
 555        run_diff_index(&rev, 1);
 556        object_array_clear(&rev.pending);
 557        return (rev.diffopt.flags.has_changes != 0);
 558}