commit - cf07f22bd8beb1ed9e048433d553e394cc323c5f
commit + b2118c49a14c29447e228bf9a2b2a38f2da4f10b
blob - 136fa542011eaf775871dd87e5f0c0a2ba5ef5a4
blob + f9e5cd6f3135e7407b0775077c23ca64fd500590
--- got/got.1
+++ got/got.1
command must be used to populate the empty repository before
.Cm got checkout
can be used.
-.It Cm in
-Short alias for
-.Cm init .
.It Cm import Oo Fl b Ar branch Oc Oo Fl m Ar message Oc Oo Fl r Ar repository-path Oc Oo Fl I Ar pattern Oc Ar directory
Create an initial commit in a repository from the file hierarchy
within the specified
This option can be used to resolve ambiguity in cases where paths
look like tag names, reference names, or object IDs.
.El
+.It Cm info Op Ar path ...
+Display meta-data stored in a work tree.
+See
+.Xr got-worktree 5
+for details.
+.Pp
+The work tree to use is resolved implicitly by walking upwards from the
+current working directory.
+.Pp
+If one or more
+.Ar path
+arguments are specified, show additional per-file information for tracked
+files located at or within these paths.
+If a
+.Ar path
+argument corresponds to the work tree's root directory, display information
+for all tracked files.
.El
.Sh ENVIRONMENT
.Bl -tag -width GOT_AUTHOR
.Pp
.Dl $ cd sys/dev/usb && got log ../../uvm
.Pp
+And this command displays work tree meta-data about all tracked files:
+.Pp
+.Dl $ cd /usr/src
+.Dl $ got info\ . | less
+.Pp
Add new files and remove obsolete files in a work tree directory:
.Pp
.Dl $ got add sys/uvm/uvm_ubc.c
blob - fb2d55929f89f48f959e0ddfdc2fa639fe2c64d0
blob + b786141f0da3d2e72828aa6588c6bb709920e2f1
--- got/got.c
+++ got/got.c
__dead static void usage_stage(void);
__dead static void usage_unstage(void);
__dead static void usage_cat(void);
+__dead static void usage_info(void);
static const struct got_error* cmd_init(int, char *[]);
static const struct got_error* cmd_import(int, char *[]);
static const struct got_error* cmd_stage(int, char *[]);
static const struct got_error* cmd_unstage(int, char *[]);
static const struct got_error* cmd_cat(int, char *[]);
+static const struct got_error* cmd_info(int, char *[]);
static struct got_cmd got_commands[] = {
- { "init", cmd_init, usage_init, "in" },
+ { "init", cmd_init, usage_init, "" },
{ "import", cmd_import, usage_import, "im" },
{ "clone", cmd_clone, usage_clone, "cl" },
{ "fetch", cmd_fetch, usage_fetch, "fe" },
{ "stage", cmd_stage, usage_stage, "sg" },
{ "unstage", cmd_unstage, usage_unstage, "ug" },
{ "cat", cmd_cat, usage_cat, "" },
+ { "info", cmd_info, usage_info, "" },
};
static void
repo_error = got_repo_close(repo);
if (error == NULL)
error = repo_error;
+ }
+ return error;
+}
+
+__dead static void
+usage_info(void)
+{
+ fprintf(stderr, "usage: %s info [path ...]\n",
+ getprogname());
+ exit(1);
+}
+
+static const struct got_error *
+print_path_info(void *arg, const char *path, mode_t mode, time_t mtime,
+ struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
+ struct got_object_id *commit_id)
+{
+ const struct got_error *err = NULL;
+ char *id_str = NULL;
+ char datebuf[128];
+ struct tm mytm, *tm;
+ struct got_pathlist_head *paths = arg;
+ struct got_pathlist_entry *pe;
+
+ /*
+ * Clear error indication from any of the path arguments which
+ * would cause this file index entry to be displayed.
+ */
+ TAILQ_FOREACH(pe, paths, entry) {
+ if (got_path_cmp(path, pe->path, strlen(path),
+ pe->path_len) == 0 ||
+ got_path_is_child(path, pe->path, pe->path_len))
+ pe->data = NULL; /* no error */
+ }
+
+ printf(GOT_COMMIT_SEP_STR);
+ if (S_ISLNK(mode))
+ printf("symlink: %s\n", path);
+ else if (S_ISREG(mode)) {
+ printf("file: %s\n", path);
+ printf("mode: %o\n", mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+ } else if (S_ISDIR(mode))
+ printf("directory: %s\n", path);
+ else
+ printf("something: %s\n", path);
+
+ tm = localtime_r(&mtime, &mytm);
+ if (tm == NULL)
+ return NULL;
+ if (strftime(datebuf, sizeof(datebuf), "%c %Z", tm) >= sizeof(datebuf))
+ return got_error(GOT_ERR_NO_SPACE);
+ printf("timestamp: %s\n", datebuf);
+
+ if (blob_id) {
+ err = got_object_id_str(&id_str, blob_id);
+ if (err)
+ return err;
+ printf("based on blob: %s\n", id_str);
+ free(id_str);
+ }
+
+ if (staged_blob_id) {
+ err = got_object_id_str(&id_str, staged_blob_id);
+ if (err)
+ return err;
+ printf("based on staged blob: %s\n", id_str);
+ free(id_str);
+ }
+
+ if (commit_id) {
+ err = got_object_id_str(&id_str, commit_id);
+ if (err)
+ return err;
+ printf("based on commit: %s\n", id_str);
+ free(id_str);
+ }
+
+ return NULL;
+}
+
+static const struct got_error *
+cmd_info(int argc, char *argv[])
+{
+ const struct got_error *error = NULL;
+ struct got_worktree *worktree = NULL;
+ char *cwd = NULL, *id_str = NULL;
+ struct got_pathlist_head paths;
+ struct got_pathlist_entry *pe;
+ char *uuidstr = NULL;
+ int ch, show_files = 0;
+
+ TAILQ_INIT(&paths);
+
+ while ((ch = getopt(argc, argv, "")) != -1) {
+ switch (ch) {
+ default:
+ usage_info();
+ /* NOTREACHED */
+ }
}
+
+ argc -= optind;
+ argv += optind;
+
+#ifndef PROFILE
+ if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
+ NULL) == -1)
+ err(1, "pledge");
+#endif
+ cwd = getcwd(NULL, 0);
+ if (cwd == NULL) {
+ error = got_error_from_errno("getcwd");
+ goto done;
+ }
+
+ error = got_worktree_open(&worktree, cwd);
+ if (error) {
+ if (error->code == GOT_ERR_NOT_WORKTREE)
+ error = wrap_not_worktree_error(error, "status", cwd);
+ goto done;
+ }
+
+ error = apply_unveil(NULL, 0, got_worktree_get_root_path(worktree));
+ if (error)
+ goto done;
+
+ if (argc >= 1) {
+ error = get_worktree_paths_from_argv(&paths, argc, argv,
+ worktree);
+ if (error)
+ goto done;
+ show_files = 1;
+ }
+
+ error = got_object_id_str(&id_str,
+ got_worktree_get_base_commit_id(worktree));
+ if (error)
+ goto done;
+
+ error = got_worktree_get_uuid(&uuidstr, worktree);
+ if (error)
+ goto done;
+
+ printf("work tree: %s\n", got_worktree_get_root_path(worktree));
+ printf("work tree base commit: %s\n", id_str);
+ printf("work tree path prefix: %s\n",
+ got_worktree_get_path_prefix(worktree));
+ printf("work tree branch reference: %s\n",
+ got_worktree_get_head_ref_name(worktree));
+ printf("work tree UUID: %s\n", uuidstr);
+ printf("repository: %s\n", got_worktree_get_repo_path(worktree));
+
+ if (show_files) {
+ struct got_pathlist_entry *pe;
+ TAILQ_FOREACH(pe, &paths, entry) {
+ if (pe->path_len == 0)
+ continue;
+ /*
+ * Assume this path will fail. This will be corrected
+ * in print_path_info() in case the path does suceeed.
+ */
+ pe->data = (void *)got_error_path(pe->path,
+ GOT_ERR_BAD_PATH);
+ }
+ error = got_worktree_path_info(worktree, &paths,
+ print_path_info, &paths, check_cancelled, NULL);
+ if (error)
+ goto done;
+ TAILQ_FOREACH(pe, &paths, entry) {
+ if (pe->data != NULL) {
+ error = pe->data; /* bad path */
+ break;
+ }
+ }
+ }
+done:
+ TAILQ_FOREACH(pe, &paths, entry)
+ free((char *)pe->path);
+ got_pathlist_free(&paths);
+ free(cwd);
+ free(id_str);
+ free(uuidstr);
return error;
}
blob - 277c66b0096dcee60d7ae5305877516328376f34
blob + f0548eaf6e23287e3754b08de478a43d1c8206c2
--- include/got_worktree.h
+++ include/got_worktree.h
* Get the path prefix associated with a worktree.
*/
const char *got_worktree_get_path_prefix(struct got_worktree *);
+
+/*
+ * Get the UUID of a work tree as a string.
+ * The caller must dispose of the returned UUID string with free(3).
+ */
+const struct got_error *got_worktree_get_uuid(char **, struct got_worktree *);
/*
* Check if a user-provided path prefix matches that of the worktree.
const struct got_error *got_worktree_unstage(struct got_worktree *,
struct got_pathlist_head *, got_worktree_checkout_cb, void *,
got_worktree_patch_cb, void *, struct got_repository *);
+
+/* A callback function which is invoked with per-path info. */
+typedef const struct got_error *(*got_worktree_path_info_cb)(void *,
+ const char *path, mode_t mode, time_t mtime,
+ struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
+ struct got_object_id *commit_id);
+
+/*
+ * Report work-tree meta data for paths in the work tree.
+ * The info callback will be invoked with the provided void * argument,
+ * a path, and meta-data arguments (see got_worktree_path_info_cb).
+ */
+const struct got_error *
+got_worktree_path_info(struct got_worktree *, struct got_pathlist_head *,
+ got_worktree_path_info_cb, void *, got_cancel_cb , void *);
blob - 0a072932c20e3ee4dec70243cb7cdc3739d3c8ba
blob + 0538db4eaaa0eac5a99d717327a150efac52b29b
--- lib/worktree.c
+++ lib/worktree.c
return err;
}
+const struct got_error *
+got_worktree_get_uuid(char **uuidstr, struct got_worktree *worktree)
+{
+ uint32_t uuid_status;
+
+ uuid_to_string(&worktree->uuid, uuidstr, &uuid_status);
+ if (uuid_status != uuid_s_ok) {
+ *uuidstr = NULL;
+ return got_error_uuid(uuid_status, "uuid_to_string");
+ }
+
+ return NULL;
+}
+
static const struct got_error *
get_ref_name(char **refname, struct got_worktree *worktree, const char *prefix)
{
const struct got_error *err = NULL;
char *uuidstr = NULL;
- uint32_t uuid_status;
*refname = NULL;
- uuid_to_string(&worktree->uuid, &uuidstr, &uuid_status);
- if (uuid_status != uuid_s_ok)
- return got_error_uuid(uuid_status, "uuid_to_string");
+ err = got_worktree_get_uuid(&uuidstr, worktree);
+ if (err)
+ return err;
- if (asprintf(refname, "%s-%s", prefix, uuidstr)
- == -1) {
+ if (asprintf(refname, "%s-%s", prefix, uuidstr) == -1) {
err = got_error_from_errno("asprintf");
*refname = NULL;
}
err = unlockerr;
return err;
}
+
+struct report_file_info_arg {
+ struct got_worktree *worktree;
+ got_worktree_path_info_cb info_cb;
+ void *info_arg;
+ struct got_pathlist_head *paths;
+ got_cancel_cb cancel_cb;
+ void *cancel_arg;
+};
+
+static const struct got_error *
+report_file_info(void *arg, struct got_fileindex_entry *ie)
+{
+ struct report_file_info_arg *a = arg;
+ struct got_pathlist_entry *pe;
+ struct got_object_id blob_id, staged_blob_id, commit_id;
+ struct got_object_id *blob_idp = NULL, *staged_blob_idp = NULL;
+ struct got_object_id *commit_idp = NULL;
+ int stage;
+
+ if (a->cancel_cb && a->cancel_cb(a->cancel_arg))
+ return got_error(GOT_ERR_CANCELLED);
+
+ TAILQ_FOREACH(pe, a->paths, entry) {
+ if (pe->path_len == 0 || strcmp(pe->path, ie->path) == 0 ||
+ got_path_is_child(ie->path, pe->path, pe->path_len))
+ break;
+ }
+ if (pe == NULL) /* not found */
+ return NULL;
+
+ if (got_fileindex_entry_has_blob(ie)) {
+ memcpy(blob_id.sha1, ie->blob_sha1, SHA1_DIGEST_LENGTH);
+ blob_idp = &blob_id;
+ }
+ stage = got_fileindex_entry_stage_get(ie);
+ if (stage == GOT_FILEIDX_STAGE_MODIFY ||
+ stage == GOT_FILEIDX_STAGE_ADD) {
+ memcpy(staged_blob_id.sha1, ie->staged_blob_sha1,
+ SHA1_DIGEST_LENGTH);
+ staged_blob_idp = &staged_blob_id;
+ }
+
+ if (got_fileindex_entry_has_commit(ie)) {
+ memcpy(commit_id.sha1, ie->commit_sha1, SHA1_DIGEST_LENGTH);
+ commit_idp = &commit_id;
+ }
+
+ return a->info_cb(a->info_arg, ie->path, got_fileindex_perms_to_st(ie),
+ (time_t)ie->mtime_sec, blob_idp, staged_blob_idp, commit_idp);
+}
+
+const struct got_error *
+got_worktree_path_info(struct got_worktree *worktree,
+ struct got_pathlist_head *paths,
+ got_worktree_path_info_cb info_cb, void *info_arg,
+ got_cancel_cb cancel_cb, void *cancel_arg)
+
+{
+ const struct got_error *err = NULL, *unlockerr;
+ struct got_fileindex *fileindex = NULL;
+ char *fileindex_path = NULL;
+ struct report_file_info_arg arg;
+
+ err = lock_worktree(worktree, LOCK_SH);
+ if (err)
+ return err;
+
+ err = open_fileindex(&fileindex, &fileindex_path, worktree);
+ if (err)
+ goto done;
+
+ arg.worktree = worktree;
+ arg.info_cb = info_cb;
+ arg.info_arg = info_arg;
+ arg.paths = paths;
+ arg.cancel_cb = cancel_cb;
+ arg.cancel_arg = cancel_arg;
+ err = got_fileindex_for_each_entry_safe(fileindex, report_file_info,
+ &arg);
+done:
+ free(fileindex_path);
+ if (fileindex)
+ got_fileindex_free(fileindex);
+ unlockerr = lock_worktree(worktree, LOCK_UN);
+ if (unlockerr && err == NULL)
+ err = unlockerr;
+ return err;
+}