builtin-ls-files.con commit Merge branch 'je/hooks' (91d4b2e)
   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 "len" characters
  42 * are the common prefix
  43 */
  44static int match(const char **spec, char *ps_matched,
  45                 const char *filename, int len)
  46{
  47        const char *m;
  48
  49        while ((m = *spec++) != NULL) {
  50                int matchlen = strlen(m + len);
  51
  52                if (!matchlen)
  53                        goto matched;
  54                if (!strncmp(m + len, filename + len, matchlen)) {
  55                        if (m[len + matchlen - 1] == '/')
  56                                goto matched;
  57                        switch (filename[len + matchlen]) {
  58                        case '/': case '\0':
  59                                goto matched;
  60                        }
  61                }
  62                if (!fnmatch(m + len, filename + len, 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 && !match(pathspec, ps_matched, ent->name, len))
  84                return;
  85
  86        fputs(tag, stdout);
  87        write_name_quoted("", 0, ent->name + offset, line_terminator, stdout);
  88        putchar(line_terminator);
  89}
  90
  91static void show_other_files(struct dir_struct *dir)
  92{
  93        int i;
  94
  95
  96        /*
  97         * Skip matching and unmerged entries for the paths,
  98         * since we want just "others".
  99         *
 100         * (Matching entries are normally pruned during
 101         * the directory tree walk, but will show up for
 102         * gitlinks because we don't necessarily have
 103         * dir->show_other_directories set to suppress
 104         * them).
 105         */
 106        for (i = 0; i < dir->nr; i++) {
 107                struct dir_entry *ent = dir->entries[i];
 108                int len, pos;
 109                struct cache_entry *ce;
 110
 111                /*
 112                 * Remove the '/' at the end that directory
 113                 * walking adds for directory entries.
 114                 */
 115                len = ent->len;
 116                if (len && ent->name[len-1] == '/')
 117                        len--;
 118                pos = cache_name_pos(ent->name, len);
 119                if (0 <= pos)
 120                        continue;       /* exact match */
 121                pos = -pos - 1;
 122                if (pos < active_nr) {
 123                        ce = active_cache[pos];
 124                        if (ce_namelen(ce) == len &&
 125                            !memcmp(ce->name, ent->name, len))
 126                                continue; /* Yup, this one exists unmerged */
 127                }
 128                show_dir_entry(tag_other, ent);
 129        }
 130}
 131
 132static void show_killed_files(struct dir_struct *dir)
 133{
 134        int i;
 135        for (i = 0; i < dir->nr; i++) {
 136                struct dir_entry *ent = dir->entries[i];
 137                char *cp, *sp;
 138                int pos, len, killed = 0;
 139
 140                for (cp = ent->name; cp - ent->name < ent->len; cp = sp + 1) {
 141                        sp = strchr(cp, '/');
 142                        if (!sp) {
 143                                /* If ent->name is prefix of an entry in the
 144                                 * cache, it will be killed.
 145                                 */
 146                                pos = cache_name_pos(ent->name, ent->len);
 147                                if (0 <= pos)
 148                                        die("bug in show-killed-files");
 149                                pos = -pos - 1;
 150                                while (pos < active_nr &&
 151                                       ce_stage(active_cache[pos]))
 152                                        pos++; /* skip unmerged */
 153                                if (active_nr <= pos)
 154                                        break;
 155                                /* pos points at a name immediately after
 156                                 * ent->name in the cache.  Does it expect
 157                                 * ent->name to be a directory?
 158                                 */
 159                                len = ce_namelen(active_cache[pos]);
 160                                if ((ent->len < len) &&
 161                                    !strncmp(active_cache[pos]->name,
 162                                             ent->name, ent->len) &&
 163                                    active_cache[pos]->name[ent->len] == '/')
 164                                        killed = 1;
 165                                break;
 166                        }
 167                        if (0 <= cache_name_pos(ent->name, sp - ent->name)) {
 168                                /* If any of the leading directories in
 169                                 * ent->name is registered in the cache,
 170                                 * ent->name will be killed.
 171                                 */
 172                                killed = 1;
 173                                break;
 174                        }
 175                }
 176                if (killed)
 177                        show_dir_entry(tag_killed, dir->entries[i]);
 178        }
 179}
 180
 181static void show_ce_entry(const char *tag, struct cache_entry *ce)
 182{
 183        int len = prefix_len;
 184        int offset = prefix_offset;
 185
 186        if (len >= ce_namelen(ce))
 187                die("git-ls-files: internal error - cache entry not superset of prefix");
 188
 189        if (pathspec && !match(pathspec, ps_matched, ce->name, len))
 190                return;
 191
 192        if (tag && *tag && show_valid_bit &&
 193            (ce->ce_flags & htons(CE_VALID))) {
 194                static char alttag[4];
 195                memcpy(alttag, tag, 3);
 196                if (isalpha(tag[0]))
 197                        alttag[0] = tolower(tag[0]);
 198                else if (tag[0] == '?')
 199                        alttag[0] = '!';
 200                else {
 201                        alttag[0] = 'v';
 202                        alttag[1] = tag[0];
 203                        alttag[2] = ' ';
 204                        alttag[3] = 0;
 205                }
 206                tag = alttag;
 207        }
 208
 209        if (!show_stage) {
 210                fputs(tag, stdout);
 211                write_name_quoted("", 0, ce->name + offset,
 212                                  line_terminator, stdout);
 213                putchar(line_terminator);
 214        }
 215        else {
 216                printf("%s%06o %s %d\t",
 217                       tag,
 218                       ntohl(ce->ce_mode),
 219                       abbrev ? find_unique_abbrev(ce->sha1,abbrev)
 220                                : sha1_to_hex(ce->sha1),
 221                       ce_stage(ce));
 222                write_name_quoted("", 0, ce->name + offset,
 223                                  line_terminator, stdout);
 224                putchar(line_terminator);
 225        }
 226}
 227
 228static void show_files(struct dir_struct *dir, const char *prefix)
 229{
 230        int i;
 231
 232        /* For cached/deleted files we don't need to even do the readdir */
 233        if (show_others || show_killed) {
 234                const char *path = ".", *base = "";
 235                int baselen = prefix_len;
 236
 237                if (baselen)
 238                        path = base = prefix;
 239                read_directory(dir, path, base, baselen, pathspec);
 240                if (show_others)
 241                        show_other_files(dir);
 242                if (show_killed)
 243                        show_killed_files(dir);
 244        }
 245        if (show_cached | show_stage) {
 246                for (i = 0; i < active_nr; i++) {
 247                        struct cache_entry *ce = active_cache[i];
 248                        if (excluded(dir, ce->name) != dir->show_ignored)
 249                                continue;
 250                        if (show_unmerged && !ce_stage(ce))
 251                                continue;
 252                        if (ce->ce_flags & htons(CE_UPDATE))
 253                                continue;
 254                        show_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);
 255                }
 256        }
 257        if (show_deleted | show_modified) {
 258                for (i = 0; i < active_nr; i++) {
 259                        struct cache_entry *ce = active_cache[i];
 260                        struct stat st;
 261                        int err;
 262                        if (excluded(dir, ce->name) != dir->show_ignored)
 263                                continue;
 264                        err = lstat(ce->name, &st);
 265                        if (show_deleted && err)
 266                                show_ce_entry(tag_removed, ce);
 267                        if (show_modified && ce_modified(ce, &st, 0))
 268                                show_ce_entry(tag_modified, ce);
 269                }
 270        }
 271}
 272
 273/*
 274 * Prune the index to only contain stuff starting with "prefix"
 275 */
 276static void prune_cache(const char *prefix)
 277{
 278        int pos = cache_name_pos(prefix, prefix_len);
 279        unsigned int first, last;
 280
 281        if (pos < 0)
 282                pos = -pos-1;
 283        active_cache += pos;
 284        active_nr -= pos;
 285        first = 0;
 286        last = active_nr;
 287        while (last > first) {
 288                int next = (last + first) >> 1;
 289                struct cache_entry *ce = active_cache[next];
 290                if (!strncmp(ce->name, prefix, prefix_len)) {
 291                        first = next+1;
 292                        continue;
 293                }
 294                last = next;
 295        }
 296        active_nr = last;
 297}
 298
 299static const char *verify_pathspec(const char *prefix)
 300{
 301        const char **p, *n, *prev;
 302        char *real_prefix;
 303        unsigned long max;
 304
 305        prev = NULL;
 306        max = PATH_MAX;
 307        for (p = pathspec; (n = *p) != NULL; p++) {
 308                int i, len = 0;
 309                for (i = 0; i < max; i++) {
 310                        char c = n[i];
 311                        if (prev && prev[i] != c)
 312                                break;
 313                        if (!c || c == '*' || c == '?')
 314                                break;
 315                        if (c == '/')
 316                                len = i+1;
 317                }
 318                prev = n;
 319                if (len < max) {
 320                        max = len;
 321                        if (!max)
 322                                break;
 323                }
 324        }
 325
 326        if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
 327                die("git-ls-files: cannot generate relative filenames containing '..'");
 328
 329        real_prefix = NULL;
 330        prefix_len = max;
 331        if (max) {
 332                real_prefix = xmalloc(max + 1);
 333                memcpy(real_prefix, prev, max);
 334                real_prefix[max] = 0;
 335        }
 336        return real_prefix;
 337}
 338
 339/*
 340 * Read the tree specified with --with-tree option
 341 * (typically, HEAD) into stage #1 and then
 342 * squash them down to stage #0.  This is used for
 343 * --error-unmatch to list and check the path patterns
 344 * that were given from the command line.  We are not
 345 * going to write this index out.
 346 */
 347static void overlay_tree(const char *tree_name, const char *prefix)
 348{
 349        struct tree *tree;
 350        unsigned char sha1[20];
 351        const char **match;
 352        struct cache_entry *last_stage0 = NULL;
 353        int i;
 354
 355        if (get_sha1(tree_name, sha1))
 356                die("tree-ish %s not found.", tree_name);
 357        tree = parse_tree_indirect(sha1);
 358        if (!tree)
 359                die("bad tree-ish %s", tree_name);
 360
 361        /* Hoist the unmerged entries up to stage #3 to make room */
 362        for (i = 0; i < active_nr; i++) {
 363                struct cache_entry *ce = active_cache[i];
 364                if (!ce_stage(ce))
 365                        continue;
 366                ce->ce_flags |= htons(CE_STAGEMASK);
 367        }
 368
 369        if (prefix) {
 370                static const char *(matchbuf[2]);
 371                matchbuf[0] = prefix;
 372                matchbuf [1] = NULL;
 373                match = matchbuf;
 374        } else
 375                match = NULL;
 376        if (read_tree(tree, 1, match))
 377                die("unable to read tree entries %s", tree_name);
 378
 379        for (i = 0; i < active_nr; i++) {
 380                struct cache_entry *ce = active_cache[i];
 381                switch (ce_stage(ce)) {
 382                case 0:
 383                        last_stage0 = ce;
 384                        /* fallthru */
 385                default:
 386                        continue;
 387                case 1:
 388                        /*
 389                         * If there is stage #0 entry for this, we do not
 390                         * need to show it.  We use CE_UPDATE bit to mark
 391                         * such an entry.
 392                         */
 393                        if (last_stage0 &&
 394                            !strcmp(last_stage0->name, ce->name))
 395                                ce->ce_flags |= htons(CE_UPDATE);
 396                }
 397        }
 398}
 399
 400static const char ls_files_usage[] =
 401        "git-ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
 402        "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
 403        "[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] "
 404        "[--] [<file>]*";
 405
 406int cmd_ls_files(int argc, const char **argv, const char *prefix)
 407{
 408        int i;
 409        int exc_given = 0, require_work_tree = 0;
 410        struct dir_struct dir;
 411
 412        memset(&dir, 0, sizeof(dir));
 413        if (prefix)
 414                prefix_offset = strlen(prefix);
 415        git_config(git_default_config);
 416
 417        for (i = 1; i < argc; i++) {
 418                const char *arg = argv[i];
 419
 420                if (!strcmp(arg, "--")) {
 421                        i++;
 422                        break;
 423                }
 424                if (!strcmp(arg, "-z")) {
 425                        line_terminator = 0;
 426                        continue;
 427                }
 428                if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) {
 429                        tag_cached = "H ";
 430                        tag_unmerged = "M ";
 431                        tag_removed = "R ";
 432                        tag_modified = "C ";
 433                        tag_other = "? ";
 434                        tag_killed = "K ";
 435                        if (arg[1] == 'v')
 436                                show_valid_bit = 1;
 437                        continue;
 438                }
 439                if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) {
 440                        show_cached = 1;
 441                        continue;
 442                }
 443                if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) {
 444                        show_deleted = 1;
 445                        continue;
 446                }
 447                if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) {
 448                        show_modified = 1;
 449                        require_work_tree = 1;
 450                        continue;
 451                }
 452                if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) {
 453                        show_others = 1;
 454                        require_work_tree = 1;
 455                        continue;
 456                }
 457                if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
 458                        dir.show_ignored = 1;
 459                        require_work_tree = 1;
 460                        continue;
 461                }
 462                if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
 463                        show_stage = 1;
 464                        continue;
 465                }
 466                if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) {
 467                        show_killed = 1;
 468                        require_work_tree = 1;
 469                        continue;
 470                }
 471                if (!strcmp(arg, "--directory")) {
 472                        dir.show_other_directories = 1;
 473                        continue;
 474                }
 475                if (!strcmp(arg, "--no-empty-directory")) {
 476                        dir.hide_empty_directories = 1;
 477                        continue;
 478                }
 479                if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
 480                        /* There's no point in showing unmerged unless
 481                         * you also show the stage information.
 482                         */
 483                        show_stage = 1;
 484                        show_unmerged = 1;
 485                        continue;
 486                }
 487                if (!strcmp(arg, "-x") && i+1 < argc) {
 488                        exc_given = 1;
 489                        add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]);
 490                        continue;
 491                }
 492                if (!prefixcmp(arg, "--exclude=")) {
 493                        exc_given = 1;
 494                        add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]);
 495                        continue;
 496                }
 497                if (!strcmp(arg, "-X") && i+1 < argc) {
 498                        exc_given = 1;
 499                        add_excludes_from_file(&dir, argv[++i]);
 500                        continue;
 501                }
 502                if (!prefixcmp(arg, "--exclude-from=")) {
 503                        exc_given = 1;
 504                        add_excludes_from_file(&dir, arg+15);
 505                        continue;
 506                }
 507                if (!prefixcmp(arg, "--exclude-per-directory=")) {
 508                        exc_given = 1;
 509                        dir.exclude_per_dir = arg + 24;
 510                        continue;
 511                }
 512                if (!strcmp(arg, "--full-name")) {
 513                        prefix_offset = 0;
 514                        continue;
 515                }
 516                if (!strcmp(arg, "--error-unmatch")) {
 517                        error_unmatch = 1;
 518                        continue;
 519                }
 520                if (!prefixcmp(arg, "--with-tree=")) {
 521                        with_tree = arg + 12;
 522                        continue;
 523                }
 524                if (!prefixcmp(arg, "--abbrev=")) {
 525                        abbrev = strtoul(arg+9, NULL, 10);
 526                        if (abbrev && abbrev < MINIMUM_ABBREV)
 527                                abbrev = MINIMUM_ABBREV;
 528                        else if (abbrev > 40)
 529                                abbrev = 40;
 530                        continue;
 531                }
 532                if (!strcmp(arg, "--abbrev")) {
 533                        abbrev = DEFAULT_ABBREV;
 534                        continue;
 535                }
 536                if (*arg == '-')
 537                        usage(ls_files_usage);
 538                break;
 539        }
 540
 541        if (require_work_tree && !is_inside_work_tree()) {
 542                const char *work_tree = get_git_work_tree();
 543                if (!work_tree || chdir(work_tree))
 544                        die("This operation must be run in a work tree");
 545        }
 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(with_tree, prefix);
 583        }
 584        show_files(&dir, prefix);
 585
 586        if (ps_matched) {
 587                /* We need to make sure all pathspec matched otherwise
 588                 * it is an error.
 589                 */
 590                int num, errors = 0;
 591                for (num = 0; pathspec[num]; num++) {
 592                        int other, found_dup;
 593
 594                        if (ps_matched[num])
 595                                continue;
 596                        /*
 597                         * The caller might have fed identical pathspec
 598                         * twice.  Do not barf on such a mistake.
 599                         */
 600                        for (found_dup = other = 0;
 601                             !found_dup && pathspec[other];
 602                             other++) {
 603                                if (other == num || !ps_matched[other])
 604                                        continue;
 605                                if (!strcmp(pathspec[other], pathspec[num]))
 606                                        /*
 607                                         * Ok, we have a match already.
 608                                         */
 609                                        found_dup = 1;
 610                        }
 611                        if (found_dup)
 612                                continue;
 613
 614                        error("pathspec '%s' did not match any file(s) known to git.",
 615                              pathspec[num] + prefix_offset);
 616                        errors++;
 617                }
 618
 619                if (errors)
 620                        fprintf(stderr, "Did you forget to 'git add'?\n");
 621
 622                return errors ? 1 : 0;
 623        }
 624
 625        return 0;
 626}