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