commit - 85f51bbadd426ab2f3f7fc70e879ac6763354479
commit + 04ca23f4590f27f82af9ef2a837afe86683d7339
blob - b7807ca827952f505c4858f826aa7a828c5ee860
blob + d08753947cf8d25b5a8bf669424775579e7cd6a8
--- got/got.1
+++ got/got.1
.El
.\".It Cm status
.\"Show current status of files.
-.It Cm log [ Fl c Ar commit ] [ Fl f ] [ Fl l Ar N ] [ Fl p ] [ Ar repository-path ]
+.It Cm log [ Fl c Ar commit ] [ Fl f ] [ Fl l Ar N ] [ Fl p ] [ Fl r Ar repository-path ] [ path ]
Display history of a repository.
-If the
-.Ar repository path
-is omitted, assume the repository is located in the current working directory.
+If a
+.Ar path
+is specified, show only commits which modified this path.
.Pp
The options for
.Cm got log
Limit history traversal to a given number of commits.
.It Fl p
Display the patch of modifications made in each commit.
+.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.
.El
.It Cm diff [ Ar repository-path ] Ar object1 Ar object2
Display the differences between two objects in the repository.
blob - bbdd8feb7717e3603a45f96b3801a65d6f196eb5
blob + 005060e75e9b9ee5cb674c01e425e0bffdbcb634
--- got/got.c
+++ got/got.c
static const struct got_error *
print_commits(struct got_object *root_obj, struct got_object_id *root_id,
- struct got_repository *repo, int show_patch, int limit,
+ struct got_repository *repo, char *path, int show_patch, int limit,
int first_parent_traversal)
{
const struct got_error *err;
struct got_commit_graph *graph;
- int ncommits;
+ int ncommits, found_obj = 0;
err = got_commit_graph_open(&graph, root_id, first_parent_traversal,
repo);
err = got_object_open_as_commit(&commit, repo, id);
if (err)
return err;
+ if (path) {
+ struct got_object *obj;
+ struct got_object_qid *pid;
+ int changed = 1;
+
+ err = got_object_open_by_path(&obj, repo, id, path);
+ if (err) {
+ if (err->code == GOT_ERR_NO_OBJ && found_obj) {
+ /*
+ * Object was added in previous commit.
+ * History stops here.
+ */
+ err = NULL;
+ }
+ break;
+ }
+ found_obj = 1;
+
+ pid = SIMPLEQ_FIRST(&commit->parent_ids);
+ if (pid != NULL) {
+ struct got_object *pobj;
+ err = got_object_open_by_path(&pobj, repo,
+ pid->id, path);
+ if (err) {
+ if (err->code != GOT_ERR_NO_OBJ) {
+ got_object_close(obj);
+ break;
+ }
+ err = NULL;
+ changed = 1;
+ } else {
+ changed = got_object_id_cmp(
+ got_object_get_id(obj),
+ got_object_get_id(pobj));
+ got_object_close(pobj);
+ }
+ }
+ if (!changed) {
+ got_object_commit_close(commit);
+ continue;
+ }
+ }
err = print_commit(commit, id, repo, show_patch);
got_object_commit_close(commit);
if (err || (limit && --limit == 0))
usage_log(void)
{
fprintf(stderr, "usage: %s log [-c commit] [-f] [ -l N ] [-p] "
- "[repository-path]\n", getprogname());
+ "[-r repository-path] [path]\n", getprogname());
exit(1);
}
cmd_log(int argc, char *argv[])
{
const struct got_error *error;
- struct got_repository *repo;
+ struct got_repository *repo = NULL;
struct got_object_id *id = NULL;
struct got_object *obj = NULL;
- char *repo_path = NULL;
+ char *repo_path = NULL, *path = NULL, *cwd = NULL, *in_repo_path = NULL;
char *start_commit = NULL;
int ch;
int show_patch = 0, limit = 0, first_parent_traversal = 0;
err(1, "pledge");
#endif
- while ((ch = getopt(argc, argv, "pc:l:f")) != -1) {
+ while ((ch = getopt(argc, argv, "pc:l:fr:")) != -1) {
switch (ch) {
case 'p':
show_patch = 1;
case 'f':
first_parent_traversal = 1;
break;
+ case 'r':
+ repo_path = realpath(optarg, NULL);
+ if (repo_path == NULL)
+ err(1, "-r option");
+ break;
default:
usage();
/* NOTREACHED */
argc -= optind;
argv += optind;
- if (argc == 0) {
- repo_path = getcwd(NULL, 0);
- if (repo_path == NULL)
- return got_error_from_errno();
- } else if (argc == 1) {
- repo_path = realpath(argv[0], NULL);
- if (repo_path == NULL)
- return got_error_from_errno();
- } else
+ if (argc == 0)
+ path = strdup("");
+ else if (argc == 1)
+ path = strdup(argv[0]);
+ else
usage_log();
+ if (path == NULL)
+ return got_error_from_errno();
+ 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);
- free(repo_path);
if (error != NULL)
- return error;
+ goto done;
if (start_commit == NULL) {
struct got_reference *head_ref;
}
}
if (error != NULL)
- return error;
- if (got_object_get_type(obj) == GOT_OBJ_TYPE_COMMIT)
- error = print_commits(obj, id, repo, show_patch, limit,
- first_parent_traversal);
- else
+ goto done;
+ if (got_object_get_type(obj) != GOT_OBJ_TYPE_COMMIT) {
error = got_error(GOT_ERR_OBJ_TYPE);
- got_object_close(obj);
+ goto done;
+ }
+
+ error = got_repo_map_path(&in_repo_path, repo, path);
+ if (error != NULL)
+ goto done;
+ if (in_repo_path) {
+ free(path);
+ path = in_repo_path;
+ }
+
+ error = print_commits(obj, id, repo, path, show_patch,
+ limit, first_parent_traversal);
+done:
+ free(path);
+ free(repo_path);
+ free(cwd);
+ if (obj)
+ got_object_close(obj);
free(id);
- got_repo_close(repo);
+ if (repo)
+ got_repo_close(repo);
return error;
}
blob - c60c51ce815bea1a4ee25e038ac6d97210ae026b
blob + 3f8ef8cb512966482a3b4410b0c4b51505fa5063
--- include/got_repository.h
+++ include/got_repository.h
*/
const struct got_error *got_repo_get_reference(struct got_reference **,
struct got_repository *, const char *);
+
+
+/* Indicate whether this is a bare repositiry (contains no git working tree). */
+int got_repo_is_bare(struct got_repository *);
+
+/* Attempt to map an arbitrary path to a path within the repository. */
+const struct got_error *got_repo_map_path(char **, struct got_repository *,
+ const char *);
blob - af1767503396d95abc5ad97aeaf9ecc73226817f
blob + 1d1d15583fd5c7e74bb6d530ad71a9b5702b0127
--- lib/got_lib_path.h
+++ lib/got_lib_path.h
* Relative paths are copied from input to buf as-is.
*/
const struct got_error *got_canonpath(const char *, char *, size_t);
+
+/*
+ * Get child part of two absolute paths. The second path must equal the first
+ * path up to some path component, and must be longer than the first path.
+ * The result is allocated with malloc(3).
+ */
+const struct got_error *got_path_skip_common_ancestor(char **, const char *,
+ const char *);
blob - d14f81c3e51d80411824abe499bce05da745ce55
blob + e874cb7cc2f3f27a2112d82cb89f69b26cfecb40
--- lib/path.c
+++ lib/path.c
} else
return got_error(GOT_ERR_NO_SPACE);
}
+
+const struct got_error *
+got_path_skip_common_ancestor(char **child, const char *parent_abspath,
+ const char *abspath)
+{
+ const struct got_error *err = NULL;
+ size_t len_parent, len, bufsize;
+
+ len_parent = strlen(parent_abspath);
+ len = strlen(abspath);
+ if (len_parent >= len)
+ return got_error(GOT_ERR_BAD_PATH);
+ if (strncmp(parent_abspath, abspath, len_parent) != 0)
+ return got_error(GOT_ERR_BAD_PATH);
+ if (abspath[len_parent] != '/')
+ return got_error(GOT_ERR_BAD_PATH);
+ bufsize = len - len_parent + 1;
+ *child = malloc(bufsize);
+ if (*child == NULL)
+ return got_error_from_errno();
+ if (strlcpy(*child, abspath + len_parent, bufsize) >= bufsize) {
+ err = got_error_from_errno();
+ free(*child);
+ *child = NULL;
+ return err;
+ }
+ return NULL;
+}
blob - ed9663ebba15d6ad33dcc70aa70b8c8af92d0086
blob + 59b728c96939fc5eb660e65375c48497faa5fb0d
--- lib/repository.c
+++ lib/repository.c
return strdup(repo->path_git_dir);
}
+int
+got_repo_is_bare(struct got_repository *repo)
+{
+ return (strcmp(repo->path, repo->path_git_dir) == 0);
+}
+
static char *
get_path_git_child(struct got_repository *repo, const char *basename)
{
got_object_idcache_free(repo->commitcache.idcache);
free(repo);
}
+
+const struct got_error *
+got_repo_map_path(char **in_repo_path, struct got_repository *repo,
+ const char *input_path)
+{
+ const struct got_error *err = NULL;
+ char *repo_abspath = NULL, *cwd = NULL;
+ struct stat sb;
+ size_t repolen, cwdlen, len;
+ char *canonpath, *path;
+
+ *in_repo_path = NULL;
+
+ cwd = getcwd(NULL, 0);
+ if (cwd == NULL)
+ return got_error_from_errno();
+
+ canonpath = strdup(input_path);
+ if (canonpath == NULL) {
+ err = got_error_from_errno();
+ goto done;
+ }
+ err = got_canonpath(input_path, canonpath, strlen(canonpath) + 1);
+ if (err)
+ goto done;
+
+ repo_abspath = got_repo_get_path(repo);
+ if (repo_abspath == NULL) {
+ err = got_error_from_errno();
+ goto done;
+ }
+
+ /* TODO: Call "get in-repository path of work-tree node" API. */
+
+ if (lstat(canonpath, &sb) != 0) {
+ if (errno != ENOENT) {
+ err = got_error_from_errno();
+ goto done;
+ }
+ /*
+ * Path is not on disk.
+ * Assume it is already relative to repository root.
+ */
+ path = strdup(canonpath);
+ } else {
+ int is_repo_child = 0, is_cwd_child = 0;
+
+ path = realpath(canonpath, NULL);
+ if (path == NULL) {
+ err = got_error_from_errno();
+ goto done;
+ }
+
+ repolen = strlen(repo_abspath);
+ cwdlen = strlen(cwd);
+ len = strlen(path);
+
+ if (len > repolen && strncmp(path, repo_abspath, repolen) == 0)
+ is_repo_child = 1;
+ if (len > cwdlen && strncmp(path, cwd, cwdlen) == 0)
+ is_cwd_child = 1;
+
+ if (strcmp(path, repo_abspath) == 0) {
+ free(path);
+ path = strdup("");
+ if (path == NULL) {
+ err = got_error_from_errno();
+ goto done;
+ }
+ } else if (is_repo_child && is_cwd_child) {
+ char *child;
+ /* TODO: Is path inside a got worktree? */
+ /* Strip common prefix with repository path. */
+ err = got_path_skip_common_ancestor(&child,
+ repo_abspath, path);
+ if (err)
+ goto done;
+ free(path);
+ path = child;
+ } else if (is_repo_child) {
+ /* Matched an on-disk path inside repository. */
+ if (got_repo_is_bare(repo)) {
+ /*
+ * Matched an on-disk path inside repository
+ * database. Treat as repository-relative.
+ */
+ } else {
+ char *child;
+ /* Strip common prefix with repository path. */
+ err = got_path_skip_common_ancestor(&child,
+ repo_abspath, path);
+ if (err)
+ goto done;
+ free(path);
+ path = child;
+ }
+ } else if (is_cwd_child) {
+ char *child;
+ /* TODO: Is path inside a got worktree? */
+ /* Strip common prefix with cwd. */
+ err = got_path_skip_common_ancestor(&child, cwd,
+ path);
+ if (err)
+ goto done;
+ free(path);
+ path = child;
+ } else {
+ /*
+ * Matched unrelated on-disk path.
+ * Treat it as repository-relative.
+ */
+ }
+ }
+
+ /* Make in-repository path absolute */
+ if (path[0] != '/') {
+ char *abspath;
+ if (asprintf(&abspath, "/%s", path) == -1) {
+ err = got_error_from_errno();
+ goto done;
+ }
+ free(path);
+ path = abspath;
+ }
+
+done:
+ free(repo_abspath);
+ free(cwd);
+ free(canonpath);
+ if (err)
+ free(path);
+ else
+ *in_repo_path = path;
+ return err;
+}