Commit Diff


commit - 07862c206e443b4b70016804c7280da2e98be6b7
commit + 31cedeafaf0a3027fba695d7ed685cb330236cb6
blob - f5d016651de4336e0e583d71845e346d2e9104f9
blob + 17cf228edf81711a6ba8664697f7dbd1c3eaafb6
--- got/got.c
+++ got/got.c
@@ -365,47 +365,7 @@ print_commit(struct got_commit_object *commit, struct 
 		if (err == 0)
 			printf("\n");
 	}
-
-	return err;
-}
-
-static const struct got_error *
-detect_change(int *changed, struct got_commit_object *commit,
-    const char *path, struct got_repository *repo)
-{
-	const struct got_error *err = NULL;
-	struct got_commit_object *pcommit = NULL;
-	struct got_tree_object *tree = NULL, *ptree = NULL;
-	struct got_object_qid *pid;
-
-	*changed = 0;
-
-	err = got_object_open_as_tree(&tree, repo, commit->tree_id);
-	if (err)
-		return err;
-
-	pid = SIMPLEQ_FIRST(&commit->parent_ids);
-	if (pid == NULL) {
-		*changed = 1;
-		goto done;
-	}
-
-	err = got_object_open_as_commit(&pcommit, repo, pid->id);
-	if (err)
-		goto done;
 
-	err = got_object_open_as_tree(&ptree, repo, pcommit->tree_id);
-	if (err)
-		goto done;
-
-	err = got_object_tree_path_changed(changed, tree, ptree, path, repo);
-done:
-	if (tree)
-		got_object_tree_close(tree);
-	if (ptree)
-		got_object_tree_close(ptree);
-	if (pcommit)
-		got_object_commit_close(pcommit);
 	return err;
 }
 
