@ -4,188 +4,217 @@
@@ -4,188 +4,217 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
#include "blob.h"
#include "tree.h"
static int line_termination = '\n';
static int recursive = 0;
#define LS_RECURSIVE 1
#define LS_TREE_ONLY 2
static int ls_options = 0;
struct path_prefix {
struct path_prefix *prev;
const char *name;
};
static struct tree_entry_list root_entry;
#define DEBUG(fmt, ...)
static void prepare_root(unsigned char *sha1)
{
unsigned char rsha[20];
unsigned long size;
void *buf;
struct tree *root_tree;
buf = read_object_with_reference(sha1, "tree", &size, rsha);
free(buf);
if (!buf)
die("Could not read %s", sha1_to_hex(sha1));
root_tree = lookup_tree(rsha);
if (!root_tree)
die("Could not read %s", sha1_to_hex(sha1));
/* Prepare a fake entry */
root_entry.directory = 1;
root_entry.executable = root_entry.symlink = 0;
root_entry.mode = S_IFDIR;
root_entry.name = "";
root_entry.item.tree = root_tree;
root_entry.parent = NULL;
}
static int string_path_prefix(char *buff, size_t blen, struct path_prefix *prefix)
static int prepare_children(struct tree_entry_list *elem)
{
int len = 0;
if (prefix) {
if (prefix->prev) {
len = string_path_prefix(buff,blen,prefix->prev);
buff += len;
blen -= len;
if (blen > 0) {
*buff = '/';
len++;
buff++;
blen--;
}
}
strncpy(buff,prefix->name,blen);
return len + strlen(prefix->name);
if (!elem->directory)
return -1;
if (!elem->item.tree->object.parsed) {
struct tree_entry_list *e;
if (parse_tree(elem->item.tree))
return -1;
/* Set up the parent link */
for (e = elem->item.tree->entries; e; e = e->next)
e->parent = elem;
}
return 0;
}
static void print_path_prefix(struct path_prefix *prefix)
static struct tree_entry_list *find_entry_0(struct tree_entry_list *elem,
const char *path,
const char *path_end)
{
if (prefix) {
if (prefix->prev) {
print_path_prefix(prefix->prev);
putchar('/');
const char *ep;
int len;
while (path < path_end) {
if (prepare_children(elem))
return NULL;
/* In elem->tree->entries, find the one that has name
* that matches what is between path and ep.
*/
elem = elem->item.tree->entries;
ep = strchr(path, '/');
if (!ep || path_end <= ep)
ep = path_end;
len = ep - path;
while (elem) {
if ((strlen(elem->name) == len) &&
!strncmp(elem->name, path, len))
break;
elem = elem->next;
}
fputs(prefix->name, stdout);
if (path_end <= ep || !elem)
return elem;
while (*ep == '/' && ep < path_end)
ep++;
path = ep;
}
return NULL;
}
/*
* return:
* -1 if prefix is *not* a subset of path
* 0 if prefix == path
* 1 if prefix is a subset of path
*/
static int pathcmp(const char *path, struct path_prefix *prefix)
static struct tree_entry_list *find_entry(const char *path,
const char *path_end)
{
char buff[PATH_MAX];
int len,slen;
if (prefix == NULL)
return 1;
len = string_path_prefix(buff, sizeof buff, prefix);
slen = strlen(path);
if (slen < len)
return -1;
/* Find tree element, descending from root, that
* corresponds to the named path, lazily expanding
* the tree if possible.
*/
if (path == path_end) {
/* Special. This is the root level */
return &root_entry;
}
return find_entry_0(&root_entry, path, path_end);
}
if (strncmp(path,buff,len) == 0) {
if (slen == len)
return 0;
else
return 1;
static void show_entry_name(struct tree_entry_list *e)
{
/* This is yucky. The root level is there for
* our convenience but we really want to do a
* forest.
*/
if (e->parent && e->parent != &root_entry) {
show_entry_name(e->parent);
putchar('/');
}
printf("%s", e->name);
}
return -1;
}
static const char *entry_type(struct tree_entry_list *e)
{
return (e->directory ? "tree" : "blob");
}
/*
* match may be NULL, or a *sorted* list of paths
*/
static void list_recursive(void *buffer,
const char *type,
unsigned long size,
struct path_prefix *prefix,
char **match, int matches)
static const char *entry_hex(struct tree_entry_list *e)
{
struct path_prefix this_prefix;
this_prefix.prev = prefix;
if (strcmp(type, "tree"))
die("expected a 'tree' node");
if (matches)
recursive = 1;
while (size) {
int namelen = strlen(buffer)+1;
void *eltbuf = NULL;
char elttype[20];
unsigned long eltsize;
unsigned char *sha1 = buffer + namelen;
char *path = strchr(buffer, ' ') + 1;
unsigned int mode;
const char *matched = NULL;
int mtype = -1;
int mindex;
if (size < namelen + 20 || sscanf(buffer, "%o", &mode) != 1)
die("corrupt 'tree' file");
buffer = sha1 + 20;
size -= namelen + 20;
this_prefix.name = path;
for ( mindex = 0; mindex < matches; mindex++) {
mtype = pathcmp(match[mindex],&this_prefix);
if (mtype >= 0) {
matched = match[mindex];
break;
}
}
return sha1_to_hex(e->directory
? e->item.tree->object.sha1
: e->item.blob->object.sha1);
}
/*
* If we're not matching, or if this is an exact match,
* print out the info
*/
if (!matches || (matched != NULL && mtype == 0)) {
printf("%06o %s %s\t", mode,
S_ISDIR(mode) ? "tree" : "blob",
sha1_to_hex(sha1));
print_path_prefix(&this_prefix);
putchar(line_termination);
}
/* forward declaration for mutually recursive routines */
static int show_entry(struct tree_entry_list *, int);
if (! recursive || ! S_ISDIR(mode))
continue;
static int show_children(struct tree_entry_list *e, int level)
{
if (prepare_children(e))
die("internal error: ls-tree show_children called with non tree");
e = e->item.tree->entries;
while (e) {
show_entry(e, level);
e = e->next;
}
return 0;
}
if (matches && ! matched)
continue;
static int show_entry(struct tree_entry_list *e, int level)
{
int err = 0;
if (! (eltbuf = read_sha1_file(sha1, elttype, &eltsize)) ) {
error("cannot read %s", sha1_to_hex(sha1));
continue;
}
if (e != &root_entry) {
printf("%06o %s %s ", e->mode, entry_type(e),
entry_hex(e));
show_entry_name(e);
putchar(line_termination);
}
/* If this is an exact directory match, we may have
* directory files following this path. Match on them.
* Otherwise, we're at a pach subcomponent, and we need
* to try to match again.
if (e->directory) {
/* If this is a directory, we have the following cases:
* (1) This is the top-level request (explicit path from the
* command line, or "root" if there is no command line).
* a. Without any flag. We show direct children. We do not
* recurse into them.
* b. With -r. We do recurse into children.
* c. With -d. We do not recurse into children.
* (2) We came here because our caller is either (1-a) or
* (1-b).
* a. Without any flag. We do not show our children (which
* are grandchildren for the original request).
* b. With -r. We continue to recurse into our children.
* c. With -d. We should not have come here to begin with.
*/
if (mtype == 0)
mindex++;
list_recursive(eltbuf, elttype, eltsize, &this_prefix, &match[mindex], matches-mindex);
free(eltbuf);
if (level == 0 && !(ls_options & LS_TREE_ONLY))
/* case (1)-a and (1)-b */
err = err | show_children(e, level+1);
else if (level && ls_options & LS_RECURSIVE)
/* case (2)-b */
err = err | show_children(e, level+1);
}
return err;
}
static int qcmp(const void *a, const void *b)
static int list_one(const char *path, const char *path_end)
{
return strcmp(*(char **)a, *(char **)b);
int err = 0;
struct tree_entry_list *e = find_entry(path, path_end);
if (!e) {
/* traditionally ls-tree does not complain about
* missing path. We may change this later to match
* what "/bin/ls -a" does, which is to complain.
*/
return err;
}
err = err | show_entry(e, 0);
return err;
}
static int list(unsigned char *sha1,char **path)
static int list(char **path)
{
void *buffer;
unsigned long size;
int npaths;
for (npaths = 0; path[npaths] != NULL; npaths++)
;
qsort(path,npaths,sizeof(char *),qcmp);
buffer = read_object_with_reference(sha1, "tree", &size, NULL);
if (!buffer)
die("unable to read sha1 file");
list_recursive(buffer, "tree", size, NULL, path, npaths);
free(buffer);
return 0;
int i;
int err = 0;
for (i = 0; path[i]; i++) {
int len = strlen(path[i]);
while (0 <= len && path[i][len] == '/')
len--;
err = err | list_one(path[i], path[i] + len);
}
return err;
}
static const char *ls_tree_usage = "git-ls-tree [-r] [-z] <key> [paths...]";
static const char *ls_tree_usage =
"git-ls-tree [-d] [-r] [-z] <tree-ish> [path...]";
int main(int argc, char **argv)
{
static char *path0[] = { "", NULL };
char **path;
unsigned char sha1[20];
while (1 < argc && argv[1][0] == '-') {
@ -194,7 +223,10 @@ int main(int argc, char **argv)
@@ -194,7 +223,10 @@ int main(int argc, char **argv)
line_termination = 0;
break;
case 'r':
recursive = 1;
ls_options |= LS_RECURSIVE;
break;
case 'd':
ls_options |= LS_TREE_ONLY;
break;
default:
usage(ls_tree_usage);
@ -206,7 +238,10 @@ int main(int argc, char **argv)
@@ -206,7 +238,10 @@ int main(int argc, char **argv)
usage(ls_tree_usage);
if (get_sha1(argv[1], sha1) < 0)
usage(ls_tree_usage);
if (list(sha1, &argv[2]) < 0)
path = (argc == 2) ? path0 : (argv + 2);
prepare_root(sha1);
if (list(path) < 0)
die("list failed");
return 0;
}