commit 07862c206e443b4b70016804c7280da2e98be6b7 from: Stefan Sperling date: Sat Sep 15 13:33:24 2018 UTC introduce got_object_tree_path_changed() and use it in 'got log' commit - 60f50a586ee410f07b141d9940434644c15c9faa commit + 07862c206e443b4b70016804c7280da2e98be6b7 blob - 9a28bc62d6e087d451cafddb19e1b8e0eb71f2a8 blob + f5d016651de4336e0e583d71845e346d2e9104f9 --- got/got.c +++ got/got.c @@ -370,23 +370,43 @@ print_commit(struct got_commit_object *commit, struct } static const struct got_error * -detect_change(int *changed, struct got_object_id *commit_id, - struct got_object_id *obj_id, const char *path, struct got_repository *repo) +detect_change(int *changed, struct got_commit_object *commit, + const char *path, struct got_repository *repo) { const struct got_error *err = NULL; - struct got_object_id *obj_id2; + struct got_commit_object *pcommit = NULL; + struct got_tree_object *tree = NULL, *ptree = NULL; + struct got_object_qid *pid; - err = got_object_id_by_path(&obj_id2, repo, commit_id, path); - if (err) { - if (err->code != GOT_ERR_NO_OBJ) - return err; + *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; - return NULL; + goto done; } - *changed = (got_object_id_cmp(obj_id, obj_id2) != 0); - free(obj_id2); - return NULL; + 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 const struct got_error * @@ -396,7 +416,7 @@ print_commits(struct got_object *root_obj, struct got_ { const struct got_error *err; struct got_commit_graph *graph; - int ncommits, found_obj = 0; + int ncommits, found_path = 0; int is_root_path = (strcmp(path, "/") == 0); err = got_commit_graph_open(&graph, root_id, first_parent_traversal, @@ -433,40 +453,44 @@ print_commits(struct got_object *root_obj, struct got_ break; if (!is_root_path) { struct got_object_id *obj_id = NULL; - struct got_object_qid *pid; - int changed = 0; + int changed; - err = got_object_id_by_path(&obj_id, repo, id, path); + err = detect_change(&changed, commit, path, repo); if (err) { - got_object_commit_close(commit); - if (err->code == GOT_ERR_NO_OBJ && found_obj) { + 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; } - break; + return err; } - found_obj = 1; + if (!changed) { + got_object_commit_close(commit); + continue; + } - pid = SIMPLEQ_FIRST(&commit->parent_ids); - if (pid) { - err = detect_change(&changed, pid->id, obj_id, - path, repo); - if (err) { - free(obj_id); + 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); - break; + continue; } + got_object_commit_close(commit); + return err; } + found_path = 1; free(obj_id); - if (!changed) { - got_object_commit_close(commit); - continue; - } } err = print_commit(commit, id, repo, show_patch); got_object_commit_close(commit); blob - 14bfd3e594043bcdffe83875bce7da1b57a655e5 blob + 352d0b0b013c3fb9410ad6fb4b5f15d40eef92f6 --- include/got_object.h +++ include/got_object.h @@ -161,6 +161,15 @@ const struct got_tree_entries *got_object_tree_get_ent struct got_tree_object *); /* + * 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. + */ +const struct got_error *got_object_tree_path_changed(int *, + struct got_tree_object *, struct got_tree_object *, const char *, + struct got_repository *); + +/* * Attempt to open a blob object in a repository. * The provided object must be of type GOT_OBJ_TYPE_BLOB. * The size_t argument specifies the block size of an associated read buffer. blob - 927693e95c735d17314713b9b787e042828d89c1 blob + 7ea8b37843eb86309f5bbcc14c08ef4f24152aa0 --- lib/object.c +++ lib/object.c @@ -786,3 +786,110 @@ done: got_object_tree_close(tree); return err; } + +const struct got_error * +got_object_tree_path_changed(int *changed, + struct got_tree_object *tree01, struct got_tree_object *tree02, + const char *path, struct got_repository *repo) +{ + const struct got_error *err = NULL; + struct got_tree_object *tree1 = NULL, *tree2 = NULL; + struct got_tree_entry *te1 = NULL, *te2 = NULL; + char *seg, *s, *s0 = NULL; + size_t len = strlen(path); + + *changed = 0; + + /* We are expecting an absolute in-repository path. */ + if (path[0] != '/') + return got_error(GOT_ERR_NOT_ABSPATH); + + /* We not do support comparing the root path. */ + if (path[1] == '\0') + return got_error(GOT_ERR_BAD_PATH); + + s0 = strdup(path); + if (s0 == NULL) { + err = got_error_from_errno(); + goto done; + } + err = got_canonpath(path, s0, len + 1); + if (err) + goto done; + + tree1 = tree01; + tree2 = tree02; + s = s0; + s++; /* skip leading '/' */ + len--; + seg = s; + while (len > 0) { + struct got_tree_object *next_tree1, *next_tree2; + + if (*s != '/') { + s++; + len--; + if (*s) + continue; + } + + /* end of path segment */ + *s = '\0'; + + te1 = find_entry_by_name(tree1, seg); + if (te1 == NULL) { + err = got_error(GOT_ERR_NO_OBJ); + goto done; + } + + te2 = find_entry_by_name(tree2, seg); + if (te2 == NULL) { + *changed = 1; + goto done; + } + + if (te1->mode != te2->mode) { + *changed = 1; + goto done; + } + + if (got_object_id_cmp(te1->id, te2->id) == 0) { + *changed = 0; + goto done; + } + + if (S_ISREG(te1->mode)) { /* final path element */ + *changed = 1; + goto done; + } + + if (len == 0) + break; + + seg = s + 1; + s++; + len--; + if (*s) { + err = got_object_open_as_tree(&next_tree1, repo, + te1->id); + te1 = NULL; + if (err) + goto done; + tree1 = next_tree1; + + err = got_object_open_as_tree(&next_tree2, repo, + te2->id); + te2 = NULL; + if (err) + goto done; + tree2 = next_tree2; + } + } +done: + free(s0); + if (tree1 != tree01) + got_object_tree_close(tree1); + if (tree2 != tree02) + got_object_tree_close(tree2); + return err; +}