Commit 0ed6ed4ca7635b211a4b7a02bacfdec1255dc4bd

Stefan Sperling 2018-06-13T18:29:04

got log: allow first-parent traversal with the commit graph

diff --git a/got/got.1 b/got/got.1
index 3bcc62f..5afbf1c 100644
--- a/got/got.1
+++ b/got/got.1
@@ -84,7 +84,7 @@ was specified use the base name of the
 .Ar path prefix .
 .\".It Cm status
 .\"Show current status of files.
-.It Cm log [ Fl p ] [ Fl c Ar commit ] [ Fl l Ar N ] [ Fl v ] [ Ar repository-path ]
+.It Cm log [ Fl p ] [ Fl c Ar commit ] [ Fl l Ar N ] [ Fl v ] [Fl f ] [ Ar repository-path ]
 Display history of the repository.
 If the
 .Fl p
@@ -105,6 +105,11 @@ commits.
 The
 .Fl v
 option enables verbose output.
+The
+.Fl f
+option restricts history traversal to the first parent of each commit.
+This shows the linear history of the current branch only, omitting any
+commits merged from other branches.
 If the
 .Ar repository path
 is omitted, use the current working directory.
diff --git a/got/got.c b/got/got.c
index 62e4d6a..59821dd 100644
--- a/got/got.c
+++ b/got/got.c
@@ -375,13 +375,15 @@ print_commit(struct got_commit_object *commit, struct got_object_id *id,
 
 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, int verbose)
