Merge branch 'jc/merge' into next
[git] / ls-tree.c
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 "quote.h"
10
11 static int line_termination = '\n';
12 #define LS_RECURSIVE 1
13 #define LS_TREE_ONLY 2
14 #define LS_SHOW_TREES 4
15 #define LS_NAME_ONLY 8
16 static int abbrev = 0;
17 static int ls_options = 0;
18 const char **pathspec;
19 static int chomp_prefix = 0;
20 static const char *prefix;
21
22 static const char ls_tree_usage[] =
23         "git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]";
24
25 static int show_recursive(const char *base, int baselen, const char *pathname)
26 {
27         const char **s;
28
29         if (ls_options & LS_RECURSIVE)
30                 return 1;
31
32         s = pathspec;
33         if (!s)
34                 return 0;
35
36         for (;;) {
37                 const char *spec = *s++;
38                 int len, speclen;
39
40                 if (!spec)
41                         return 0;
42                 if (strncmp(base, spec, baselen))
43                         continue;
44                 len = strlen(pathname);
45                 spec += baselen;
46                 speclen = strlen(spec);
47                 if (speclen <= len)
48                         continue;
49                 if (memcmp(pathname, spec, len))
50                         continue;
51                 return 1;
52         }
53 }
54
55 static int show_tree(unsigned char *sha1, const char *base, int baselen,
56                      const char *pathname, unsigned mode, int stage)
57 {
58         int retval = 0;
59         const char *type = "blob";
60
61         if (S_ISDIR(mode)) {
62                 if (show_recursive(base, baselen, pathname)) {
63                         retval = READ_TREE_RECURSIVE;
64                         if (!(ls_options & LS_SHOW_TREES))
65                                 return retval;
66                 }
67                 type = "tree";
68         }
69         else if (ls_options & LS_TREE_ONLY)
70                 return 0;
71
72         if (chomp_prefix &&
73             (baselen < chomp_prefix || memcmp(prefix, base, chomp_prefix)))
74                 return 0;
75
76         if (!(ls_options & LS_NAME_ONLY))
77                 printf("%06o %s %s\t", mode, type,
78                                 abbrev ? find_unique_abbrev(sha1,abbrev)
79                                         : sha1_to_hex(sha1));
80         write_name_quoted(base + chomp_prefix, baselen - chomp_prefix,
81                           pathname,
82                           line_termination, stdout);
83         putchar(line_termination);
84         return retval;
85 }
86
87 int main(int argc, const char **argv)
88 {
89         unsigned char sha1[20];
90         struct tree *tree;
91
92         prefix = setup_git_directory();
93         if (prefix && *prefix)
94                 chomp_prefix = strlen(prefix);
95         while (1 < argc && argv[1][0] == '-') {
96                 switch (argv[1][1]) {
97                 case 'z':
98                         line_termination = 0;
99                         break;
100                 case 'r':
101                         ls_options |= LS_RECURSIVE;
102                         break;
103                 case 'd':
104                         ls_options |= LS_TREE_ONLY;
105                         break;
106                 case 't':
107                         ls_options |= LS_SHOW_TREES;
108                         break;
109                 case '-':
110                         if (!strcmp(argv[1]+2, "name-only") ||
111                             !strcmp(argv[1]+2, "name-status")) {
112                                 ls_options |= LS_NAME_ONLY;
113                                 break;
114                         }
115                         if (!strcmp(argv[1]+2, "full-name")) {
116                                 chomp_prefix = 0;
117                                 break;
118                         }
119                         if (!strncmp(argv[1]+2, "abbrev=",7)) {
120                                 abbrev = strtoul(argv[1]+9, NULL, 10);
121                                 if (abbrev && abbrev < MINIMUM_ABBREV)
122                                         abbrev = MINIMUM_ABBREV;
123                                 else if (abbrev > 40)
124                                         abbrev = 40;
125                                 break;
126                         }
127                         if (!strcmp(argv[1]+2, "abbrev")) {
128                                 abbrev = DEFAULT_ABBREV;
129                                 break;
130                         }
131                         /* otherwise fallthru */
132                 default:
133                         usage(ls_tree_usage);
134                 }
135                 argc--; argv++;
136         }
137         /* -d -r should imply -t, but -d by itself should not have to. */
138         if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
139             ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
140                 ls_options |= LS_SHOW_TREES;
141
142         if (argc < 2)
143                 usage(ls_tree_usage);
144         if (get_sha1(argv[1], sha1) < 0)
145                 usage(ls_tree_usage);
146
147         pathspec = get_pathspec(prefix, argv + 2);
148         tree = parse_tree_indirect(sha1);
149         if (!tree)
150                 die("not a tree object");
151         read_tree_recursive(tree, "", 0, 0, pathspec, show_tree);
152
153         return 0;
154 }