summaryrefslogtreecommitdiff
path: root/ls-tree.c
blob: d005643ee08e03be2309c02e72522c6a81e8a1c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
 * GIT - The information manager from hell
 *
 * Copyright (C) Linus Torvalds, 2005
 */
#include "cache.h"
#include "blob.h"
#include "tree.h"
#include "quote.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
static int ls_options = 0;
const char **pathspec;
static int chomp_prefix = 0;
static const char *prefix;

static const char ls_tree_usage[] =
	"git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] [--full-name] <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(unsigned char *sha1, const char *base, int baselen,
		     const char *pathname, unsigned mode, int stage)
{
	int retval = 0;
	const char *type = "blob";

	if (S_ISDIR(mode)) {
		if (show_recursive(base, baselen, pathname)) {
			retval = READ_TREE_RECURSIVE;
			if (!(ls_options & LS_SHOW_TREES))
				return retval;
		}
		type = "tree";
	}
	else if (ls_options & LS_TREE_ONLY)
		return 0;

	if (chomp_prefix &&
	    (baselen < chomp_prefix || memcmp(prefix, base, chomp_prefix)))
		return 0;

	if (!(ls_options & LS_NAME_ONLY))
		printf("%06o %s %s\t", mode, type, sha1_to_hex(sha1));
	write_name_quoted(base + chomp_prefix, baselen - chomp_prefix,
			  pathname,
			  line_termination, stdout);
	putchar(line_termination);
	return retval;
}

int main(int argc, const char **argv)
{
	unsigned char sha1[20];
	struct tree *tree;

	prefix = setup_git_directory();
	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 '-':
			if (!strcmp(argv[1]+2, "name-only") ||
			    !strcmp(argv[1]+2, "name-status")) {
				ls_options |= LS_NAME_ONLY;
				break;
			}
			if (!strcmp(argv[1]+2, "full-name")) {
				chomp_prefix = 0;
				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) < 0)
		usage(ls_tree_usage);

	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;
}