commit 31cedeafaf0a3027fba695d7ed685cb330236cb6 from: Stefan Sperling date: Sat Sep 15 17:42:15 2018 UTC add path filtering support to commit graph 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. */