store file's base commit ID in got_commitable and use it for OOD check
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
diff --git a/include/got_worktree.h b/include/got_worktree.h
index 37547bf..204c0b9 100644
--- a/include/got_worktree.h
+++ b/include/got_worktree.h
@@ -37,6 +37,7 @@ struct got_commitable {
unsigned char status;
struct got_object_id *blob_id;
struct got_object_id *base_blob_id;
+ struct got_object_id *base_commit_id;
mode_t mode;
};
diff --git a/lib/worktree.c b/lib/worktree.c
index 562647c..c6e5bb6 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -2237,6 +2237,7 @@ free_commitable(struct got_commitable *ct)
free(ct->ondisk_path);
free(ct->blob_id);
free(ct->base_blob_id);
+ free(ct->base_commit_id);
free(ct);
}
@@ -2314,6 +2315,11 @@ collect_commitables(void *arg, unsigned char status, const char *relpath,
err = got_error_from_errno("got_object_id_dup");
goto done;
}
+ ct->base_commit_id = got_object_id_dup(commit_id);
+ if (ct->base_commit_id == NULL) {
+ err = got_error_from_errno("got_object_id_dup");
+ goto done;
+ }
}
ct->path = strdup(path);
if (ct->path == NULL) {
@@ -2680,7 +2686,8 @@ write_tree(struct got_object_id **new_tree_id,
if (err)
goto done;
}
- err = report_ct_status(ct, status_cb, status_arg);
+ err = report_ct_status(ct, status_cb,
+ status_arg);
if (err)
goto done;
} else {
@@ -2785,29 +2792,33 @@ check_ct_out_of_date(struct got_commitable *ct, struct got_repository *repo,
struct got_object_id *id_in_head;
/*
- * XXX This should probably be checking each commit from
- * worktree's base_commit -> head and verify that the
- * same blob exists in each of these commits.
- * Removals+additions within this line of history could mean
- * that renames have occured in which case we should really
- * be forcing the user to run an update...
+ * Require that modified/deleted files are based on the branch head.
+ * This requirement could be relaxed to force less update operations
+ * on users but, for now, we want to play it safe and see how it goes.
+ *
+ * If this check is relaxed, it must still ensure that no modifications
+ * were made to files *and their parents* in commits between the file's
+ * base commit and the branch head. Otherwise, tree conflicts will not
+ * be detected reliably.
*/
- err = got_object_id_by_path(&id_in_head, repo,
- head_commit_id, ct->in_repo_path);
- if (err) {
- if (err->code != GOT_ERR_NO_TREE_ENTRY)
- return err;
- if (ct->status != GOT_STATUS_ADD)
+ if (ct->status != GOT_STATUS_ADD) {
+ if (got_object_id_cmp(ct->base_commit_id, head_commit_id) != 0)
return got_error(GOT_ERR_COMMIT_OUT_OF_DATE);
- err = NULL;
- id_in_head = NULL;
+ return NULL;
}
- if (id_in_head && got_object_id_cmp(id_in_head, ct->base_blob_id) != 0)
- err = got_error(GOT_ERR_COMMIT_OUT_OF_DATE);
+ /* Require that added files don't exist in the branch head. */
+ err = got_object_id_by_path(&id_in_head, repo, head_commit_id,
+ ct->in_repo_path);
+ if (err && err->code != GOT_ERR_NO_TREE_ENTRY)
+ return err;
+ if (id_in_head) {
+ free(id_in_head);
+ return got_error(GOT_ERR_COMMIT_OUT_OF_DATE);
+ }
free(id_in_head);
- return err;
+ return NULL;
}
const struct got_error *