commit - 07862c206e443b4b70016804c7280da2e98be6b7
commit + 31cedeafaf0a3027fba695d7ed685cb330236cb6
blob - f5d016651de4336e0e583d71845e346d2e9104f9
blob + 17cf228edf81711a6ba8664697f7dbd1c3eaafb6
--- got/got.c
+++ got/got.c
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;
}
{
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;
}
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
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
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
/*
* 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
#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;
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;
};
static struct got_commit_graph *
-alloc_graph(void)
+alloc_graph(const char *path)
{
struct got_commit_graph *graph;
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;
}
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;
}
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,
}
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) {
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;
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
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;
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();
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);
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;
*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)
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;
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;
}
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;
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)
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
*/
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
}
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
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;
}
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;
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. */