From a69e4452b464e56324454a69450bf3e76a5d3b0b Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 6 Apr 2012 15:11:11 -0400 Subject: [PATCH] core: checkout: Add --from-stdin option This allows checking out many branches in one go, useful in combination with the --union flag for ostbuild to combine a lot of components. --- src/ostree/ot-builtin-checkout.c | 270 +++++++++++++++++++++---------- 1 file changed, 188 insertions(+), 82 deletions(-) diff --git a/src/ostree/ot-builtin-checkout.c b/src/ostree/ot-builtin-checkout.c index a2a56b05..c7c08524 100644 --- a/src/ostree/ot-builtin-checkout.c +++ b/src/ostree/ot-builtin-checkout.c @@ -25,20 +25,24 @@ #include "ot-builtins.h" #include "ostree.h" +#include + #include -static gboolean user_mode; +static gboolean opt_user_mode; static gboolean opt_atomic_retarget; static gboolean opt_no_triggers; -static char *subpath; +static char *opt_subpath; static gboolean opt_union; +static gboolean opt_from_stdin; static GOptionEntry options[] = { - { "user-mode", 'U', 0, G_OPTION_ARG_NONE, &user_mode, "Do not change file ownership or initialze extended attributes", NULL }, - { "subpath", 0, 0, G_OPTION_ARG_STRING, &subpath, "Checkout sub-directory PATH", "PATH" }, + { "user-mode", 'U', 0, G_OPTION_ARG_NONE, &opt_user_mode, "Do not change file ownership or initialze extended attributes", NULL }, + { "subpath", 0, 0, G_OPTION_ARG_STRING, &opt_subpath, "Checkout sub-directory PATH", "PATH" }, { "union", 0, 0, G_OPTION_ARG_NONE, &opt_union, "Keep existing directories, overwrite existing files", NULL }, { "atomic-retarget", 0, 0, G_OPTION_ARG_NONE, &opt_atomic_retarget, "Make a symbolic link for destination, suffix with checksum", NULL }, { "no-triggers", 0, 0, G_OPTION_ARG_NONE, &opt_no_triggers, "Don't run triggers", NULL }, + { "from-stdin", 0, 0, G_OPTION_ARG_NONE, &opt_from_stdin, "Process many checkouts from standard input", NULL }, { NULL } }; @@ -116,6 +120,110 @@ parse_commit_from_symlink (GFile *symlink, return ret; } +static gboolean +process_one_checkout (OstreeRepo *repo, + const char *resolved_commit, + const char *subpath, + GFile *target, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + OstreeRepoFile *root = NULL; + OstreeRepoFile *subtree = NULL; + GFileInfo *file_info = NULL; + + root = (OstreeRepoFile*)ostree_repo_file_new_root (repo, resolved_commit); + if (!ostree_repo_file_ensure_resolved (root, error)) + goto out; + + if (subpath) + subtree = (OstreeRepoFile*)g_file_resolve_relative_path ((GFile*)root, subpath); + else + subtree = g_object_ref (root); + + file_info = g_file_query_info ((GFile*)subtree, OSTREE_GIO_FAST_QUERYINFO, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, error); + if (!file_info) + goto out; + + if (!ostree_repo_checkout_tree (repo, opt_user_mode ? OSTREE_REPO_CHECKOUT_MODE_USER : 0, + opt_union ? OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES : 0, + target, subtree, file_info, cancellable, error)) + goto out; + + ret = TRUE; + out: + g_clear_object (&subtree); + g_clear_object (&root); + g_clear_object (&file_info); + return ret; +} + +static gboolean +process_many_checkouts (OstreeRepo *repo, + GFile *target, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GInputStream *stdin_stream = NULL; + GDataInputStream *stdin_data = NULL; + char *revision = NULL; + char *subpath = NULL; + char *resolved_commit = NULL; + gsize len; + GError *temp_error = NULL; + + stdin_stream = (GInputStream*)g_unix_input_stream_new (0, FALSE); + stdin_data = g_data_input_stream_new (stdin_stream); + + while ((revision = g_data_input_stream_read_upto (stdin_data, "", 1, &len, + cancellable, &temp_error)) != NULL) + { + if (revision[0] == '\0') + break; + + /* Read the null byte */ + (void) g_data_input_stream_read_byte (stdin_data, cancellable, NULL); + g_free (subpath); + subpath = g_data_input_stream_read_upto (stdin_data, "", 1, &len, + cancellable, &temp_error); + if (temp_error) + { + g_propagate_error (error, temp_error); + goto out; + } + + /* Read the null byte */ + (void) g_data_input_stream_read_byte (stdin_data, cancellable, NULL); + + if (!ostree_repo_resolve_rev (repo, revision, FALSE, &resolved_commit, error)) + goto out; + + if (!process_one_checkout (repo, resolved_commit, subpath, target, + cancellable, error)) + goto out; + + g_free (revision); + } + if (temp_error) + { + g_propagate_error (error, temp_error); + goto out; + } + + ret = TRUE; + out: + g_free (subpath); + g_free (revision); + g_free (resolved_commit); + g_clear_object (&stdin_stream); + g_clear_object (&stdin_data); + return ret; +} + gboolean ostree_builtin_checkout (int argc, char **argv, GFile *repo_path, GError **error) { @@ -129,9 +237,6 @@ ostree_builtin_checkout (int argc, char **argv, GFile *repo_path, GError **error const char *destination; char *suffixed_destination = NULL; char *tmp_destination = NULL; - OstreeRepoFile *root = NULL; - OstreeRepoFile *subtree = NULL; - GFileInfo *file_info = NULL; GFileInfo *symlink_file_info = NULL; GFile *checkout_target = NULL; GFile *checkout_target_tmp = NULL; @@ -157,93 +262,97 @@ ostree_builtin_checkout (int argc, char **argv, GFile *repo_path, GError **error "COMMIT must be specified"); goto out; } - - commit = argv[1]; - if (argc < 3) - destination = commit; - else - destination = argv[2]; - if (!ostree_repo_resolve_rev (repo, commit, FALSE, &resolved_commit, error)) - goto out; - - if (opt_atomic_retarget) + if (opt_from_stdin) { - GError *temp_error = NULL; - - suffixed_destination = g_strconcat (destination, "-", resolved_commit, NULL); - checkout_target = ot_gfile_new_for_path (suffixed_destination); - tmp_destination = g_strconcat (suffixed_destination, ".tmp", NULL); - checkout_target_tmp = ot_gfile_new_for_path (tmp_destination); - symlink_target = ot_gfile_new_for_path (destination); - - if (!parse_commit_from_symlink (symlink_target, &existing_commit, - cancellable, &temp_error)) + if (opt_atomic_retarget) { - if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - { - skip_checkout = FALSE; - g_clear_error (&temp_error); - } - else - { - g_propagate_error (error, temp_error); - goto out; - } + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "--atomic-retarget may not be used with --from-stdin"); + goto out; } - else - { - skip_checkout = strcmp (existing_commit, resolved_commit) == 0; - } - } - else - { + + destination = argv[1]; checkout_target = ot_gfile_new_for_path (destination); - skip_checkout = FALSE; - } - if (!skip_checkout) - { - root = (OstreeRepoFile*)ostree_repo_file_new_root (repo, resolved_commit); - if (!ostree_repo_file_ensure_resolved (root, error)) + if (!process_many_checkouts (repo, checkout_target, cancellable, error)) goto out; - - if (subpath) - { - subtree = (OstreeRepoFile*)g_file_resolve_relative_path ((GFile*)root, subpath); - } - else - { - subtree = g_object_ref (root); - } - - file_info = g_file_query_info ((GFile*)subtree, OSTREE_GIO_FAST_QUERYINFO, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable, error); - if (!file_info) - goto out; - - if (!ostree_repo_checkout_tree (repo, user_mode ? OSTREE_REPO_CHECKOUT_MODE_USER : 0, - opt_union ? OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES : 0, - checkout_target_tmp ? checkout_target_tmp : checkout_target, - subtree, file_info, cancellable, error)) - goto out; - + if (!opt_no_triggers) { - if (!ostree_run_triggers_in_root (checkout_target_tmp ? checkout_target_tmp : checkout_target, - cancellable, error)) + if (!ostree_run_triggers_in_root (checkout_target, cancellable, error)) goto out; } + } + else + { + commit = argv[1]; + if (argc < 3) + destination = commit; + else + destination = argv[2]; + + if (!ostree_repo_resolve_rev (repo, commit, FALSE, &resolved_commit, error)) + goto out; if (opt_atomic_retarget) { - if (!ot_gfile_rename (checkout_target_tmp, checkout_target, cancellable, error)) - goto out; - if (!atomic_symlink_swap (symlink_target, - ot_gfile_get_basename_cached (checkout_target), - cancellable, error)) + GError *temp_error = NULL; + + suffixed_destination = g_strconcat (destination, "-", resolved_commit, NULL); + checkout_target = ot_gfile_new_for_path (suffixed_destination); + tmp_destination = g_strconcat (suffixed_destination, ".tmp", NULL); + checkout_target_tmp = ot_gfile_new_for_path (tmp_destination); + symlink_target = ot_gfile_new_for_path (destination); + + if (!parse_commit_from_symlink (symlink_target, &existing_commit, + cancellable, &temp_error)) + { + if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + skip_checkout = FALSE; + g_clear_error (&temp_error); + } + else + { + g_propagate_error (error, temp_error); + goto out; + } + } + else + { + skip_checkout = strcmp (existing_commit, resolved_commit) == 0; + } + } + else + { + checkout_target = ot_gfile_new_for_path (destination); + skip_checkout = FALSE; + } + + if (!skip_checkout) + { + if (!process_one_checkout (repo, resolved_commit, opt_subpath, + checkout_target_tmp ? checkout_target_tmp : checkout_target, + cancellable, error)) goto out; + + if (!opt_no_triggers) + { + if (!ostree_run_triggers_in_root (checkout_target_tmp ? checkout_target_tmp : checkout_target, + cancellable, error)) + goto out; + } + + if (opt_atomic_retarget) + { + if (!ot_gfile_rename (checkout_target_tmp, checkout_target, cancellable, error)) + goto out; + if (!atomic_symlink_swap (symlink_target, + ot_gfile_get_basename_cached (checkout_target), + cancellable, error)) + goto out; + } } } @@ -259,9 +368,6 @@ ostree_builtin_checkout (int argc, char **argv, GFile *repo_path, GError **error if (context) g_option_context_free (context); g_clear_object (&repo); - g_clear_object (&root); - g_clear_object (&subtree); - g_clear_object (&file_info); g_clear_object (&symlink_file_info); return ret; }