core: Allow specifying a previous commit tree as a basis for a new commit

It's pretty trivial to map a previously existing commit tree into a
mutable tree too.  While we're here change the command line arguments
for commit so that we can now properly overlay any combination of
directory, commit, or tarfile.
This commit is contained in:
Colin Walters 2011-12-21 20:10:10 -05:00
parent c8377c7c5c
commit add55849ab
6 changed files with 146 additions and 61 deletions

View File

@ -28,6 +28,7 @@ struct _OstreeMutableTree
{ {
GObject parent_instance; GObject parent_instance;
char *contents_checksum;
char *metadata_checksum; char *metadata_checksum;
GHashTable *files; GHashTable *files;
@ -43,6 +44,7 @@ ostree_mutable_tree_finalize (GObject *object)
self = OSTREE_MUTABLE_TREE (object); self = OSTREE_MUTABLE_TREE (object);
g_free (self->contents_checksum);
g_free (self->metadata_checksum); g_free (self->metadata_checksum);
g_hash_table_destroy (self->files); g_hash_table_destroy (self->files);
@ -82,6 +84,20 @@ ostree_mutable_tree_get_metadata_checksum (OstreeMutableTree *self)
return self->metadata_checksum; 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 static gboolean
set_error_noent (GError **error, const char *path) set_error_noent (GError **error, const char *path)
{ {

View File

@ -56,6 +56,11 @@ void ostree_mutable_tree_set_metadata_checksum (OstreeMutableTree *self,
const char *ostree_mutable_tree_get_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, gboolean ostree_mutable_tree_replace_file (OstreeMutableTree *self,
const char *name, const char *name,
const char *checksum, const char *checksum,

View File

@ -1370,6 +1370,7 @@ ostree_repo_stage_directory_to_mtree (OstreeRepo *self,
GError **error) GError **error)
{ {
gboolean ret = FALSE; gboolean ret = FALSE;
OstreeRepoFile *repo_dir = NULL;
GError *temp_error = NULL; GError *temp_error = NULL;
GFileInfo *child_info = NULL; GFileInfo *child_info = NULL;
OstreeMutableTree *child_mtree = NULL; OstreeMutableTree *child_mtree = NULL;
@ -1380,26 +1381,38 @@ ostree_repo_stage_directory_to_mtree (OstreeRepo *self,
GVariant *xattrs = NULL; GVariant *xattrs = NULL;
GInputStream *file_input = NULL; GInputStream *file_input = NULL;
child_info = g_file_query_info (dir, OSTREE_GIO_FAST_QUERYINFO, /* We can only reuse checksums directly if there's no modifier */
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, if (OSTREE_IS_REPO_FILE (dir) && modifier == NULL)
cancellable, error); repo_dir = (OstreeRepoFile*)dir;
if (!child_info)
goto out;
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); g_clear_object (&child_info);
if (!xattrs) g_clear_object (&modified_info);
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);
dir_enum = g_file_enumerate_children ((GFile*)dir, OSTREE_GIO_FAST_QUERYINFO, dir_enum = g_file_enumerate_children ((GFile*)dir, OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
@ -1428,6 +1441,13 @@ ostree_repo_stage_directory_to_mtree (OstreeRepo *self,
modifier, cancellable, error)) modifier, cancellable, error))
goto out; 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 else
{ {
ot_clear_checksum (&child_file_checksum); ot_clear_checksum (&child_file_checksum);
@ -1568,43 +1588,50 @@ ostree_repo_stage_mtree (OstreeRepo *self,
gboolean ret = FALSE; gboolean ret = FALSE;
GChecksum *ret_contents_checksum_obj = NULL; GChecksum *ret_contents_checksum_obj = NULL;
char *ret_contents_checksum = NULL; char *ret_contents_checksum = NULL;
GHashTable *dir_metadata_checksums; GHashTable *dir_metadata_checksums = NULL;
GHashTable *dir_contents_checksums; GHashTable *dir_contents_checksums = NULL;
GVariant *serialized_tree = NULL; GVariant *serialized_tree = NULL;
GHashTableIter hash_iter; GHashTableIter hash_iter;
gpointer key, value; gpointer key, value;
dir_contents_checksums = g_hash_table_new_full (g_str_hash, g_str_equal, if (ostree_mutable_tree_get_contents_checksum (mtree))
(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))
{ {
const char *name = key; ret_contents_checksum = g_strdup (ostree_mutable_tree_get_contents_checksum (mtree));
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)));
} }
else
serialized_tree = create_tree_variant_from_hashes (ostree_mutable_tree_get_files (mtree), {
dir_contents_checksums, dir_contents_checksums = g_hash_table_new_full (g_str_hash, g_str_equal,
dir_metadata_checksums); (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, g_hash_table_iter_init (&hash_iter, ostree_mutable_tree_get_subdirs (mtree));
serialized_tree, &ret_contents_checksum_obj, while (g_hash_table_iter_next (&hash_iter, &key, &value))
cancellable, error)) {
goto out; const char *name = key;
ret_contents_checksum = g_strdup (g_checksum_get_string (ret_contents_checksum_obj)); 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; ret = TRUE;
ot_transfer_out_value(out_contents_checksum, &ret_contents_checksum); ot_transfer_out_value(out_contents_checksum, &ret_contents_checksum);

View File

@ -35,7 +35,7 @@ static char *subject;
static char *body; static char *body;
static char *parent; static char *parent;
static char *branch; static char *branch;
static gboolean tar; static char **trees;
static gint owner_uid = -1; static gint owner_uid = -1;
static gint owner_gid = -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" }, { "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" }, { "branch", 'b', 0, G_OPTION_ARG_STRING, &branch, "Branch", "branch" },
{ "parent", 'p', 0, G_OPTION_ARG_STRING, &parent, "Parent commit", "commit" }, { "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-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" }, { "owner-gid", 0, 0, G_OPTION_ARG_INT, &owner_gid, "Set file ownership group id", "GID" },
{ NULL } { NULL }
@ -68,7 +68,7 @@ ostree_builtin_commit (int argc, char **argv, const char *repo_path, GError **er
char *contents_checksum = NULL; char *contents_checksum = NULL;
GCancellable *cancellable = NULL; GCancellable *cancellable = NULL;
OstreeMutableTree *mtree = NULL; OstreeMutableTree *mtree = NULL;
int i; char *tree_type = NULL;
context = g_option_context_new ("[ARG] - Commit a new revision"); context = g_option_context_new ("[ARG] - Commit a new revision");
g_option_context_add_main_entries (context, options, NULL); 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 (); mtree = ostree_mutable_tree_new ();
if (argc == 1) if (argc == 1 && (trees == NULL || trees[0] == NULL))
{ {
char *current_dir = g_get_current_dir (); char *current_dir = g_get_current_dir ();
arg = ot_gfile_new_for_path (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 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); tree = *tree_iter;
arg = ot_gfile_new_for_path (argv[i]);
if (tar) 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, if (!ostree_repo_stage_archive_to_mtree (repo, arg, mtree, modifier,
cancellable, error)) cancellable, error))
goto out; 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, if (!ostree_repo_stage_directory_to_mtree (repo, arg, mtree, modifier,
cancellable, error)) cancellable, error))
goto out; 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_clear_object (&mtree);
g_free (contents_checksum); g_free (contents_checksum);
g_free (parent); g_free (parent);
g_free (tree_type);
if (metadata_mappedf) if (metadata_mappedf)
g_mapped_file_unref (metadata_mappedf); g_mapped_file_unref (metadata_mappedf);
if (context) if (context)

View File

@ -19,7 +19,7 @@
set -e set -e
echo "1..18" echo "1..19"
. libtest.sh . libtest.sh
@ -151,3 +151,6 @@ echo "ok local clone checkout"
$OSTREE checkout -U test2 checkout-user-test2 $OSTREE checkout -U test2 checkout-user-test2
echo "ok user checkout" echo "ok user checkout"
$OSTREE commit -b test2 -s "Another commit" --tree=ref=test2
echo "ok commit from ref"

View File

@ -39,7 +39,7 @@ echo not > subdir/2/notempty
tar -c -z -f ../foo.tar.gz . tar -c -z -f ../foo.tar.gz .
cd .. 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" echo "ok tar commit"
cd ${test_tmpdir} cd ${test_tmpdir}
@ -62,7 +62,7 @@ echo foo1 > foo
ln foo bar ln foo bar
tar czf ${test_tmpdir}/hardlinktest.tar.gz . tar czf ${test_tmpdir}/hardlinktest.tar.gz .
cd ${test_tmpdir} 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 rm -rf hardlinktest
echo "ok hardlink commit" 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 files1 -z -f files1.tar.gz .
tar -c -C files2 -z -f files2.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" echo "ok tar multicommit"
cd ${test_tmpdir} cd ${test_tmpdir}