diff --git a/src/libostree/ostree-mutable-tree.c b/src/libostree/ostree-mutable-tree.c index 943ef311..1c698b91 100644 --- a/src/libostree/ostree-mutable-tree.c +++ b/src/libostree/ostree-mutable-tree.c @@ -28,6 +28,7 @@ struct _OstreeMutableTree { GObject parent_instance; + char *contents_checksum; char *metadata_checksum; GHashTable *files; @@ -43,6 +44,7 @@ ostree_mutable_tree_finalize (GObject *object) self = OSTREE_MUTABLE_TREE (object); + g_free (self->contents_checksum); g_free (self->metadata_checksum); g_hash_table_destroy (self->files); @@ -82,6 +84,20 @@ ostree_mutable_tree_get_metadata_checksum (OstreeMutableTree *self) return self->metadata_checksum; } +void +ostree_mutable_tree_set_contents_checksum (OstreeMutableTree *self, + const char *checksum) +{ + g_free (self->contents_checksum); + self->contents_checksum = g_strdup (checksum); +} + +const char * +ostree_mutable_tree_get_contents_checksum (OstreeMutableTree *self) +{ + return self->contents_checksum; +} + static gboolean set_error_noent (GError **error, const char *path) { diff --git a/src/libostree/ostree-mutable-tree.h b/src/libostree/ostree-mutable-tree.h index 42e9e772..413da028 100644 --- a/src/libostree/ostree-mutable-tree.h +++ b/src/libostree/ostree-mutable-tree.h @@ -56,6 +56,11 @@ void ostree_mutable_tree_set_metadata_checksum (OstreeMutableTree *self, const char *ostree_mutable_tree_get_metadata_checksum (OstreeMutableTree *self); +void ostree_mutable_tree_set_contents_checksum (OstreeMutableTree *self, + const char *checksum); + +const char *ostree_mutable_tree_get_contents_checksum (OstreeMutableTree *self); + gboolean ostree_mutable_tree_replace_file (OstreeMutableTree *self, const char *name, const char *checksum, diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 9070cd09..493c7bcc 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -1370,6 +1370,7 @@ ostree_repo_stage_directory_to_mtree (OstreeRepo *self, GError **error) { gboolean ret = FALSE; + OstreeRepoFile *repo_dir = NULL; GError *temp_error = NULL; GFileInfo *child_info = NULL; OstreeMutableTree *child_mtree = NULL; @@ -1380,26 +1381,38 @@ ostree_repo_stage_directory_to_mtree (OstreeRepo *self, GVariant *xattrs = NULL; GInputStream *file_input = NULL; - child_info = g_file_query_info (dir, OSTREE_GIO_FAST_QUERYINFO, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable, error); - if (!child_info) - goto out; + /* We can only reuse checksums directly if there's no modifier */ + if (OSTREE_IS_REPO_FILE (dir) && modifier == NULL) + repo_dir = (OstreeRepoFile*)dir; - modified_info = create_modified_file_info (child_info, modifier); + if (repo_dir) + { + ostree_mutable_tree_set_metadata_checksum (mtree, ostree_repo_file_get_checksum (repo_dir)); + ostree_mutable_tree_set_contents_checksum (mtree, ostree_repo_file_tree_get_content_checksum (repo_dir)); + } + else + { + child_info = g_file_query_info (dir, OSTREE_GIO_FAST_QUERYINFO, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, error); + if (!child_info) + goto out; + + modified_info = create_modified_file_info (child_info, modifier); + + xattrs = ostree_get_xattrs_for_file (dir, error); + if (!xattrs) + goto out; + + if (!stage_directory_meta (self, modified_info, xattrs, &child_file_checksum, + cancellable, error)) + goto out; + + ostree_mutable_tree_set_metadata_checksum (mtree, g_checksum_get_string (child_file_checksum)); - xattrs = ostree_get_xattrs_for_file (dir, error); - if (!xattrs) - goto out; - - if (!stage_directory_meta (self, modified_info, xattrs, &child_file_checksum, - cancellable, error)) - goto out; - - ostree_mutable_tree_set_metadata_checksum (mtree, g_checksum_get_string (child_file_checksum)); - - g_clear_object (&child_info); - g_clear_object (&modified_info); + g_clear_object (&child_info); + g_clear_object (&modified_info); + } dir_enum = g_file_enumerate_children ((GFile*)dir, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, @@ -1428,6 +1441,13 @@ ostree_repo_stage_directory_to_mtree (OstreeRepo *self, modifier, cancellable, error)) goto out; } + else if (repo_dir) + { + if (!ostree_mutable_tree_replace_file (mtree, name, + ostree_repo_file_get_checksum ((OstreeRepoFile*) child), + error)) + goto out; + } else { ot_clear_checksum (&child_file_checksum); @@ -1568,43 +1588,50 @@ ostree_repo_stage_mtree (OstreeRepo *self, gboolean ret = FALSE; GChecksum *ret_contents_checksum_obj = NULL; char *ret_contents_checksum = NULL; - GHashTable *dir_metadata_checksums; - GHashTable *dir_contents_checksums; + GHashTable *dir_metadata_checksums = NULL; + GHashTable *dir_contents_checksums = NULL; GVariant *serialized_tree = NULL; GHashTableIter hash_iter; gpointer key, value; - dir_contents_checksums = g_hash_table_new_full (g_str_hash, g_str_equal, - (GDestroyNotify)g_free, (GDestroyNotify)g_free); - dir_metadata_checksums = g_hash_table_new_full (g_str_hash, g_str_equal, - (GDestroyNotify)g_free, (GDestroyNotify)g_free); - - g_hash_table_iter_init (&hash_iter, ostree_mutable_tree_get_subdirs (mtree)); - while (g_hash_table_iter_next (&hash_iter, &key, &value)) + if (ostree_mutable_tree_get_contents_checksum (mtree)) { - const char *name = key; - OstreeMutableTree *child_dir = value; - char *child_dir_contents_checksum; - - if (!ostree_repo_stage_mtree (self, child_dir, &child_dir_contents_checksum, - cancellable, error)) - goto out; - - g_hash_table_replace (dir_contents_checksums, g_strdup (name), - child_dir_contents_checksum); /* Transfer ownership */ - g_hash_table_replace (dir_metadata_checksums, g_strdup (name), - g_strdup (ostree_mutable_tree_get_metadata_checksum (child_dir))); + ret_contents_checksum = g_strdup (ostree_mutable_tree_get_contents_checksum (mtree)); } - - serialized_tree = create_tree_variant_from_hashes (ostree_mutable_tree_get_files (mtree), - dir_contents_checksums, - dir_metadata_checksums); + else + { + dir_contents_checksums = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify)g_free, (GDestroyNotify)g_free); + dir_metadata_checksums = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify)g_free, (GDestroyNotify)g_free); - if (!stage_gvariant_object (self, OSTREE_OBJECT_TYPE_DIR_TREE, - serialized_tree, &ret_contents_checksum_obj, - cancellable, error)) - goto out; - ret_contents_checksum = g_strdup (g_checksum_get_string (ret_contents_checksum_obj)); + g_hash_table_iter_init (&hash_iter, ostree_mutable_tree_get_subdirs (mtree)); + while (g_hash_table_iter_next (&hash_iter, &key, &value)) + { + const char *name = key; + OstreeMutableTree *child_dir = value; + char *child_dir_contents_checksum; + + if (!ostree_repo_stage_mtree (self, child_dir, &child_dir_contents_checksum, + cancellable, error)) + goto out; + + g_hash_table_replace (dir_contents_checksums, g_strdup (name), + child_dir_contents_checksum); /* Transfer ownership */ + g_hash_table_replace (dir_metadata_checksums, g_strdup (name), + g_strdup (ostree_mutable_tree_get_metadata_checksum (child_dir))); + } + + serialized_tree = create_tree_variant_from_hashes (ostree_mutable_tree_get_files (mtree), + dir_contents_checksums, + dir_metadata_checksums); + + if (!stage_gvariant_object (self, OSTREE_OBJECT_TYPE_DIR_TREE, + serialized_tree, &ret_contents_checksum_obj, + cancellable, error)) + goto out; + ret_contents_checksum = g_strdup (g_checksum_get_string (ret_contents_checksum_obj)); + } ret = TRUE; ot_transfer_out_value(out_contents_checksum, &ret_contents_checksum); diff --git a/src/ostree/ot-builtin-commit.c b/src/ostree/ot-builtin-commit.c index cdaf10bb..d84a77ac 100644 --- a/src/ostree/ot-builtin-commit.c +++ b/src/ostree/ot-builtin-commit.c @@ -35,7 +35,7 @@ static char *subject; static char *body; static char *parent; static char *branch; -static gboolean tar; +static char **trees; static gint owner_uid = -1; static gint owner_gid = -1; @@ -46,7 +46,7 @@ static GOptionEntry options[] = { { "metadata-variant", 0, 0, G_OPTION_ARG_FILENAME, &metadata_bin_path, "File containing serialized variant, in host endianness", "path" }, { "branch", 'b', 0, G_OPTION_ARG_STRING, &branch, "Branch", "branch" }, { "parent", 'p', 0, G_OPTION_ARG_STRING, &parent, "Parent commit", "commit" }, - { "tar", 0, 0, G_OPTION_ARG_NONE, &tar, "Given arguments are tar files", NULL }, + { "tree", 0, 0, G_OPTION_ARG_STRING_ARRAY, &trees, "Overlay the given argument as a tree", "NAME" }, { "owner-uid", 0, 0, G_OPTION_ARG_INT, &owner_uid, "Set file ownership user id", "UID" }, { "owner-gid", 0, 0, G_OPTION_ARG_INT, &owner_gid, "Set file ownership group id", "GID" }, { NULL } @@ -68,7 +68,7 @@ ostree_builtin_commit (int argc, char **argv, const char *repo_path, GError **er char *contents_checksum = NULL; GCancellable *cancellable = NULL; OstreeMutableTree *mtree = NULL; - int i; + char *tree_type = NULL; context = g_option_context_new ("[ARG] - Commit a new revision"); g_option_context_add_main_entries (context, options, NULL); @@ -133,7 +133,7 @@ ostree_builtin_commit (int argc, char **argv, const char *repo_path, GError **er mtree = ostree_mutable_tree_new (); - if (argc == 1) + if (argc == 1 && (trees == NULL || trees[0] == NULL)) { char *current_dir = g_get_current_dir (); arg = ot_gfile_new_for_path (current_dir); @@ -145,22 +145,55 @@ ostree_builtin_commit (int argc, char **argv, const char *repo_path, GError **er } else { - for (i = 1; i < argc; i++) + const char *const*tree_iter; + const char *tree; + const char *eq; + + for (tree_iter = (const char *const*)trees; *tree_iter; tree_iter++) { - g_clear_object (&arg); - arg = ot_gfile_new_for_path (argv[i]); - if (tar) + tree = *tree_iter; + + eq = strchr (tree, '='); + if (!eq) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Missing type in tree specification '%s'", tree); + goto out; + } + g_free (tree_type); + tree_type = g_strndup (tree, eq - tree); + tree = eq + 1; + + g_clear_object (&arg); + if (strcmp (tree_type, "dir") == 0) + { + arg = ot_gfile_new_for_path (tree); + if (!ostree_repo_stage_directory_to_mtree (repo, arg, mtree, modifier, + cancellable, error)) + goto out; + } + else if (strcmp (tree_type, "tar") == 0) + { + arg = ot_gfile_new_for_path (tree); if (!ostree_repo_stage_archive_to_mtree (repo, arg, mtree, modifier, cancellable, error)) goto out; } - else + else if (strcmp (tree_type, "ref") == 0) { + if (!ostree_repo_read_commit (repo, tree, &arg, cancellable, error)) + goto out; + if (!ostree_repo_stage_directory_to_mtree (repo, arg, mtree, modifier, cancellable, error)) goto out; } + else + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid tree type specification '%s'", tree_type); + goto out; + } } } @@ -185,6 +218,7 @@ ostree_builtin_commit (int argc, char **argv, const char *repo_path, GError **er g_clear_object (&mtree); g_free (contents_checksum); g_free (parent); + g_free (tree_type); if (metadata_mappedf) g_mapped_file_unref (metadata_mappedf); if (context) diff --git a/tests/t0000-basic.sh b/tests/t0000-basic.sh index e73631b9..3ef89d2b 100755 --- a/tests/t0000-basic.sh +++ b/tests/t0000-basic.sh @@ -19,7 +19,7 @@ set -e -echo "1..18" +echo "1..19" . libtest.sh @@ -151,3 +151,6 @@ echo "ok local clone checkout" $OSTREE checkout -U test2 checkout-user-test2 echo "ok user checkout" + +$OSTREE commit -b test2 -s "Another commit" --tree=ref=test2 +echo "ok commit from ref" diff --git a/tests/t0006-libarchive.sh b/tests/t0006-libarchive.sh index 7094c221..4173c97a 100755 --- a/tests/t0006-libarchive.sh +++ b/tests/t0006-libarchive.sh @@ -39,7 +39,7 @@ echo not > subdir/2/notempty tar -c -z -f ../foo.tar.gz . cd .. -$OSTREE commit -s "from tar" -b test-tar --tar foo.tar.gz +$OSTREE commit -s "from tar" -b test-tar --tree=tar=foo.tar.gz echo "ok tar commit" cd ${test_tmpdir} @@ -62,7 +62,7 @@ echo foo1 > foo ln foo bar tar czf ${test_tmpdir}/hardlinktest.tar.gz . cd ${test_tmpdir} -$OSTREE commit -s 'hardlinks' -b test-hardlinks --tar hardlinktest.tar.gz +$OSTREE commit -s 'hardlinks' -b test-hardlinks --tree=tar=hardlinktest.tar.gz rm -rf hardlinktest echo "ok hardlink commit" @@ -89,7 +89,7 @@ echo "new" > files2/subdir/new tar -c -C files1 -z -f files1.tar.gz . tar -c -C files2 -z -f files2.tar.gz . -$OSTREE commit -s 'multi tar' -b multicommit --tar files1.tar.gz files2.tar.gz +$OSTREE commit -s 'multi tar' -b multicommit --tree=tar=files1.tar.gz --tree=tar=files2.tar.gz echo "ok tar multicommit" cd ${test_tmpdir}