commit - eb4304b986b18116e2fe6b1b45f805ec895d451c
commit + 036813ee25618d0c6e72887d36846d41422f4d46
blob - 48514a29c9cc7968d92411e66131d3edcdb962fe
blob + cd9ee7874acc4f166113c8f1c8ddfbef4364b17f
--- lib/worktree.c
+++ lib/worktree.c
}
struct collect_commitables_arg {
- struct got_pathlist_head *paths;
+ struct got_pathlist_head *commitable_paths;
struct got_repository *repo;
struct got_worktree *worktree;
};
static const struct got_error *
-collect_commitables(void *arg, unsigned char status, const char *path,
+collect_commitables(void *arg, unsigned char status, const char *relpath,
struct got_object_id *id)
{
struct collect_commitables_arg *a = arg;
const struct got_error *err = NULL;
struct commitable *ct = NULL;
struct got_pathlist_entry *new = NULL;
- char *parent_path = NULL;
+ char *parent_path = NULL, *path = NULL;
if (status == GOT_STATUS_CONFLICT)
return got_error(GOT_ERR_COMMIT_CONFLICT);
status != GOT_STATUS_DELETE)
return NULL;
- if (strchr(path, '/') == NULL) {
- parent_path = strdup("/");
+ if (asprintf(&path, "/%s", relpath) == -1) {
+ err = got_error_from_errno();
+ goto done;
+ }
+ if (strcmp(path, "/") == 0) {
+ parent_path = strdup("");
if (parent_path == NULL)
return got_error_from_errno();
} else {
return err;
}
- ct = malloc(sizeof(*ct));
+ ct = calloc(1, sizeof(*ct));
if (ct == NULL) {
err = got_error_from_errno();
goto done;
}
ct->status = status;
- ct->id = NULL;
- ct->base_id = got_object_id_dup(id);
- if (ct->base_id == NULL) {
- err = got_error_from_errno();
- goto done;
+ ct->id = NULL; /* will be filled in when blob gets created */
+ if (ct->status != GOT_STATUS_ADD) {
+ ct->base_id = got_object_id_dup(id);
+ if (ct->base_id == NULL) {
+ err = got_error_from_errno();
+ goto done;
+ }
}
err = got_object_id_by_path(&ct->tree_id, a->repo,
a->worktree->base_commit_id, parent_path);
err = got_error_from_errno();
goto done;
}
- err = got_pathlist_insert(&new, a->paths, ct->path, ct);
+ err = got_pathlist_insert(&new, a->commitable_paths, ct->path, ct);
done:
if (ct && (err || new == NULL))
free_commitable(ct);
free(parent_path);
+ free(path);
return err;
}
-struct write_tree_arg {
- struct got_pathlist_head *commitable_paths;
- struct got_object_idset *affected_trees;
- struct got_repository *repo;
-};
+static const struct got_error *write_tree(struct got_object_id **,
+ struct got_tree_object *, const char *, struct got_pathlist_head *,
+ struct got_repository *);
static const struct got_error *
-write_tree(struct got_object_id *base_tree_id, void *data, void *arg)
+write_subtree(struct got_object_id **new_subtree_id,
+ struct got_tree_entry *te, const char *parent_path,
+ struct got_pathlist_head *commitable_paths, struct got_repository *repo)
{
const struct got_error *err = NULL;
- struct write_tree_arg *a = arg;
- const struct got_tree_entries *base_entries = NULL;
- struct got_pathlist_head new_entries;
- struct got_tree_entries new_tree_entries;
- struct got_tree_object *base_tree = NULL;
- struct got_tree_entry *te;
- struct got_pathlist_entry *pe;
- struct got_object_id *new_tree_id = NULL;
+ struct got_tree_object *subtree;
+ char *subpath;
- TAILQ_INIT(&new_entries);
- new_tree_entries.nentries = 0;
- SIMPLEQ_INIT(&new_tree_entries.head);
+ if (asprintf(&subpath, "%s%s%s", parent_path,
+ parent_path[0] == '\0' ? "" : "/", te->name) == -1)
+ return got_error_from_errno();
- err = got_object_open_as_tree(&base_tree, a->repo, base_tree_id);
+ err = got_object_open_as_tree(&subtree, repo, te->id);
if (err)
return err;
- base_entries = got_object_tree_get_entries(base_tree);
+ err = write_tree(new_subtree_id, subtree, subpath, commitable_paths,
+ repo);
+ got_object_tree_close(subtree);
+ free(subpath);
+ return err;
+}
- SIMPLEQ_FOREACH(te, &base_entries->head, entry) {
- struct got_tree_entry *new_te = NULL;
- struct got_pathlist_entry *new_pe = NULL;
- int ct_found = 0;
- TAILQ_FOREACH(pe, a->commitable_paths, entry) {
- struct commitable *ct = NULL;
- char *ct_name = NULL;
+static const struct got_error *
+match_ct_parent_path(int *match, struct commitable *ct, const char *path)
+{
+ const struct got_error *err = NULL;
+ char *ct_parent_path = NULL;
- ct = pe->data;
+ *match = 0;
- if (got_object_id_cmp(ct->base_id, te->id) != 0)
- continue; /* not part of this tree */
+ if (strchr(ct->path, '/') == NULL) {
+ ct_parent_path = strdup("/");
+ if (ct_parent_path == NULL)
+ return got_error_from_errno();
+ } else {
+ err = got_path_dirname(&ct_parent_path, ct->path);
+ if (err)
+ return err;
+ }
- ct_name = basename(pe->path);
- if (ct_name == NULL) {
- err = got_error_from_errno();
- goto done;
- }
- /* Commitable and tree entry must correspond. */
- if (strcmp(te->name, ct_name) != 0)
- continue;
+ *match = (strcmp(path, ct_parent_path) == 0);
+ free(ct_parent_path);
+ return err;
+}
- ct_found = 1;
+static const struct got_error *
+alloc_modified_blob_tree_entry(struct got_tree_entry **new_te,
+ struct got_tree_entry *te, struct commitable *ct)
+{
+ const struct got_error *err = NULL;
- if (ct->status == GOT_STATUS_DELETE) {
- /* Deleted entries disappear. */
- break;
- }
+ *new_te = NULL;
- /* Modified entries get updated mode and ID. */
- if (ct->status == GOT_STATUS_MODIFY) {
- err = got_object_tree_entry_dup(&new_te, te);
- if (err)
- goto done;
- free(new_te->id);
- } else if (ct->status == GOT_STATUS_ADD) {
- /* Added entries get... well, added. */
- new_te = calloc(1, sizeof(*new_te));
- if (new_te == NULL) {
- err = got_error_from_errno();
- goto done;
- }
- new_te->name = strdup(ct_name);
- if (new_te->name == NULL) {
- err = got_error_from_errno();
- goto done;
- }
- }
- new_te->mode = GOT_DEFAULT_FILE_MODE; /* XXX */
- new_te->id = got_object_id_dup(ct->id);
- if (new_te->id == NULL) {
- err = got_error_from_errno();
- goto done;
- }
- break;
- }
+ err = got_object_tree_entry_dup(new_te, te);
+ if (err)
+ goto done;
- if (new_te == NULL) {
- if (ct_found) /* GOT_STATUS_DELETE */
- continue;
+ /* XXX TODO: update mode from disk (derive from ct?)! */
+ (*new_te)->mode = GOT_DEFAULT_FILE_MODE;
+
+ free((*new_te)->id);
+ (*new_te)->id = got_object_id_dup(ct->id);
+ if ((*new_te)->id == NULL) {
+ err = got_error_from_errno();
+ goto done;
+ }
+done:
+ if (err && *new_te) {
+ got_object_tree_entry_close(*new_te);
+ *new_te = NULL;
+ }
+ return err;
+}
+
+static const struct got_error *
+alloc_added_blob_tree_entry(struct got_tree_entry **new_te,
+ struct commitable *ct)
+{
+ const struct got_error *err = NULL;
+ char *ct_name;
+
+ *new_te = NULL;
+
+ *new_te = calloc(1, sizeof(*new_te));
+ if (*new_te == NULL)
+ return got_error_from_errno();
+
+ ct_name = basename(ct->path);
+ if (ct_name == NULL) {
+ err = got_error_from_errno();
+ goto done;
+ }
+ (*new_te)->name = strdup(ct_name);
+ if ((*new_te)->name == NULL) {
+ err = got_error_from_errno();
+ goto done;
+ }
+
+ /* XXX TODO: update mode from disk (derive from ct?)! */
+ (*new_te)->mode = GOT_DEFAULT_FILE_MODE;
+
+ (*new_te)->id = got_object_id_dup(ct->id);
+ if ((*new_te)->id == NULL) {
+ err = got_error_from_errno();
+ goto done;
+ }
+done:
+ if (err && *new_te) {
+ got_object_tree_entry_close(*new_te);
+ *new_te = NULL;
+ }
+ return err;
+}
+
+static const struct got_error *
+insert_tree_entry(struct got_tree_entry *new_te,
+ struct got_pathlist_head *paths)
+{
+ const struct got_error *err = NULL;
+ struct got_pathlist_entry *new_pe;
+
+ err = got_pathlist_insert(&new_pe, paths, new_te->name, new_te);
+ if (err)
+ return err;
+ if (new_pe == NULL)
+ return got_error(GOT_ERR_TREE_DUP_ENTRY);
+ return NULL;
+}
+
+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)
+{
+ const struct got_error *err = NULL;
+ struct got_pathlist_entry *pe;
+
+ *ctp = NULL;
+
+ TAILQ_FOREACH(pe, commitable_paths, entry) {
+ struct commitable *ct = pe->data;
+ char *ct_name = NULL;
+ int path_matches;
+
+ if (ct->status != GOT_STATUS_MODIFY &&
+ ct->status != GOT_STATUS_DELETE)
+ continue;
+
+ if (got_object_id_cmp(ct->base_id, te->id) != 0)
+ continue;
+
+ err = match_ct_parent_path(&path_matches, ct, base_tree_path);
+ if (err)
+ return err;
+ if (!path_matches)
+ continue;
+
+ ct_name = basename(pe->path);
+ if (ct_name == NULL)
+ return got_error_from_errno();
+
+ if (strcmp(te->name, ct_name) != 0)
+ continue;
+
+ *ctp = ct;
+ break;
+ }
+
+ return err;
+}
+
+static const struct got_error *
+write_tree(struct got_object_id **new_tree_id,
+ struct got_tree_object *base_tree, const char *path_base_tree,
+ struct got_pathlist_head *commitable_paths,
+ struct got_repository *repo)
+{
+ const struct got_error *err = NULL;
+ const struct got_tree_entries *base_entries = NULL;
+ struct got_pathlist_head paths;
+ struct got_tree_entries new_tree_entries;
+ struct got_tree_entry *te;
+ struct got_pathlist_entry *pe;
+
+ TAILQ_INIT(&paths);
+ new_tree_entries.nentries = 0;
+ SIMPLEQ_INIT(&new_tree_entries.head);
+
+ /* Insert, and recurse into, newly added entries first. */
+ TAILQ_FOREACH(pe, commitable_paths, entry) {
+ struct commitable *ct = pe->data;
+ char *child_path = NULL;
+
+ if (ct->status != GOT_STATUS_ADD)
+ continue;
+
+ printf("pe->path='%s'\n", pe->path);
+ printf("path_base_tree='%s'\n", path_base_tree);
+ if (!got_path_is_child(pe->path, path_base_tree,
+ strlen(path_base_tree))) {
+ printf("not a child\n");
+ continue;
+ }
+
+ err = got_path_skip_common_ancestor(&child_path, path_base_tree,
+ pe->path);
+ if (err)
+ goto done;
+
+ if (strchr(child_path, '/') == NULL) {
+ struct got_tree_entry *new_te;
+ err = alloc_added_blob_tree_entry(&new_te, ct);
+ if (err)
+ goto done;
+ err = insert_tree_entry(new_te, &paths);
+ if (err)
+ goto done;
+ } else {
+ /* TODO: Create subtree and insert element for it. */
+ }
+ }
+
+ /* Handle modified and deleted entries. */
+ base_entries = got_object_tree_get_entries(base_tree);
+ SIMPLEQ_FOREACH(te, &base_entries->head, entry) {
+ struct got_tree_entry *new_te = NULL;
+ struct commitable *ct = NULL;
+
+ if (S_ISDIR(te->mode)) {
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, repo);
+ if (err)
+ goto done;
+ err = insert_tree_entry(new_te, &paths);
+ if (err)
+ goto done;
+ continue;
}
- err = got_pathlist_insert(&new_pe, &new_entries,
- new_te->name, new_te);
- if (err)
- goto done;
- if (new_pe == NULL) {
- err = got_error(GOT_ERR_TREE_DUP_ENTRY);
- goto done;
+ err = match_deleted_or_modified_ct(&ct, te, path_base_tree,
+ commitable_paths);
+ if (ct) {
+ /* NB: Deleted entries get dropped here. */
+ if (ct->status == GOT_STATUS_MODIFY) {
+ err = alloc_modified_blob_tree_entry(&new_te,
+ te, ct);
+ if (err)
+ goto done;
+ err = insert_tree_entry(new_te, &paths);
+ if (err)
+ goto done;
+ }
+ } else {
+ /* Entry is unchanged; just copy it. */
+ err = got_object_tree_entry_dup(&new_te, te);
+ if (err)
+ goto done;
+ err = insert_tree_entry(new_te, &paths);
+ if (err)
+ goto done;
}
}
- TAILQ_FOREACH(pe, &new_entries, entry) {
+ /* Write new list of entries; deleted entries have been dropped. */
+ TAILQ_FOREACH(pe, &paths, entry) {
struct got_tree_entry *te = pe->data;
new_tree_entries.nentries++;
SIMPLEQ_INSERT_TAIL(&new_tree_entries.head, te, entry);
}
-
- err = got_object_tree_create(&new_tree_id, &new_tree_entries, a->repo);
+ err = got_object_tree_create(new_tree_id, &new_tree_entries, repo);
done:
- free(new_tree_id);
got_object_tree_entries_close(&new_tree_entries);
- got_pathlist_free(&new_entries);
+ got_pathlist_free(&paths);
if (base_tree)
got_object_tree_close(base_tree);
return err;
{
const struct got_error *err = NULL, *unlockerr = NULL;
struct collect_commitables_arg cc_arg;
- struct write_tree_arg wt_arg;
- struct got_pathlist_head paths;
+ struct got_pathlist_head commitable_paths;
struct got_pathlist_entry *pe;
char *relpath = NULL;
struct got_commit_object *base_commit = NULL;
struct got_tree_object *base_tree = NULL;
- struct got_object_idset *tree_ids = NULL;
+ struct got_object_id *new_tree_id = NULL;
*new_commit_id = NULL;
- TAILQ_INIT(&paths);
+ TAILQ_INIT(&commitable_paths);
if (ondisk_path) {
err = got_path_skip_common_ancestor(&relpath,
return err;
}
- tree_ids = got_object_idset_alloc();
- if (tree_ids == NULL)
- return got_error_from_errno();
-
err = lock_worktree(worktree, LOCK_EX);
if (err)
goto done;
- cc_arg.paths = &paths;
+ cc_arg.commitable_paths = &commitable_paths;
cc_arg.worktree = worktree;
cc_arg.repo = repo;
err = got_worktree_status(worktree, relpath ? relpath : "",
/* TODO: collect commit message if not specified */
- /* Collect IDs of affected leaf trees. */
- TAILQ_FOREACH(pe, &paths, entry) {
- struct commitable *ct = pe->data;
-
- if (got_object_idset_contains(tree_ids, ct->tree_id))
- continue;
-
- err = got_object_idset_add(tree_ids, ct->tree_id, NULL);
- if (err)
- goto done;
- }
-
/* Create blobs from added and modified files and record their IDs. */
- TAILQ_FOREACH(pe, &paths, entry) {
+ TAILQ_FOREACH(pe, &commitable_paths, entry) {
struct commitable *ct = pe->data;
char *ondisk_path;
goto done;
}
- /* Write new leaf tree objects. */
- wt_arg.commitable_paths = &paths;
- wt_arg.affected_trees = got_object_idset_alloc();
- if (wt_arg.affected_trees == NULL) {
- err = got_error_from_errno();
- goto done;
- }
- wt_arg.repo = repo;
- err = got_object_idset_for_each(tree_ids, write_tree, &wt_arg);
- if (err)
- goto done;
-
err = got_object_open_as_commit(&base_commit, repo,
worktree->base_commit_id);
if (err)
goto done;
- err = got_object_open_as_tree(&base_tree, repo,
- base_commit->tree_id);
+ err = got_object_open_as_tree(&base_tree, repo, base_commit->tree_id);
if (err)
goto done;
+
+ /* Recursively write new tree objects. */
+ err = write_tree(&new_tree_id, base_tree, "/", &commitable_paths, repo);
+ if (err)
+ goto done;
+
+ /* TODO: Write new commit. */
+
done:
unlockerr = lock_worktree(worktree, LOCK_SH);
if (unlockerr && err == NULL)
err = unlockerr;
- TAILQ_FOREACH(pe, &paths, entry) {
+ TAILQ_FOREACH(pe, &commitable_paths, entry) {
struct commitable *ct = pe->data;
free_commitable(ct);
}
- got_object_idset_free(tree_ids);
- got_pathlist_free(&paths);
+ got_pathlist_free(&commitable_paths);
if (base_tree)
got_object_tree_close(base_tree);
+ if (base_commit)
+ got_object_commit_close(base_commit);
free(relpath);
return err;
}
blob - 0a36cee5ce87d6281a30d8b60719e054ec0fba0d
blob + f621ba08e8af3b84dcd1904a424886ab6ef6827b
--- regress/cmdline/commit.sh
+++ regress/cmdline/commit.sh
function test_commit_basic {
local testroot=`test_init commit_basic`
+ find $testroot/repo/.git/objects > /tmp/1
+
got checkout $testroot/repo $testroot/wt > /dev/null
ret="$?"
if [ "$ret" != "0" ]; then
(cd $testroot/wt && got commit -m 'test commit_basic' > $testroot/stdout)
+ find $testroot/repo/.git/objects > /tmp/2
+
local head_rev=`git_show_head $testroot/repo`
echo "M alpha" > $testroot/stdout.expected
echo "D beta" >> $testroot/stdout.expected