/* * GIT - The information manager from hell * * Copyright (C) Linus Torvalds, 2005 */ #include "cache.h" static int line_termination = '\n'; static int recursive = 0; struct path_prefix { struct path_prefix *prev; const char *name; }; #define DEBUG(fmt, ...) static int string_path_prefix(char *buff, size_t blen, struct path_prefix *prefix) { 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); } return 0; } static void print_path_prefix(struct path_prefix *prefix) { if (prefix) { if (prefix->prev) { print_path_prefix(prefix->prev); putchar('/'); } fputs(prefix->name, stdout); } } /* * 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) { 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; if (strncmp(path,buff,len) == 0) { if (slen == len) return 0; else return 1; } return -1; } /* * 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) { 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; } } /* * 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); } if (! recursive || ! S_ISDIR(mode)) continue; if (matches && ! matched) continue; if (! (eltbuf = read_sha1_file(sha1, elttype, &eltsize)) ) { error("cannot read %s", sha1_to_hex(sha1)); continue; } /* 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 (mtype == 0) mindex++; list_recursive(eltbuf, elttype, eltsize, &this_prefix, &match[mindex], matches-mindex); free(eltbuf); } } static int qcmp(const void *a, const void *b) { return strcmp(*(char **)a, *(char **)b); } static int list(unsigned char *sha1,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; } static const char *ls_tree_usage = "git-ls-tree [-r] [-z] [paths...]"; int main(int argc, char **argv) { unsigned char sha1[20]; while (1 < argc && argv[1][0] == '-') { switch (argv[1][1]) { case 'z': line_termination = 0; break; case 'r': recursive = 1; break; default: usage(ls_tree_usage); } argc--; argv++; } if (argc < 2) usage(ls_tree_usage); if (get_sha1(argv[1], sha1) < 0) usage(ls_tree_usage); if (list(sha1, &argv[2]) < 0) die("list failed"); return 0; }