|  | /* | 
|  | * GIT - The information manager from hell | 
|  | * | 
|  | * Copyright (C) Linus Torvalds, 2005 | 
|  | */ | 
|  | #include "cache.h" | 
|  | #include "blob.h" | 
|  | #include "tree.h" | 
|  | #include "commit.h" | 
|  | #include "quote.h" | 
|  | #include "builtin.h" | 
|  |  | 
|  | static int line_termination = '\n'; | 
|  | #define LS_RECURSIVE 1 | 
|  | #define LS_TREE_ONLY 2 | 
|  | #define LS_SHOW_TREES 4 | 
|  | #define LS_NAME_ONLY 8 | 
|  | #define LS_SHOW_SIZE 16 | 
|  | static int abbrev; | 
|  | static int ls_options; | 
|  | static const char **pathspec; | 
|  | static int chomp_prefix; | 
|  | static const char *ls_tree_prefix; | 
|  |  | 
|  | static const char ls_tree_usage[] = | 
|  | "git-ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]"; | 
|  |  | 
|  | static int show_recursive(const char *base, int baselen, const char *pathname) | 
|  | { | 
|  | const char **s; | 
|  |  | 
|  | if (ls_options & LS_RECURSIVE) | 
|  | return 1; | 
|  |  | 
|  | s = pathspec; | 
|  | if (!s) | 
|  | return 0; | 
|  |  | 
|  | for (;;) { | 
|  | const char *spec = *s++; | 
|  | int len, speclen; | 
|  |  | 
|  | if (!spec) | 
|  | return 0; | 
|  | if (strncmp(base, spec, baselen)) | 
|  | continue; | 
|  | len = strlen(pathname); | 
|  | spec += baselen; | 
|  | speclen = strlen(spec); | 
|  | if (speclen <= len) | 
|  | continue; | 
|  | if (memcmp(pathname, spec, len)) | 
|  | continue; | 
|  | return 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int show_tree(const unsigned char *sha1, const char *base, int baselen, | 
|  | const char *pathname, unsigned mode, int stage) | 
|  | { | 
|  | int retval = 0; | 
|  | const char *type = blob_type; | 
|  | unsigned long size; | 
|  |  | 
|  | if (S_ISGITLINK(mode)) { | 
|  | /* | 
|  | * Maybe we want to have some recursive version here? | 
|  | * | 
|  | * Something like: | 
|  | * | 
|  | if (show_subprojects(base, baselen, pathname)) { | 
|  | if (fork()) { | 
|  | chdir(base); | 
|  | exec ls-tree; | 
|  | } | 
|  | waitpid(); | 
|  | } | 
|  | * | 
|  | * ..or similar.. | 
|  | */ | 
|  | type = commit_type; | 
|  | } else if (S_ISDIR(mode)) { | 
|  | if (show_recursive(base, baselen, pathname)) { | 
|  | retval = READ_TREE_RECURSIVE; | 
|  | if (!(ls_options & LS_SHOW_TREES)) | 
|  | return retval; | 
|  | } | 
|  | type = tree_type; | 
|  | } | 
|  | else if (ls_options & LS_TREE_ONLY) | 
|  | return 0; | 
|  |  | 
|  | if (chomp_prefix && | 
|  | (baselen < chomp_prefix || memcmp(ls_tree_prefix, base, chomp_prefix))) | 
|  | return 0; | 
|  |  | 
|  | if (!(ls_options & LS_NAME_ONLY)) { | 
|  | if (ls_options & LS_SHOW_SIZE) { | 
|  | if (!strcmp(type, blob_type)) { | 
|  | sha1_object_info(sha1, &size); | 
|  | printf("%06o %s %s %7lu\t", mode, type, | 
|  | abbrev ? find_unique_abbrev(sha1, abbrev) | 
|  | : sha1_to_hex(sha1), | 
|  | size); | 
|  | } else | 
|  | printf("%06o %s %s %7c\t", mode, type, | 
|  | abbrev ? find_unique_abbrev(sha1, abbrev) | 
|  | : sha1_to_hex(sha1), | 
|  | '-'); | 
|  | } else | 
|  | printf("%06o %s %s\t", mode, type, | 
|  | abbrev ? find_unique_abbrev(sha1, abbrev) | 
|  | : sha1_to_hex(sha1)); | 
|  | } | 
|  | write_name_quoted(base + chomp_prefix, baselen - chomp_prefix, | 
|  | pathname, | 
|  | line_termination, stdout); | 
|  | putchar(line_termination); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | int cmd_ls_tree(int argc, const char **argv, const char *prefix) | 
|  | { | 
|  | unsigned char sha1[20]; | 
|  | struct tree *tree; | 
|  |  | 
|  | git_config(git_default_config); | 
|  | ls_tree_prefix = prefix; | 
|  | if (prefix && *prefix) | 
|  | chomp_prefix = strlen(prefix); | 
|  | while (1 < argc && argv[1][0] == '-') { | 
|  | switch (argv[1][1]) { | 
|  | case 'z': | 
|  | line_termination = 0; | 
|  | break; | 
|  | case 'r': | 
|  | ls_options |= LS_RECURSIVE; | 
|  | break; | 
|  | case 'd': | 
|  | ls_options |= LS_TREE_ONLY; | 
|  | break; | 
|  | case 't': | 
|  | ls_options |= LS_SHOW_TREES; | 
|  | break; | 
|  | case 'l': | 
|  | ls_options |= LS_SHOW_SIZE; | 
|  | break; | 
|  | case '-': | 
|  | if (!strcmp(argv[1]+2, "name-only") || | 
|  | !strcmp(argv[1]+2, "name-status")) { | 
|  | ls_options |= LS_NAME_ONLY; | 
|  | break; | 
|  | } | 
|  | if (!strcmp(argv[1]+2, "long")) { | 
|  | ls_options |= LS_SHOW_SIZE; | 
|  | break; | 
|  | } | 
|  | if (!strcmp(argv[1]+2, "full-name")) { | 
|  | chomp_prefix = 0; | 
|  | break; | 
|  | } | 
|  | if (!prefixcmp(argv[1]+2, "abbrev=")) { | 
|  | abbrev = strtoul(argv[1]+9, NULL, 10); | 
|  | if (abbrev && abbrev < MINIMUM_ABBREV) | 
|  | abbrev = MINIMUM_ABBREV; | 
|  | else if (abbrev > 40) | 
|  | abbrev = 40; | 
|  | break; | 
|  | } | 
|  | if (!strcmp(argv[1]+2, "abbrev")) { | 
|  | abbrev = DEFAULT_ABBREV; | 
|  | break; | 
|  | } | 
|  | /* otherwise fallthru */ | 
|  | default: | 
|  | usage(ls_tree_usage); | 
|  | } | 
|  | argc--; argv++; | 
|  | } | 
|  | /* -d -r should imply -t, but -d by itself should not have to. */ | 
|  | if ( (LS_TREE_ONLY|LS_RECURSIVE) == | 
|  | ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options)) | 
|  | ls_options |= LS_SHOW_TREES; | 
|  |  | 
|  | if (argc < 2) | 
|  | usage(ls_tree_usage); | 
|  | if (get_sha1(argv[1], sha1)) | 
|  | die("Not a valid object name %s", argv[1]); | 
|  |  | 
|  | pathspec = get_pathspec(prefix, argv + 2); | 
|  | tree = parse_tree_indirect(sha1); | 
|  | if (!tree) | 
|  | die("not a tree object"); | 
|  | read_tree_recursive(tree, "", 0, 0, pathspec, show_tree); | 
|  |  | 
|  | return 0; | 
|  | } |