Commit 8bf5b3c9ffbda6ccd1cbcc790feead341d31c357

Stefan Sperling 2018-03-17T17:42:48

rewrite 'got log' without recursion

diff --git a/got/got.c b/got/got.c
index 3ae4fc6..176c609 100644
--- a/got/got.c
+++ b/got/got.c
@@ -230,55 +230,105 @@ done:
 }
 
 static const struct got_error *
-print_commit_object(struct got_object *, struct got_object_id *,
-    struct got_repository *);
-
-static const struct got_error *
-print_parent_commits(struct got_commit_object *commit,
+print_commit(struct got_commit_object *commit, struct got_object_id *id,
     struct got_repository *repo)
 {
-	struct got_parent_id *pid;
 	const struct got_error *err = NULL;
-	struct got_object *obj;
+	char *buf;
 
-	SIMPLEQ_FOREACH(pid, &commit->parent_ids, entry) {
-		err = got_object_open(&obj, repo, pid->id);
-		if (err != NULL)
-			return err;
-		if (got_object_get_type(obj) != GOT_OBJ_TYPE_COMMIT)
-			return got_error(GOT_ERR_OBJ_TYPE);
-		err = print_commit_object(obj, pid->id, repo);
-		got_object_close(obj);
-	}
+	err = got_object_id_str(&buf, id);
+	if (err)
+		return err;
 
-	return err;
+	printf("-----------------------------------------------\n");
+	printf("commit: %s\n", buf);
+	printf("Author: %s\n", commit->author);
+	printf("\n%s\n", commit->logmsg);
+
+	free(buf);
+	return NULL;
 }
 
+struct commit_queue_entry {
+	TAILQ_ENTRY(commit_queue_entry) entry;
+	struct got_object_id *id;
+	struct got_commit_object *commit;
+};
+
 static const struct got_error *
-print_commit_object(struct got_object *obj, struct got_object_id *id,
+print_commits(struct got_object *root_obj, struct got_object_id *root_id,
     struct got_repository *repo)
 {
-	struct got_commit_object *commit;
-	char *buf;
 	const struct got_error *err;
+	struct got_commit_object *root_commit;
+	TAILQ_HEAD(, commit_queue_entry) commits;
+	struct commit_queue_entry *entry;
 
-	err = got_object_commit_open(&commit, repo, obj);
-	if (err)
-		return err;
+	TAILQ_INIT(&commits);
 
-	err = got_object_id_str(&buf, id);
+	err = got_object_commit_open(&root_commit, repo, root_obj);
 	if (err)
 		return err;
 
-	printf("-----------------------------------------------\n");
-	printf("commit: %s\n", buf);
-	printf("Author: %s\n", commit->author);
-	printf("\n%s\n", commit->logmsg);
+	entry = calloc(1, sizeof(*entry));
+	if (entry == NULL)
+		return got_error(GOT_ERR_NO_MEM);
+	entry->id = got_object_id_dup(root_id);
+	if (entry->id == NULL) {
+		free(entry);
+		return got_error(GOT_ERR_NO_MEM);
+	}
+	entry->commit = root_commit;
+	TAILQ_INSERT_HEAD(&commits, entry, entry);
 
-	free(buf);
+	while (!TAILQ_EMPTY(&commits)) {
+		struct got_parent_id *pid;
+
+		entry = TAILQ_FIRST(&commits);
+		err = print_commit(entry->commit, entry->id, repo);
+		if (err)
+			break;
+
+		SIMPLEQ_FOREACH(pid, &entry->commit->parent_ids, entry) {
+			struct got_object *obj;
+			struct got_commit_object *pcommit;
+			struct commit_queue_entry *pentry;
+
+			err = got_object_open(&obj, repo, pid->id);
+			if (err)
+				break;
+			if (got_object_get_type(obj) != GOT_OBJ_TYPE_COMMIT) {
+				err = got_error(GOT_ERR_OBJ_TYPE);
+				break;
+			}
+
+			err = got_object_commit_open(&pcommit, repo, obj);
+			got_object_close(obj);
+			if (err)
+				break;
+
+			pentry = calloc(1, sizeof(*pentry));
+			if (pentry == NULL) {
+				err = got_error(GOT_ERR_NO_MEM);
+				got_object_commit_close(pcommit);
+				break;
+			}
+			pentry->id = got_object_id_dup(pid->id);
+			if (pentry->id == NULL) {
+				err = got_error(GOT_ERR_NO_MEM);
+				got_object_commit_close(pcommit);
+				break;
+			}
+			pentry->commit = pcommit;
+			TAILQ_INSERT_TAIL(&commits, pentry, entry);
+		}
+
+		TAILQ_REMOVE(&commits, entry, entry);
+		got_object_commit_close(entry->commit);
+		free(entry->id);
+		free(entry);
+	}
 
-	err = print_parent_commits(commit, repo);
-	got_object_commit_close(commit);
 	return err;
 }
 
@@ -325,17 +375,15 @@ cmd_log(int argc, char *argv[])
 	error = got_object_open(&obj, repo, id);
 	if (error != NULL)
 		return error;
-	if (got_object_get_type(obj) == GOT_OBJ_TYPE_COMMIT) {
-		error = print_commit_object(obj, id, repo);
-		if (error)
-			return error;
-	} else
-		return got_error(GOT_ERR_OBJ_TYPE);
+	if (got_object_get_type(obj) == GOT_OBJ_TYPE_COMMIT)
+		error = print_commits(obj, id, repo);
+	else
+		error = got_error(GOT_ERR_OBJ_TYPE);
 	got_object_close(obj);
 	free(id);
 	got_ref_close(head_ref);
 	got_repo_close(repo);
-	return NULL;
+	return error;
 }
 
 #ifdef notyet
diff --git a/include/got_object.h b/include/got_object.h
index 3b482fc..93541c4 100644
--- a/include/got_object.h
+++ b/include/got_object.h
@@ -57,6 +57,7 @@ struct got_repository;
 
 const struct got_error *got_object_id_str(char **, struct got_object_id *);
 int got_object_id_cmp(struct got_object_id *, struct got_object_id *);
+struct got_object_id *got_object_id_dup(struct got_object_id *);
 int got_object_get_type(struct got_object *);
 const struct got_error *got_object_open(struct got_object **,
     struct got_repository *, struct got_object_id *);
diff --git a/lib/object.c b/lib/object.c
index b1f7520..f5cf92a 100644
--- a/lib/object.c
+++ b/lib/object.c
@@ -77,6 +77,18 @@ got_object_id_cmp(struct got_object_id *id1, struct got_object_id *id2)
 	return memcmp(id1->sha1, id2->sha1, SHA1_DIGEST_LENGTH);
 }
 
+struct got_object_id *
+got_object_id_dup(struct got_object_id *id1)
+{
+	struct got_object_id *id2;
+
+	id2 = malloc(sizeof(*id2));
+	if (id2 == NULL)
+		return NULL;
+	memcpy(id2, id1, sizeof(*id2));
+	return id2;
+}
+
 int
 got_object_get_type(struct got_object *obj)
 {