diff --git a/Makefile-rpm-ostree.am b/Makefile-rpm-ostree.am
index 694b962e..f983d509 100644
--- a/Makefile-rpm-ostree.am
+++ b/Makefile-rpm-ostree.am
@@ -25,6 +25,7 @@ rpm_ostree_SOURCES = src/app/main.c \
src/app/rpmostree-compose-builtins.h \
src/app/rpmostree-builtin-upgrade.c \
src/app/rpmostree-builtin-rollback.c \
+ src/app/rpmostree-builtin-deploy.c \
src/app/rpmostree-builtin-rebase.c \
src/app/rpmostree-builtin-status.c \
src/app/rpmostree-builtin-db.c \
diff --git a/man/rpm-ostree.xml b/man/rpm-ostree.xml
index 02cfcb58..695a0aa8 100644
--- a/man/rpm-ostree.xml
+++ b/man/rpm-ostree.xml
@@ -151,6 +151,29 @@ Boston, MA 02111-1307, USA.
+
+ deploy
+
+
+
+ Similar to upgrade, but download a specific
+ version of the current tree which may be newer or older than your
+ running filesystem tree. The tree to download can be specified by
+ its SHA256 checksum, or by its "version" metadata value.
+
+
+
+ or to initiate a
+ reboot after the downloaded tree is prepared.
+
+
+
+ to download only /usr/share/rpm in
+ order to preview the package changes with the current tree.
+
+
+
+
compose
diff --git a/src/app/main.c b/src/app/main.c
index b461fbe8..c0b73845 100644
--- a/src/app/main.c
+++ b/src/app/main.c
@@ -38,6 +38,7 @@ static RpmOstreeCommand commands[] = {
{ "compose", rpmostree_builtin_compose },
#endif
{ "db", rpmostree_builtin_db },
+ { "deploy", rpmostree_builtin_deploy },
{ "rebase", rpmostree_builtin_rebase },
{ "rollback", rpmostree_builtin_rollback },
{ "status", rpmostree_builtin_status },
diff --git a/src/app/rpmostree-builtin-deploy.c b/src/app/rpmostree-builtin-deploy.c
new file mode 100644
index 00000000..f5f392d1
--- /dev/null
+++ b/src/app/rpmostree-builtin-deploy.c
@@ -0,0 +1,171 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "rpmostree-builtins.h"
+#include "rpmostree-libbuiltin.h"
+#include "rpmostree-rpm-util.h"
+#include "rpmostree-dbus-helpers.h"
+
+#include
+
+static char *opt_osname;
+static gboolean opt_reboot;
+static gboolean opt_preview;
+
+static GOptionEntry option_entries[] = {
+ { "os", 0, 0, G_OPTION_ARG_STRING, &opt_osname, "Operate on provided OSNAME", "OSNAME" },
+ { "reboot", 'r', 0, G_OPTION_ARG_NONE, &opt_reboot, "Initiate a reboot after upgrade is prepared", NULL },
+ /* XXX As much as I dislike the inconsistency with "rpm-ostree upgrade",
+ * calling this option --check-diff doesn't really make sense here.
+ * A --preview option would work for both commands if we wanted to
+ * deprecate --check-diff. */
+ { "preview", 0, 0, G_OPTION_ARG_NONE, &opt_preview, "Just preview package differences", NULL },
+ { NULL }
+};
+
+static GVariant *
+get_args_variant (void)
+{
+ GVariantDict dict;
+
+ g_variant_dict_init (&dict, NULL);
+ g_variant_dict_insert (&dict, "reboot", "b", opt_reboot);
+
+ return g_variant_dict_end (&dict);
+}
+
+static void
+default_deployment_changed_cb (GObject *object,
+ GParamSpec *pspec,
+ GVariant **value)
+{
+ g_object_get (object, pspec->name, value, NULL);
+}
+
+int
+rpmostree_builtin_deploy (int argc,
+ char **argv,
+ GCancellable *cancellable,
+ GError **error)
+{
+ int exit_status = EXIT_FAILURE;
+ GOptionContext *context;
+ glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
+ glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
+ g_autoptr(GVariant) default_deployment = NULL;
+ g_autofree char *transaction_address = NULL;
+ const char * const packages[] = { NULL };
+ const char *revision;
+
+ context = g_option_context_new ("REVISION - Deploy a specific commit");
+
+ if (!rpmostree_option_context_parse (context,
+ option_entries,
+ &argc, &argv,
+ RPM_OSTREE_BUILTIN_FLAG_NONE,
+ cancellable,
+ &sysroot_proxy,
+ error))
+ goto out;
+
+ if (argc < 2)
+ {
+ rpmostree_usage_error (context, "REVISION must be specified", error);
+ goto out;
+ }
+
+ revision = argv[1];
+
+ if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname,
+ cancellable, &os_proxy, error))
+ goto out;
+
+ if (opt_preview)
+ {
+ if (!rpmostree_os_call_download_deploy_rpm_diff_sync (os_proxy,
+ revision,
+ packages,
+ &transaction_address,
+ cancellable,
+ error))
+ goto out;
+ }
+ else
+ {
+ /* This will set the GVariant if the default deployment changes. */
+ g_signal_connect (os_proxy, "notify::default-deployment",
+ G_CALLBACK (default_deployment_changed_cb),
+ &default_deployment);
+
+ if (!rpmostree_os_call_deploy_sync (os_proxy,
+ revision,
+ get_args_variant (),
+ &transaction_address,
+ cancellable,
+ error))
+ goto out;
+ }
+
+ if (!rpmostree_transaction_get_response_sync (sysroot_proxy,
+ transaction_address,
+ cancellable,
+ error))
+ goto out;
+
+ if (opt_preview)
+ {
+ g_autoptr(GVariant) result = NULL;
+ g_autoptr(GVariant) details = NULL;
+
+ if (!rpmostree_os_call_get_cached_deploy_rpm_diff_sync (os_proxy,
+ revision,
+ packages,
+ &result,
+ &details,
+ cancellable,
+ error))
+ goto out;
+
+ rpmostree_print_package_diffs (result);
+ }
+ else if (!opt_reboot && default_deployment != NULL)
+ {
+ const char *sysroot_path;
+
+ sysroot_path = rpmostree_sysroot_get_path (sysroot_proxy);
+
+ if (!rpmostree_print_treepkg_diff_from_sysroot_path (sysroot_path,
+ cancellable,
+ error))
+ goto out;
+
+ g_print ("Run \"systemctl reboot\" to start a reboot\n");
+ }
+
+ exit_status = EXIT_SUCCESS;
+
+out:
+ /* Does nothing if using the message bus. */
+ rpmostree_cleanup_peer ();
+
+ return exit_status;
+}
diff --git a/src/app/rpmostree-builtins.h b/src/app/rpmostree-builtins.h
index d0654ae0..95df63cc 100644
--- a/src/app/rpmostree-builtins.h
+++ b/src/app/rpmostree-builtins.h
@@ -39,6 +39,7 @@ typedef struct {
BUILTINPROTO(compose);
BUILTINPROTO(upgrade);
+BUILTINPROTO(deploy);
BUILTINPROTO(rebase);
BUILTINPROTO(rollback);
BUILTINPROTO(status);