commit 5de5890b2ef3bcc0dc71dcbb4ecea948a6fb9599 from: Stefan Sperling date: Thu Oct 18 13:22:28 2018 UTC add a 'got tree' subcommand commit - 0996b108f50d63f22a8a3b948385eaf29c34f51c commit + 5de5890b2ef3bcc0dc71dcbb4ecea948a6fb9599 blob - e99745ffbbb099aa45d6347792f08048589f6528 blob + 6d7473d0cdb2d5c68cd1fc7c5756d6fc84e88692 --- got/got.1 +++ got/got.1 @@ -159,6 +159,27 @@ Use the repository at the specified path. If not specified, assume the repository is located at or above the current working directory. .El +.It Cm tree [ Fl c Ar commit ] [ Fl r Ar repository-path ] [ Fl i ] [ Ar path ] +Display a listing of files and directories at the specified +directory path in the repository. +If no path is specified, the root directory is listed. +.Pp +The options for +.Cm got tree +are as follows: +.Bl -tag -width Ds +.It Fl c Ar commit +List files and directories as they appear in the specified +.Ar commit . +The expected argument is the name of a branch or a SHA1 hash which corresponds +to a commit object. +.It Fl r Ar repository-path +Use the repository at the specified path. +If not specified, assume the repository is located at or above the current +working directory. +.It Fl i +Show object IDs of files (blob objects) and directories (tree objects). +.El .Sh EXIT STATUS .Ex -std got .Sh EXAMPLES blob - 7da102c7114314fb3207ac976ed1e2c409564843 blob + 972474021c895f2e1d0b925f2356d655dfbbaceb --- got/got.c +++ got/got.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -54,11 +55,13 @@ __dead static void usage_checkout(void); __dead static void usage_log(void); __dead static void usage_diff(void); __dead static void usage_blame(void); +__dead static void usage_tree(void); static const struct got_error* cmd_checkout(int, char *[]); static const struct got_error* cmd_log(int, char *[]); static const struct got_error* cmd_diff(int, char *[]); static const struct got_error* cmd_blame(int, char *[]); +static const struct got_error* cmd_tree(int, char *[]); #ifdef notyet static const struct got_error* cmd_status(int, char *[]); #endif @@ -72,6 +75,8 @@ static struct cmd got_commands[] = { "compare files and directories" }, { "blame", cmd_blame, usage_blame, " show when lines in a file were changed" }, + { "tree", cmd_tree, usage_tree, + " list files and directories in repository" }, #ifdef notyet { "status", cmd_status, usage_status, "show modification status of files" }, @@ -781,8 +786,164 @@ done: error = repo_error; } return error; +} + +__dead static void +usage_tree(void) +{ + fprintf(stderr, "usage: %s tree [-c commit] [-r repository-path] [-i] path\n", + getprogname()); + exit(1); +} + + +static const struct got_error * +print_tree(const char *path, struct got_object_id *commit_id, + int show_ids, struct got_repository *repo) +{ + const struct got_error *err = NULL; + struct got_object_id *tree_id = NULL; + struct got_tree_object *tree = NULL; + const struct got_tree_entries *entries; + struct got_tree_entry *te; + + err = got_object_id_by_path(&tree_id, repo, commit_id, path); + if (err) + goto done; + + err = got_object_open_as_tree(&tree, repo, tree_id); + if (err) + goto done; + entries = got_object_tree_get_entries(tree); + te = SIMPLEQ_FIRST(&entries->head); + while (te) { + char *id = NULL; + if (show_ids) { + char *id_str; + err = got_object_id_str(&id_str, te->id); + if (err) + goto done; + if (asprintf(&id, "%s ", id_str) == -1) { + err = got_error_from_errno(); + free(id_str); + goto done; + } + free(id_str); + } + printf("%s%s%s\n", id ? id : "", + te->name, S_ISDIR(te->mode) ? "/" : ""); + te = SIMPLEQ_NEXT(te, entry); + free(id); + } +done: + if (tree) + got_object_tree_close(tree); + free(tree_id); + return err; } +static const struct got_error * +cmd_tree(int argc, char *argv[]) +{ + const struct got_error *error; + struct got_repository *repo = NULL; + char *path, *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL; + struct got_object_id *commit_id = NULL; + char *commit_id_str = NULL; + int show_ids = 0; + int ch; + +#ifndef PROFILE + if (pledge("stdio rpath wpath cpath flock proc exec sendfd", NULL) + == -1) + err(1, "pledge"); +#endif + + while ((ch = getopt(argc, argv, "c:r:i")) != -1) { + switch (ch) { + case 'c': + commit_id_str = optarg; + break; + case 'r': + repo_path = realpath(optarg, NULL); + if (repo_path == NULL) + err(1, "-r option"); + break; + case 'i': + show_ids = 1; + break; + default: + usage(); + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; + + if (argc == 1) + path = argv[0]; + else if (argc > 1) + usage_tree(); + else + path = "/"; + + cwd = getcwd(NULL, 0); + if (cwd == NULL) { + error = got_error_from_errno(); + goto done; + } + if (repo_path == NULL) { + repo_path = strdup(cwd); + if (repo_path == NULL) { + error = got_error_from_errno(); + goto done; + } + } + + error = got_repo_open(&repo, repo_path); + if (error != NULL) + goto done; + + error = got_repo_map_path(&in_repo_path, repo, path); + if (error != NULL) + goto done; + + if (commit_id_str == NULL) { + struct got_reference *head_ref; + error = got_ref_open(&head_ref, repo, GOT_REF_HEAD); + if (error != NULL) + goto done; + error = got_ref_resolve(&commit_id, repo, head_ref); + got_ref_close(head_ref); + if (error != NULL) + goto done; + } else { + struct got_object *obj; + error = got_object_open_by_id_str(&obj, repo, commit_id_str); + if (error != NULL) + goto done; + commit_id = got_object_id_dup(got_object_get_id(obj)); + if (commit_id == NULL) + error = got_error_from_errno(); + got_object_close(obj); + } + + error = print_tree(in_repo_path, commit_id, show_ids, repo); +done: + free(in_repo_path); + free(repo_path); + free(cwd); + free(commit_id); + if (repo) { + const struct got_error *repo_error; + repo_error = got_repo_close(repo); + if (error == NULL) + error = repo_error; + } + return error; +} + #ifdef notyet static const struct got_error * cmd_status(int argc __unused, char *argv[] __unused)