From: Junio C Hamano <hidden> Date: 2016-06-15 22:45:08
"Shawn O. Pearce" [off-list ref] writes:
... It seems pretty harmless to allow an object we
aren't going to transmit but that we want to use as a delta base
in a thin pack to be missing. At worst we just get a little bit
more data transfer.
If the check is only about a thin delta base that is not going to be
transmit, I'd agree. But I do not see how you are distinguishing that
case and the case where an object you are actually sending is missing (in
which case we would want to error out, wouldn't we?)
From: Shawn O. Pearce <hidden> Date: 2016-06-15 22:45:08
Junio C Hamano [off-list ref] wrote:
"Shawn O. Pearce" [off-list ref] writes:
quoted
... It seems pretty harmless to allow an object we
aren't going to transmit but that we want to use as a delta base
in a thin pack to be missing. At worst we just get a little bit
more data transfer.
If the check is only about a thin delta base that is not going to be
transmit, I'd agree. But I do not see how you are distinguishing that
case and the case where an object you are actually sending is missing (in
which case we would want to error out, wouldn't we?)
Arrgh. Good catch. My patch is flawed in that it does not correctly
fail if we really needed the missing object in this output pack.
I don't think that would be hard to fix. I'll respin something
shortly.
--
Shawn.
From: Shawn O. Pearce <hidden> Date: 2016-06-15 22:45:08
Junio C Hamano [off-list ref] wrote:
quoted
If the check is only about a thin delta base that is not going to be
transmit, I'd agree. But I do not see how you are distinguishing that
case and the case where an object you are actually sending is missing (in
which case we would want to error out, wouldn't we?)
Turns out to be pretty simple I think. We just delay the
error handling for ->type < 0 until write_object(). If we
get this far we know we wanted to include the object but
we really don't have it. Up until that point its fine
for us to get objects which are missing, we'll just wind
up with a suboptimal pack.
We also don't even need to report the error from sha1_object_info
as it already issues an error message (see sha1_loose_object_info).
--8<--
pack-objects: Allow missing base objects when creating thin packs
If we are building a thin pack and one of the base objects we would
consider for deltification is missing its OK, the other side already
has that base object. We may be able to get a delta from another
object, or we can simply send the new object whole (no delta).
This allows a shallow clone which may have only commits and trees
(but only partial blobs) to generate a pack for a fetch client,
so the shallow clone only needs to contain objects that are not
in the common base.
Signed-off-by: Shawn O. Pearce <redacted>
---
builtin-pack-objects.c | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
@@ -243,6 +243,8 @@ static unsigned long write_object(struct sha1file *f,crc32_begin(f);type=entry->type;+if(type<0)+die("unable to read object %s",sha1_to_hex(entry->idx.sha1));/* write limit if limited packsize and not first object */limit=pack_size_limit&&nr_written?
@@ -1096,9 +1098,6 @@ static void check_object(struct object_entry *entry)}entry->type=sha1_object_info(entry->idx.sha1,&entry->size);-if(entry->type<0)-die("unable to get type of object %s",-sha1_to_hex(entry->idx.sha1));}staticintpack_offset_sort(constvoid*_a,constvoid*_b)
From: Nicolas Pitre <hidden> Date: 2016-06-15 22:45:08
On Mon, 11 Aug 2008, Shawn O. Pearce wrote:
quoted
Junio C Hamano [off-list ref] wrote:
quoted
If the check is only about a thin delta base that is not going to be
transmit, I'd agree. But I do not see how you are distinguishing that
case and the case where an object you are actually sending is missing (in
which case we would want to error out, wouldn't we?)
Turns out to be pretty simple I think. We just delay the
error handling for ->type < 0 until write_object(). If we
get this far we know we wanted to include the object but
we really don't have it. Up until that point its fine
for us to get objects which are missing, we'll just wind
up with a suboptimal pack.
If you're going to die anyway due to an object with unknown type, better
do so _before_ going through the delta search phase and leaving a
partial pack behind. IOW, the type check can be performed in
prepare_pack() instead of write_object() like:
@@ -1722,8 +1733,12 @@ static void prepare_pack(int window, int depth)if(entry->no_try_delta)continue;-if(!entry->preferred_base)+if(!entry->preferred_base){nr_deltas++;+if(entry->type<0)+die("unable to get type of object %s",+sha1_to_hex(entry->idx.sha1));+}delta_list[n++]=entry;}
Also a comment in check_object() mentioning where the return value of
sha1_object_info() is verified would be in order.
And I also agree with Junio about a test script for this so the usage is
fully demonstrated, and to ensure it keeps on working as intended
(most people will simply never exercise this otherwise).
Nicolas
From: Shawn O. Pearce <hidden> Date: 2016-06-15 22:45:08
If we are building a thin pack and one of the base objects we would
consider for deltification is missing its OK, the other side already
has that base object. We may be able to get a delta from another
object, or we can simply send the new object whole (no delta).
This change allows a shallow clone to store only the objects which
are unique to it, as well as the boundary commit and its trees, but
avoids storing the boundary blobs. This special form of a shallow
clone is able to represent just the difference between two trees.
Pack objects change suggested by Nicolas Pitre.
Signed-off-by: Shawn O. Pearce <redacted>
---
Nicolas Pitre [off-list ref] wrote:
> On Mon, 11 Aug 2008, Shawn O. Pearce wrote:
> > > Junio C Hamano [off-list ref] wrote:
> > > > If the check is only about a thin delta base that is not going to be
> > > > transmit, I'd agree.
>
> If you're going to die anyway due to an object with unknown type, better
> do so _before_ going through the delta search phase and leaving a
> partial pack behind.
Now with a test! :-)
builtin-pack-objects.c | 15 ++++++--
t/t5306-pack-nobase.sh | 80 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 91 insertions(+), 4 deletions(-)
create mode 100755 t/t5306-pack-nobase.sh
@@ -1096,9 +1096,12 @@ static void check_object(struct object_entry *entry)}entry->type=sha1_object_info(entry->idx.sha1,&entry->size);-if(entry->type<0)-die("unable to get type of object %s",-sha1_to_hex(entry->idx.sha1));+/*+*Theerrorconditionischeckedinprepare_pack().Thisis+*topermitamissingpreferredbaseobjecttobeignored+*asapreferredbase.Doingsocanresultinalarger+*packfile,butthetransferwillstilltakeplace.+*/}staticintpack_offset_sort(constvoid*_a,constvoid*_b)
@@ -1722,8 +1725,12 @@ static void prepare_pack(int window, int depth)if(entry->no_try_delta)continue;-if(!entry->preferred_base)+if(!entry->preferred_base){nr_deltas++;+if(entry->type<0)+die("unable to get type of object %s",+sha1_to_hex(entry->idx.sha1));+}delta_list[n++]=entry;}
@@ -0,0 +1,80 @@+#!/bin/sh+#+# Copyright (c) 2008 Google Inc.+#++test_description='git-pack-objectwithmissingbase++'+../test-lib.sh++# Create A-B chain+#+test_expect_success\+'setup base'\+'forainabcdefghi;doecho$a>>text;done&&+echoside>side&&+gitupdate-index--addtextside&&+A=$(echoA|gitcommit-tree$(gitwrite-tree))&&++echom>>text&&+gitupdate-indextext&&+B=$(echoB|gitcommit-tree$(gitwrite-tree)-p$A)&&+gitupdate-refHEAD$B+'++# Create repository with C whose parent is B.+# Repository contains C, C^{tree}, C:text, B, B^{tree}.+# Repository is missing B:text (best delta base for C:text).+# Repository is missing A (parent of B).+# Repository is missing A:side.+#+test_expect_success\+'setup patch_clone'\+'base_objects=$(pwd)/.git/objects&&+(mkdirpatch_clone&&+cdpatch_clone&&+gitinit&&+echo"$base_objects">.git/objects/info/alternates&&+echoq>>text&&+gitread-tree$B&&+gitupdate-indextext&&+gitupdate-refHEAD$(echoC|gitcommit-tree$(gitwrite-tree)-p$B)&&+rm.git/objects/info/alternates&&++git--git-dir=../.gitcat-filecommit$B|+githash-object-tcommit-w--stdin&&++git--git-dir=../.gitcat-filetree"$B^{tree}"|+githash-object-ttree-w--stdin+)&&+C=$(git--git-dir=patch_clone/.gitrev-parseHEAD)+'++# Clone patch_clone indirectly by cloning base and fetching.+#+test_expect_success\+'indirectly clone patch_clone'\+'(mkdiruser_clone&&+cduser_clone&&+gitinit&&+gitpull../.git&&+test$(gitrev-parseHEAD)=$B++gitpull../patch_clone/.git&&+test$(gitrev-parseHEAD)=$C+)+'++# Cloning the patch_clone directly should fail.+#+test_expect_success\+'clone of patch_clone is incomplete'\+'(mkdiruser_direct&&+cduser_direct&&+gitinit&&+test_must_failgitfetch../patch_clone/.git+)+'++test_done
From: Shawn O. Pearce <hidden> Date: 2016-06-15 22:45:08
Nicolas Pitre [off-list ref] wrote:
On Tue, 12 Aug 2008, Shawn O. Pearce wrote:
quoted
+# Clone patch_clone indirectly by cloning base and fetching.
+#
+test_expect_success \
+ 'indirectly clone patch_clone' \
+ '(mkdir user_clone &&
+ cd user_clone &&
+ git init &&
+ git pull ../.git &&
+ test $(git rev-parse HEAD) = $B
+
+ git pull ../patch_clone/.git &&
+ test $(git rev-parse HEAD) = $C
+ )
+ '
What if the first test command fails? Won't its result be ignored?
Isn't the exit status of the subshell the exit status of the last
command in the subshell?
I just changed the test line to compare to "x$C" instead of $C
and it correctly detected the error condition:
$ git diff
From: Shawn O. Pearce <hidden> Date: 2016-06-15 22:45:08
If we are building a thin pack and one of the base objects we would
consider for deltification is missing its OK, the other side already
has that base object. We may be able to get a delta from another
object, or we can simply send the new object whole (no delta).
This change allows a shallow clone to store only the objects which
are unique to it, as well as the boundary commit and its trees, but
avoids storing the boundary blobs. This special form of a shallow
clone is able to represent just the difference between two trees.
Pack objects change suggested by Nicolas Pitre.
Signed-off-by: Shawn O. Pearce <redacted>
---
Nicolas Pitre [off-list ref] wrote:
> On Tue, 12 Aug 2008, Shawn O. Pearce wrote:
> I'm not talking about the last command but the "test $(git rev-parse
> HEAD) = $B" line.
Oh, right. Good catch.
builtin-pack-objects.c | 15 ++++++--
t/t5306-pack-nobase.sh | 80 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 91 insertions(+), 4 deletions(-)
create mode 100755 t/t5306-pack-nobase.sh
@@ -1096,9 +1096,12 @@ static void check_object(struct object_entry *entry)}entry->type=sha1_object_info(entry->idx.sha1,&entry->size);-if(entry->type<0)-die("unable to get type of object %s",-sha1_to_hex(entry->idx.sha1));+/*+*Theerrorconditionischeckedinprepare_pack().Thisis+*topermitamissingpreferredbaseobjecttobeignored+*asapreferredbase.Doingsocanresultinalarger+*packfile,butthetransferwillstilltakeplace.+*/}staticintpack_offset_sort(constvoid*_a,constvoid*_b)
@@ -1722,8 +1725,12 @@ static void prepare_pack(int window, int depth)if(entry->no_try_delta)continue;-if(!entry->preferred_base)+if(!entry->preferred_base){nr_deltas++;+if(entry->type<0)+die("unable to get type of object %s",+sha1_to_hex(entry->idx.sha1));+}delta_list[n++]=entry;}
@@ -0,0 +1,80 @@+#!/bin/sh+#+# Copyright (c) 2008 Google Inc.+#++test_description='git-pack-objectwithmissingbase++'+../test-lib.sh++# Create A-B chain+#+test_expect_success\+'setup base'\+'forainabcdefghi;doecho$a>>text;done&&+echoside>side&&+gitupdate-index--addtextside&&+A=$(echoA|gitcommit-tree$(gitwrite-tree))&&++echom>>text&&+gitupdate-indextext&&+B=$(echoB|gitcommit-tree$(gitwrite-tree)-p$A)&&+gitupdate-refHEAD$B+'++# Create repository with C whose parent is B.+# Repository contains C, C^{tree}, C:text, B, B^{tree}.+# Repository is missing B:text (best delta base for C:text).+# Repository is missing A (parent of B).+# Repository is missing A:side.+#+test_expect_success\+'setup patch_clone'\+'base_objects=$(pwd)/.git/objects&&+(mkdirpatch_clone&&+cdpatch_clone&&+gitinit&&+echo"$base_objects">.git/objects/info/alternates&&+echoq>>text&&+gitread-tree$B&&+gitupdate-indextext&&+gitupdate-refHEAD$(echoC|gitcommit-tree$(gitwrite-tree)-p$B)&&+rm.git/objects/info/alternates&&++git--git-dir=../.gitcat-filecommit$B|+githash-object-tcommit-w--stdin&&++git--git-dir=../.gitcat-filetree"$B^{tree}"|+githash-object-ttree-w--stdin+)&&+C=$(git--git-dir=patch_clone/.gitrev-parseHEAD)+'++# Clone patch_clone indirectly by cloning base and fetching.+#+test_expect_success\+'indirectly clone patch_clone'\+'(mkdiruser_clone&&+cduser_clone&&+gitinit&&+gitpull../.git&&+test$(gitrev-parseHEAD)=$B&&++gitpull../patch_clone/.git&&+test$(gitrev-parseHEAD)=$C+)+'++# Cloning the patch_clone directly should fail.+#+test_expect_success\+'clone of patch_clone is incomplete'\+'(mkdiruser_direct&&+cduser_direct&&+gitinit&&+test_must_failgitfetch../patch_clone/.git+)+'++test_done
From: Nicolas Pitre <hidden> Date: 2016-06-15 22:45:08
On Tue, 12 Aug 2008, Shawn O. Pearce wrote:
If we are building a thin pack and one of the base objects we would
consider for deltification is missing its OK, the other side already
has that base object. We may be able to get a delta from another
object, or we can simply send the new object whole (no delta).
This change allows a shallow clone to store only the objects which
are unique to it, as well as the boundary commit and its trees, but
avoids storing the boundary blobs. This special form of a shallow
clone is able to represent just the difference between two trees.
Pack objects change suggested by Nicolas Pitre.
Signed-off-by: Shawn O. Pearce <redacted>
---
Nicolas Pitre [off-list ref] wrote:
> On Tue, 12 Aug 2008, Shawn O. Pearce wrote:
> I'm not talking about the last command but the "test $(git rev-parse
> HEAD) = $B" line.
Oh, right. Good catch.
Acked-by: Nicolas Pitre <redacted>
quoted hunk
+++ b/t/t5306-pack-nobase.sh
@@ -0,0 +1,80 @@+#!/bin/sh+#+# Copyright (c) 2008 Google Inc.