/** * ostree_diff_dirs: * @flags: Flags * @a: First directory path, or %NULL * @b: First directory path * @modified: (element-type OstreeDiffItem): Modified files * @removed: (element-type Gio.File): Removed files * @added: (element-type Gio.File): Added files * @cancellable: Cancellable * @error: Error * * Compute the difference between directory @a and @b as 3 separate * sets of #OstreeDiffItem in @modified, @removed, and @added. */ gboolean ostree_diff_dirs (OstreeDiffFlags flags, GFile *a, GFile *b, GPtrArray *modified, GPtrArray *removed, GPtrArray *added, GCancellable *cancellable, GError **error) { return ostree_diff_dirs_with_options (flags, a, b, modified, removed, added, NULL, cancellable, error); }
gboolean ostree_builtin_diff (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeRepo) repo = NULL; const char *src; const char *target; g_autofree char *src_prev = NULL; g_autoptr(GFile) srcf = NULL; g_autoptr(GFile) targetf = NULL; g_autoptr(GPtrArray) modified = NULL; g_autoptr(GPtrArray) removed = NULL; g_autoptr(GPtrArray) added = NULL; context = g_option_context_new ("REV TARGETDIR"); if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error)) goto out; if (argc < 2) { gchar *help = g_option_context_get_help (context, TRUE, NULL); g_printerr ("%s\n", help); g_free (help); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "REV must be specified"); goto out; } if (argc == 2) { src_prev = g_strconcat (argv[1], "^", NULL); src = src_prev; target = argv[1]; } else { src = argv[1]; target = argv[2]; } if (!opt_stats && !opt_fs_diff) opt_fs_diff = TRUE; if (opt_fs_diff) { OstreeDiffFlags diff_flags = OSTREE_DIFF_FLAGS_NONE; if (opt_no_xattrs) diff_flags |= OSTREE_DIFF_FLAGS_IGNORE_XATTRS; if (!parse_file_or_commit (repo, src, &srcf, cancellable, error)) goto out; if (!parse_file_or_commit (repo, target, &targetf, cancellable, error)) goto out; modified = g_ptr_array_new_with_free_func ((GDestroyNotify)ostree_diff_item_unref); removed = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); added = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); OstreeDiffDirsOptions diff_opts = { opt_owner_uid, opt_owner_gid }; if (!ostree_diff_dirs_with_options (diff_flags, srcf, targetf, modified, removed, added, &diff_opts, cancellable, error)) goto out; ostree_diff_print (srcf, targetf, modified, removed, added); } if (opt_stats) { g_autoptr(GHashTable) reachable_a = NULL; g_autoptr(GHashTable) reachable_b = NULL; g_autoptr(GHashTable) reachable_intersection = NULL; g_autofree char *rev_a = NULL; g_autofree char *rev_b = NULL; g_autofree char *size = NULL; guint a_size; guint b_size; guint64 total_common; if (!ostree_repo_resolve_rev (repo, src, FALSE, &rev_a, error)) goto out; if (!ostree_repo_resolve_rev (repo, target, FALSE, &rev_b, error)) goto out; if (!ostree_repo_traverse_commit (repo, rev_a, 0, &reachable_a, cancellable, error)) goto out; if (!ostree_repo_traverse_commit (repo, rev_b, 0, &reachable_b, cancellable, error)) goto out; a_size = g_hash_table_size (reachable_a); b_size = g_hash_table_size (reachable_b); g_print ("[A] Object Count: %u\n", a_size); g_print ("[B] Object Count: %u\n", b_size); reachable_intersection = reachable_set_intersect (reachable_a, reachable_b); g_print ("Common Object Count: %u\n", g_hash_table_size (reachable_intersection)); if (!object_set_total_size (repo, reachable_intersection, &total_common, cancellable, error)) goto out; size = g_format_size_full (total_common, 0); g_print ("Common Object Size: %s\n", size); } ret = TRUE; out: return ret; }
/** * ostree_diff_dirs_with_options: * @flags: Flags * @a: First directory path, or %NULL * @b: First directory path * @modified: (element-type OstreeDiffItem): Modified files * @removed: (element-type Gio.File): Removed files * @added: (element-type Gio.File): Added files * @cancellable: Cancellable * @options: (allow-none): Options * @error: Error * * Compute the difference between directory @a and @b as 3 separate * sets of #OstreeDiffItem in @modified, @removed, and @added. */ gboolean ostree_diff_dirs_with_options (OstreeDiffFlags flags, GFile *a, GFile *b, GPtrArray *modified, GPtrArray *removed, GPtrArray *added, OstreeDiffDirsOptions *options, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GError *temp_error = NULL; g_autoptr(GFileEnumerator) dir_enum = NULL; g_autoptr(GFile) child_a = NULL; g_autoptr(GFile) child_b = NULL; g_autoptr(GFileInfo) child_a_info = NULL; g_autoptr(GFileInfo) child_b_info = NULL; OstreeDiffDirsOptions default_opts = OSTREE_DIFF_DIRS_OPTIONS_INIT; if (!options) options = &default_opts; /* If we're diffing versus a repo, and either of them have xattrs disabled, * then disable for both. */ OstreeRepo *repo; if (OSTREE_IS_REPO_FILE (a)) repo = ostree_repo_file_get_repo ((OstreeRepoFile*)a); else if (OSTREE_IS_REPO_FILE (b)) repo = ostree_repo_file_get_repo ((OstreeRepoFile*)b); else repo = NULL; if (repo != NULL && repo->disable_xattrs) flags |= OSTREE_DIFF_FLAGS_IGNORE_XATTRS; if (a == NULL) { if (!diff_add_dir_recurse (b, added, cancellable, error)) goto out; ret = TRUE; goto out; } child_a_info = g_file_query_info (a, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error); if (!child_a_info) goto out; child_b_info = g_file_query_info (b, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error); if (!child_b_info) goto out; /* Fast path test for unmodified directories */ if (g_file_info_get_file_type (child_a_info) == G_FILE_TYPE_DIRECTORY && g_file_info_get_file_type (child_b_info) == G_FILE_TYPE_DIRECTORY && OSTREE_IS_REPO_FILE (a) && OSTREE_IS_REPO_FILE (b)) { OstreeRepoFile *a_repof = (OstreeRepoFile*) a; OstreeRepoFile *b_repof = (OstreeRepoFile*) b; if (strcmp (ostree_repo_file_tree_get_contents_checksum (a_repof), ostree_repo_file_tree_get_contents_checksum (b_repof)) == 0) { ret = TRUE; goto out; } } g_clear_object (&child_a_info); g_clear_object (&child_b_info); dir_enum = g_file_enumerate_children (a, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error); if (!dir_enum) goto out; while ((child_a_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error)) != NULL) { const char *name; GFileType child_a_type; GFileType child_b_type; name = g_file_info_get_name (child_a_info); g_clear_object (&child_a); child_a = g_file_get_child (a, name); child_a_type = g_file_info_get_file_type (child_a_info); g_clear_object (&child_b); child_b = g_file_get_child (b, name); g_clear_object (&child_b_info); child_b_info = g_file_query_info (child_b, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, &temp_error); if (!child_b_info) { if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_clear_error (&temp_error); g_ptr_array_add (removed, g_object_ref (child_a)); } else { g_propagate_error (error, temp_error); goto out; } } else { if (options->owner_uid >= 0) g_file_info_set_attribute_uint32 (child_b_info, "unix::uid", options->owner_uid); if (options->owner_gid >= 0) g_file_info_set_attribute_uint32 (child_b_info, "unix::gid", options->owner_gid); child_b_type = g_file_info_get_file_type (child_b_info); if (child_a_type != child_b_type) { OstreeDiffItem *diff_item = diff_item_new (child_a, child_a_info, child_b, child_b_info, NULL, NULL); g_ptr_array_add (modified, diff_item); } else { OstreeDiffItem *diff_item = NULL; if (!diff_files (flags, child_a, child_a_info, child_b, child_b_info, &diff_item, cancellable, error)) goto out; if (diff_item) g_ptr_array_add (modified, diff_item); /* Transfer ownership */ if (child_a_type == G_FILE_TYPE_DIRECTORY) { if (!ostree_diff_dirs_with_options (flags, child_a, child_b, modified, removed, added, options, cancellable, error)) goto out; } } } g_clear_object (&child_a_info); } if (temp_error != NULL) { g_propagate_error (error, temp_error); goto out; } g_clear_object (&dir_enum); dir_enum = g_file_enumerate_children (b, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error); if (!dir_enum) goto out; g_clear_object (&child_b_info); while ((child_b_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error)) != NULL) { const char *name; name = g_file_info_get_name (child_b_info); g_clear_object (&child_a); child_a = g_file_get_child (a, name); g_clear_object (&child_b); child_b = g_file_get_child (b, name); g_clear_object (&child_a_info); child_a_info = g_file_query_info (child_a, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, &temp_error); if (!child_a_info) { if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_clear_error (&temp_error); g_ptr_array_add (added, g_object_ref (child_b)); if (g_file_info_get_file_type (child_b_info) == G_FILE_TYPE_DIRECTORY) { if (!diff_add_dir_recurse (child_b, added, cancellable, error)) goto out; } } else { g_propagate_error (error, temp_error); goto out; } } g_clear_object (&child_b_info); } if (temp_error != NULL) { g_propagate_error (error, temp_error); goto out; } ret = TRUE; out: return ret; }