pull: Add ability to fetch refs/summary if no ref specified

This allows us to fetch all refs, which is useful for ostbuild where
we do want the buildroot/ refs.
This commit is contained in:
Colin Walters 2012-02-27 08:37:51 -05:00
parent b783ebc189
commit 2440338968
1 changed files with 280 additions and 86 deletions

View File

@ -1,6 +1,6 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2011 Colin Walters <walters@verbum.org>
* Copyright (C) 2011,2012 Colin Walters <walters@verbum.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -83,6 +83,7 @@ fetch_uri (OstreeRepo *repo,
SoupURI *uri,
const char *tmp_prefix,
GFile **out_temp_filename,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
@ -136,6 +137,43 @@ fetch_uri (OstreeRepo *repo,
return ret;
}
static gboolean
fetch_uri_contents_utf8 (OstreeRepo *repo,
SoupSession *soup,
SoupURI *uri,
char **out_contents,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
GFile *tmpf = NULL;
char *ret_contents = NULL;
gsize len;
if (!fetch_uri (repo, soup, uri, "tmp-", &tmpf, cancellable, error))
goto out;
if (!g_file_load_contents (tmpf, cancellable, &ret_contents, &len, NULL, error))
goto out;
if (!g_utf8_validate (ret_contents, -1, NULL))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid UTF-8");
goto out;
}
ret = TRUE;
ot_transfer_out_value (out_contents, &ret_contents);
out:
if (tmpf)
(void) unlink (ot_gfile_get_path_cached (tmpf));
g_clear_object (&tmpf);
g_free (ret_contents);
return ret;
}
static gboolean
fetch_object (OstreeRepo *repo,
SoupSession *soup,
@ -143,6 +181,7 @@ fetch_object (OstreeRepo *repo,
const char *checksum,
OstreeObjectType objtype,
GFile **out_temp_path,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
@ -156,7 +195,8 @@ fetch_object (OstreeRepo *repo,
relpath = g_build_filename (soup_uri_get_path (obj_uri), objpath, NULL);
soup_uri_set_path (obj_uri, relpath);
if (!fetch_uri (repo, soup, obj_uri, ostree_object_type_to_string (objtype), &ret_temp_path, error))
if (!fetch_uri (repo, soup, obj_uri, ostree_object_type_to_string (objtype), &ret_temp_path,
cancellable, error))
goto out;
ret = TRUE;
@ -178,6 +218,7 @@ fetch_and_store_object (OstreeRepo *repo,
OstreeObjectType objtype,
gboolean *out_is_pending,
GVariant **out_metadata,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
@ -197,25 +238,25 @@ fetch_and_store_object (OstreeRepo *repo,
if (!(stored_path || pending_path))
{
if (!fetch_object (repo, soup, baseuri, checksum, objtype, &temp_path, error))
if (!fetch_object (repo, soup, baseuri, checksum, objtype, &temp_path, cancellable, error))
goto out;
}
if (temp_path)
{
file_info = g_file_query_info (temp_path, OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, error);
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
if (!file_info)
goto out;
input = (GInputStream*)g_file_read (temp_path, NULL, error);
input = (GInputStream*)g_file_read (temp_path, cancellable, error);
if (!input)
goto out;
}
if (pending_path || temp_path)
{
if (!ostree_repo_stage_object (repo, objtype, checksum, file_info, NULL, input, NULL, error))
if (!ostree_repo_stage_object (repo, objtype, checksum, file_info, NULL, input, cancellable, error))
goto out;
log_verbose ("Staged object: %s.%s", checksum, ostree_object_type_to_string (objtype));
@ -253,6 +294,7 @@ fetch_and_store_tree_recurse (OstreeRepo *repo,
SoupSession *soup,
SoupURI *base_uri,
const char *rev,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
@ -270,7 +312,8 @@ fetch_and_store_tree_recurse (OstreeRepo *repo,
GFile *pending_path = NULL;
GInputStream *input = NULL;
if (!fetch_and_store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_DIR_TREE, &is_pending, &tree, error))
if (!fetch_and_store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_DIR_TREE,
&is_pending, &tree, cancellable, error))
goto out;
if (!is_pending)
@ -302,13 +345,13 @@ fetch_and_store_tree_recurse (OstreeRepo *repo,
if (ostree_repo_get_mode (repo) == OSTREE_REPO_MODE_BARE)
{
if (!ostree_repo_find_object (repo, OSTREE_OBJECT_TYPE_RAW_FILE, checksum,
&stored_path, &pending_path, NULL, error))
&stored_path, &pending_path, cancellable, error))
goto out;
}
else
{
if (!ostree_repo_find_object (repo, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT, checksum,
&stored_path, &pending_path, NULL, error))
&stored_path, &pending_path, cancellable, error))
goto out;
}
@ -321,6 +364,7 @@ fetch_and_store_tree_recurse (OstreeRepo *repo,
if (!fetch_object (repo, soup, base_uri, checksum,
OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META,
&meta_temp_path,
cancellable,
error))
goto out;
@ -337,10 +381,11 @@ fetch_and_store_tree_recurse (OstreeRepo *repo,
if (!fetch_object (repo, soup, base_uri, checksum,
OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT,
&content_temp_path,
cancellable,
error))
goto out;
input = (GInputStream*)g_file_read (content_temp_path, NULL, error);
input = (GInputStream*)g_file_read (content_temp_path, cancellable, error);
if (!input)
goto out;
}
@ -353,7 +398,7 @@ fetch_and_store_tree_recurse (OstreeRepo *repo,
if (!ostree_repo_stage_object (repo, OSTREE_OBJECT_TYPE_RAW_FILE,
checksum,
archive_file_info, archive_xattrs, input,
NULL, error))
cancellable, error))
goto out;
}
@ -386,10 +431,11 @@ fetch_and_store_tree_recurse (OstreeRepo *repo,
if (!ostree_validate_checksum_string (meta_checksum, error))
goto out;
if (!fetch_and_store_object (repo, soup, base_uri, meta_checksum, OSTREE_OBJECT_TYPE_DIR_META, NULL, NULL, error))
if (!fetch_and_store_object (repo, soup, base_uri, meta_checksum, OSTREE_OBJECT_TYPE_DIR_META,
NULL, NULL, cancellable, error))
goto out;
if (!fetch_and_store_tree_recurse (repo, soup, base_uri, tree_checksum, error))
if (!fetch_and_store_tree_recurse (repo, soup, base_uri, tree_checksum, cancellable, error))
goto out;
}
}
@ -423,6 +469,7 @@ fetch_and_store_commit_recurse (OstreeRepo *repo,
SoupSession *soup,
SoupURI *base_uri,
const char *rev,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
@ -431,7 +478,8 @@ fetch_and_store_commit_recurse (OstreeRepo *repo,
const char *tree_meta_checksum;
gboolean is_pending;
if (!fetch_and_store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_COMMIT, &is_pending, &commit, error))
if (!fetch_and_store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_COMMIT,
&is_pending, &commit, cancellable, error))
goto out;
if (!is_pending)
@ -442,10 +490,12 @@ fetch_and_store_commit_recurse (OstreeRepo *repo,
g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
if (!fetch_and_store_object (repo, soup, base_uri, tree_meta_checksum, OSTREE_OBJECT_TYPE_DIR_META, NULL, NULL, error))
if (!fetch_and_store_object (repo, soup, base_uri, tree_meta_checksum, OSTREE_OBJECT_TYPE_DIR_META,
NULL, NULL, cancellable, error))
goto out;
if (!fetch_and_store_tree_recurse (repo, soup, base_uri, tree_contents_checksum, error))
if (!fetch_and_store_tree_recurse (repo, soup, base_uri, tree_contents_checksum,
cancellable, error))
goto out;
}
@ -454,79 +504,64 @@ fetch_and_store_commit_recurse (OstreeRepo *repo,
ot_clear_gvariant (&commit);
return ret;
}
static gboolean
ostree_builtin_pull (int argc, char **argv, GFile *repo_path, GError **error)
fetch_ref_contents (OstreeRepo *repo,
SoupSession *soup,
SoupURI *base_uri,
const char *ref,
char **out_contents,
GCancellable *cancellable,
GError **error)
{
GOptionContext *context;
gboolean ret = FALSE;
OstreeRepo *repo = NULL;
const char *remote;
const char *branch;
char *key = NULL;
char *baseurl = NULL;
char *ret_contents = NULL;
char *refpath = NULL;
GFile *tempf = NULL;
char *remote_ref = NULL;
char *original_rev = NULL;
GKeyFile *config = NULL;
SoupURI *base_uri = NULL;
SoupURI *target_uri = NULL;
SoupSession *soup = NULL;
char *rev = NULL;
context = g_option_context_new ("REMOTE BRANCH - Download data from remote repository");
g_option_context_add_main_entries (context, options, NULL);
if (!g_option_context_parse (context, &argc, &argv, error))
target_uri = soup_uri_copy (base_uri);
refpath = g_build_filename (soup_uri_get_path (target_uri), "refs", "heads", ref, NULL);
soup_uri_set_path (target_uri, refpath);
if (!fetch_uri_contents_utf8 (repo, soup, target_uri, &ret_contents, cancellable, error))
goto out;
repo = ostree_repo_new (repo_path);
if (!ostree_repo_check (repo, error))
g_strchomp (ret_contents);
if (!ostree_validate_checksum_string (ret_contents, error))
goto out;
if (argc < 3)
{
ot_util_usage_error (context, "REMOTE and BRANCH must be specified", error);
goto out;
}
ret = TRUE;
ot_transfer_out_value (out_contents, &ret_contents);
out:
g_free (refpath);
g_free (ret_contents);
if (target_uri)
soup_uri_free (target_uri);
return ret;
}
remote = argv[1];
branch = argv[2];
static gboolean
pull_one_commit (OstreeRepo *repo,
const char *remote,
const char *branch,
const char *rev,
SoupSession *soup,
SoupURI *base_uri,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
char *key = NULL;
char *remote_ref = NULL;
char *baseurl = NULL;
char *original_rev = NULL;
remote_ref = g_strdup_printf ("%s/%s", remote, branch);
if (!ostree_repo_resolve_rev (repo, remote_ref, TRUE, &original_rev, error))
goto out;
config = ostree_repo_get_config (repo);
key = g_strdup_printf ("remote \"%s\"", remote);
baseurl = g_key_file_get_string (config, key, "url", error);
if (!baseurl)
goto out;
base_uri = soup_uri_new (baseurl);
if (!base_uri)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to parse url '%s'", baseurl);
goto out;
}
target_uri = soup_uri_copy (base_uri);
g_free (refpath);
refpath = g_build_filename (soup_uri_get_path (target_uri), "refs", "heads", branch, NULL);
soup_uri_set_path (target_uri, refpath);
soup = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
NULL);
if (!fetch_uri (repo, soup, target_uri, "ref-", &tempf, error))
goto out;
if (!ot_gfile_load_contents_utf8 (tempf, &rev, NULL, NULL, error))
goto out;
g_strchomp (rev);
if (original_rev && strcmp (rev, original_rev) == 0)
{
g_print ("No changes in %s\n", remote_ref);
@ -539,10 +574,10 @@ ostree_builtin_pull (int argc, char **argv, GFile *repo_path, GError **error)
if (!ostree_repo_prepare_transaction (repo, NULL, error))
goto out;
if (!fetch_and_store_commit_recurse (repo, soup, base_uri, rev, error))
if (!fetch_and_store_commit_recurse (repo, soup, base_uri, rev, cancellable, error))
goto out;
if (!ostree_repo_commit_transaction (repo, NULL, error))
if (!ostree_repo_commit_transaction (repo, cancellable, error))
goto out;
if (!ostree_repo_write_ref (repo, remote, branch, rev, error))
@ -550,25 +585,184 @@ ostree_builtin_pull (int argc, char **argv, GFile *repo_path, GError **error)
g_print ("remote %s is now %s\n", remote_ref, rev);
}
ret = TRUE;
out:
g_free (key);
g_free (remote_ref);
g_free (baseurl);
g_free (original_rev);
return ret;
}
static gboolean
parse_ref_summary (const char *contents,
GHashTable **out_refs,
GError **error)
{
gboolean ret = FALSE;
GHashTable *ret_refs = NULL;
char **lines = NULL;
char **iter = NULL;
char *ref = NULL;
char *sha256 = NULL;
ret_refs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
lines = g_strsplit_set (contents, "\n", -1);
for (iter = lines; *iter; iter++)
{
const char *line = *iter;
const char *spc;
if (!*line)
continue;
spc = strchr (line, ' ');
if (!spc)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid ref summary file; missing ' ' in line");
goto out;
}
g_free (ref);
ref = g_strdup (spc + 1);
if (!ostree_validate_rev (ref, error))
goto out;
g_free (sha256);
sha256 = g_strndup (line, spc - line);
if (!ostree_validate_checksum_string (sha256, error))
goto out;
g_hash_table_replace (ret_refs, ref, sha256);
/* Transfer ownership */
ref = NULL;
sha256 = NULL;
}
ret = TRUE;
ot_transfer_out_value (out_refs, &ret_refs);
out:
if (ret_refs)
g_hash_table_unref (ret_refs);
g_strfreev (lines);
return ret;
}
static gboolean
ostree_builtin_pull (int argc, char **argv, GFile *repo_path, GError **error)
{
GOptionContext *context;
gboolean ret = FALSE;
OstreeRepo *repo = NULL;
const char *remote;
const char *branch;
SoupSession *soup = NULL;
char *path = NULL;
char *baseurl = NULL;
char *summary_data = NULL;
SoupURI *base_uri = NULL;
SoupURI *summary_uri = NULL;
GKeyFile *config = NULL;
GCancellable *cancellable = NULL;
GHashTable *refs_to_fetch = NULL;
GHashTableIter hash_iter;
gpointer key, value;
char *branch_rev = NULL;
context = g_option_context_new ("REMOTE [BRANCH] - Download data from remote repository");
g_option_context_add_main_entries (context, options, NULL);
if (!g_option_context_parse (context, &argc, &argv, error))
goto out;
repo = ostree_repo_new (repo_path);
if (!ostree_repo_check (repo, error))
goto out;
if (argc < 2)
{
ot_util_usage_error (context, "REMOTE must be specified", error);
goto out;
}
remote = argv[1];
if (argc == 2)
branch = NULL;
else
branch = argv[2];
soup = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
NULL);
config = ostree_repo_get_config (repo);
key = g_strdup_printf ("remote \"%s\"", remote);
baseurl = g_key_file_get_string (config, key, "url", error);
if (!baseurl)
goto out;
base_uri = soup_uri_new (baseurl);
if (!base_uri)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to parse url '%s'", baseurl);
goto out;
}
if (branch != NULL)
{
char *contents;
if (!fetch_ref_contents (repo, soup, base_uri, branch, &contents, cancellable, error))
goto out;
/* Transfer ownership of contents */
refs_to_fetch = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
g_hash_table_insert (refs_to_fetch, g_strdup (branch), contents);
}
else
{
summary_uri = soup_uri_copy (base_uri);
path = g_build_filename (soup_uri_get_path (summary_uri), "refs", "summary", NULL);
soup_uri_set_path (summary_uri, path);
if (!fetch_uri_contents_utf8 (repo, soup, summary_uri, &summary_data, cancellable, error))
goto out;
if (!parse_ref_summary (summary_data, &refs_to_fetch, error))
goto out;
}
g_hash_table_iter_init (&hash_iter, refs_to_fetch);
while (g_hash_table_iter_next (&hash_iter, &key, &value))
{
const char *ref = key;
const char *sha256 = value;
if (!pull_one_commit (repo, remote, ref, sha256, soup, base_uri, cancellable, error))
goto out;
}
ret = TRUE;
out:
if (refs_to_fetch)
g_hash_table_unref (refs_to_fetch);
g_free (path);
g_free (baseurl);
g_free (summary_data);
g_free (branch_rev);
if (context)
g_option_context_free (context);
if (tempf)
(void) unlink (ot_gfile_get_path_cached (tempf));
g_clear_object (&tempf);
g_free (key);
g_free (rev);
g_free (remote_ref);
g_free (original_rev);
g_free (baseurl);
g_free (refpath);
g_clear_object (&soup);
if (base_uri)
soup_uri_free (base_uri);
if (target_uri)
soup_uri_free (target_uri);
if (summary_uri)
soup_uri_free (summary_uri);
g_clear_object (&repo);
g_clear_object (&soup);
return ret;