Re: [PATCH v2 07/12] bundle: safely handle --objects option

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Mar 07 2022, Derrick Stolee via GitGitGadget wrote:

> From: Derrick Stolee <derrickstolee@xxxxxxxxxx>
>
> Since 'git bundle' uses setup_revisions() to specify the object walk,
> some options do not make sense to include during the pack-objects child
> process. Further, these options are used for a call to
> traverse_commit_list() which would then require a callback which is
> currently NULL.
>
> By populating the callback we prevent a segfault in the case of adding
> the --objects flag. This is really a redundant statement because the
> command is constructing a pack-file containing all objects in the
> discovered commit range.
>
> Adding --objects to a 'git bundle' command might cause a slower command,
> but at least it will not have a hard failure when the user supplies this
> option. We can also disable walking trees and blobs in advance of this
> walk.
>
> Signed-off-by: Derrick Stolee <derrickstolee@xxxxxxxxxx>
> ---
>  bundle.c               | 10 +++++++++-
>  t/t6020-bundle-misc.sh | 12 ++++++++++++
>  2 files changed, 21 insertions(+), 1 deletion(-)
>
> diff --git a/bundle.c b/bundle.c
> index a0bb687b0f4..dc56db9a50a 100644
> --- a/bundle.c
> +++ b/bundle.c
> @@ -451,6 +451,12 @@ struct bundle_prerequisites_info {
>  	int fd;
>  };
>  
> +
> +static void ignore_object(struct object *obj, const char *v, void *data)
> +{
> +	/* Do nothing. */
> +}
> +
>  static void write_bundle_prerequisites(struct commit *commit, void *data)
>  {
>  	struct bundle_prerequisites_info *bpi = data;
> @@ -544,7 +550,9 @@ int create_bundle(struct repository *r, const char *path,
>  		die("revision walk setup failed");
>  	bpi.fd = bundle_fd;
>  	bpi.pending = &revs_copy.pending;
> -	traverse_commit_list(&revs, write_bundle_prerequisites, NULL, &bpi);
> +
> +	revs.blob_objects = revs.tree_objects = 0;
> +	traverse_commit_list(&revs, write_bundle_prerequisites, ignore_object, &bpi);
>  	object_array_remove_duplicates(&revs_copy.pending);
>  
>  	/* write bundle refs */
> diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh
> index b13e8a52a93..6522401617d 100755
> --- a/t/t6020-bundle-misc.sh
> +++ b/t/t6020-bundle-misc.sh
> @@ -475,4 +475,16 @@ test_expect_success 'clone from bundle' '
>  	test_cmp expect actual
>  '
>  
> +test_expect_success 'unfiltered bundle with --objects' '
> +	git bundle create all-objects.bdl \
> +		--all --objects &&
> +	git bundle create all.bdl \
> +		--all &&
> +
> +	# Compare the headers of these files.
> +	head -11 all.bdl >expect &&
> +	head -11 all-objects.bdl >actual &&
> +	test_cmp expect actual
> +'
> +
>  test_done

Re this comment on v1: https://lore.kernel.org/git/220307.86fsntzsda.gmgdl@xxxxxxxxxxxxxxxxxxx/

This series also passes your tests with this on top:
	
	diff --git a/bundle.c b/bundle.c
	index 3846108f7a6..1f022f53336 100644
	--- a/bundle.c
	+++ b/bundle.c
	@@ -468,11 +468,6 @@ struct bundle_prerequisites_info {
	 };
	 
	 
	-static void ignore_object(struct object *obj, const char *v, void *data)
	-{
	-	/* Do nothing. */
	-}
	-
	 static void write_bundle_prerequisites(struct commit *commit, void *data)
	 {
	 	struct bundle_prerequisites_info *bpi = data;
	@@ -598,7 +593,7 @@ int create_bundle(struct repository *r, const char *path,
	 	 */
	 	revs.filter = NULL;
	 	revs.blob_objects = revs.tree_objects = 0;
	-	traverse_commit_list(&revs, write_bundle_prerequisites, ignore_object, &bpi);
	+	traverse_commit_list(&revs, write_bundle_prerequisites, NULL, &bpi);
	 	object_array_remove_duplicates(&revs_copy.pending);
	 
	 	/* write bundle refs */
	diff --git a/list-objects.c b/list-objects.c
	index 9422625b39e..d44a1db2262 100644
	--- a/list-objects.c
	+++ b/list-objects.c
	@@ -227,7 +227,7 @@ static void process_tag(struct traversal_context *ctx,
	 					       ctx->filter);
	 	if (r & LOFR_MARK_SEEN)
	 		tag->object.flags |= SEEN;
	-	if (r & LOFR_DO_SHOW)
	+	if (r & LOFR_DO_SHOW && ctx->show_object)
	 		ctx->show_object(&tag->object, name, ctx->show_data);
	 }
	 

Aside from whether that's a good idea, doesn't that at least point to
missing test coverage here, see traverse_non_commits() and other paths
in list-objects.c that'll call ctx->show_object().

I think an actually sensible patch for this is the below, i.e. the API
is conflating "do show" with "should we show AND we have a callback?":
	
	diff --git a/bundle.c b/bundle.c
	index 3846108f7a6..1f022f53336 100644
	--- a/bundle.c
	+++ b/bundle.c
	@@ -468,11 +468,6 @@ struct bundle_prerequisites_info {
	 };
	 
	 
	-static void ignore_object(struct object *obj, const char *v, void *data)
	-{
	-	/* Do nothing. */
	-}
	-
	 static void write_bundle_prerequisites(struct commit *commit, void *data)
	 {
	 	struct bundle_prerequisites_info *bpi = data;
	@@ -598,7 +593,7 @@ int create_bundle(struct repository *r, const char *path,
	 	 */
	 	revs.filter = NULL;
	 	revs.blob_objects = revs.tree_objects = 0;
	-	traverse_commit_list(&revs, write_bundle_prerequisites, ignore_object, &bpi);
	+	traverse_commit_list(&revs, write_bundle_prerequisites, NULL, &bpi);
	 	object_array_remove_duplicates(&revs_copy.pending);
	 
	 	/* write bundle refs */
	diff --git a/list-objects.c b/list-objects.c
	index 9422625b39e..1725cb252a9 100644
	--- a/list-objects.c
	+++ b/list-objects.c
	@@ -21,6 +21,22 @@ struct traversal_context {
	 	struct filter *filter;
	 };
	 
	+static void show_commit(struct traversal_context *ctx, struct commit *commit,
	+			void *data)
	+{
	+	if (!ctx->show_commit)
	+		return;
	+	ctx->show_commit(commit, data);
	+}
	+
	+static void show_object(struct traversal_context *ctx, struct object *object,
	+			const char *path, void *data)
	+{
	+	if (!ctx->show_object)
	+		return;
	+	ctx->show_object(object, path, data);
	+}
	+
	 static void process_blob(struct traversal_context *ctx,
	 			 struct blob *blob,
	 			 struct strbuf *path,
	@@ -60,7 +76,7 @@ static void process_blob(struct traversal_context *ctx,
	 	if (r & LOFR_MARK_SEEN)
	 		obj->flags |= SEEN;
	 	if (r & LOFR_DO_SHOW)
	-		ctx->show_object(obj, path->buf, ctx->show_data);
	+		show_object(ctx, obj, path->buf, ctx->show_data);
	 	strbuf_setlen(path, pathlen);
	 }
	 
	@@ -194,7 +210,7 @@ static void process_tree(struct traversal_context *ctx,
	 	if (r & LOFR_MARK_SEEN)
	 		obj->flags |= SEEN;
	 	if (r & LOFR_DO_SHOW)
	-		ctx->show_object(obj, base->buf, ctx->show_data);
	+		show_object(ctx, obj, base->buf, ctx->show_data);
	 	if (base->len)
	 		strbuf_addch(base, '/');
	 
	@@ -210,7 +226,7 @@ static void process_tree(struct traversal_context *ctx,
	 	if (r & LOFR_MARK_SEEN)
	 		obj->flags |= SEEN;
	 	if (r & LOFR_DO_SHOW)
	-		ctx->show_object(obj, base->buf, ctx->show_data);
	+		show_object(ctx, obj, base->buf, ctx->show_data);
	 
	 	strbuf_setlen(base, baselen);
	 	free_tree_buffer(tree);
	@@ -228,7 +244,7 @@ static void process_tag(struct traversal_context *ctx,
	 	if (r & LOFR_MARK_SEEN)
	 		tag->object.flags |= SEEN;
	 	if (r & LOFR_DO_SHOW)
	-		ctx->show_object(&tag->object, name, ctx->show_data);
	+		show_object(ctx, &tag->object, name, ctx->show_data);
	 }
	 
	 static void mark_edge_parents_uninteresting(struct commit *commit,
	@@ -402,7 +418,7 @@ static void do_traverse(struct traversal_context *ctx)
	 		if (r & LOFR_MARK_SEEN)
	 			commit->object.flags |= SEEN;
	 		if (r & LOFR_DO_SHOW)
	-			ctx->show_commit(commit, ctx->show_data);
	+			show_commit(ctx, commit, ctx->show_data);
	 
	 		if (ctx->revs->tree_blobs_in_commit_order)
	 			/*

I think that'll do what you want, and also seems to set us up for safer
API use going forward, i.e. we have a couple of NULL-passing callers
already.



[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux