don't traverse into unmodified subtrees during commit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
diff --git a/lib/worktree.c b/lib/worktree.c
index 9f94387..87be49d 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -2217,6 +2217,7 @@ done:
struct commitable {
char *path;
+ char *in_repo_path;
char *ondisk_path;
unsigned char status;
struct got_object_id *id;
@@ -2228,6 +2229,7 @@ static void
free_commitable(struct commitable *ct)
{
free(ct->path);
+ free(ct->in_repo_path);
free(ct->ondisk_path);
free(ct->id);
free(ct->base_id);
@@ -2293,6 +2295,13 @@ collect_commitables(void *arg, unsigned char status, const char *relpath,
ct->mode = sb.st_mode;
}
+ if (asprintf(&ct->in_repo_path, "%s%s%s", a->worktree->path_prefix,
+ got_path_is_root_dir(a->worktree->path_prefix) ? "" : "/",
+ relpath) == -1) {
+ err = got_error_from_errno();
+ goto done;
+ }
+
ct->status = status;
ct->id = NULL; /* will be filled in when blob gets created */
if (ct->status != GOT_STATUS_ADD) {
@@ -2469,6 +2478,42 @@ report_ct_status(struct commitable *ct,
}
static const struct got_error *
+match_modified_subtree(int *modified, struct got_tree_entry *te,
+ const char *base_tree_path, struct got_pathlist_head *commitable_paths)
+{
+ const struct got_error *err = NULL;
+ struct got_pathlist_entry *pe;
+ char *te_path;
+
+ *modified = 0;
+
+ if (asprintf(&te_path, "%s%s%s", base_tree_path,
+ got_path_is_root_dir(base_tree_path) ? "" : "/",
+ te->name) == -1)
+ return got_error_from_errno();
+
+ TAILQ_FOREACH(pe, commitable_paths, entry) {
+ struct commitable *ct = pe->data;
+ char *ct_path;
+
+ if (asprintf(&ct_path, "%s%s%s", base_tree_path,
+ got_path_is_root_dir(base_tree_path) ? "" : "/",
+ te->name) == -1) {
+ err = got_error_from_errno();
+ break;
+ }
+
+ *modified = got_path_is_child(ct->in_repo_path, te_path,
+ strlen(te_path));
+ if (*modified)
+ break;
+ }
+
+ free(te_path);
+ return err;
+}
+
+static const struct got_error *
match_deleted_or_modified_ct(struct commitable **ctp,
struct got_tree_entry *te, const char *base_tree_path,
struct got_pathlist_head *commitable_paths)
@@ -2601,15 +2646,23 @@ write_tree(struct got_object_id **new_tree_id,
struct commitable *ct = NULL;
if (S_ISDIR(te->mode)) {
+ int modified;
err = got_object_tree_entry_dup(&new_te, te);
if (err)
goto done;
- free(new_te->id);
- err = write_subtree(&new_te->id, te,
- path_base_tree, commitable_paths,
- status_cb, status_arg, repo);
+ err = match_modified_subtree(&modified, te,
+ path_base_tree, commitable_paths);
if (err)
goto done;
+ /* Avoid recursion into unmodified subtrees. */
+ if (modified) {
+ free(new_te->id);
+ err = write_subtree(&new_te->id, te,
+ path_base_tree, commitable_paths,
+ status_cb, status_arg, repo);
+ if (err)
+ goto done;
+ }
err = insert_tree_entry(new_te, &paths);
if (err)
goto done;