builtin-ls-files.con commit mailinfo: tests for RFC2047 examples (c32815f)
   1/*
   2 * This merges the file listing in the directory cache index
   3 * with the actual working directory list, and shows different
   4 * combinations of the two.
   5 *
   6 * Copyright (C) Linus Torvalds, 2005
   7 */
   8#include "cache.h"
   9#include "quote.h"
  10#include "dir.h"
  11#include "builtin.h"
  12#include "tree.h"
  13
  14static int abbrev;
  15static int show_deleted;
  16static int show_cached;
  17static int show_others;
  18static int show_stage;
  19static int show_unmerged;
  20static int show_modified;
  21static int show_killed;
  22static int show_valid_bit;
  23static int line_terminator = '\n';
  24
  25static int prefix_len;
  26static int prefix_offset;
  27static const char **pathspec;
  28static int error_unmatch;
  29static char *ps_matched;
  30static const char *with_tree;
  31
  32static const char *tag_cached = "";
  33static const char *tag_unmerged = "";
  34static const char *tag_removed = "";
  35static const char *tag_other = "";
  36static const char *tag_killed = "";
  37static const char *tag_modified = "";
  38
  39
  40/*
  41 * Match a pathspec against a filename. The first "skiplen" characters
  42 * are the common prefix
  43 */
  44int pathspec_match(const char **spec, char *ps_matched,
  45                   const char *filename, int skiplen)
  46{
  47        const char *m;
  48
  49        while ((m = *spec++) != NULL) {
  50                int matchlen = strlen(m + skiplen);
  51
  52                if (!matchlen)
  53                        goto matched;
  54                if (!strncmp(m + skiplen, filename + skiplen, matchlen)) {
  55                        if (m[skiplen + matchlen - 1] == '/')
  56                                goto matched;
  57                        switch (filename[skiplen + matchlen]) {
  58                        case '/': case '\0':
  59                                goto matched;
  60                        }
  61                }
  62                if (!fnmatch(m + skiplen, filename + skiplen, 0))
  63                        goto matched;
  64                if (ps_matched)
  65                        ps_matched++;
  66                continue;
  67        matched:
  68                if (ps_matched)
  69                        *ps_matched = 1;
  70                return 1;
  71        }
  72        return 0;
  73}
  74
  75static void show_dir_entry(const char *tag, struct dir_entry *ent)
  76{
  77        int len = prefix_len;
  78        int offset = prefix_offset;
  79
  80        if (len >= ent->len)
  81                die("git ls-files: internal error - directory entry not superset of prefix");
  82
  83        if (pathspec && !pathspec_match(pathspec, ps_matched, ent->name, len))
  84                return;
  85
  86        fputs(tag, stdout);
  87        write_name_quoted(ent->name + offset, stdout, line_terminator);
  88}
  89
  90static void show_other_files(struct dir_struct *dir)
  91{
  92        int i;
  93
  94        for (i = 0; i < dir->nr; i++) {
  95                struct dir_entry *ent = dir->entries[i];
  96                if (!cache_name_is_other(ent->name, ent->len))
  97                        continue;
  98                show_dir_entry(tag_other, ent);
  99        }
 100}
 101
 102static void show_killed_files(struct dir_struct *dir)
 103{
 104        int i;
 105        for (i = 0; i < dir->nr; i++) {
 106                struct dir_entry *ent = dir->entries[i];
 107                char *cp, *sp;
 108                int pos, len, killed = 0;
 109
 110                for (cp = ent->name; cp - ent->name < ent->len; cp = sp + 1) {
 111                        sp = strchr(cp, '/');
 112                        if (!sp) {
 113                                /* If ent->name is prefix of an entry in the
 114                                 * cache, it will be killed.
 115                                 */
 116                                pos = cache_name_pos(ent->name, ent->len);
 117                                if (0 <= pos)
 118                                        die("bug in show-killed-files");
 119                                pos = -pos - 1;
 120                                while (pos < active_nr &&
 121                                       ce_stage(active_cache[pos]))
 122                                        pos++; /* skip unmerged */
 123                                if (active_nr <= pos)
 124                                        break;
 125                                /* pos points at a name immediately after
 126                                 * ent->name in the cache.  Does it expect
 127                                 * ent->name to be a directory?
 128                                 */
 129                                len = ce_namelen(active_cache[pos]);
 130                                if ((ent->len < len) &&
 131                                    !strncmp(active_cache[pos]->name,
 132                                             ent->name, ent->len) &&
 133                                    active_cache[pos]->name[ent->len] == '/')
 134                                        killed = 1;
 135                                break;
 136                        }
 137                        if (0 <= cache_name_pos(ent->name, sp - ent->name)) {
 138                                /* If any of the leading directories in
 139                                 * ent->name is registered in the cache,
 140                                 * ent->name will be killed.
 141                                 */
 142                                killed = 1;
 143                                break;
 144                        }
 145                }
 146                if (killed)
 147                        show_dir_entry(tag_killed, dir->entries[i]);
 148        }
 149}
 150
 151static void show_ce_entry(const char *tag, struct cache_entry *ce)
 152{
 153        int len = prefix_len;
 154        int offset = prefix_offset;
 155
 156        if (len >= ce_namelen(ce))
 157                die("git ls-files: internal error - cache entry not superset of prefix");
 158
 159        if (pathspec && !pathspec_match(pathspec, ps_matched, ce->name, len))
 160                return;
 161
 162        if (tag && *tag && show_valid_bit &&
 163            (ce->ce_flags & CE_VALID)) {
 164                static char alttag[4];
 165                memcpy(alttag, tag, 3);
 166                if (isalpha(tag[0]))
 167                        alttag[0] = tolower(tag[0]);
 168                else if (tag[0] == '?')
 169                        alttag[0] = '!';
 170                else {
 171                        alttag[0] = 'v';
 172                        alttag[1] = tag[0];
 173                        alttag[2] = ' ';
 174                        alttag[3] = 0;
 175                }
 176                tag = alttag;
 177        }
 178
 179        if (!show_stage) {
 180                fputs(tag, stdout);
 181        } else {
 182                printf("%s%06o %s %d\t",
 183                       tag,
 184                       ce->ce_mode,
 185                       abbrev ? find_unique_abbrev(ce->sha1,abbrev)
 186                                : sha1_to_hex(ce->sha1),
 187                       ce_stage(ce));
 188        }
 189        write_name_quoted(ce->name + offset, stdout, line_terminator);
 190}
 191
 192static void show_files(struct dir_struct *dir, const char *prefix)
 193{
 194        int i;
 195
 196        /* For cached/deleted files we don't need to even do the readdir */
 197        if (show_others || show_killed) {
 198                const char *path = ".", *base = "";
 199                int baselen = prefix_len;
 200
 201                if (baselen)
 202                        path = base = prefix;
 203                read_directory(dir, path, base, baselen, pathspec);
 204                if (show_others)
 205                        show_other_files(dir);
 206                if (show_killed)
 207                        show_killed_files(dir);
 208        }
 209        if (show_cached | show_stage) {
 210                for (i = 0; i < active_nr; i++) {
 211                        struct cache_entry *ce = active_cache[i];
 212                        int dtype = ce_to_dtype(ce);
 213                        if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
 214                                continue;
 215                        if (show_unmerged && !ce_stage(ce))
 216                                continue;
 217                        if (ce->ce_flags & CE_UPDATE)
 218                                continue;
 219                        show_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);
 220                }
 221        }
 222        if (show_deleted | show_modified) {
 223                for (i = 0; i < active_nr; i++) {
 224                        struct cache_entry *ce = active_cache[i];
 225                        struct stat st;
 226                        int err;
 227                        int dtype = ce_to_dtype(ce);
 228                        if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
 229                                continue;
 230                        if (ce->ce_flags & CE_UPDATE)
 231                                continue;
 232                        err = lstat(ce->name, &st);
 233                        if (show_deleted && err)
 234                                show_ce_entry(tag_removed, ce);
 235                        if (show_modified && ce_modified(ce, &st, 0))
 236                                show_ce_entry(tag_modified, ce);
 237                }
 238        }
 239}
 240
 241/*
 242 * Prune the index to only contain stuff starting with "prefix"
 243 */
 244static void prune_cache(const char *prefix)
 245{
 246        int pos = cache_name_pos(prefix, prefix_len);
 247        unsigned int first, last;
 248
 249        if (pos < 0)
 250                pos = -pos-1;
 251        memmove(active_cache, active_cache + pos,
 252                (active_nr - pos) * sizeof(struct cache_entry *));
 253        active_nr -= pos;
 254        first = 0;
 255        last = active_nr;
 256        while (last > first) {
 257                int next = (last + first) >> 1;
 258                struct cache_entry *ce = active_cache[next];
 259                if (!strncmp(ce->name, prefix, prefix_len)) {
 260                        first = next+1;
 261                        continue;
 262                }
 263                last = next;
 264        }
 265        active_nr = last;
 266}
 267
 268static const char *verify_pathspec(const char *prefix)
 269{
 270        const char **p, *n, *prev;
 271        unsigned long max;
 272
 273        prev = NULL;
 274        max = PATH_MAX;
 275        for (p = pathspec; (n = *p) != NULL; p++) {
 276                int i, len = 0;
 277                for (i = 0; i < max; i++) {
 278                        char c = n[i];
 279                        if (prev && prev[i] != c)
 280                                break;
 281                        if (!c || c == '*' || c == '?')
 282                                break;
 283                        if (c == '/')
 284                                len = i+1;
 285                }
 286                prev = n;
 287                if (len < max) {
 288                        max = len;
 289                        if (!max)
 290                                break;
 291                }
 292        }
 293
 294        if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
 295                die("git ls-files: cannot generate relative filenames containing '..'");
 296
 297        prefix_len = max;
 298        return max ? xmemdupz(prev, max) : NULL;
 299}
 300
 301/*
 302 * Read the tree specified with --with-tree option
 303 * (typically, HEAD) into stage #1 and then
 304 * squash them down to stage #0.  This is used for
 305 * --error-unmatch to list and check the path patterns
 306 * that were given from the command line.  We are not
 307 * going to write this index out.
 308 */
 309void overlay_tree_on_cache(const char *tree_name, const char *prefix)
 310{
 311        struct tree *tree;
 312        unsigned char sha1[20];
 313        const char **match;
 314        struct cache_entry *last_stage0 = NULL;
 315        int i;
 316
 317        if (get_sha1(tree_name, sha1))
 318                die("tree-ish %s not found.", tree_name);
 319        tree = parse_tree_indirect(sha1);
 320        if (!tree)
 321                die("bad tree-ish %s", tree_name);
 322
 323        /* Hoist the unmerged entries up to stage #3 to make room */
 324        for (i = 0; i < active_nr; i++) {
 325                struct cache_entry *ce = active_cache[i];
 326                if (!ce_stage(ce))
 327                        continue;
 328                ce->ce_flags |= CE_STAGEMASK;
 329        }
 330
 331        if (prefix) {
 332                static const char *(matchbuf[2]);
 333                matchbuf[0] = prefix;
 334                matchbuf[1] = NULL;
 335                match = matchbuf;
 336        } else
 337                match = NULL;
 338        if (read_tree(tree, 1, match))
 339                die("unable to read tree entries %s", tree_name);
 340
 341        for (i = 0; i < active_nr; i++) {
 342                struct cache_entry *ce = active_cache[i];
 343                switch (ce_stage(ce)) {
 344                case 0:
 345                        last_stage0 = ce;
 346                        /* fallthru */
 347                default:
 348                        continue;
 349                case 1:
 350                        /*
 351                         * If there is stage #0 entry for this, we do not
 352                         * need to show it.  We use CE_UPDATE bit to mark
 353                         * such an entry.
 354                         */
 355                        if (last_stage0 &&
 356                            !strcmp(last_stage0->name, ce->name))
 357                                ce->ce_flags |= CE_UPDATE;
 358                }
 359        }
 360}
 361
 362int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)
 363{
 364        /*
 365         * Make sure all pathspec matched; otherwise it is an error.
 366         */
 367        int num, errors = 0;
 368        for (num = 0; pathspec[num]; num++) {
 369                int other, found_dup;
 370
 371                if (ps_matched[num])
 372                        continue;
 373                /*
 374                 * The caller might have fed identical pathspec
 375                 * twice.  Do not barf on such a mistake.
 376                 */
 377                for (found_dup = other = 0;
 378                     !found_dup && pathspec[other];
 379                     other++) {
 380                        if (other == num || !ps_matched[other])
 381                                continue;
 382                        if (!strcmp(pathspec[other], pathspec[num]))
 383                                /*
 384                                 * Ok, we have a match already.
 385                                 */
 386                                found_dup = 1;
 387                }
 388                if (found_dup)
 389                        continue;
 390
 391                error("pathspec '%s' did not match any file(s) known to git.",
 392                      pathspec[num] + prefix_offset);
 393                errors++;
 394        }
 395        return errors;
 396}
 397
 398static const char ls_files_usage[] =
 399        "git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
 400        "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
 401        "[ --exclude-per-directory=<filename> ] [--exclude-standard] "
 402        "[--full-name] [--abbrev] [--] [<file>]*";
 403
 404int cmd_ls_files(int argc, const char **argv, const char *prefix)
 405{
 406        int i;
 407        int exc_given = 0, require_work_tree = 0;
 408        struct dir_struct dir;
 409
 410        memset(&dir, 0, sizeof(dir));
 411        if (prefix)
 412                prefix_offset = strlen(prefix);
 413        git_config(git_default_config, NULL);
 414
 415        for (i = 1; i < argc; i++) {
 416                const char *arg = argv[i];
 417
 418                if (!strcmp(arg, "--")) {
 419                        i++;
 420                        break;
 421                }
 422                if (!strcmp(arg, "-z")) {
 423                        line_terminator = 0;
 424                        continue;
 425                }
 426                if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) {
 427                        tag_cached = "H ";
 428                        tag_unmerged = "M ";
 429                        tag_removed = "R ";
 430                        tag_modified = "C ";
 431                        tag_other = "? ";
 432                        tag_killed = "K ";
 433                        if (arg[1] == 'v')
 434                                show_valid_bit = 1;
 435                        continue;
 436                }
 437                if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) {
 438                        show_cached = 1;
 439                        continue;
 440                }
 441                if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) {
 442                        show_deleted = 1;
 443                        continue;
 444                }
 445                if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) {
 446                        show_modified = 1;
 447                        require_work_tree = 1;
 448                        continue;
 449                }
 450                if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) {
 451                        show_others = 1;
 452                        require_work_tree = 1;
 453                        continue;
 454                }
 455                if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
 456                        dir.show_ignored = 1;
 457                        require_work_tree = 1;
 458                        continue;
 459                }
 460                if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
 461                        show_stage = 1;
 462                        continue;
 463                }
 464                if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) {
 465                        show_killed = 1;
 466                        require_work_tree = 1;
 467                        continue;
 468                }
 469                if (!strcmp(arg, "--directory")) {
 470                        dir.show_other_directories = 1;
 471                        continue;
 472                }
 473                if (!strcmp(arg, "--no-empty-directory")) {
 474                        dir.hide_empty_directories = 1;
 475                        continue;
 476                }
 477                if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
 478                        /* There's no point in showing unmerged unless
 479                         * you also show the stage information.
 480                         */
 481                        show_stage = 1;
 482                        show_unmerged = 1;
 483                        continue;
 484                }
 485                if (!strcmp(arg, "-x") && i+1 < argc) {
 486                        exc_given = 1;
 487                        add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]);
 488                        continue;
 489                }
 490                if (!prefixcmp(arg, "--exclude=")) {
 491                        exc_given = 1;
 492                        add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]);
 493                        continue;
 494                }
 495                if (!strcmp(arg, "-X") && i+1 < argc) {
 496                        exc_given = 1;
 497                        add_excludes_from_file(&dir, argv[++i]);
 498                        continue;
 499                }
 500                if (!prefixcmp(arg, "--exclude-from=")) {
 501                        exc_given = 1;
 502                        add_excludes_from_file(&dir, arg+15);
 503                        continue;
 504                }
 505                if (!prefixcmp(arg, "--exclude-per-directory=")) {
 506                        exc_given = 1;
 507                        dir.exclude_per_dir = arg + 24;
 508                        continue;
 509                }
 510                if (!strcmp(arg, "--exclude-standard")) {
 511                        exc_given = 1;
 512                        setup_standard_excludes(&dir);
 513                        continue;
 514                }
 515                if (!strcmp(arg, "--full-name")) {
 516                        prefix_offset = 0;
 517                        continue;
 518                }
 519                if (!strcmp(arg, "--error-unmatch")) {
 520                        error_unmatch = 1;
 521                        continue;
 522                }
 523                if (!prefixcmp(arg, "--with-tree=")) {
 524                        with_tree = arg + 12;
 525                        continue;
 526                }
 527                if (!prefixcmp(arg, "--abbrev=")) {
 528                        abbrev = strtoul(arg+9, NULL, 10);
 529                        if (abbrev && abbrev < MINIMUM_ABBREV)
 530                                abbrev = MINIMUM_ABBREV;
 531                        else if (abbrev > 40)
 532                                abbrev = 40;
 533                        continue;
 534                }
 535                if (!strcmp(arg, "--abbrev")) {
 536                        abbrev = DEFAULT_ABBREV;
 537                        continue;
 538                }
 539                if (*arg == '-')
 540                        usage(ls_files_usage);
 541                break;
 542        }
 543
 544        if (require_work_tree && !is_inside_work_tree())
 545                setup_work_tree();
 546
 547        pathspec = get_pathspec(prefix, argv + i);
 548
 549        /* Verify that the pathspec matches the prefix */
 550        if (pathspec)
 551                prefix = verify_pathspec(prefix);
 552
 553        /* Treat unmatching pathspec elements as errors */
 554        if (pathspec && error_unmatch) {
 555                int num;
 556                for (num = 0; pathspec[num]; num++)
 557                        ;
 558                ps_matched = xcalloc(1, num);
 559        }
 560
 561        if (dir.show_ignored && !exc_given) {
 562                fprintf(stderr, "%s: --ignored needs some exclude pattern\n",
 563                        argv[0]);
 564                exit(1);
 565        }
 566
 567        /* With no flags, we default to showing the cached files */
 568        if (!(show_stage | show_deleted | show_others | show_unmerged |
 569              show_killed | show_modified))
 570                show_cached = 1;
 571
 572        read_cache();
 573        if (prefix)
 574                prune_cache(prefix);
 575        if (with_tree) {
 576                /*
 577                 * Basic sanity check; show-stages and show-unmerged
 578                 * would not make any sense with this option.
 579                 */
 580                if (show_stage || show_unmerged)
 581                        die("ls-files --with-tree is incompatible with -s or -u");
 582                overlay_tree_on_cache(with_tree, prefix);
 583        }
 584        show_files(&dir, prefix);
 585
 586        if (ps_matched) {
 587                int bad;
 588                bad = report_path_error(ps_matched, pathspec, prefix_offset);
 589                if (bad)
 590                        fprintf(stderr, "Did you forget to 'git add'?\n");
 591
 592                return bad ? 1 : 0;
 593        }
 594
 595        return 0;
 596}