commit - 60f50a586ee410f07b141d9940434644c15c9faa
commit + 07862c206e443b4b70016804c7280da2e98be6b7
blob - 9a28bc62d6e087d451cafddb19e1b8e0eb71f2a8
blob + f5d016651de4336e0e583d71845e346d2e9104f9
--- got/got.c
+++ got/got.c
}
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 *
{
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,
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
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
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;
+}