+    struct got_repository *repo, int show_patch, int limit, int verbose,
+    int first_parent_traversal)
 {
 	const struct got_error *err;
 	struct got_commit_graph *graph;
 	int ncommits;
 
-	err = got_commit_graph_open(&graph, root_id, repo);
+	err = got_commit_graph_open(&graph, root_id, first_parent_traversal,
+	    repo);
 	if (err)
 		return err;
 	err = got_commit_graph_iter_start(graph, root_id);
@@ -425,7 +427,7 @@ print_commits(struct got_object *root_obj, struct got_object_id *root_id,
 __dead static void
 usage_log(void)
 {
-	fprintf(stderr, "usage: %s log [-p] [-c commit] [ -l N ] "
+	fprintf(stderr, "usage: %s log [-pf] [-c commit] [ -l N ] "
 	    "[repository-path]\n", getprogname());
 	exit(1);
 }
@@ -440,7 +442,7 @@ cmd_log(int argc, char *argv[])
 	char *repo_path = NULL;
 	char *start_commit = NULL;
 	int ch;
-	int show_patch = 0, limit = 0, verbose = 0;
+	int show_patch = 0, limit = 0, verbose = 0, first_parent_traversal = 0;
 	const char *errstr;
 
 #ifndef PROFILE
@@ -448,7 +450,7 @@ cmd_log(int argc, char *argv[])
 		err(1, "pledge");
 #endif
 
-	while ((ch = getopt(argc, argv, "pc:l:v")) != -1) {
+	while ((ch = getopt(argc, argv, "pc:l:vf")) != -1) {
 		switch (ch) {
 		case 'p':
 			show_patch = 1;
@@ -464,6 +466,9 @@ cmd_log(int argc, char *argv[])
 		case 'v':
 			verbose = 1;
 			break;
+		case 'f':
+			first_parent_traversal = 1;
+			break;
 		default:
 			usage();
 			/* NOTREACHED */
@@ -511,7 +516,7 @@ cmd_log(int argc, char *argv[])
 		return error;
 	if (got_object_get_type(obj) == GOT_OBJ_TYPE_COMMIT)
 		error = print_commits(obj, id, repo, show_patch, limit,
-		    verbose);
+		    verbose, first_parent_traversal);
 	else
 		error = got_error(GOT_ERR_OBJ_TYPE);
 	got_object_close(obj);
diff --git a/include/got_commit_graph.h b/include/got_commit_graph.h
index a480124..80e6b81 100644
--- a/include/got_commit_graph.h
+++ b/include/got_commit_graph.h
@@ -17,7 +17,7 @@
 struct got_commit_graph;
 
 const struct got_error *got_commit_graph_open(struct got_commit_graph **,
-    struct got_object_id *commit_id, struct got_repository *repo);
+    struct got_object_id *commit_id, int, struct got_repository *repo);
 void got_commit_graph_close(struct got_commit_graph *);
 
 const struct got_error *
diff --git a/lib/commit_graph.c b/lib/commit_graph.c
index 5a88b22..9e8852e 100644
--- a/lib/commit_graph.c
+++ b/lib/commit_graph.c
@@ -64,6 +64,9 @@ struct got_commit_graph {
 	/* The commit at which traversal began (youngest commit in node_ids). */
 	struct got_commit_graph_node *head_node;
 
+	int flags;
+#define GOT_COMMIT_GRAPH_FIRST_PARENT_TRAVERSAL		0x01
+
 	/*
 	 * A set of object IDs of known parent commits which we have not yet
 	 * traversed. Each commit ID in this set represents a branch in commit
@@ -220,6 +223,41 @@ add_vertex(struct got_object_id_queue *ids, struct got_object_id *id)
 }
 
 static const struct got_error *
+advance_open_branches(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(graph->open_branches, commit_id);
+	if (err && err->code != GOT_ERR_NO_OBJ)
+		return err;
+
+	if (graph->flags & GOT_COMMIT_GRAPH_FIRST_PARENT_TRAVERSAL) {
+		qid = SIMPLEQ_FIRST(&commit->parent_ids);
+		if (qid == NULL)
+			return NULL;
+		err = got_object_idset_add(NULL, graph->open_branches, qid->id,
+		    node);
+		if (err && err->code != GOT_ERR_OBJ_EXISTS)
+			return err;
+		return NULL;
+	}
+
+	SIMPLEQ_FOREACH(qid, &commit->parent_ids, entry) {
+		if (got_object_idset_get(graph->node_ids, qid->id))
+			continue; /* parent already traversed */
+		err = got_object_idset_add(NULL, graph->open_branches,
+		    qid->id, node);
+		if (err && err->code != GOT_ERR_OBJ_EXISTS)
+			return err;
+	}
+
+	return NULL;
+}
+
+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)
@@ -248,20 +286,8 @@ add_node(struct got_commit_graph_node **new_node,
 	err = got_object_idset_add((void **)(&existing_node),
 	    graph->node_ids, &node->id, node);
 	if (err == NULL) {
-		struct got_object_qid *qid;
-
 		add_node_to_iter_list(graph, node, child_node);
-		err = got_object_idset_remove(graph->open_branches, commit_id);
-		if (err && err->code != GOT_ERR_NO_OBJ)
-			return err;
-		SIMPLEQ_FOREACH(qid, &commit->parent_ids, entry) {
-			if (got_object_idset_get(graph->node_ids, qid->id))
-				continue; /* parent already traversed */
-			err = got_object_idset_add(NULL, graph->open_branches,
-			    qid->id, node);
-			if (err && err->code != GOT_ERR_OBJ_EXISTS)
-				return err;
-		}
+		err = advance_open_branches(graph, node, commit_id, commit);
 		*new_node = node;
 	} else if (err->code == GOT_ERR_OBJ_EXISTS) {
 		err = NULL;
@@ -296,7 +322,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, struct got_repository *repo)
+    struct got_object_id *commit_id, int first_parent_traversal,
+    struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
 	struct got_commit_object *commit;
@@ -313,6 +340,9 @@ got_commit_graph_open(struct got_commit_graph **graph,
 		return got_error_from_errno();
 	}
 
+	if (first_parent_traversal)
+		(*graph)->flags |= GOT_COMMIT_GRAPH_FIRST_PARENT_TRAVERSAL;
+
 	err = add_node(&(*graph)->head_node, *graph, commit_id, commit, NULL);
 	got_object_commit_close(commit);
 	if (err) {