From dab4611263e5cd2dbf8fd1ecbde88e8ac5e68ac6 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 22 Dec 2011 18:47:30 -0500 Subject: [PATCH] core: Add --skip-if-unchanged option for commit There's not much point for OS builds to have "empty" commits. --- src/libostree/ostree-repo.c | 33 ++++++++++++++++++++ src/libostree/ostree-repo.h | 4 +++ src/ostree/ot-builtin-commit.c | 55 ++++++++++++++++++++++++++++------ tests/t0000-basic.sh | 8 ++++- 4 files changed, 90 insertions(+), 10 deletions(-) diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 220994dd..9c295aa9 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -1044,6 +1044,39 @@ ostree_repo_commit_transaction (OstreeRepo *self, return ret; } +gboolean +ostree_repo_abort_transaction (OstreeRepo *self, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + OstreeRepoPrivate *priv = GET_PRIVATE (self); + GFile *f = NULL; + GHashTableIter iter; + gpointer key, value; + + g_return_val_if_fail (priv->in_transaction == TRUE, FALSE); + + priv->in_transaction = FALSE; + + g_hash_table_iter_init (&iter, priv->pending_transaction_tmpfiles); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + const char *filename = value; + + g_clear_object (&f); + f = g_file_get_child (priv->tmp_dir, filename); + + (void) unlink (ot_gfile_get_path_cached (f)); + } + + ret = TRUE; + out: + g_hash_table_remove_all (priv->pending_transaction_tmpfiles); + g_clear_object (&f); + return ret; +} + static gboolean stage_gvariant_object (OstreeRepo *self, OstreeObjectType type, diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index 272d1594..08ebd58c 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -88,6 +88,10 @@ gboolean ostree_repo_commit_transaction (OstreeRepo *self, GCancellable *cancellable, GError **error); +gboolean ostree_repo_abort_transaction (OstreeRepo *self, + GCancellable *cancellable, + GError **error); + gboolean ostree_repo_has_object (OstreeRepo *self, OstreeObjectType objtype, const char *checksum, diff --git a/src/ostree/ot-builtin-commit.c b/src/ostree/ot-builtin-commit.c index d97c27da..5fcc0371 100644 --- a/src/ostree/ot-builtin-commit.c +++ b/src/ostree/ot-builtin-commit.c @@ -35,6 +35,7 @@ static char *subject; static char *body; static char *parent; static char *branch; +static gboolean skip_if_unchanged; static char **trees; static gint owner_uid = -1; static gint owner_gid = -1; @@ -49,6 +50,7 @@ static GOptionEntry options[] = { { "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" }, + { "skip-if-unchanged", 0, 0, G_OPTION_ARG_NONE, &skip_if_unchanged, "If the contents are unchanged from previous commit, do nothing", NULL }, { NULL } }; @@ -61,6 +63,7 @@ ostree_builtin_commit (int argc, char **argv, GFile *repo_path, GError **error) GFile *arg = NULL; char *parent = NULL; char *commit_checksum = NULL; + GVariant *parent_commit = NULL; GVariant *metadata = NULL; GMappedFile *metadata_mappedf = NULL; GFile *metadata_f = NULL; @@ -69,6 +72,7 @@ ostree_builtin_commit (int argc, char **argv, GFile *repo_path, GError **error) GCancellable *cancellable = NULL; OstreeMutableTree *mtree = NULL; char *tree_type = NULL; + gboolean skip_commit = FALSE; context = g_option_context_new ("[ARG] - Commit a new revision"); g_option_context_add_main_entries (context, options, NULL); @@ -128,6 +132,13 @@ ostree_builtin_commit (int argc, char **argv, GFile *repo_path, GError **error) if (!ostree_repo_resolve_rev (repo, branch, TRUE, &parent, error)) goto out; + if (skip_if_unchanged && parent) + { + if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, + parent, &parent_commit, error)) + goto out; + } + if (!ostree_repo_prepare_transaction (repo, cancellable, error)) goto out; @@ -200,24 +211,50 @@ ostree_builtin_commit (int argc, char **argv, GFile *repo_path, GError **error) if (!ostree_repo_stage_mtree (repo, mtree, &contents_checksum, cancellable, error)) goto out; - if (!ostree_repo_stage_commit (repo, branch, parent, subject, body, metadata, - contents_checksum, ostree_mutable_tree_get_metadata_checksum (mtree), - &commit_checksum, cancellable, error)) - goto out; + if (skip_if_unchanged && parent_commit) + { + const char *parent_contents_checksum; + const char *parent_metadata_checksum; - if (!ostree_repo_commit_transaction (repo, cancellable, error)) - goto out; + g_variant_get_child (parent_commit, 6, "&s", &parent_contents_checksum); + g_variant_get_child (parent_commit, 7, "&s", &parent_metadata_checksum); - if (!ostree_repo_write_ref (repo, NULL, branch, commit_checksum, error)) - goto out; + if (strcmp (contents_checksum, parent_contents_checksum) == 0 + && strcmp (ostree_mutable_tree_get_metadata_checksum (mtree), + parent_metadata_checksum) == 0) + skip_commit = TRUE; + } + + if (!skip_commit) + { + if (!ostree_repo_stage_commit (repo, branch, parent, subject, body, metadata, + contents_checksum, ostree_mutable_tree_get_metadata_checksum (mtree), + &commit_checksum, cancellable, error)) + goto out; + + if (!ostree_repo_commit_transaction (repo, cancellable, error)) + goto out; + + if (!ostree_repo_write_ref (repo, NULL, branch, commit_checksum, error)) + goto out; + + g_print ("%s\n", commit_checksum); + } + else + { + if (!ostree_repo_abort_transaction (repo, cancellable, error)) + goto out; + + g_print ("%s\n", parent); + } ret = TRUE; - g_print ("%s\n", commit_checksum); out: g_clear_object (&arg); g_clear_object (&mtree); g_free (contents_checksum); g_free (parent); + ot_clear_gvariant(&parent_commit); g_free (tree_type); if (metadata_mappedf) g_mapped_file_unref (metadata_mappedf); diff --git a/tests/t0000-basic.sh b/tests/t0000-basic.sh index 62e15b19..9176d8fd 100755 --- a/tests/t0000-basic.sh +++ b/tests/t0000-basic.sh @@ -19,7 +19,7 @@ set -e -echo "1..20" +echo "1..21" . libtest.sh @@ -158,3 +158,9 @@ echo "ok commit from ref" $OSTREE commit -b trees/test2 -s 'ref with / in it' --tree=ref=test2 echo "ok commit ref with /" +old_rev=$($OSTREE rev-parse test2) +$OSTREE commit --skip-if-unchanged -b test2 -s 'should not be committed' --tree=ref=test2 +new_rev=$($OSTREE rev-parse test2) +assert_streq "${old_rev}" "${new_rev}" +echo "ok commit --skip-if-unchanged" +