@@ -416,17 +376,15 @@ print_commits(struct got_object *root_obj, struct got_
 {
 	const struct got_error *err;
 	struct got_commit_graph *graph;
-	int ncommits, found_path = 0;
-	int is_root_path = (strcmp(path, "/") == 0);
 
-	err = got_commit_graph_open(&graph, root_id, first_parent_traversal,
-	    repo);
+	err = got_commit_graph_open(&graph, root_id, path,
+	    first_parent_traversal, repo);
 	if (err)
 		return err;
-	err = got_commit_graph_iter_start(graph, root_id);
+	err = got_commit_graph_iter_start(graph, root_id, repo);
 	if (err)
 		goto done;
-	do {
+	while (1) {
 		struct got_commit_object *commit;
 		struct got_object_id *id;
 
@@ -438,8 +396,7 @@ print_commits(struct got_object *root_obj, struct got_
 			}
 			if (err->code != GOT_ERR_ITER_NEED_MORE)
 				break;
-			err = got_commit_graph_fetch_commits(&ncommits,
-			    graph, 1, repo);
+			err = got_commit_graph_fetch_commits(graph, 1, repo);
 			if (err)
 				break;
 			else
@@ -451,52 +408,11 @@ print_commits(struct got_object *root_obj, struct got_
 		err = got_object_open_as_commit(&commit, repo, id);
 		if (err)
 			break;
-		if (!is_root_path) {
-			struct got_object_id *obj_id = NULL;
-			int changed;
-
-			err = detect_change(&changed, commit, path, repo);
-			if (err) {
-				if (err->code == GOT_ERR_NO_OBJ) {
-					/*
-					 * History of the path stops here
-					 * on the current commit's branch.
-					 * Keep logging on other branches.
-					 */
-					err = NULL;
-					got_object_commit_close(commit);
-					continue;
-				}
-				return err;
-			}
-			if (!changed) {
-				got_object_commit_close(commit);
-				continue;
-			}
-
-			err = got_object_id_by_path(&obj_id, repo, id, path);
-			if (err) {
-				if (err->code == GOT_ERR_NO_OBJ && found_path) {
-					/*
-					 * History of the path stops here
-					 * on the current commit's branch.
-					 * Keep logging on other branches.
-					 */
-					err = NULL;
-					got_object_commit_close(commit);
-					continue;
-				}
-				got_object_commit_close(commit);
-				return err;
-			}
-			found_path = 1;
-			free(obj_id);
-		}
 		err = print_commit(commit, id, repo, show_patch);
 		got_object_commit_close(commit);
 		if (err || (limit && --limit == 0))
 			break;
-	} while (ncommits > 0);
+	}
 done:
 	got_commit_graph_close(graph);
 	return err;
blob - 80e6b815ba4e77ffee25babcd23898cd5f4379db
blob + fbe86e36c640b315dd868838306be8b800074b33
--- include/got_commit_graph.h
+++ include/got_commit_graph.h
@@ -17,15 +17,16 @@
 struct got_commit_graph;
 
 const struct got_error *got_commit_graph_open(struct got_commit_graph **,
-    struct got_object_id *commit_id, int, struct got_repository *repo);
+    struct got_object_id *commit_id, const char *, int,
+    struct got_repository *repo);
 void got_commit_graph_close(struct got_commit_graph *);
 
 const struct got_error *
-got_commit_graph_fetch_commits(int *, struct got_commit_graph *, int,
+got_commit_graph_fetch_commits(struct got_commit_graph *, int,
     struct got_repository *);
 const struct got_error *got_commit_graph_fetch_commits_up_to(int *,
     struct got_commit_graph *, struct got_object_id *, struct got_repository *);
 const struct got_error *got_commit_graph_iter_start(
-    struct got_commit_graph *, struct got_object_id *);
+    struct got_commit_graph *, struct got_object_id *, struct got_repository *);
 const struct got_error *got_commit_graph_iter_next(struct got_object_id **,
     struct got_commit_graph *);
blob - 352d0b0b013c3fb9410ad6fb4b5f15d40eef92f6
blob + 43f6b034a132e1dff675fb4804eb8960103c1d86
--- include/got_object.h
+++ include/got_object.h
@@ -162,8 +162,8 @@ const struct got_tree_entries *got_object_tree_get_ent
 
 /*
  * Compare two trees and indicate whether the entry at the specified path
- * differs. The path must not be the root path "/"; got_object_id_dup() can
- * be used to compare the tree roots instead.
+ * differs between them. The path must not be the root path "/"; the function
+ * got_object_id_cmp() should be used instead to compare the tree roots.
  */
 const struct got_error *got_object_tree_path_changed(int *,
     struct got_tree_object *, struct got_tree_object *, const char *,
blob - 65e8cca6ce1b2fab9a8976be0edc564e12df487e
blob + 830f24eb5907f52dcc34271a2cc8f92b9e261260
--- lib/commit_graph.c
+++ lib/commit_graph.c
@@ -34,6 +34,7 @@
 #include "got_lib_inflate.h"
 #include "got_lib_object.h"
 #include "got_lib_object_idset.h"
+#include "got_lib_path.h"
 
 struct got_commit_graph_node {
 	struct got_object_id id;
@@ -91,6 +92,9 @@ struct got_commit_graph {
 	size_t ntips;
 #define GOT_COMMIT_GRAPH_MIN_TIPS 100	/* minimum amount of tips to allocate */
 
+	/* Path of tree entry of interest to the API user. */
+	char *path;
+
 	/* The next commit to return when the API user asks for one. */
 	struct got_commit_graph_node *iter_node;
 
@@ -99,7 +103,7 @@ struct got_commit_graph {
 };
 
 static struct got_commit_graph *
-alloc_graph(void)
+alloc_graph(const char *path)
 {
 	struct got_commit_graph *graph;
 
@@ -107,8 +111,15 @@ alloc_graph(void)
 	if (graph == NULL)
 		return NULL;
 
+	graph->path = strdup(path);
+	if (graph->path == NULL) {
+		free(graph);
+		return NULL;
+	}
+
 	graph->node_ids = got_object_idset_alloc();
 	if (graph->node_ids == NULL) {
+		free(graph->path);
 		free(graph);
 		return NULL;
 	}
@@ -116,6 +127,7 @@ alloc_graph(void)
 	graph->open_branches = got_object_idset_alloc();
 	if (graph->open_branches == NULL) {
 		got_object_idset_free(graph->node_ids);
+		free(graph->path);
 		free(graph);
 		return NULL;
 	}
@@ -149,7 +161,59 @@ is_root_node(struct got_commit_graph_node *node)
 	return node->nparents == 0;
 }
 #endif
+
+static const struct got_error *
+detect_changed_path(int *changed, struct got_commit_object *commit,
+    struct got_object_id *commit_id, const char *path,
+    struct got_repository *repo)
+{
+	const struct got_error *err = NULL;
+	struct got_commit_object *pcommit = NULL;
+	struct got_tree_object *tree = NULL, *ptree = NULL;
+	struct got_object_qid *pid;
+
+	if (got_path_is_root_dir(path)) {
+		*changed = 1;
+		return NULL;
+	}
+
+	*changed = 0;
+
+	err = got_object_open_as_tree(&tree, repo, commit->tree_id);
+	if (err)
+		return err;
+
+	pid = SIMPLEQ_FIRST(&commit->parent_ids);
+	if (pid == NULL) {
+		struct got_object_id *obj_id;
+		err = got_object_id_by_path(&obj_id, repo, commit_id, path);
+		if (err)
+			err = NULL;
+		else
+			*changed = 1;
+		free(obj_id);
+		goto done;
+	}
+
+	err = got_object_open_as_commit(&pcommit, repo, pid->id);
+	if (err)
+		goto done;
+
+	err = got_object_open_as_tree(&ptree, repo, pcommit->tree_id);
+	if (err)
+		goto done;
 
+	err = got_object_tree_path_changed(changed, tree, ptree, path, repo);
+done:
+	if (tree)
+		got_object_tree_close(tree);
+	if (ptree)
+		got_object_tree_close(ptree);
+	if (pcommit)
+		got_object_commit_close(pcommit);
+	return err;
+}
+
 static void
 add_node_to_iter_list(struct got_commit_graph *graph,
     struct got_commit_graph_node *node,
@@ -230,15 +294,26 @@ add_vertex(struct got_object_id_queue *ids, struct got
 }
 
 static const struct got_error *
-advance_open_branches(struct got_commit_graph *graph,
+close_branch(struct got_commit_graph *graph, struct got_object_id *commit_id)
+{
+	const struct got_error *err;
+
+	err = got_object_idset_remove(NULL, graph->open_branches, commit_id);
+	if (err && err->code != GOT_ERR_NO_OBJ)
+		return err;
+	return NULL;
+}
+
+static const struct got_error *
+advance_branch(struct got_commit_graph *graph,
     struct got_commit_graph_node *node,
     struct got_object_id *commit_id, struct got_commit_object *commit)
 {
 	const struct got_error *err;
 	struct got_object_qid *qid;
 
-	err = got_object_idset_remove(NULL, graph->open_branches, commit_id);
-	if (err && err->code != GOT_ERR_NO_OBJ)
+	err = close_branch(graph, commit_id);
+	if (err)
 		return err;
 
 	if (graph->flags & GOT_COMMIT_GRAPH_FIRST_PARENT_TRAVERSAL) {
@@ -278,11 +353,13 @@ free_node(struct got_commit_graph_node *node)
 static const struct got_error *
 add_node(struct got_commit_graph_node **new_node,
     struct got_commit_graph *graph, struct got_object_id *commit_id,
-    struct got_commit_object *commit, struct got_commit_graph_node *child_node)
+    struct got_commit_object *commit, struct got_commit_graph_node *child_node,
+    struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
 	struct got_commit_graph_node *node;
 	struct got_object_qid *pid;
+	int changed = 0, branch_done = 0;
 
 	*new_node = NULL;
 
@@ -314,8 +391,29 @@ add_node(struct got_commit_graph_node **new_node,
 		return err;
 	}
 
-	add_node_to_iter_list(graph, node, child_node);
-	err = advance_open_branches(graph, node, commit_id, commit);
+	err = detect_changed_path(&changed, commit, commit_id, graph->path,
+	    repo);
+	if (err) {
+		if (err->code == GOT_ERR_NO_OBJ) {
+			/*
+			 * History of the path stops here on the current
+			 * branch. Keep going on other branches.
+			 */
+			err = NULL;
+			branch_done = 1;
+		} else {
+			free_node(node);
+			return err;
+		}
+	}
+
+	if (changed)
+		add_node_to_iter_list(graph, node, child_node);
+
+	if (branch_done)
+		err = close_branch(graph, commit_id);
+	else
+		err = advance_branch(graph, node, commit_id, commit);
 	if (err)
 		free_node(node);
 	else
@@ -326,8 +424,8 @@ add_node(struct got_commit_graph_node **new_node,
 
 const struct got_error *
 got_commit_graph_open(struct got_commit_graph **graph,
-    struct got_object_id *commit_id, int first_parent_traversal,
-    struct got_repository *repo)
+    struct got_object_id *commit_id, const char *path,
+    int first_parent_traversal, struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
 	struct got_commit_object *commit;
@@ -338,7 +436,16 @@ got_commit_graph_open(struct got_commit_graph **graph,
 	if (err)
 		return err;
 
-	*graph = alloc_graph();
+	/* The path must exist in our initial commit. */
+	if (!got_path_is_root_dir(path)) {
+		struct got_object_id *obj_id;
+		err = got_object_id_by_path(&obj_id, repo, commit_id, path);
+		if (err)
+			return err;
+		free(obj_id);
+	}
+
+	*graph = alloc_graph(path);
 	if (*graph == NULL) {
 		got_object_commit_close(commit);
 		return got_error_from_errno();
@@ -347,7 +454,8 @@ got_commit_graph_open(struct got_commit_graph **graph,
 	if (first_parent_traversal)
 		(*graph)->flags |= GOT_COMMIT_GRAPH_FIRST_PARENT_TRAVERSAL;
 
-	err = add_node(&(*graph)->head_node, *graph, commit_id, commit, NULL);
+	err = add_node(&(*graph)->head_node, *graph, commit_id, commit, NULL,
+	    repo);
 	got_object_commit_close(commit);
 	if (err) {
 		got_commit_graph_close(*graph);
@@ -374,8 +482,8 @@ gather_branch_tips(struct got_object_id *id, void *dat
 
 static const struct got_error *
 fetch_commits_from_open_branches(int *ncommits, int *wanted_id_added,
-    struct got_commit_graph *graph, struct got_repository *repo,
-    struct got_object_id *wanted_id)
+    struct got_object_id **changed_id, struct got_commit_graph *graph,
+    struct got_repository *repo, struct got_object_id *wanted_id)
 {
 	const struct got_error *err;
 	struct gather_branch_tips_arg arg;
@@ -384,6 +492,8 @@ fetch_commits_from_open_branches(int *ncommits, int *w
 	*ncommits = 0;
 	if (wanted_id_added)
 		*wanted_id_added = 0;
+	if (changed_id)
+		*changed_id = NULL;
 
 	arg.ntips = got_object_idset_num_elements(graph->open_branches);
 	if (arg.ntips == 0)
@@ -412,6 +522,7 @@ fetch_commits_from_open_branches(int *ncommits, int *w
 		struct got_object_id *commit_id;
 		struct got_commit_graph_node *child_node, *new_node;
 		struct got_commit_object *commit;
+		int changed;
 
 		commit_id = &graph->tips[i].id;
 		child_node = graph->tips[i].node;
@@ -420,7 +531,21 @@ fetch_commits_from_open_branches(int *ncommits, int *w
 		if (err)
 			break;
 
-		err = add_node(&new_node, graph, commit_id, commit, child_node);
+		err = detect_changed_path(&changed, commit, commit_id,
+		    graph->path, repo);
+		if (err) {
+			got_object_commit_close(commit);
+			if (err->code != GOT_ERR_NO_OBJ)
+				break;
+			err = close_branch(graph, commit_id);
+			if (err)
+				break;
+			continue;
+		}
+		if (changed && changed_id && *changed_id == NULL)
+			*changed_id = commit_id;
+		err = add_node(&new_node, graph, commit_id, commit, child_node,
+		    repo);
 		got_object_commit_close(commit);
 		if (err)
 			break;
@@ -434,22 +559,22 @@ fetch_commits_from_open_branches(int *ncommits, int *w
 }
 
 const struct got_error *
-got_commit_graph_fetch_commits(int *nfetched, struct got_commit_graph *graph,
-    int limit, struct got_repository *repo)
+got_commit_graph_fetch_commits(struct got_commit_graph *graph, int limit,
+    struct got_repository *repo)
 {
 	const struct got_error *err;
-	int ncommits;
+	int nfetched = 0, ncommits;
+	struct got_object_id *changed_id = NULL;
 
-	*nfetched = 0;
-
-	while (*nfetched < limit) {
+	while (nfetched < limit) {
 		err = fetch_commits_from_open_branches(&ncommits, NULL,
-		    graph, repo, NULL);
+		    &changed_id, graph, repo, NULL);
 		if (err)
 			return err;
 		if (ncommits == 0)
 			break;
-		*nfetched += ncommits;
+		if (changed_id)
+			nfetched += ncommits;
 	}
 
 	return NULL;
@@ -470,7 +595,7 @@ got_commit_graph_fetch_commits_up_to(int *nfetched,
 
 	while (!wanted_id_added) {
 		err = fetch_commits_from_open_branches(&ncommits,
-		    &wanted_id_added, graph, repo, wanted_id);
+		    &wanted_id_added, NULL, graph, repo, wanted_id);
 		if (err)
 			return err;
 		if (ncommits == 0)
@@ -495,19 +620,51 @@ got_commit_graph_close(struct got_commit_graph *graph)
 	got_object_idset_for_each(graph->node_ids, free_node_iter, NULL);
 	got_object_idset_free(graph->node_ids);
 	free(graph->tips);
+	free(graph->path);
 	free(graph);
 }
 
 const struct got_error *
 got_commit_graph_iter_start(struct got_commit_graph *graph,
-    struct got_object_id *id)
+    struct got_object_id *id, struct got_repository *repo)
 {
+	const struct got_error *err = NULL;
 	struct got_commit_graph_node *start_node;
+	struct got_commit_object *commit;
+	int changed;
 
 	start_node = got_object_idset_get(graph->node_ids, id);
 	if (start_node == NULL)
 		return got_error(GOT_ERR_NO_OBJ);
 
+
+	err = got_object_open_as_commit(&commit, repo, &start_node->id);
+	if (err)
+		return err;
+
+	err = detect_changed_path(&changed, commit, &start_node->id,
+	    graph->path, repo);
+	if (err) {
+		got_object_commit_close(commit);
+		return err;
+	}
+
+	if (!changed) {
+		/* Locate first commit which changed graph->path. */
+		struct got_object_id *changed_id = NULL;
+		while (changed_id == NULL) {
+			int ncommits;
+			err = fetch_commits_from_open_branches(&ncommits, NULL,
+			    &changed_id, graph, repo, NULL);
+			if (err) {
+				got_object_commit_close(commit);
+				return err;
+			}
+		}
+		start_node = got_object_idset_get(graph->node_ids, changed_id);
+	}
+	got_object_commit_close(commit);
+
 	graph->iter_node = start_node;
 	return NULL;
 }
blob - 1d1d15583fd5c7e74bb6d530ad71a9b5702b0127
blob + 46595f8d660ac6b02340670eaa92dd3be2aa93c0
--- lib/got_lib_path.h
+++ lib/got_lib_path.h
@@ -48,3 +48,6 @@ const struct got_error *got_canonpath(const char *, ch
  */
 const struct got_error *got_path_skip_common_ancestor(char **, const char *,
     const char *);
+
+/* Determine whether a path points to the root directory "/" . */
+int got_path_is_root_dir(const char *);
blob - e874cb7cc2f3f27a2112d82cb89f69b26cfecb40
blob + c78fdb20bdb77cb22d2610b7d886f4effaee2e01
--- lib/path.c
+++ lib/path.c
@@ -133,3 +133,9 @@ got_path_skip_common_ancestor(char **child, const char
 	}
 	return NULL;
 }
+
+int
+got_path_is_root_dir(const char *path)
+{
+	return (path[0] == '/' && path[1] == '\0');
+}
blob - 76613e9e4a6efe6647c06f0c12b58af00adc1656
blob + b727865084cee79075eb5b564dd6fd3f6bfb3435
--- tog/tog.c
+++ tog/tog.c
@@ -764,23 +764,21 @@ queue_commits(struct got_commit_graph *graph, struct c
 	const struct got_error *err = NULL;
 	struct got_object_id *id;
 	struct commit_queue_entry *entry;
-	int nfetched, nqueued = 0, found_obj = 0;
+	int nqueued = 0, found_obj = 0;
 	int is_root_path = strcmp(path, "/") == 0;
 
-	err = got_commit_graph_iter_start(graph, start_id);
+	err = got_commit_graph_iter_start(graph, start_id, repo);
 	if (err)
 		return err;
 
 	entry = TAILQ_LAST(&commits->head, commit_queue_head);
 	if (entry && got_object_id_cmp(entry->id, start_id) == 0) {
-		int nfetched;
-
 		/* Start ID's commit is already on the queue; skip over it. */
 		err = got_commit_graph_iter_next(&id, graph);
 		if (err && err->code != GOT_ERR_ITER_NEED_MORE)
 			return err;
 
-		err = got_commit_graph_fetch_commits(&nfetched, graph, 1, repo);
+		err = got_commit_graph_fetch_commits(graph, 1, repo);
 		if (err)
 			return err;
 	}
@@ -796,8 +794,7 @@ queue_commits(struct got_commit_graph *graph, struct c
 				err = NULL;
 				break;
 			}
-			err = got_commit_graph_fetch_commits(&nfetched,
-			    graph, 1, repo);
+			err = got_commit_graph_fetch_commits(graph, 1, repo);
 			if (err)
 				return err;
 			continue;
@@ -1167,7 +1164,7 @@ open_log_view(struct tog_view *view, struct got_object
 		return err;
 
 	/* The graph contains all commits. */
-	err = got_commit_graph_open(&s->graph, head_id, 0, repo);
+	err = got_commit_graph_open(&s->graph, head_id, "/", 0, repo);
 	if (err)
 		goto done;
 	/* The commit queue contains a subset of commits filtered by path. */