[hacktree/wip/import] a lot more code
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [hacktree/wip/import] a lot more code
- Date: Fri, 14 Oct 2011 23:08:48 +0000 (UTC)
commit ce3d5ec51a976acf02ef4ae8e8fb14658a4f2f95
Author: Colin Walters <walters verbum org>
Date: Fri Oct 14 19:08:17 2011 -0400
a lot more code
src/libhacktree/hacktree-core.h | 30 +-
src/libhacktree/hacktree-repo.c | 855 ++++++++++++++++++++++++++++++---------
src/libhacktree/hacktree-repo.h | 16 +-
src/libhtutil/ht-unix-utils.c | 54 +++
src/libhtutil/ht-unix-utils.h | 4 +
5 files changed, 750 insertions(+), 209 deletions(-)
---
diff --git a/src/libhacktree/hacktree-core.h b/src/libhacktree/hacktree-core.h
index f49ebd7..47ddf8c 100644
--- a/src/libhacktree/hacktree-core.h
+++ b/src/libhacktree/hacktree-core.h
@@ -31,40 +31,44 @@ G_BEGIN_DECLS
typedef enum {
HACKTREE_SERIALIZED_TREE_VARIANT = 1,
HACKTREE_SERIALIZED_COMMIT_VARIANT = 2
- HACKTREE_SERIALIZED_XATTRS_VARIANT = 3
+ HACKTREE_SERIALIZED_DIRMETA_VARIANT = 3
} HacktreeSerializedVariantType;
#define HACKTREE_SERIALIZED_VARIANT_FORMAT G_VARIANT_TYPE("(uv)")
+#define HACKTREE_DIR_META_VERSION 0
+/*
+ * dirmeta objects:
+ * u - Version
+ * u - uid
+ * u - gid
+ * u - mode
+ * ay - xattrs
+ */
+#define HACKTREE_DIRMETA_GVARIANT_FORMAT G_VARIANT_TYPE("(uuuuay)")
+
#define HACKTREE_TREE_VERSION 0
/*
* Tree objects:
* u - Version
* a{sv} - Metadata
- * a(ss) - array of (checksum, filename) for files
- * as - array of tree checksums for directories
- * a(suuus) - array of (dirname, uid, gid, mode, xattr_checksum) for directories
+ * a(ss) - array of (filename, checksum) for files
+ * a(sss) - array of (dirname, tree_checksum, meta_checksum) for directories
*/
-#define HACKTREE_TREE_GVARIANT_FORMAT G_VARIANT_TYPE("(ua{sv}a(ss)asa(suuus)")
+#define HACKTREE_TREE_GVARIANT_FORMAT G_VARIANT_TYPE("(ua{sv}a(ss)a(sss)")
#define HACKTREE_COMMIT_VERSION 0
/*
* Commit objects:
* u - Version
* a{sv} - Metadata
+ * s - parent checksum
* s - subject
* s - body
* t - Timestamp in seconds since the epoch (UTC)
* s - Tree SHA256
*/
-#define HACKTREE_COMMIT_GVARIANT_FORMAT G_VARIANT_TYPE("(ua{sv}ssts)")
-
-/*
- * xattr objects:
- * u - Version
- * ay - data
- */
-#define HACKTREE_XATTR_GVARIANT_FORMAT G_VARIANT_TYPE("(uay)")
+#define HACKTREE_COMMIT_GVARIANT_FORMAT G_VARIANT_TYPE("(ua{sv}sssts)")
gboolean hacktree_get_xattrs_for_directory (const char *path,
char **out_xattrs,
diff --git a/src/libhacktree/hacktree-repo.c b/src/libhacktree/hacktree-repo.c
index 927051f..a5878f9 100644
--- a/src/libhacktree/hacktree-repo.c
+++ b/src/libhacktree/hacktree-repo.c
@@ -159,7 +159,7 @@ hacktree_repo_new (const char *path)
}
static gboolean
-parse_checksum_file (HacktreeRepo *repo,
+parse_checksum_file (HacktreeRepo *self,
const char *path,
char **sha256,
GError **error)
@@ -203,15 +203,15 @@ hacktree_repo_check (HacktreeRepo *self, GError **error)
priv->inited = TRUE;
- return parse_checksum_file (repo, priv->head_ref_path, &priv->current_head, error);
+ return parse_checksum_file (self, priv->head_ref_path, &priv->current_head, error);
}
static gboolean
-import_gvariant (HacktreeRepo *repo,
- HacktreeSerializedVariantType type,
- GVariant *variant,
- GChecksum **out_checksum,
- GError **error)
+import_gvariant_object (HacktreeRepo *self,
+ HacktreeSerializedVariantType type,
+ GVariant *variant,
+ GChecksum **out_checksum,
+ GError **error)
{
HacktreeRepoPrivate *priv = GET_PRIVATE (self);
GVariantBuilder builder;
@@ -247,7 +247,7 @@ import_gvariant (HacktreeRepo *repo,
NULL, error))
goto out;
- if (!link_one_file (repo, tmp_name, FALSE, FALSE, out_checksum, error))
+ if (!link_one_file (self, tmp_name, FALSE, FALSE, out_checksum, error))
goto out;
ret = TRUE;
@@ -263,17 +263,82 @@ import_gvariant (HacktreeRepo *repo,
return ret;
}
-static GVariant *
-import_directory (HacktreeRepo *self,
- const char *path,
- GError **error)
+static gboolean
+load_gvariant_object (HacktreeRepo *self,
+ HacktreeSerializedVariantType expected_type,
+ const char *sha256,
+ GVariant **out_variant,
+ GError **error)
+{
+ GMappedFile *mfile = NULL;
+ gboolean ret = FALSE;
+ GVariant *ret_variant = NULL;
+ GVariant *container = NULL;
+ char *path = NULL;
+
+ path = get_object_path_for_checksum (self, sha256);
+
+ mfile = g_mapped_file_new (priv->index_path, FALSE, error);
+ if (mfile == NULL)
+ goto out;
+ else
+ {
+ guint32 type;
+ container = g_variant_new_from_data (HACKTREE_SERIALIZED_VARIANT_FORMAT,
+ g_mapped_file_get_contents (mfile),
+ g_mapped_file_get_length (mfile),
+ FALSE,
+ (GDestroyNotify) g_mapped_file_unref,
+ mfile);
+ if (!g_variant_is_of_type (container, HACKTREE_SERIALIZED_VARIANT_FORMAT))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted metadata object '%s'", sha256);
+ goto out;
+ }
+ g_variant_get (container, HACKTREE_SERIALIZED_VARIANT_FORMAT,
+ &type, &ret_variant);
+ if (type != expected_type)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted metadata object '%s'; found type %u, expected %u", sha256,
+ type, (guint32)expected_type);
+ goto out;
+
+ }
+ mfile = NULL;
+ }
+
+ ret = TRUE;
+ out:
+ if (!ret)
+ {
+ if (!ret_variant)
+ g_variant_unref (ret_variant);
+ }
+ else
+ *out_variant = ret_variant;
+ if (container != NULL)
+ g_variant_unref (container);
+ g_free (path);
+ if (mfile != NULL)
+ g_mapped_file_unref (mfile);
+ return ret;
+}
+
+static gboolean
+import_directory_meta (HacktreeRepo *self,
+ const char *path,
+ GVariant **out_variant,
+ GChecksum **out_checksum,
+ GError **error)
{
- GVariant *ret = NULL;
+ gboolean ret = FALSE;
struct stat stbuf;
char *basename = NULL;
- GChecksum *xattr_checksum = NULL;
+ GChecksum *ret_checksum = NULL;
const char *xattr_checksum_string;
- GVariant *xattr_variant = NULL;
+ GVariant *dirmeta = NULL;
char *xattrs = NULL;
gsize xattr_len;
@@ -293,32 +358,34 @@ import_directory (HacktreeRepo *self,
if (!hacktree_get_xattrs_for_directory (path, &xattrs, &xattr_len, error))
goto out;
- if (xattrs != NULL)
- {
- xattr_variant = g_variant_new_fixed_array (G_VARIANT_TYPE ("y"),
- xattrs, xattr_len, 1);
- g_variant_ref_sink (xattr_variant);
- if (!import_gvariant (self, HACKTREE_SERIALIZED_XATTRS_VARIANT, xattr_variant, &xattr_checksum, error))
+ dirmeta = g_variant_new (G_VARIANT_TYPE ("(uuuu ay)"),
+ HACKTREE_DIR_META_VERSION,
+ (guint32)stbuf.st_uid,
+ (guint32)stbuf.st_gid,
+ (guint32)(stbuf.st_mode & ~S_IFMT),
+ g_variant_new_fixed_array (G_VARIANT_TYPE ("y"),
+ xattrs, xattr_len, 1));
+ g_variant_ref_sink (dirmeta);
+
+ if (!import_gvariant_object (self, HACKTREE_SERIALIZED_DIRMETA_VARIANT,
+ dirmeta, &ret_checksum, error))
goto out;
- xattr_checksum_string = g_checksum_get_string (xattr_checksum);
- }
- else
- xattr_checksum_string = HACKTREE_EMPTY_STRING_SHA256;
-
- basename = g_path_get_basename (path);
-
- ret = g_variant_new (G_VARIANT_TYPE ("(suuus)"),
- basename,
- (guint32)stbuf.st_uid,
- (guint32)stbuf.st_gid,
- (guint32)(stbuf.st_mode & ~S_IFMT),
- xattr_checksum_string);
+ ret = TRUE;
out:
- if (xattr_checksum != NULL)
- g_checksum_free (xattr_checksum);
+ if (!ret)
+ {
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ if (dirmeta != NULL)
+ g_variant_unref (dirmeta);
+ }
+ else
+ {
+ *out_checksum = ret_checksum;
+ *out_variant = dirmeta;
+ }
g_free (xattrs);
- g_free (basename);
return ret;
}
@@ -468,22 +535,184 @@ hacktree_repo_link_file (HacktreeRepo *self,
return TRUE;
}
+typedef struct _ParsedTreeData ParsedTreeData;
+typedef struct _ParsedDirectoryData ParsedDirectoryData;
+
+struct _ParsedDirectoryData {
+ ParsedTreeData *tree_data;
+ char *metadata_sha256;
+ GVariant *meta_data;
+};
+
+static void
+parsed_directory_data_free (ParsedDirectoryData *pdata)
+{
+ if (pdata == NULL)
+ return;
+ g_variant_unref (pdata->tree_data);
+ g_free (pdata->metadata_sha256);
+ g_variant_unref (pdata->meta_data);
+ g_free (pdata);
+}
+
+struct _ParsedTreeData {
+ GHashTable *files; /* char* filename -> char* checksum */
+ GHashTable *directories; /* char* dirname -> ParsedDirectoryData* */
+};
+
+static ParsedTreeData *
+parsed_tree_data_new (void)
+{
+ ParsedTreeData *ret = g_new0 (ParsedTreeData, 1);
+ ret->files = g_hash_table_new (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_free);
+ ret->directories = g_hash_table_new (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)parsed_directory_data_free);
+}
+
static void
-commit_data_free (CommitData *data)
+parsed_tree_data_free (ParsedTreeData *pdata)
{
- if (!data)
+ if (pdata == NULL)
return;
- g_checksum_free (data->checksum);
+ g_hash_table_destroy (pdata->files);
+ g_hash_table_destroy (pdata->directories);
+ g_free (pdata);
+}
+
+static gboolean
+parse_tree (HacktreeRepo *self,
+ const char *sha256,
+ ParsedTreeData **out_pdata,
+ GError **error)
+{
+ HacktreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ ParsedTreeData *ret_pdata = NULL;
+ int i, n;
+ guint32 version;
+ GVariant *tree_variant = NULL;
+ GVariant *meta_variant = NULL;
+ GVariant *files_variant = NULL;
+ GVariant *dirs_variant = NULL;
+
+ if (!load_gvariant_object (self, HACKTREE_SERIALIZED_TREE_VARIANT,
+ sha256, &tree_variant, error))
+ goto out;
+
+ g_variant_get (tree_variant, "(u a{sv}@a(ss)@a(sss))",
+ &version, &meta_variant, &files_variant, &dirs_variant);
+
+ ret_pdata = parsed_tree_data_new ();
+ n = g_variant_n_children (files_variant);
+ for (i = 0; i < n; i++)
+ {
+ const char *filename;
+ const char *checksum;
+
+ g_variant_get_child_value (files_variant, i, "(ss)", &filename, &checksum);
+
+ g_hash_table_insert (ret_pdata, g_strdup (filename), g_strdup (checksum));
+ }
+
+ n = g_variant_n_children (dirs_variant);
+ for (i = 0; i < n; i++)
+ {
+ const char *dirname;
+ const char *tree_checksum;
+ const char *meta_checksum;
+ ParsedTreeData *child_tree = NULL;
+ GVariant *metadata = NULL;
+ ParsedDirectoryData *child_dir = NULL;
+
+ g_variant_get_child_value (files_variant, i, "(sss)",
+ &filename, &tree_checksum, &meta_checksum);
+
+ if (!parse_tree (self, tree_checksum, &child_tree, error))
+ goto out;
+
+ if (!load_gvariant_object (self, HACKTREE_SERIALIZED_DIRMETA_VARIANT,
+ meta_checksum, &metadata, error))
+ {
+ parsed_tree_data_free (child_tree);
+ goto out;
+ }
+
+ child_dir = g_new0 (ParsedDirectoryData, 1);
+ child_dir->tree_data = child_tree;
+ child_dir->metadata_sha256 = g_strdup (meta_checksum);
+ child_dir->meta_data = g_variant_ref_sink (metadata);
+
+ g_hash_table_insert (ret_pdata->directories, g_strdup (dirname), child_dir);
+ }
+
+ ret = TRUE;
+ out:
+ if (!ret)
+ parsed_tree_data_free (ret_pdata);
+ else
+ *out_pdata = ret_pdata;
+ if (tree_variant)
+ g_variant_unref (tree_variant);
+ if (meta_variant)
+ g_variant_unref (meta_variant);
+ if (files_variant)
+ g_variant_unref (files_variant);
+ if (dirs_variant)
+ g_variant_unref (dirs_variant);
+ return ret;
+}
+
+static gboolean
+load_commit_and_trees (HacktreeRepo *self,
+ const char *commit_sha256,
+ GVariant **out_commit,
+ ParsedTreeData **out_tree_data,
+ GError **error)
+{
+ HacktreeRepoPrivate *priv = GET_PRIVATE (self);
+ GVariant *ret_commit = NULL;
+ ParsedTreeData *ret_tree_data = NULL;
+ gboolean ret = FALSE;
+ const char *tree_checksum;
+
+ if (!priv->current_head)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Can't load current commit; no HEAD reference");
+ goto out;
+ }
+
+ if (!load_gvariant_object (self, commit_sha256, &ret_commit, error))
+ goto out;
+
+ g_variant_get_child (ret_commit, 5, "&s", &tree_checksum);
+
+ if (!parse_tree (self, tree_checksum, &ret_tree_data, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (!ret)
+ {
+ if (ret_commit)
+ g_variant_unref (ret_commit);
+ parsed_tree_data_free (ret_tree_data);
+ }
+ else
+ {
+ *out_commit = ret_commit;
+ *out_tree_data = ret_tree_data;
+ }
+ return FALSE;
}
static void
init_tree_builders (GVariantBuilder *files_builder,
- GVariantBuilder *directories_checksum_builder,
- GVariantBuilder *directories_data_builder)
+ GVariantBuilder *directories_builder)
{
- g_variant_builder_init (files_builder, G_VARIANT_TYPE ("a(ss)"));
- g_variant_builder_init (directories_builder, G_VARIANT_TYPE ("as"));
- g_variant_builder_init (directories_data_builder, G_VARIANT_TYPE ("a(suuus)"));
}
static GVariant *
@@ -495,211 +724,459 @@ create_empty_gvariant_dict (void)
}
static gboolean
-commit_tree_from_builders (HacktreeRepo *repo,
- GHashTable *dir_to_checksum,
- GVariantBuilder *files_builder,
- GVariantBuilder *directories_checksum_builder,
- GVariantBuilder *directories_data_builder,
- GChecksum **out_checksum,
- GError **error)
+import_parsed_tree (HacktreeRepo *self,
+ ParsedTreeData *tree,
+ GChecksum **out_checksum,
+ GError **error)
{
gboolean ret = FALSE;
GVariant *tree = NULL;
+ gboolean builders_initialized = FALSE;
+ GVariantBuilder files_builder;
+ GVariantBuilder dirs_builder;
+ GHashTableIter hash_iter;
+ gpointer key, value;
+
+ g_variant_builder_init (&files_builder, G_VARIANT_TYPE ("a(ss)"));
+ g_variant_builder_init (&dirs_builder, G_VARIANT_TYPE ("a(sss)"));
+ builders_initialized = TRUE;
+
+ hash_iter = g_hash_table_iter_init (&hash_iter, tree->files);
+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
+ {
+ const char *name = key;
+ const char *checksum = value;
+
+ g_variant_builder_add (&files_builder, "(ss)", name, checksum);
+ }
+
+ hash_iter = g_hash_table_iter_init (&hash_iter, tree->directories);
+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
+ {
+ const char *name = key;
+ GChecksum *dir_checksum = NULL;
+ ParsedDirectoryData *dir = value;
- tree = g_variant_new (G_VARIANT_TYPE ("u a{sv}@a(ss)@as a(suuus)"),
+ if (!import_parsed_tree (self, dir->tree_data, &dir_checksum, error))
+ goto out;
+
+ g_variant_builder_add (&dirs_builder, "(sss)",
+ name, g_checksum_get_string (dir_checksum), dir->metadata_sha256);
+ }
+
+ tree = g_variant_new (G_VARIANT_TYPE ("u a{sv}@a(ss)@a(sss)"),
0,
create_empty_gvariant_dict (),
g_variant_builder_end (&files_builder),
- g_variant_builder_end (&dir_checksum_builder),
- g_variant_builder_end (&dir_data_builder));
+ g_variant_builder_end (&dirs_builder));
+ builders_initialized = FALSE;
g_variant_ref_sink (tree);
- if (!import_gvariant (self, HACKTREE_SERIALIZED_TREE_VARIANT, tree, out_checksum, error))
+ if (!import_gvariant_object (self, HACKTREE_SERIALIZED_TREE_VARIANT, tree, out_checksum, error))
goto out;
ret = TRUE;
out:
+ if (builders_initialized)
+ {
+ g_variant_builder_clear (&files_builder);
+ g_variant_builder_clear (&dirs_builder);
+ }
if (tree)
g_variant_unref (tree);
return ret;
}
static gboolean
-load_gvariant_object (HacktreeRepo *self,
- const char *sha256,
- GVariant **out_variant,
- GError **error)
+check_path (const char *filename,
+ GError **error)
{
- GMappedFile *mfile = NULL;
gboolean ret = FALSE;
- char *path = NULL;
- path = get_object_path_for_checksum (repo, sha256);
+ if (!*filename)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Invalid empty filename");
+ goto out;
+ }
+
+ if (ht_util_filename_has_dotdot (filename))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Path uplink '..' in filename '%s' not allowed (yet)", filename);
+ goto out;
+ }
- mfile = g_mapped_file_new (priv->index_path, FALSE, error);
- if (mfile == NULL)
- goto out;
- else
+ if (g_path_is_absolute (filename))
{
- *out_variant = g_variant_new_from_data (HACKTREE_INDEX_GVARIANT_FORMAT,
- g_mapped_file_get_contents (mfile),
- g_mapped_file_get_length (mfile),
- FALSE,
- (GDestroyNotify) g_mapped_file_unref,
- mfile);
- mfile = NULL;
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Absolute filename '%s' not allowed (yet)", filename);
+ goto out;
}
ret = TRUE;
out:
- g_free (path);
- if (mfile != NULL)
- g_mapped_file_unref (mfile);
return ret;
}
-gboolean
-hacktree_repo_commit_files (HacktreeRepo *repo,
- const char *subject,
- const char *body,
- GVariant *metadata,
- const char *base,
- GPtrArray *files,
- GError **error)
-{
- char *abspath = NULL;
+static gboolean
+walk_parsed_tree (HacktreeRepo *self,
+ const char *filename,
+ ParsedTreeData *tree,
+ int *out_filename_index, /* out*/
+ char **out_component, /* out, but do not free */
+ ParsedTreeData **out_tree, /* out, but do not free */
+ GError **error)
+{
gboolean ret = FALSE;
+ GPtrArray *components = NULL;
+ GSList *iter;
+ ParsedTreeData *current_tree = tree;
+ const char *component = NULL;
+ const char *file_sha1;
+ ParsedDirectorData *dir;
int i;
- int current_tree_depth = -1;
- GPtrArray *sorted_files = NULL;
- gboolean builders_initialized = FALSE;
- GHashTable *dir_to_checksum = NULL;
- GVariantBuilder files_builder;
- GVariantBuilder dir_checksum_builder;
- GVariantBuilder dir_data_builder;
- GVariant *commit = NULL;
-
- dir_to_checksum = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
- sorted_files = ht_util_sort_filenames_by_component_length (files);
+ int ret_filename_index = 0;
- builders_initialized = TRUE;
- init_tree_builders (&files_builder,
- &dir_checksum_builder,
- &dir_data_builder);
+ components = ht_util_path_split (filename);
+ g_assert (components != NULL);
- for (i = 0; i < sorted_files->len; i++)
+ current_tree = tree;
+ for (i = 0; i < components->len - 1; i++)
{
- const char *filename = files->pdata[i];
- CommitData *data = NULL;
- struct stat stbuf;
- int n_components;
-
- if (ht_util_filename_has_dotdot (filename))
+ component = components->pdata[i];
+ file_sha1 = g_hash_table_lookup (current_tree->files, component);
+ dir = g_hash_table_lookup (current_tree->directories, component);
+
+ if (!(file_sha1 || dir))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Path uplink '..' in filename '%s' not allowed (yet)", filename);
+ "No such file or directory: %s",
+ filename);
goto out;
}
+ else if (file_sha1)
+ break;
+ else if (!dir)
+ g_assert_not_reached ();
+ current_tree = dir;
+ ret_filename_index++;
+ }
+
+ if (file_sha1 && iter->next)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Encountered non-directory '%s' in '%s'",
+ iter->data,
+ filename);
+ goto out;
+ }
+
+ ret = TRUE;
+ g_assert (!(file_sha1 && dir));
+ *out_filename_index = i;
+ *out_filename = component;
+ *out_tree = current_tree;
+ out:
+ g_ptr_array_free (components);
+ return ret;
+}
+
+static gboolean
+remove_files_from_tree (HacktreeRepo *self,
+ const char *base,
+ GPtrArray *removed_files,
+ ParsedTreeData *tree,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ int i;
+
+ for (i = 0; i < removed_files->len; i++)
+ {
+ const char *filename = removed_files->pdata[i];
+ int filename_index;
+ const char *component;
+ ParsedTreeData *parent;
+ const char *file_sha1;
+ ParsedTreeData *dir;
+
+ if (!check_path (filename, error))
+ goto out;
+
+ if (!walk_parsed_tree (self, filename, &filename_index, (char*)&component, &parent, error))
+ goto out;
+
+ file_sha1 = g_hash_table_lookup (parent->files, component);
+ dir = g_hash_table_lookup (parent->directories, component);
+
+ if (file_sha1)
+ g_hash_table_remove (parent->files, component);
+ else if (dir)
+ g_hash_table_remove (parent->directories, component);
+ else
+ g_assert_not_reached ();
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+static gboolean
+add_one_directory_to_tree_and_import (HacktreeRepo *self,
+ const char *basename,
+ const char *abspath,
+ ParsedTreeData *tree,
+ ParsedDirectoryData *dir,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *dirmeta = NULL;
+ GChecksum *dir_meta_checksum = NULL;
+
+ g_assert (tree != NULL);
+
+ if (!import_directory_meta (self, abspath, &dirmeta, &dir_meta_checksum, error))
+ goto out;
+
+ if (dir)
+ {
+ g_variant_unref (dir->metadata);
+ dir->metadata = dirmeta;
+ }
+ else
+ {
+ dir = g_new0 (ParsedDirectoryData, 1);
+ dir->tree_data = parsed_tree_data_new ();
+ dir->metadata_sha256 = g_strdup (g_checksum_get_string (dir_meta_checksum));
+ dir->metadata = dirmeta;
+ g_hash_table_insert (tree->directories, g_strdup (basename), dir);
+ }
+
+ ret = TRUE;
+ out:
+ if (dir_meta_checksum)
+ g_checksum_free (dir_meta_checksum);
+ return ret;
+}
+
+static gboolean
+add_one_file_to_tree_and_import (HacktreeRepo *self,
+ const char *basename,
+ const char *abspath,
+ ParsedTreeData *tree,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GChecksum *checksum = NULL;
+
+ g_assert (tree != NULL);
+
+ if (!link_one_file (self, abspath, FALSE, FALSE, &checksum, error))
+ goto out;
+
+ g_hash_table_replace (tree->files, g_strdup (basename),
+ g_strdup (g_checksum_get_string (checksum)));
+
+ ret = TRUE;
+ out:
+ if (checksum)
+ g_checksum_free (checksum);
+ return ret;
+}
+
+static gboolean
+add_one_path_to_tree_and_import (HacktreeRepo *self,
+ const char *base,
+ const char *filename,
+ ParsedTreeData *tree,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GPtrArray *components = NULL;
+ struct stat stbuf;
+ GSList *iter;
+ char *component_abspath = NULL;
+ ParsedTreeData *current_tree = tree;
+ const char *component = NULL;
+ const char *file_sha1;
+ ParsedDirectoryData *dir;
+ int i;
+ gboolean is_directory;
- if (g_path_is_absolute (filename))
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Absolute filename '%s' not allowed (yet)", filename);
- goto out;
- }
+ if (!check_path (filename, error))
+ goto out;
- n_components = ht_util_count_filename_components (filename);
- if (current_tree_depth == -1)
- current_tree_depth = n_components;
- else if (n_components < current_tree_depth)
+ abspath = g_build_filename (base, filename, NULL);
+
+ if (!lstat (abspath, &stbuf))
+ {
+ ht_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ is_directory = S_ISDIR(stbuf.st_mode);
+
+ if (components)
+ g_ptr_array_free (components);
+ components = ht_util_path_split (filename);
+ g_assert (components->len > 0);
+
+ current_tree = tree;
+ for (i = 0; i < components->len; i++)
+ {
+ component = components->pdata[i];
+ g_free (component_abspath);
+ component_abspath = ht_util_path_join_n (base, components, i);
+ file_sha1 = g_hash_table_lookup (current_tree->files, component);
+ dir = g_hash_table_lookup (current_tree->directories, component);
+
+ if (i < components->len - 1)
{
- if (!commit_tree_from_builders (self, dir_to_checksum,
- &files_builder,
- &dir_checksum_builder,
- &dir_data_builder,
- error))
+ if (file_sha1 != NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Encountered non-directory '%s' in '%s'",
+ component,
+ filename);
+ goto out;
+ }
+ /* Implicitly add intermediate directories */
+ if (!add_one_directory_to_tree_and_import (self, component, abspath, current_tree, dir, &error))
goto out;
- init_tree_builders (&files_builder,
- &dir_checksum_builder,
- &dir_data_builder);
}
-
- g_free (abspath);
- abspath = g_build_filename (base, filename, NULL);
-
- if (lstat (abspath, &stbuf) < 0)
+ else if (is_directory)
{
- ht_util_set_error_from_errno (error, errno);
- goto out;
+ if (file_sha1 != NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "File '%s' can't be overwritten by directory",
+ filename);
+ goto out;
+ }
+ if (!add_one_directory_to_tree_and_import (self, component, abspath, current_tree, dir, &error))
+ goto out;
}
-
- if (S_ISDIR (stbuf.st_mode))
+ else
{
- GVariant *dirdata;
- GChecksum *tree_checksum = NULL;
-
- dirdata = import_directory (abspath, error);
- if (!dirdata)
- goto out;
- g_variant_ref_sink (dirdata);
-
- if (!import_gvariant (self, dirdata, &tree_checksum, error))
+ g_assert (!is_directory);
+ if (dir != NULL)
{
- g_variant_unref (dirdata);
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "File '%s' can't be overwritten by directory",
+ filename);
goto out;
}
+ if (!add_one_file_to_tree_and_import (self, component, abspath, current_tree, &error))
+ goto out;
+ }
+ }
- g_variant_builder_add (&dir_checksum_builder,
- "s", g_checksum_get_string (tree_checksum));
- g_checksum_free (tree_checksum);
- tree_checksum = NULL;
-
- g_variant_builder_add (&dir_data_builder, dirdata);
+ ret = TRUE;
+ out:
+ g_free (component_abspath);
+ g_free (abspath);
+ return ret;
+}
- g_variant_unref (dirdata);
- }
- else
- {
- GChecksum *file_checksum = NULL;
- if (!link_one_file (self, abspath, TRUE, FALSE, &file_checksum, error))
- goto out;
-
- g_variant_builder_add (&dir_checksum_builder,
- "s", g_checksum_get_string (file_checksum));
- g_checksum_free (file_checksum);
- }
+static gboolean
+add_files_to_tree_and_import (HacktreeRepo *self,
+ const char *base,
+ GPtrArray *added_files,
+ ParsedTreeData *tree,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ int i;
+
+ for (i = 0; i < added_files->len; i++)
+ {
+ const char *path = added_files->pdata[i];
+
+ if (!add_one_path_to_tree_and_import (self, base, path, tree, error))
+ goto out;
}
- if (sorted_files->len > 0)
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+gboolean
+hacktree_repo_commit (HacktreeRepo *self,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ const char *base,
+ GPtrArray *modified_files,
+ GPtrArray *removed_files,
+ GChecksum **out_commit,
+ GError **error)
+{
+ HacktreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ ParsedTreeData *tree = NULL;
+ GVariant *previous_commit = NULL;
+ GVariant *commit = NULL;
+ GVariant *new_tree = NULL;
+ GChecksum *root_checksum = NULL;
+ GChecksum *ret_commit_checksum = NULL;
+ GDateTime *now = NULL;
+
+ g_return_val_if_fail (priv->inited, FALSE);
+
+ if (priv->current_head)
{
- if (!commit_tree_from_builders (self, &files_builder,
- &dir_checksum_builder,
- &dir_data_builder))
+ if (!load_commit_and_trees (self, priv->current_head, &previous_commit, &tree, error))
goto out;
- builders_initialized = FALSE;
+ }
+ else
+ {
+ /* Initial commit */
+ tree = parsed_tree_data_new ();
}
- {
- GDateTime *now = g_date_time_new_now_utc ();
-
- commit = g_variant_new (G_VARIANT_TYPE("(u a{sv}ssts)"),
- HACKTREE_COMMIT_VERSION,
- create_empty_gvariant_dict (),
- subject, body,
- g_date_time_to_unix () / G_TIME_SPAN_SECOND,
-
-
+ if (!remove_files_from_tree (self, removed_files, tree, error))
+ goto out;
+
+ if (!add_files_to_tree_and_import (self, modified_files, tree, error))
+ goto out;
+
+ if (!import_parsed_tree (self, tree, &root_checksum, error))
+ goto out;
+
+ now = g_date_time_new_now_utc ();
+ commit = g_variant_new (G_VARIANT_TYPE("(u a{sv}sssts)"),
+ HACKTREE_COMMIT_VERSION,
+ create_empty_gvariant_dict (),
+ priv->current_head ? priv->current_head : "";
+ subject, body,
+ g_date_time_to_unix () / G_TIME_SPAN_SECOND,
+ g_checksum_get_string (root_checksum));
+ if (!import_gvariant_object (self, HACKTREE_SERIALIZED_COMMIT_VARIANT,
+ commit, &commit_checksum, error))
+ goto out;
ret = TRUE;
out:
- g_hash_table_destroy (dir_to_checksum);
- if (sorted_files != NULL)
- g_ptr_array_free (sorted_files);
- if (builders_initialized)
+ if (!ret)
{
- g_variant_builder_clear (&files_builder);
- g_variant_builder_clear (&dir_checksum_builder);
- g_variant_builder_clear (&dir_data_builder);
+ if (ret_commit_checksum)
+ g_checksum_free (ret_commit_checksum);
+ }
+ else
+ {
+ *out_commit = ret_commit_checksum;
}
+ if (root_checksum)
+ g_checksum_free (root_checksum);
+ if (previous_commit)
+ g_variant_unref (previous_commit);
+ parsed_tree_data_free (tree);
+ if (commit)
+ g_variant_unref (commit);
+ if (now)
+ g_date_time_unref (now);
g_free (abspath);
return ret;
}
@@ -732,7 +1209,7 @@ hacktree_repo_import_commit (HacktreeRepo *self,
}
static gboolean
-iter_object_dir (HacktreeRepo *repo,
+iter_object_dir (HacktreeRepo *self,
GFile *dir,
HacktreeRepoObjectIter callback,
gpointer user_data,
@@ -764,7 +1241,7 @@ iter_object_dir (HacktreeRepo *repo,
if (strlen (name) == 62 && type != G_FILE_TYPE_DIRECTORY)
{
char *path = g_build_filename (dirpath, name, NULL);
- callback (repo, path, file_info, user_data);
+ callback (self, path, file_info, user_data);
g_free (path);
}
diff --git a/src/libhacktree/hacktree-repo.h b/src/libhacktree/hacktree-repo.h
index 0c97422..0186e0c 100644
--- a/src/libhacktree/hacktree-repo.h
+++ b/src/libhacktree/hacktree-repo.h
@@ -59,13 +59,15 @@ gboolean hacktree_repo_link_file (HacktreeRepo *repo,
gboolean force,
GError **error);
-gboolean hacktree_repo_commit_files (HacktreeRepo *repo,
- const char *subject,
- const char *body,
- GVariant *metadata,
- const char *base,
- GPtrArray *files,
- GError **error);
+gboolean hacktree_repo_commit (HacktreeRepo *repo,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ const char *base,
+ GPtrArray *modified_files,
+ GPtrArray *removed_files,
+ GChecksum **out_commit,
+ GError **error);
gboolean hacktree_repo_import_tree (HacktreeRepo *repo,
GVariant *tree_variant,
diff --git a/src/libhtutil/ht-unix-utils.c b/src/libhtutil/ht-unix-utils.c
index 10c38df..d51753a 100644
--- a/src/libhtutil/ht-unix-utils.c
+++ b/src/libhtutil/ht-unix-utils.c
@@ -93,6 +93,60 @@ ht_util_filename_has_dotdot (const char *path)
return last == '\0' || last == '/';
}
+GPtrArray *
+ht_util_path_split (const char *path)
+{
+ GPtrArray *ret = NULL;
+ const char *p;
+ const char *slash;
+
+ g_return_if_fail (path[0] != '/');
+
+ ret = g_ptr_array_new ();
+ g_ptr_array_set_free_func (ret, g_free);
+
+ p = path;
+ do {
+ slash = strchr (p, '/');
+ if (!slash)
+ {
+ ret = g_ptr_array_add (ret, g_strdup (p));
+ p = NULL;
+ }
+ else
+ {
+ ret = g_ptr_array_add (ret, g_strndup (p, slash - p));
+ p += 1;
+ }
+ } while (p);
+
+ return ret;
+}
+
+char *
+ht_util_path_join_n (const char *base, GPtrArray *components, int n)
+{
+ int max = MAX(n+1, components->len);
+ GPtrArray *subcomponents;
+ char *path;
+
+ subcomponents = g_ptr_array_new ();
+
+ if (base != NULL)
+ g_ptr_array_add (subcomponents, base);
+
+ for (i = 0; i < max; i++)
+ {
+ g_ptr_array_add (subcomponents, components->pdata[i]);
+ }
+ g_ptr_array_add (subcomponents, NULL);
+
+ path = g_build_filenamev (subcomponents->pdata);
+ g_ptr_array_free (subcomponents);
+
+ return path;
+}
+
void
ht_util_set_error_from_errno (GError **error,
gint saved_errno)
diff --git a/src/libhtutil/ht-unix-utils.h b/src/libhtutil/ht-unix-utils.h
index 1c69b09..1527ce7 100644
--- a/src/libhtutil/ht-unix-utils.h
+++ b/src/libhtutil/ht-unix-utils.h
@@ -38,6 +38,10 @@ gboolean ht_util_filename_has_dotdot (const char *path);
GPtrArray *ht_util_sort_filenames_by_component_length (GPtrArray *files);
+GPtrArray* ht_util_path_split (const char *path);
+
+char *ht_util_path_join_n (const char *base, GPtrArray *components, int n);
+
int ht_util_count_filename_components (const char *path);
int ht_util_open_file_read (const char *path, GError **error);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]