Fix pack-index issue on 64-bit platforms a bit more portably.
[git.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_type;
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_type;
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         git_config(git_default_config);
94         if (prefix && *prefix)
95                 chomp_prefix = strlen(prefix);
96         while (1 < argc && argv[1][0] == '-') {
97                 switch (argv[1][1]) {
98                 case 'z':
99                         line_termination = 0;
100                         break;
101                 case 'r':
102                         ls_options |= LS_RECURSIVE;
103                         break;
104                 case 'd':
105                         ls_options |= LS_TREE_ONLY;
106                         break;
107                 case 't':
108                         ls_options |= LS_SHOW_TREES;
109                         break;
110                 case '-':
111                         if (!strcmp(argv[1]+2, "name-only") ||
112                             !strcmp(argv[1]+2, "name-status")) {
113                                 ls_options |= LS_NAME_ONLY;
114                                 break;
115                         }
116                         if (!strcmp(argv[1]+2, "full-name")) {
117                                 chomp_prefix = 0;
118                                 break;
119                         }
120                         if (!strncmp(argv[1]+2, "abbrev=",7)) {
121                                 abbrev = strtoul(argv[1]+9, NULL, 10);
122                                 if (abbrev && abbrev < MINIMUM_ABBREV)
123                                         abbrev = MINIMUM_ABBREV;
124                                 else if (abbrev > 40)
125                                         abbrev = 40;
126                                 break;
127                         }
128                         if (!strcmp(argv[1]+2, "abbrev")) {
129                                 abbrev = DEFAULT_ABBREV;
130                                 break;
131                         }
132                         /* otherwise fallthru */
133                 default:
134                         usage(ls_tree_usage);
135                 }
136                 argc--; argv++;
137         }
138         /* -d -r should imply -t, but -d by itself should not have to. */
139         if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
140             ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
141                 ls_options |= LS_SHOW_TREES;
142
143         if (argc < 2)
144                 usage(ls_tree_usage);
145         if (get_sha1(argv[1], sha1))
146                 die("Not a valid object name %s", argv[1]);
147
148         pathspec = get_pathspec(prefix, argv + 2);
149         tree = parse_tree_indirect(sha1);
150         if (!tree)
151                 die("not a tree object");
152         read_tree_recursive(tree, "", 0, 0, pathspec, show_tree);
153
154         return 0;
155 }