builtin-ls-tree.con commit Merge branch 'aj/fix-read-tree-from-scratch' (31f0bce)
   1/*
   2 * GIT - The information manager from hell
   3 *
   4 * Copyright (C) Linus Torvalds, 2005
   5 */
   6#include "cache.h"
   7#include "blob.h"
   8#include "tree.h"
   9#include "commit.h"
  10#include "quote.h"
  11#include "builtin.h"
  12
  13static int line_termination = '\n';
  14#define LS_RECURSIVE 1
  15#define LS_TREE_ONLY 2
  16#define LS_SHOW_TREES 4
  17#define LS_NAME_ONLY 8
  18#define LS_SHOW_SIZE 16
  19static int abbrev;
  20static int ls_options;
  21static const char **pathspec;
  22static int chomp_prefix;
  23static const char *ls_tree_prefix;
  24
  25static const char ls_tree_usage[] =
  26        "git ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] <tree-ish> [path...]";
  27
  28static int show_recursive(const char *base, int baselen, const char *pathname)
  29{
  30        const char **s;
  31
  32        if (ls_options & LS_RECURSIVE)
  33                return 1;
  34
  35        s = pathspec;
  36        if (!s)
  37                return 0;
  38
  39        for (;;) {
  40                const char *spec = *s++;
  41                int len, speclen;
  42
  43                if (!spec)
  44                        return 0;
  45                if (strncmp(base, spec, baselen))
  46                        continue;
  47                len = strlen(pathname);
  48                spec += baselen;
  49                speclen = strlen(spec);
  50                if (speclen <= len)
  51                        continue;
  52                if (memcmp(pathname, spec, len))
  53                        continue;
  54                return 1;
  55        }
  56}
  57
  58static int show_tree(const unsigned char *sha1, const char *base, int baselen,
  59                const char *pathname, unsigned mode, int stage, void *context)
  60{
  61        int retval = 0;
  62        const char *type = blob_type;
  63
  64        if (S_ISGITLINK(mode)) {
  65                /*
  66                 * Maybe we want to have some recursive version here?
  67                 *
  68                 * Something similar to this incomplete example:
  69                 *
  70                if (show_subprojects(base, baselen, pathname))
  71                        retval = READ_TREE_RECURSIVE;
  72                 *
  73                 */
  74                type = commit_type;
  75        } else if (S_ISDIR(mode)) {
  76                if (show_recursive(base, baselen, pathname)) {
  77                        retval = READ_TREE_RECURSIVE;
  78                        if (!(ls_options & LS_SHOW_TREES))
  79                                return retval;
  80                }
  81                type = tree_type;
  82        }
  83        else if (ls_options & LS_TREE_ONLY)
  84                return 0;
  85
  86        if (chomp_prefix &&
  87            (baselen < chomp_prefix || memcmp(ls_tree_prefix, base, chomp_prefix)))
  88                return 0;
  89
  90        if (!(ls_options & LS_NAME_ONLY)) {
  91                if (ls_options & LS_SHOW_SIZE) {
  92                        char size_text[24];
  93                        if (!strcmp(type, blob_type)) {
  94                                unsigned long size;
  95                                if (sha1_object_info(sha1, &size) == OBJ_BAD)
  96                                        strcpy(size_text, "BAD");
  97                                else
  98                                        snprintf(size_text, sizeof(size_text),
  99                                                 "%lu", size);
 100                        } else
 101                                strcpy(size_text, "-");
 102                        printf("%06o %s %s %7s\t", mode, type,
 103                               abbrev ? find_unique_abbrev(sha1, abbrev)
 104                                      : sha1_to_hex(sha1),
 105                               size_text);
 106                } else
 107                        printf("%06o %s %s\t", mode, type,
 108                               abbrev ? find_unique_abbrev(sha1, abbrev)
 109                                      : sha1_to_hex(sha1));
 110        }
 111        write_name_quotedpfx(base + chomp_prefix, baselen - chomp_prefix,
 112                          pathname, stdout, line_termination);
 113        return retval;
 114}
 115
 116int cmd_ls_tree(int argc, const char **argv, const char *prefix)
 117{
 118        unsigned char sha1[20];
 119        struct tree *tree;
 120
 121        git_config(git_default_config, NULL);
 122        ls_tree_prefix = prefix;
 123        if (prefix && *prefix)
 124                chomp_prefix = strlen(prefix);
 125        while (1 < argc && argv[1][0] == '-') {
 126                switch (argv[1][1]) {
 127                case 'z':
 128                        line_termination = 0;
 129                        break;
 130                case 'r':
 131                        ls_options |= LS_RECURSIVE;
 132                        break;
 133                case 'd':
 134                        ls_options |= LS_TREE_ONLY;
 135                        break;
 136                case 't':
 137                        ls_options |= LS_SHOW_TREES;
 138                        break;
 139                case 'l':
 140                        ls_options |= LS_SHOW_SIZE;
 141                        break;
 142                case '-':
 143                        if (!strcmp(argv[1]+2, "name-only") ||
 144                            !strcmp(argv[1]+2, "name-status")) {
 145                                ls_options |= LS_NAME_ONLY;
 146                                break;
 147                        }
 148                        if (!strcmp(argv[1]+2, "long")) {
 149                                ls_options |= LS_SHOW_SIZE;
 150                                break;
 151                        }
 152                        if (!strcmp(argv[1]+2, "full-name")) {
 153                                chomp_prefix = 0;
 154                                break;
 155                        }
 156                        if (!strcmp(argv[1]+2, "full-tree")) {
 157                                ls_tree_prefix = prefix = NULL;
 158                                chomp_prefix = 0;
 159                                break;
 160                        }
 161                        if (!prefixcmp(argv[1]+2, "abbrev=")) {
 162                                abbrev = strtoul(argv[1]+9, NULL, 10);
 163                                if (abbrev && abbrev < MINIMUM_ABBREV)
 164                                        abbrev = MINIMUM_ABBREV;
 165                                else if (abbrev > 40)
 166                                        abbrev = 40;
 167                                break;
 168                        }
 169                        if (!strcmp(argv[1]+2, "abbrev")) {
 170                                abbrev = DEFAULT_ABBREV;
 171                                break;
 172                        }
 173                        /* otherwise fallthru */
 174                default:
 175                        usage(ls_tree_usage);
 176                }
 177                argc--; argv++;
 178        }
 179        /* -d -r should imply -t, but -d by itself should not have to. */
 180        if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
 181            ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
 182                ls_options |= LS_SHOW_TREES;
 183
 184        if (argc < 2)
 185                usage(ls_tree_usage);
 186        if (get_sha1(argv[1], sha1))
 187                die("Not a valid object name %s", argv[1]);
 188
 189        pathspec = get_pathspec(prefix, argv + 2);
 190        tree = parse_tree_indirect(sha1);
 191        if (!tree)
 192                die("not a tree object");
 193        read_tree_recursive(tree, "", 0, 0, pathspec, show_tree, NULL);
 194
 195        return 0;
 196}