From: Sun Chao <redacted>
When calling `git pack-redundant --all`, if there are too many local
packs and too many redundant objects within them, the too deep iteration
of `get_permutations` will exhaust all the resources, and the process of
`git pack-redundant` will be killed.
The following script could create a repository with too many redundant
packs, and running `git pack-redundant --all` in the `test.git` repo
will die soon.
#!/bin/sh
repo="$(pwd)/test.git"
work="$(pwd)/test"
i=1
max=199
if test -d "$repo" || test -d "$work"; then
echo >&2 "ERROR: '$repo' or '$work' already exist"
exit 1
fi
git init -q --bare "$repo"
git --git-dir="$repo" config gc.auto 0
git --git-dir="$repo" config transfer.unpackLimit 0
git clone -q "$repo" "$work" 2>/dev/null
while :; do
cd "$work"
echo "loop $i: $(date +%s)" >$i
git add $i
git commit -q -sm "loop $i"
git push -q origin HEAD:master
printf "\rCreate pack %4d/%d\t" $i $max
if test $i -ge $max; then break; fi
cd "$repo"
git repack -q
if test $(($i % 2)) -eq 0; then
git repack -aq
pack=$(ls -t $repo/objects/pack/*.pack | head -1)
touch "${pack%.pack}.keep"
fi
i=$((i+1))
done
printf "\ndone\n"
To get the `min` unique pack list, we can replace the iteration in
`minimize` function with a new algorithm, and this could solve this
issue:
1. Get the unique and non_uniqe packs, add the unique packs to the
`min` list.
2. Remove the objects of unique packs from non_unique packs, then each
object left in the non_unique packs will have at least two copies.
3. Sort the non_unique packs by the objects' size, more objects first,
and add the first non_unique pack to `min` list.
4. Drop the duplicated objects from other packs in the ordered
non_unique pack list, and repeat step 3.
Original PR and discussions: https://github.com/jiangxin/git/pull/25
Signed-off-by: Sun Chao <redacted>
Signed-off-by: Jiang Xin <redacted>
---
builtin/pack-redundant.c | 116 ++++++++++++++++++++++++---------------
1 file changed, 73 insertions(+), 43 deletions(-)
@@ -421,14 +421,52 @@ static inline off_t pack_set_bytecount(struct pack_list *pl)returnret;}-staticvoidminimize(structpack_list**min)+staticintcmp_pack_list_reverse(constvoid*a,constvoid*b){-structpack_list*pl,*unique=NULL,-*non_unique=NULL,*min_perm=NULL;-structpll*perm,*perm_all,*perm_ok=NULL,*new_perm;-structllist*missing;-off_tmin_perm_size=0,perm_size;-intn;+structpack_list*pl_a=*((structpack_list**)a);+structpack_list*pl_b=*((structpack_list**)b);+size_tsz_a=pl_a->all_objects->size;+size_tsz_b=pl_b->all_objects->size;++if(sz_a==sz_b)+return0;+elseif(sz_a<sz_b)+return1;+else+return-1;+}++/* Sort pack_list, greater size of all_objects first */+staticvoidsort_pack_list(structpack_list**pl)+{+structpack_list**ary,*p;+inti;+size_tn=pack_list_size(*pl);++if(n<2)+return;++/* prepare an array of packed_list for easier sorting */+ary=xcalloc(n,sizeof(structpack_list*));+for(n=0,p=*pl;p;p=p->next)+ary[n++]=p;++QSORT(ary,n,cmp_pack_list_reverse);++/* link them back again */+for(i=0;i<n-1;i++)+ary[i]->next=ary[i+1];+ary[n-1]->next=NULL;+*pl=ary[0];++free(ary);+}+++staticvoidminimize(structpack_list**min,structllist*ignore)+{+structpack_list*pl,*unique=NULL,*non_unique=NULL;+structllist*missing,*unique_pack_objects;pl=local_packs;while(pl){
@@ -446,49 +484,41 @@ static void minimize(struct pack_list **min)pl=pl->next;}+*min=unique;+/* return if there are no objects missing from the unique set */if(missing->size==0){-*min=unique;free(missing);return;}-/* find the permutations which contain all missing objects */-for(n=1;n<=pack_list_size(non_unique)&&!perm_ok;n++){-perm_all=perm=get_permutations(non_unique,n);-while(perm){-if(is_superset(perm->pl,missing)){-new_perm=xmalloc(sizeof(structpll));-memcpy(new_perm,perm,sizeof(structpll));-new_perm->next=perm_ok;-perm_ok=new_perm;-}-perm=perm->next;-}-if(perm_ok)-break;-pll_free(perm_all);-}-if(perm_ok==NULL)-die("Internal error: No complete sets found!");--/* find the permutation with the smallest size */-perm=perm_ok;-while(perm){-perm_size=pack_set_bytecount(perm->pl);-if(!min_perm_size||min_perm_size>perm_size){-min_perm_size=perm_size;-min_perm=perm->pl;-}-perm=perm->next;-}-*min=min_perm;-/* add the unique packs to the list */-pl=unique;+unique_pack_objects=llist_copy(all_objects);+llist_sorted_difference_inplace(unique_pack_objects,missing);++/* remove all the ignored objects and unique pack objects from the non_unique packs */+pl=non_unique;while(pl){-pack_list_insert(min,pl);+llist_sorted_difference_inplace(pl->all_objects,ignore);+llist_sorted_difference_inplace(pl->all_objects,unique_pack_objects);pl=pl->next;}++while((pl=non_unique)){+/* sort the non_unique packs, greater size of all_objects first */+sort_pack_list(&non_unique);+if(non_unique->all_objects->size==0)+break;++pack_list_insert(min,non_unique);++while((pl=pl->next)){+if(pl->all_objects->size==0)+break;+llist_sorted_difference_inplace(pl->all_objects,non_unique->all_objects);+}++non_unique=non_unique->next;+}}staticvoidload_all_objects(void)
@@ -667,7 +697,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix)pl=pl->next;}-minimize(&min);+minimize(&min,ignore);if(verbose){fprintf(stderr,"There are %lu packs available in alt-odbs.\n",
@@ -285,78 +276,6 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)}}-staticvoidpll_free(structpll*l)-{-structpll*old;-structpack_list*opl;--while(l){-old=l;-while(l->pl){-opl=l->pl;-l->pl=opl->next;-free(opl);-}-l=l->next;-free(old);-}-}--/* all the permutations have to be free()d at the same time,-*sincetheyrefertoeachother-*/-staticstructpll*get_permutations(structpack_list*list,intn)-{-structpll*subset,*ret=NULL,*new_pll=NULL;--if(list==NULL||pack_list_size(list)<n||n==0)-returnNULL;--if(n==1){-while(list){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=NULL;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-list=list->next;-}-returnret;-}--while(list->next){-subset=get_permutations(list->next,n-1);-while(subset){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=subset->pl;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-subset=subset->next;-}-list=list->next;-}-returnret;-}--staticintis_superset(structpack_list*pl,structllist*list)-{-structllist*diff;--diff=llist_copy(list);--while(pl){-llist_sorted_difference_inplace(diff,pl->all_objects);-if(diff->size==0){/* we're done */-llist_free(diff);-return1;-}-pl=pl->next;-}-llist_free(diff);-return0;-}-staticsize_tsizeof_union(structpacked_git*p1,structpacked_git*p2){size_tret=0;
Sun Chao is my former colleague at Huawei. He finds a bug of git-pack-redundant.
When I was in Huawei, I develop a program to manage fork tree of repositories,
using alternate repo for forks to save disk spaces.
Sun Chao finds if there are too many packs and many of them overlap each
other, running `git pack-redundant --all` will exhaust all memories and the
process will be killed by kernel.
There is a script in commit log of commit 2/3, which can be used to create a
repository with lots of redundant packs. Running `git pack-redundant
--all` in it can reproduce this issue.
Updates of reroll v2:
* Add test cases in t5322.
* Fix a bug in patch 2/3.
--
Jiang Xin (1):
t5322: test cases for git-pack-redundant
Sun Chao (2):
pack-redundant: new algorithm to find min packs
pack-redundant: remove unused functions
builtin/pack-redundant.c | 181 ++++++++++++++------------------------
t/t5322-pack-redundant.sh | 69 +++++++++++++++
2 files changed, 137 insertions(+), 113 deletions(-)
create mode 100755 t/t5322-pack-redundant.sh
--
2.20.0.3.gc45e608566
From: Sun Chao <redacted>
When calling `git pack-redundant --all`, if there are too many local
packs and too many redundant objects within them, the too deep iteration
of `get_permutations` will exhaust all the resources, and the process of
`git pack-redundant` will be killed.
The following script could create a repository with too many redundant
packs, and running `git pack-redundant --all` in the `test.git` repo
will die soon.
#!/bin/sh
repo="$(pwd)/test.git"
work="$(pwd)/test"
i=1
max=199
if test -d "$repo" || test -d "$work"; then
echo >&2 "ERROR: '$repo' or '$work' already exist"
exit 1
fi
git init -q --bare "$repo"
git --git-dir="$repo" config gc.auto 0
git --git-dir="$repo" config transfer.unpackLimit 0
git clone -q "$repo" "$work" 2>/dev/null
while :; do
cd "$work"
echo "loop $i: $(date +%s)" >$i
git add $i
git commit -q -sm "loop $i"
git push -q origin HEAD:master
printf "\rCreate pack %4d/%d\t" $i $max
if test $i -ge $max; then break; fi
cd "$repo"
git repack -q
if test $(($i % 2)) -eq 0; then
git repack -aq
pack=$(ls -t $repo/objects/pack/*.pack | head -1)
touch "${pack%.pack}.keep"
fi
i=$((i+1))
done
printf "\ndone\n"
To get the `min` unique pack list, we can replace the iteration in
`minimize` function with a new algorithm, and this could solve this
issue:
1. Get the unique and non_uniqe packs, add the unique packs to the
`min` list.
2. Remove the objects of unique packs from non_unique packs, then each
object left in the non_unique packs will have at least two copies.
3. Sort the non_unique packs by the objects' size, more objects first,
and add the first non_unique pack to `min` list.
4. Drop the duplicated objects from other packs in the ordered
non_unique pack list, and repeat step 3.
Original PR and discussions: https://github.com/jiangxin/git/pull/25
Signed-off-by: Sun Chao <redacted>
Signed-off-by: Jiang Xin <redacted>
---
builtin/pack-redundant.c | 109 ++++++++++++++++++++++++---------------
1 file changed, 68 insertions(+), 41 deletions(-)
@@ -421,14 +421,52 @@ static inline off_t pack_set_bytecount(struct pack_list *pl)returnret;}+staticintcmp_pack_list_reverse(constvoid*a,constvoid*b)+{+structpack_list*pl_a=*((structpack_list**)a);+structpack_list*pl_b=*((structpack_list**)b);+size_tsz_a=pl_a->all_objects->size;+size_tsz_b=pl_b->all_objects->size;++if(sz_a==sz_b)+return0;+elseif(sz_a<sz_b)+return1;+else+return-1;+}++/* Sort pack_list, greater size of all_objects first */+staticvoidsort_pack_list(structpack_list**pl)+{+structpack_list**ary,*p;+inti;+size_tn=pack_list_size(*pl);++if(n<2)+return;++/* prepare an array of packed_list for easier sorting */+ary=xcalloc(n,sizeof(structpack_list*));+for(n=0,p=*pl;p;p=p->next)+ary[n++]=p;++QSORT(ary,n,cmp_pack_list_reverse);++/* link them back again */+for(i=0;i<n-1;i++)+ary[i]->next=ary[i+1];+ary[n-1]->next=NULL;+*pl=ary[0];++free(ary);+}++staticvoidminimize(structpack_list**min){-structpack_list*pl,*unique=NULL,-*non_unique=NULL,*min_perm=NULL;-structpll*perm,*perm_all,*perm_ok=NULL,*new_perm;-structllist*missing;-off_tmin_perm_size=0,perm_size;-intn;+structpack_list*pl,*unique=NULL,*non_unique=NULL;+structllist*missing,*unique_pack_objects;pl=local_packs;while(pl){
@@ -446,49 +484,37 @@ static void minimize(struct pack_list **min)pl=pl->next;}+*min=unique;+/* return if there are no objects missing from the unique set */if(missing->size==0){-*min=unique;free(missing);return;}-/* find the permutations which contain all missing objects */-for(n=1;n<=pack_list_size(non_unique)&&!perm_ok;n++){-perm_all=perm=get_permutations(non_unique,n);-while(perm){-if(is_superset(perm->pl,missing)){-new_perm=xmalloc(sizeof(structpll));-memcpy(new_perm,perm,sizeof(structpll));-new_perm->next=perm_ok;-perm_ok=new_perm;-}-perm=perm->next;-}-if(perm_ok)-break;-pll_free(perm_all);-}-if(perm_ok==NULL)-die("Internal error: No complete sets found!");--/* find the permutation with the smallest size */-perm=perm_ok;-while(perm){-perm_size=pack_set_bytecount(perm->pl);-if(!min_perm_size||min_perm_size>perm_size){-min_perm_size=perm_size;-min_perm=perm->pl;-}-perm=perm->next;-}-*min=min_perm;-/* add the unique packs to the list */-pl=unique;+unique_pack_objects=llist_copy(all_objects);+llist_sorted_difference_inplace(unique_pack_objects,missing);++/* remove unique pack objects from the non_unique packs */+pl=non_unique;while(pl){-pack_list_insert(min,pl);+llist_sorted_difference_inplace(pl->all_objects,unique_pack_objects);pl=pl->next;}++while(non_unique){+/* sort the non_unique packs, greater size of all_objects first */+sort_pack_list(&non_unique);+if(non_unique->all_objects->size==0)+break;++pack_list_insert(min,non_unique);++for(pl=non_unique->next;pl&&pl->all_objects->size>0;pl=pl->next)+llist_sorted_difference_inplace(pl->all_objects,non_unique->all_objects);++non_unique=non_unique->next;+}}staticvoidload_all_objects(void)
@@ -285,78 +285,6 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)}}-staticvoidpll_free(structpll*l)-{-structpll*old;-structpack_list*opl;--while(l){-old=l;-while(l->pl){-opl=l->pl;-l->pl=opl->next;-free(opl);-}-l=l->next;-free(old);-}-}--/* all the permutations have to be free()d at the same time,-*sincetheyrefertoeachother-*/-staticstructpll*get_permutations(structpack_list*list,intn)-{-structpll*subset,*ret=NULL,*new_pll=NULL;--if(list==NULL||pack_list_size(list)<n||n==0)-returnNULL;--if(n==1){-while(list){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=NULL;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-list=list->next;-}-returnret;-}--while(list->next){-subset=get_permutations(list->next,n-1);-while(subset){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=subset->pl;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-subset=subset->next;-}-list=list->next;-}-returnret;-}--staticintis_superset(structpack_list*pl,structllist*list)-{-structllist*diff;--diff=llist_copy(list);--while(pl){-llist_sorted_difference_inplace(diff,pl->all_objects);-if(diff->size==0){/* we're done */-llist_free(diff);-return1;-}-pl=pl->next;-}-llist_free(diff);-return0;-}-staticsize_tsizeof_union(structpacked_git*p1,structpacked_git*p2){size_tret=0;
Sun Chao (my former colleague at Huawei) found a bug of
git-pack-redundant. If there are too many packs and many of them overlap
each other, running `git pack-redundant --all` will exhaust all memories
and the process will be killed by kernel.
There is a script in commit log of commit 2/3, which can be used to
create a repository with lots of redundant packs. Running `git
pack-redundant --all` in it can reproduce this issue.
Updates of reroll v3:
* Rename test case file from t5322 to t5323, for I see t5322 exist in
commit 404dead121: "pack-objects: add --sparse option".
Jiang Xin (1):
t5323: test cases for git-pack-redundant
Sun Chao (2):
pack-redundant: new algorithm to find min packs
pack-redundant: remove unused functions
builtin/pack-redundant.c | 181 +++++++++++++++++-----------------------------
t/t5323-pack-redundant.sh | 84 +++++++++++++++++++++
2 files changed, 152 insertions(+), 113 deletions(-)
create mode 100755 t/t5323-pack-redundant.sh
--
2.14.5.agit.2
From: Sun Chao <redacted>
When calling `git pack-redundant --all`, if there are too many local
packs and too many redundant objects within them, the too deep iteration
of `get_permutations` will exhaust all the resources, and the process of
`git pack-redundant` will be killed.
The following script could create a repository with too many redundant
packs, and running `git pack-redundant --all` in the `test.git` repo
will die soon.
#!/bin/sh
repo="$(pwd)/test.git"
work="$(pwd)/test"
i=1
max=199
if test -d "$repo" || test -d "$work"; then
echo >&2 "ERROR: '$repo' or '$work' already exist"
exit 1
fi
git init -q --bare "$repo"
git --git-dir="$repo" config gc.auto 0
git --git-dir="$repo" config transfer.unpackLimit 0
git clone -q "$repo" "$work" 2>/dev/null
while :; do
cd "$work"
echo "loop $i: $(date +%s)" >$i
git add $i
git commit -q -sm "loop $i"
git push -q origin HEAD:master
printf "\rCreate pack %4d/%d\t" $i $max
if test $i -ge $max; then break; fi
cd "$repo"
git repack -q
if test $(($i % 2)) -eq 0; then
git repack -aq
pack=$(ls -t $repo/objects/pack/*.pack | head -1)
touch "${pack%.pack}.keep"
fi
i=$((i+1))
done
printf "\ndone\n"
To get the `min` unique pack list, we can replace the iteration in
`minimize` function with a new algorithm, and this could solve this
issue:
1. Get the unique and non_uniqe packs, add the unique packs to the
`min` list.
2. Remove the objects of unique packs from non_unique packs, then each
object left in the non_unique packs will have at least two copies.
3. Sort the non_unique packs by the objects' size, more objects first,
and add the first non_unique pack to `min` list.
4. Drop the duplicated objects from other packs in the ordered
non_unique pack list, and repeat step 3.
Original PR and discussions: https://github.com/jiangxin/git/pull/25
Signed-off-by: Sun Chao <redacted>
Signed-off-by: Jiang Xin <redacted>
---
builtin/pack-redundant.c | 109 +++++++++++++++++++++++++++++------------------
1 file changed, 68 insertions(+), 41 deletions(-)
@@ -421,14 +421,52 @@ static inline off_t pack_set_bytecount(struct pack_list *pl)returnret;}+staticintcmp_pack_list_reverse(constvoid*a,constvoid*b)+{+structpack_list*pl_a=*((structpack_list**)a);+structpack_list*pl_b=*((structpack_list**)b);+size_tsz_a=pl_a->all_objects->size;+size_tsz_b=pl_b->all_objects->size;++if(sz_a==sz_b)+return0;+elseif(sz_a<sz_b)+return1;+else+return-1;+}++/* Sort pack_list, greater size of all_objects first */+staticvoidsort_pack_list(structpack_list**pl)+{+structpack_list**ary,*p;+inti;+size_tn=pack_list_size(*pl);++if(n<2)+return;++/* prepare an array of packed_list for easier sorting */+ary=xcalloc(n,sizeof(structpack_list*));+for(n=0,p=*pl;p;p=p->next)+ary[n++]=p;++QSORT(ary,n,cmp_pack_list_reverse);++/* link them back again */+for(i=0;i<n-1;i++)+ary[i]->next=ary[i+1];+ary[n-1]->next=NULL;+*pl=ary[0];++free(ary);+}++staticvoidminimize(structpack_list**min){-structpack_list*pl,*unique=NULL,-*non_unique=NULL,*min_perm=NULL;-structpll*perm,*perm_all,*perm_ok=NULL,*new_perm;-structllist*missing;-off_tmin_perm_size=0,perm_size;-intn;+structpack_list*pl,*unique=NULL,*non_unique=NULL;+structllist*missing,*unique_pack_objects;pl=local_packs;while(pl){
@@ -446,49 +484,37 @@ static void minimize(struct pack_list **min)pl=pl->next;}+*min=unique;+/* return if there are no objects missing from the unique set */if(missing->size==0){-*min=unique;free(missing);return;}-/* find the permutations which contain all missing objects */-for(n=1;n<=pack_list_size(non_unique)&&!perm_ok;n++){-perm_all=perm=get_permutations(non_unique,n);-while(perm){-if(is_superset(perm->pl,missing)){-new_perm=xmalloc(sizeof(structpll));-memcpy(new_perm,perm,sizeof(structpll));-new_perm->next=perm_ok;-perm_ok=new_perm;-}-perm=perm->next;-}-if(perm_ok)-break;-pll_free(perm_all);-}-if(perm_ok==NULL)-die("Internal error: No complete sets found!");--/* find the permutation with the smallest size */-perm=perm_ok;-while(perm){-perm_size=pack_set_bytecount(perm->pl);-if(!min_perm_size||min_perm_size>perm_size){-min_perm_size=perm_size;-min_perm=perm->pl;-}-perm=perm->next;-}-*min=min_perm;-/* add the unique packs to the list */-pl=unique;+unique_pack_objects=llist_copy(all_objects);+llist_sorted_difference_inplace(unique_pack_objects,missing);++/* remove unique pack objects from the non_unique packs */+pl=non_unique;while(pl){-pack_list_insert(min,pl);+llist_sorted_difference_inplace(pl->all_objects,unique_pack_objects);pl=pl->next;}++while(non_unique){+/* sort the non_unique packs, greater size of all_objects first */+sort_pack_list(&non_unique);+if(non_unique->all_objects->size==0)+break;++pack_list_insert(min,non_unique);++for(pl=non_unique->next;pl&&pl->all_objects->size>0;pl=pl->next)+llist_sorted_difference_inplace(pl->all_objects,non_unique->all_objects);++non_unique=non_unique->next;+}}staticvoidload_all_objects(void)
@@ -285,78 +285,6 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)}}-staticvoidpll_free(structpll*l)-{-structpll*old;-structpack_list*opl;--while(l){-old=l;-while(l->pl){-opl=l->pl;-l->pl=opl->next;-free(opl);-}-l=l->next;-free(old);-}-}--/* all the permutations have to be free()d at the same time,-*sincetheyrefertoeachother-*/-staticstructpll*get_permutations(structpack_list*list,intn)-{-structpll*subset,*ret=NULL,*new_pll=NULL;--if(list==NULL||pack_list_size(list)<n||n==0)-returnNULL;--if(n==1){-while(list){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=NULL;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-list=list->next;-}-returnret;-}--while(list->next){-subset=get_permutations(list->next,n-1);-while(subset){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=subset->pl;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-subset=subset->next;-}-list=list->next;-}-returnret;-}--staticintis_superset(structpack_list*pl,structllist*list)-{-structllist*diff;--diff=llist_copy(list);--while(pl){-llist_sorted_difference_inplace(diff,pl->all_objects);-if(diff->size==0){/* we're done */-llist_free(diff);-return1;-}-pl=pl->next;-}-llist_free(diff);-return0;-}-staticsize_tsizeof_union(structpacked_git*p1,structpacked_git*p2){size_tret=0;
From: Sun Chao <redacted>
Remove unused functions to find `min` packs, such as `get_permutations`,
`pll_free`, etc.
Signed-off-by: Sun Chao <redacted>
Signed-off-by: Jiang Xin <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
builtin/pack-redundant.c | 86 ------------------------------------------------
1 file changed, 86 deletions(-)
@@ -285,78 +271,6 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)}}-staticvoidpll_free(structpll*l)-{-structpll*old;-structpack_list*opl;--while(l){-old=l;-while(l->pl){-opl=l->pl;-l->pl=opl->next;-free(opl);-}-l=l->next;-free(old);-}-}--/* all the permutations have to be free()d at the same time,-*sincetheyrefertoeachother-*/-staticstructpll*get_permutations(structpack_list*list,intn)-{-structpll*subset,*ret=NULL,*new_pll=NULL;--if(list==NULL||pack_list_size(list)<n||n==0)-returnNULL;--if(n==1){-while(list){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=NULL;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-list=list->next;-}-returnret;-}--while(list->next){-subset=get_permutations(list->next,n-1);-while(subset){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=subset->pl;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-subset=subset->next;-}-list=list->next;-}-returnret;-}--staticintis_superset(structpack_list*pl,structllist*list)-{-structllist*diff;--diff=llist_copy(list);--while(pl){-llist_sorted_difference_inplace(diff,pl->all_objects);-if(diff->size==0){/* we're done */-llist_free(diff);-return1;-}-pl=pl->next;-}-llist_free(diff);-return0;-}-staticsize_tsizeof_union(structpacked_git*p1,structpacked_git*p2){size_tret=0;
From: Sun Chao <redacted>
I'm particularly grateful to Junio and JiangXin for fixing the patches,
and I noticed Junio send a new commit to remove more unused codes and
suggest to SQUASH it.
So I create this new version of patches to do this work, I also have
checked the left codes and remove a unused struct based on Junio's
last commit of `https://github.com/gitster/git/commits/sc/pack-redundant`.
--
Sun Chao (1):
pack-redundant: remove unused functions
builtin/pack-redundant.c | 86 ------------------------------------------------
1 file changed, 86 deletions(-)
--
2.8.1
From: Sun Chao <redacted>
I'm particularly grateful to Junio and JiangXin for fixing the patches,
and I noticed Junio send a new commit to remove more unused codes and
suggest to SQUASH it.
So I create this new version of patches to do this work, I also have
checked the left codes and remove a unused struct based on Junio's
last commit of `https://github.com/gitster/git/commits/sc/pack-redundant`.
--
Sun Chao (1):
pack-redundant: remove unused functions
builtin/pack-redundant.c | 86 ------------------------------------------------
1 file changed, 86 deletions(-)
--
2.8.1
+
+. ./test-lib.sh
+
+create_commits()
+{
+ set -e
+ parent=
+ for name in A B C D E F G H I J K L M
+ do
+ test_tick
+ T=$(git write-tree)
+ if test -z "$parent"
+ then
+ sha1=$(echo $name | git commit-tree $T)
There is a considerable effort going on to switch from SHA-1 to a
different hash function, so please don't add any new $sha1 variable;
call it $oid or $commit instead.
Please perform all setup tasks in a test_expect_success block, so we
get verbose and trace output about what's going on.
Don't use 'set -e', use an &&-chain instead. To fail the test if a
command in the for loop were to fail you could do something like this:
for ....
do
do-this &&
do-that ||
return 1
done
Don't run a git command (especially the particular command the test
script focuses on) upstream of a pipe, because it hides the command's
exit code. Use an intermediate file instead.
+ sed -e "s#^.*/pack-\(.*\)\.\(idx\|pack\)#\1#g" | \
This sed command doesn't seem to work on macOS (on Travis CI), and
causes the test to fail with:
++git pack-redundant --all
++sed -e 's#^.*/pack-\(.*\)\.\(idx\|pack\)#\1#g'
++sort -u
++read p
++sort
++eval echo '${P.git/objects/pack/pack-0cf5cb6afaa1bae36b8e61ca398dbe29a15bc74e.idx}'
./test-lib.sh: line 697: ${P.git/objects/pack/pack-0cf5cb6afaa1bae36b8e61ca398dbe29a15bc74e.idx}: bad substitution
++test_cmp expected actual
++diff -u expected actual
--- expected 2019-01-09 01:53:45.000000000 +0000
+++ actual 2019-01-09 01:53:45.000000000 +0000
@@ -1,4 +0,0 @@
-P1:24ee080366509364d04a138cd4e168dc4ff33354
-P4:139d8b0cfe7e8970a8f3533835f90278d88de474
-P5:23e0f02d822fa4bfe5ee63337ba5632cd7be208e
-P6:deeb289f1749972f1cd57c3b9f359ece2361f60a
error: last command exited with $?=1
not ok 2 - git pack-redundant --all
I'm not sure what's wrong with it, though.
Minor nit: 'git pack-redundant' prints one filename per line, so the
'g' at the end of the 's###g' is not necessary.
+ sort -u | \
+ while read p; do eval echo "\${P$p}"; done | \
+ sort > actual && \
Style nit: no space between redirection operator and filename
Don't run a git command (especially the particular command the test
script focuses on) upstream of a pipe, because it hides the command's
exit code. Use an intermediate file instead.
quoted
+ sed -e "s#^.*/pack-\(.*\)\.\(idx\|pack\)#\1#g" | \
This sed command doesn't seem to work on macOS (on Travis CI), and
causes the test to fail with:
++git pack-redundant --all
++sed -e 's#^.*/pack-\(.*\)\.\(idx\|pack\)#\1#g'
++sort -u
++read p
++sort
++eval echo '${P.git/objects/pack/pack-0cf5cb6afaa1bae36b8e61ca398dbe29a15bc74e.idx}'
./test-lib.sh: line 697: ${P.git/objects/pack/pack-0cf5cb6afaa1bae36b8e61ca398dbe29a15bc74e.idx}: bad substitution
++test_cmp expected actual
++diff -u expected actual
--- expected 2019-01-09 01:53:45.000000000 +0000
+++ actual 2019-01-09 01:53:45.000000000 +0000
@@ -1,4 +0,0 @@
-P1:24ee080366509364d04a138cd4e168dc4ff33354
-P4:139d8b0cfe7e8970a8f3533835f90278d88de474
-P5:23e0f02d822fa4bfe5ee63337ba5632cd7be208e
-P6:deeb289f1749972f1cd57c3b9f359ece2361f60a
error: last command exited with $?=1
not ok 2 - git pack-redundant --all
I'm not sure what's wrong with it, though.
So, it appears that 'sed' in macOS doesn't understand the
'\(idx\|pack\)' part of that regex. Turning that command into
sed -e "s#^.git/objects/pack/pack-\($OID_REGEX\)\..*#\1#" out | \
makes it work even on macOS, but note that those 40 hexdigits are not
actual OIDs but file content checksums, so using $OID_REGEX is not the
right thing to do here (though I'm not sure what is supposed to be
used instead, as $_x40 hardcodes the number of hexdigits).
Alas, the test as a whole still fails with the following on macOS:
++diff -u expected actual
--- expected 2019-01-09 15:54:49.000000000 +0000
+++ actual 2019-01-09 15:54:49.000000000 +0000
@@ -1,4 +1,4 @@
P1:24ee080366509364d04a138cd4e168dc4ff33354
-P4:139d8b0cfe7e8970a8f3533835f90278d88de474
+P3:0cf5cb6afaa1bae36b8e61ca398dbe29a15bc74e
P5:23e0f02d822fa4bfe5ee63337ba5632cd7be208e
-P6:deeb289f1749972f1cd57c3b9f359ece2361f60a
+P7:4ecc1eb138516a26654cd4e3570b322c0820f170
error: last command exited with $?=1
+
+. ./test-lib.sh
+
+create_commits()
+{
+ set -e
+ parent=
+ for name in A B C D E F G H I J K L M
+ do
+ test_tick
+ T=$(git write-tree)
+ if test -z "$parent"
+ then
+ sha1=$(echo $name | git commit-tree $T)
There is a considerable effort going on to switch from SHA-1 to a
different hash function, so please don't add any new $sha1 variable;
call it $oid or $commit instead.
Will do.
quoted
+
+# Create commits and packs
+create_commits
+create_redundant_packs
Please perform all setup tasks in a test_expect_success block, so we
get verbose and trace output about what's going on.
Will do like this:
test_expect_success 'setup' '
create_commits &&
create_redundant_packs
'
Don't use 'set -e', use an &&-chain instead. To fail the test if a
command in the for loop were to fail you could do something like this:
for ....
do
do-this &&
do-that ||
return 1
done
Don't run a git command (especially the particular command the test
script focuses on) upstream of a pipe, because it hides the command's
exit code. Use an intermediate file instead.
quoted
+ sed -e "s#^.*/pack-\(.*\)\.\(idx\|pack\)#\1#g" | \
This sed command doesn't seem to work on macOS (on Travis CI), and
causes the test to fail with:
It works if rewrite as follows:
git pack-redundant --all >out &&
sed -E -e "s#.*/pack-(.*)\.(idx|pack)#\1#" out | \
Without `-E`, MasOS has to write two seperate sed commands, such as:
git pack-redundant --all >out &&
sed -e "s#.*/pack-\(.*\)\.idx#\1#" out | \
sed -e "s#.*/pack-\(.*\)\.pack#\1#"
Option '-E' is an alias for -r in GNU sed 4.2 (added in 4.2, not documented
unti 4.3), released on May 11 2009. I prefer the `-E` version.
Minor nit: 'git pack-redundant' prints one filename per line, so the
'g' at the end of the 's###g' is not necessary.
quoted
+ sort -u | \
+ while read p; do eval echo "\${P$p}"; done | \
+ sort > actual && \
Style nit: no space between redirection operator and filename
From: Johannes Sixt <hidden> Date: 2019-01-10 07:11:32
Am 10.01.19 um 04:28 schrieb Jiang Xin:
SZEDER Gábor [off-list ref] 于2019年1月9日周三 下午8:56写道:
quoted
Use something like
find .git/objects -type f | grep -v pack >out &&
test_must_be_empty out
instead, so we get an informative error message on failure.
if `grep -v pack` return empty output, it will return error, so
I will use `sed -e "/objects\/pack\//d" >out` instead.
So, you could even write this as
find .git/objects -type f >out &&
! grep -v pack out # must be empty
or
! find .git/objects -type f | grep -v pack
if you want to be terse.
-- Hannes
From: SZEDER Gábor <hidden> Date: 2019-01-10 11:57:11
On Thu, Jan 10, 2019 at 11:28:34AM +0800, Jiang Xin wrote:
SZEDER Gábor [off-list ref] 于2019年1月9日周三 下午8:56写道:
quoted
quoted
+ sed -e "s#^.*/pack-\(.*\)\.\(idx\|pack\)#\1#g" | \
This sed command doesn't seem to work on macOS (on Travis CI), and
causes the test to fail with:
It works if rewrite as follows:
git pack-redundant --all >out &&
sed -E -e "s#.*/pack-(.*)\.(idx|pack)#\1#" out | \
Without `-E`, MasOS has to write two seperate sed commands, such as:
git pack-redundant --all >out &&
sed -e "s#.*/pack-\(.*\)\.idx#\1#" out | \
sed -e "s#.*/pack-\(.*\)\.pack#\1#"
Option '-E' is an alias for -r in GNU sed 4.2 (added in 4.2, not documented
unti 4.3), released on May 11 2009. I prefer the `-E` version.
Is 'sed -E' portable enough, e.g. to the various BSDs, Solaris, and
whatnot? I don't know, but POSIX doesn't mention it, there is not a
single instance of it in our current codebase, and it appears that
we've never used it before, either. OTOH,
't/check-non-portable-shell.pl' doesn't catch it as non-portable
construct...
Sun Chao (my former colleague at Huawei) found a bug of
git-pack-redundant. If there are too many packs and many of them
overlap
each other, running `git pack-redundant --all` will exhaust all memories
and the process will be killed by kernel.
There is a script in commit log of commit 2/3, which can be used to
create a repository with lots of redundant packs. Running `git
pack-redundant --all` in it can reproduce this issue.
SZEDER reported that t5233 won't pass for MacOS. See solution in patch
4/5.
Changes since reroll v4:
* Rewrite t5323, add more test cases.
* Add two new patches, one for refactor, and another changed sorting
method and fixed t5323 for the new algorithm.
Range diff with sc/pack-redundant feature branch:
1: 702267a888 < -: ---------- t5323: test cases for git-pack-redundant
-: ---------- > 1: 40fea5d67f t5323: test cases for git-pack-redundant
2: c4b133d858 = 2: 50cd5a5b47 pack-redundant: new algorithm to find min packs
-: ---------- > 3: 6338c6fad4 pack-redundant: rename pack_list.all_objects
-: ---------- > 4: 734f4d8a8b pack-redundant: consistent sort method
3: 2351d7e8b5 ! 5: b7ccdea1ad pack-redundant: remove unused functions
@@ -13,7 +13,7 @@
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@
- struct llist *all_objects;
+ size_t all_objects_size;
} *local_packs = NULL, *altodb_packs = NULL;
-struct pll {
@@ -105,7 +105,7 @@
- diff = llist_copy(list);
-
- while (pl) {
-- llist_sorted_difference_inplace(diff, pl->all_objects);
+- llist_sorted_difference_inplace(diff, pl->remaining_objects);
- if (diff->size == 0) { /* we're done */
- llist_free(diff);
- return 1;
Jiang Xin (3):
t5323: test cases for git-pack-redundant
pack-redundant: rename pack_list.all_objects
pack-redundant: consistent sort method
Sun Chao (2):
pack-redundant: new algorithm to find min packs
pack-redundant: remove unused functions
builtin/pack-redundant.c | 221 +++++++++++++++-----------------------
t/t5323-pack-redundant.sh | 157 +++++++++++++++++++++++++++
2 files changed, 242 insertions(+), 136 deletions(-)
create mode 100755 t/t5323-pack-redundant.sh
--
2.20.1.101.gc01fadde4e
From: Sun Chao <redacted>
When calling `git pack-redundant --all`, if there are too many local
packs and too many redundant objects within them, the too deep iteration
of `get_permutations` will exhaust all the resources, and the process of
`git pack-redundant` will be killed.
The following script could create a repository with too many redundant
packs, and running `git pack-redundant --all` in the `test.git` repo
will die soon.
#!/bin/sh
repo="$(pwd)/test.git"
work="$(pwd)/test"
i=1
max=199
if test -d "$repo" || test -d "$work"; then
echo >&2 "ERROR: '$repo' or '$work' already exist"
exit 1
fi
git init -q --bare "$repo"
git --git-dir="$repo" config gc.auto 0
git --git-dir="$repo" config transfer.unpackLimit 0
git clone -q "$repo" "$work" 2>/dev/null
while :; do
cd "$work"
echo "loop $i: $(date +%s)" >$i
git add $i
git commit -q -sm "loop $i"
git push -q origin HEAD:master
printf "\rCreate pack %4d/%d\t" $i $max
if test $i -ge $max; then break; fi
cd "$repo"
git repack -q
if test $(($i % 2)) -eq 0; then
git repack -aq
pack=$(ls -t $repo/objects/pack/*.pack | head -1)
touch "${pack%.pack}.keep"
fi
i=$((i+1))
done
printf "\ndone\n"
To get the `min` unique pack list, we can replace the iteration in
`minimize` function with a new algorithm, and this could solve this
issue:
1. Get the unique and non_uniqe packs, add the unique packs to the
`min` list.
2. Remove the objects of unique packs from non_unique packs, then each
object left in the non_unique packs will have at least two copies.
3. Sort the non_unique packs by the objects' size, more objects first,
and add the first non_unique pack to `min` list.
4. Drop the duplicated objects from other packs in the ordered
non_unique pack list, and repeat step 3.
Original PR and discussions: https://github.com/jiangxin/git/pull/25
Signed-off-by: Sun Chao <redacted>
Signed-off-by: Jiang Xin <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
builtin/pack-redundant.c | 109 ++++++++++++++++++++++++---------------
1 file changed, 68 insertions(+), 41 deletions(-)
@@ -421,14 +421,52 @@ static inline off_t pack_set_bytecount(struct pack_list *pl)returnret;}+staticintcmp_pack_list_reverse(constvoid*a,constvoid*b)+{+structpack_list*pl_a=*((structpack_list**)a);+structpack_list*pl_b=*((structpack_list**)b);+size_tsz_a=pl_a->all_objects->size;+size_tsz_b=pl_b->all_objects->size;++if(sz_a==sz_b)+return0;+elseif(sz_a<sz_b)+return1;+else+return-1;+}++/* Sort pack_list, greater size of all_objects first */+staticvoidsort_pack_list(structpack_list**pl)+{+structpack_list**ary,*p;+inti;+size_tn=pack_list_size(*pl);++if(n<2)+return;++/* prepare an array of packed_list for easier sorting */+ary=xcalloc(n,sizeof(structpack_list*));+for(n=0,p=*pl;p;p=p->next)+ary[n++]=p;++QSORT(ary,n,cmp_pack_list_reverse);++/* link them back again */+for(i=0;i<n-1;i++)+ary[i]->next=ary[i+1];+ary[n-1]->next=NULL;+*pl=ary[0];++free(ary);+}++staticvoidminimize(structpack_list**min){-structpack_list*pl,*unique=NULL,-*non_unique=NULL,*min_perm=NULL;-structpll*perm,*perm_all,*perm_ok=NULL,*new_perm;-structllist*missing;-off_tmin_perm_size=0,perm_size;-intn;+structpack_list*pl,*unique=NULL,*non_unique=NULL;+structllist*missing,*unique_pack_objects;pl=local_packs;while(pl){
@@ -446,49 +484,37 @@ static void minimize(struct pack_list **min)pl=pl->next;}+*min=unique;+/* return if there are no objects missing from the unique set */if(missing->size==0){-*min=unique;free(missing);return;}-/* find the permutations which contain all missing objects */-for(n=1;n<=pack_list_size(non_unique)&&!perm_ok;n++){-perm_all=perm=get_permutations(non_unique,n);-while(perm){-if(is_superset(perm->pl,missing)){-new_perm=xmalloc(sizeof(structpll));-memcpy(new_perm,perm,sizeof(structpll));-new_perm->next=perm_ok;-perm_ok=new_perm;-}-perm=perm->next;-}-if(perm_ok)-break;-pll_free(perm_all);-}-if(perm_ok==NULL)-die("Internal error: No complete sets found!");--/* find the permutation with the smallest size */-perm=perm_ok;-while(perm){-perm_size=pack_set_bytecount(perm->pl);-if(!min_perm_size||min_perm_size>perm_size){-min_perm_size=perm_size;-min_perm=perm->pl;-}-perm=perm->next;-}-*min=min_perm;-/* add the unique packs to the list */-pl=unique;+unique_pack_objects=llist_copy(all_objects);+llist_sorted_difference_inplace(unique_pack_objects,missing);++/* remove unique pack objects from the non_unique packs */+pl=non_unique;while(pl){-pack_list_insert(min,pl);+llist_sorted_difference_inplace(pl->all_objects,unique_pack_objects);pl=pl->next;}++while(non_unique){+/* sort the non_unique packs, greater size of all_objects first */+sort_pack_list(&non_unique);+if(non_unique->all_objects->size==0)+break;++pack_list_insert(min,non_unique);++for(pl=non_unique->next;pl&&pl->all_objects->size>0;pl=pl->next)+llist_sorted_difference_inplace(pl->all_objects,non_unique->all_objects);++non_unique=non_unique->next;+}}staticvoidload_all_objects(void)
@@ -498,20 +498,20 @@ static void minimize(struct pack_list **min)/* remove unique pack objects from the non_unique packs */pl=non_unique;while(pl){-llist_sorted_difference_inplace(pl->all_objects,unique_pack_objects);+llist_sorted_difference_inplace(pl->remaining_objects,unique_pack_objects);pl=pl->next;}while(non_unique){-/* sort the non_unique packs, greater size of all_objects first */+/* sort the non_unique packs, greater size of remaining_objects first */sort_pack_list(&non_unique);-if(non_unique->all_objects->size==0)+if(non_unique->remaining_objects->size==0)break;pack_list_insert(min,non_unique);-for(pl=non_unique->next;pl&&pl->all_objects->size>0;pl=pl->next)-llist_sorted_difference_inplace(pl->all_objects,non_unique->all_objects);+for(pl=non_unique->next;pl&&pl->remaining_objects->size>0;pl=pl->next)+llist_sorted_difference_inplace(pl->remaining_objects,non_unique->remaining_objects);non_unique=non_unique->next;}
@@ -590,11 +590,11 @@ static struct pack_list * add_pack(struct packed_git *p)base+=256*4+((p->index_version<2)?4:8);step=the_hash_algo->rawsz+((p->index_version<2)?4:0);while(off<p->num_objects*step){-llist_insert_back(l.all_objects,(conststructobject_id*)(base+off));+llist_insert_back(l.remaining_objects,(conststructobject_id*)(base+off));off+=step;}/* this list will be pruned in cmp_two_packs later */-l.unique_objects=llist_copy(l.all_objects);+l.unique_objects=llist_copy(l.remaining_objects);if(p->pack_local)returnpack_list_insert(&local_packs,&l);else
From: Jiang Xin <redacted>
SZEDER reported that test case t5323 has different test result on MacOS.
This is because `cmp_pack_list_reverse` cannot give identical result
when two pack being sorted has the same size of remaining_objects.
Changes to the sorting function will make consistent test result for
t5323.
The new algorithm to find redundant packs is a trade-off to save memory
resources, and the result of it may be different with old one, and may
be not the best result sometimes. Update t5323 for the new algorithm.
Reported-by: SZEDER Gábor <redacted>
Signed-off-by: Jiang Xin <redacted>
---
builtin/pack-redundant.c | 22 +++++++++++++++-------
t/t5323-pack-redundant.sh | 2 +-
2 files changed, 16 insertions(+), 8 deletions(-)
@@ -421,16 +422,22 @@ static inline off_t pack_set_bytecount(struct pack_list *pl)returnret;}-staticintcmp_pack_list_reverse(constvoid*a,constvoid*b)+staticintcmp_remaining_objects(constvoid*a,constvoid*b){structpack_list*pl_a=*((structpack_list**)a);structpack_list*pl_b=*((structpack_list**)b);-size_tsz_a=pl_a->remaining_objects->size;-size_tsz_b=pl_b->remaining_objects->size;-if(sz_a==sz_b)-return0;-elseif(sz_a<sz_b)+/* if have the same remaining_objects, big pack first */+if(pl_a->remaining_objects->size==pl_b->remaining_objects->size)+if(pl_a->all_objects_size==pl_b->all_objects_size)+return0;+elseif(pl_a->all_objects_size<pl_b->all_objects_size)+return1;+else+return-1;++/* sort according to remaining objects, more remaining objects first */+if(pl_a->remaining_objects->size<pl_b->remaining_objects->size)return1;elsereturn-1;
@@ -451,7 +458,7 @@ static void sort_pack_list(struct pack_list **pl)for(n=0,p=*pl;p;p=p->next)ary[n++]=p;-QSORT(ary,n,cmp_pack_list_reverse);+QSORT(ary,n,cmp_remaining_objects);/* link them back again */for(i=0;i<n-1;i++)
@@ -593,6 +600,7 @@ static struct pack_list * add_pack(struct packed_git *p)llist_insert_back(l.remaining_objects,(conststructobject_id*)(base+off));off+=step;}+l.all_objects_size=l.remaining_objects->size;/* this list will be pruned in cmp_two_packs later */l.unique_objects=llist_copy(l.remaining_objects);if(p->pack_local)
From: Sun Chao <redacted>
Remove unused functions to find `min` packs, such as `get_permutations`,
`pll_free`, etc.
Signed-off-by: Sun Chao <redacted>
Signed-off-by: Jiang Xin <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
builtin/pack-redundant.c | 86 ----------------------------------------
1 file changed, 86 deletions(-)
@@ -286,78 +272,6 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)}}-staticvoidpll_free(structpll*l)-{-structpll*old;-structpack_list*opl;--while(l){-old=l;-while(l->pl){-opl=l->pl;-l->pl=opl->next;-free(opl);-}-l=l->next;-free(old);-}-}--/* all the permutations have to be free()d at the same time,-*sincetheyrefertoeachother-*/-staticstructpll*get_permutations(structpack_list*list,intn)-{-structpll*subset,*ret=NULL,*new_pll=NULL;--if(list==NULL||pack_list_size(list)<n||n==0)-returnNULL;--if(n==1){-while(list){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=NULL;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-list=list->next;-}-returnret;-}--while(list->next){-subset=get_permutations(list->next,n-1);-while(subset){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=subset->pl;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-subset=subset->next;-}-list=list->next;-}-returnret;-}--staticintis_superset(structpack_list*pl,structllist*list)-{-structllist*diff;--diff=llist_copy(list);--while(pl){-llist_sorted_difference_inplace(diff,pl->remaining_objects);-if(diff->size==0){/* we're done */-llist_free(diff);-return1;-}-pl=pl->next;-}-llist_free(diff);-return0;-}-staticsize_tsizeof_union(structpacked_git*p1,structpacked_git*p2){size_tret=0;
On Thu, Jan 10, 2019 at 11:28:34AM +0800, Jiang Xin wrote:
quoted
SZEDER Gábor [off-list ref] 于2019年1月9日周三 下午8:56写道:
quoted
quoted
+ sed -e "s#^.*/pack-\(.*\)\.\(idx\|pack\)#\1#g" | \
This sed command doesn't seem to work on macOS (on Travis CI), and
causes the test to fail with:
It works if rewrite as follows:
git pack-redundant --all >out &&
sed -E -e "s#.*/pack-(.*)\.(idx|pack)#\1#" out | \
Without `-E`, MasOS has to write two seperate sed commands, such as:
git pack-redundant --all >out &&
sed -e "s#.*/pack-\(.*\)\.idx#\1#" out | \
sed -e "s#.*/pack-\(.*\)\.pack#\1#"
Option '-E' is an alias for -r in GNU sed 4.2 (added in 4.2, not documented
unti 4.3), released on May 11 2009. I prefer the `-E` version.
Is 'sed -E' portable enough, e.g. to the various BSDs, Solaris, and
whatnot? I don't know, but POSIX doesn't mention it, there is not a
single instance of it in our current codebase, and it appears that
we've never used it before, either. OTOH,
If we can use "two seperate sed commands" i would (really) prefer to so,
to avoid "sed -E".
My conclusion is that it is not portable enough.
't/check-non-portable-shell.pl' doesn't catch it as non-portable
construct...
Good point.
Actually that script only checks "known non-portable" options.
Every time somebody finds a non-portable option, we update it.
A growing blacklist, so to say.
May be we should have a white list instead.
@@ -421,16 +422,22 @@ static inline off_t pack_set_bytecount(struct pack_list *pl) return ret; }-static int cmp_pack_list_reverse(const void *a, const void *b)+static int cmp_remaining_objects(const void *a, const void *b) { struct pack_list *pl_a = *((struct pack_list **)a); struct pack_list *pl_b = *((struct pack_list **)b);- size_t sz_a = pl_a->remaining_objects->size;- size_t sz_b = pl_b->remaining_objects->size;- if (sz_a == sz_b)- return 0;- else if (sz_a < sz_b)+ /* if have the same remaining_objects, big pack first */+ if (pl_a->remaining_objects->size == pl_b->remaining_objects->size)+ if (pl_a->all_objects_size == pl_b->all_objects_size)+ return 0;+ else if (pl_a->all_objects_size < pl_b->all_objects_size)+ return 1;+ else+ return -1;
My compiler complains about the above nested if statements:
builtin/pack-redundant.c: In function ‘cmp_remaining_objects’:
builtin/pack-redundant.c:345:5: error: suggest explicit braces to avoid ambiguous ‘else’ [-Werror=parentheses]
if (pl_a->remaining_objects->size == pl_b->remaining_objects->size)
^
cc1: all warnings being treated as errors
Makefile:2302: recipe for target 'builtin/pack-redundant.o' failed
After adding a pair of {} to the outer if statement
't5323-pack-redundant.sh' passed successfully even on macOS (on Travis
CI).
@@ -446,49 +484,37 @@ static void minimize(struct pack_list **min) pl = pl->next; }+ *min = unique;+ /* return if there are no objects missing from the unique set */ if (missing->size == 0) {- *min = unique; free(missing); return; }- /* find the permutations which contain all missing objects */- for (n = 1; n <= pack_list_size(non_unique) && !perm_ok; n++) {- perm_all = perm = get_permutations(non_unique, n);- while (perm) {- if (is_superset(perm->pl, missing)) {- new_perm = xmalloc(sizeof(struct pll));- memcpy(new_perm, perm, sizeof(struct pll));- new_perm->next = perm_ok;- perm_ok = new_perm;- }- perm = perm->next;- }- if (perm_ok)- break;- pll_free(perm_all);- }
Please make sure that all commits in the patch series can be build
cleanly without any warnings (with '-Werror' or preferably with 'make
DEVELOPER=1') and pass the test suite. This is important, because
unbuildable commits will cause trouble later on, when e.g. 'git
bisect' happens to pick such a commit.
In this case, the removal of the above loop removes all callsites of
the static functions get_permutations(), is_superset(), and
pll_free(), resulting the following compiler error:
builtin/pack-redundant.c: At top level:
builtin/pack-redundant.c:289:13: error: ‘pll_free’ defined but not used [-Werror=unused-function]
static void pll_free(struct pll *l)
^
builtin/pack-redundant.c:309:21: error: ‘get_permutations’ defined but not used [-Werror=unused-function]
static struct pll * get_permutations(struct pack_list *list, int n)
^
builtin/pack-redundant.c:343:12: error: ‘is_superset’ defined but not used [-Werror=unused-function]
static int is_superset(struct pack_list *pl, struct llist *list)
^
I see that the last patch in this series removes those three
unused functions, but that patch should be squashed into this one to
keep Git buildable with '-Werror' or DEVELOPER=1.
Furthermore, after building this patch (without '-Werror'), several
tests in 't5323-pack-redundant.sh' fail. To avoid the test failure I
think the fourth patch ensuring a consistent sort order should be
squashed in as well.
Sun Chao (my former colleague at Huawei) found a bug of
git-pack-redundant. If there are too many packs and many of them
overlap each other, running `git pack-redundant --all` will
exhaust all memories and the process will be killed by kernel.
There is a script in commit log of commit 2/5, which can be used to
create a repository with lots of redundant packs. Running `git
pack-redundant --all` in it can reproduce this issue.
Junio C Hamano [off-list ref] 于2019年1月12日周六 上午2:00写道:
quoted
quoted
Yikes. Can't "git pack-objects" get the input directly without
overlong printf, something along the lines of...
P1=$(git -C .git/objects/pack pack-objects pack <<-EOF
$A
$B
$C
...
$R
EOF
)
Find that no space before <OID>, because git-pack-objects not allow that,
and mached parentheses should in the same line.
So Will write like this:
create_pack_1() {
P1=$(git -C .git/objects/pack pack-objects pack <<-EOF) &&
$T
Isn't the whole point of <<-EOF (notice the leading dash) to allow
us to indent the here-doc with horizontal tab?
The reason that indents are not stripped even with `<<-EOF` is I mixed
tabs and spaces to make a better align.
If put the heredoc outside the parentheses, it will failed on MacOS, so
use the syntax Junio previously suggested.
SZEDER Gábor [off-list ref] 于2019年1月11日周五 上午9:19写道:
I see that the last patch in this series removes those three
unused functions, but that patch should be squashed into this one to
keep Git buildable with '-Werror' or DEVELOPER=1.
Furthermore, after building this patch (without '-Werror'), several
tests in 't5323-pack-redundant.sh' fail. To avoid the test failure I
think the fourth patch ensuring a consistent sort order should be
squashed in as well.
Patch 3/5 to 5/5 can be squashed to patch 2/5.
## Changes since reroll v5
1: 40fea5d67f ! 1: 7e4e703083 t5323: test cases for git-pack-redundant
@@ -22,8 +22,7 @@
+
+. ./test-lib.sh
+
-+create_commits()
-+{
++create_commits() {
+ parent=
+ for name in A B C D E F G H I J K L M N O P Q R
+ do
@@ -39,54 +38,98 @@
+ parent=$oid ||
+ return 1
+ done
-+ git update-ref refs/heads/master $M
++ git update-ref refs/heads/master $R
+}
+
-+create_pack_1()
-+{
-+ P1=$(cd .git/objects/pack; printf "$T\n$A\n$B\n$C\n$D\n$E\n$F\n$R\n" | git pack-objects pack 2>/dev/null) &&
++create_pack_1() {
++ P1=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ $T
++ $A
++ $B
++ $C
++ $D
++ $E
++ $F
++ $R
++ EOF
++ ) &&
+ eval P$P1=P1:$P1
+}
+
-+create_pack_2()
-+{
-+ P2=$(cd .git/objects/pack; printf "$B\n$C\n$D\n$E\n$G\n$H\n$I\n" | git pack-objects pack 2>/dev/null) &&
++create_pack_2() {
++ P2=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ $B
++ $C
++ $D
++ $E
++ $G
++ $H
++ $I
++ EOF
++ ) &&
+ eval P$P2=P2:$P2
+}
+
-+create_pack_3()
-+{
-+ P3=$(cd .git/objects/pack; printf "$F\n$I\n$J\n$K\n$L\n$M\n" | git pack-objects pack 2>/dev/null) &&
++create_pack_3() {
++ P3=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ $F
++ $I
++ $J
++ $K
++ $L
++ $M
++ EOF
++ ) &&
+ eval P$P3=P3:$P3
+}
+
-+create_pack_4()
-+{
-+ P4=$(cd .git/objects/pack; printf "$J\n$K\n$L\n$M\n$P\n" | git pack-objects pack 2>/dev/null) &&
++create_pack_4() {
++ P4=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ $J
++ $K
++ $L
++ $M
++ $P
++ EOF
++ ) &&
+ eval P$P4=P4:$P4
+}
+
-+create_pack_5()
-+{
-+ P5=$(cd .git/objects/pack; printf "$G\n$H\n$N\n$O\n" | git pack-objects pack 2>/dev/null) &&
++create_pack_5() {
++ P5=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ $G
++ $H
++ $N
++ $O
++ EOF
++ ) &&
+ eval P$P5=P5:$P5
+}
+
-+create_pack_6()
-+{
-+ P6=$(cd .git/objects/pack; printf "$N\n$O\n$Q\n" | git pack-objects pack 2>/dev/null) &&
++create_pack_6() {
++ P6=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ $N
++ $O
++ $Q
++ EOF
++ ) &&
+ eval P$P6=P6:$P6
+}
+
-+create_pack_7()
-+{
-+ P7=$(cd .git/objects/pack; printf "$P\n$Q\n" | git pack-objects pack 2>/dev/null) &&
++create_pack_7() {
++ P7=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ $P
++ $Q
++ EOF
++ ) &&
+ eval P$P7=P7:$P7
+}
+
-+create_pack_8()
-+{
-+ P8=$(cd .git/objects/pack; printf "$A\n" | git pack-objects pack 2>/dev/null) &&
++create_pack_8() {
++ P8=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ $A
++ EOF
++ ) &&
+ eval P$P8=P8:$P8
+}
+
@@ -110,10 +153,12 @@
+
+test_expect_success 'one of pack-2/pack-3 is redundant' '
+ git pack-redundant --all >out &&
-+ sed -E -e "s#.*/pack-(.*)\.(idx|pack)#\1#" out | \
-+ sort -u | \
-+ while read p; do eval echo "\${P$p}"; done | \
-+ sort >actual && \
++ sed \
++ -e "s#.*/pack-\(.*\)\.idx#\1#" \
++ -e "s#.*/pack-\(.*\)\.pack#\1#" out |
++ sort -u |
++ while read p; do eval echo "\${P$p}"; done |
++ sort >actual &&
+ test_cmp expected actual
+'
+
@@ -121,6 +166,7 @@
+ create_pack_6 && create_pack_7
+'
+
++# Only after calling create_pack_6, we can use $P6 variable.
+cat >expected <<EOF
+P2:$P2
+P4:$P4
@@ -129,10 +175,12 @@
+
+test_expect_success 'pack 2, 4, and 6 are redundant' '
+ git pack-redundant --all >out &&
-+ sed -E -e "s#.*/pack-(.*)\.(idx|pack)#\1#" out | \
-+ sort -u | \
-+ while read p; do eval echo "\${P$p}"; done | \
-+ sort >actual && \
++ sed \
++ -e "s#.*/pack-\(.*\)\.idx#\1#" \
++ -e "s#.*/pack-\(.*\)\.pack#\1#" out |
++ sort -u |
++ while read p; do eval echo "\${P$p}"; done |
++ sort >actual &&
+ test_cmp expected actual
+'
+
@@ -147,24 +195,26 @@
+P8:$P8
+EOF
+
-+test_expect_success 'pack-8, subset of pack-1, is also redundant' '
++test_expect_success 'pack-8 (subset of pack-1) is also redundant' '
+ git pack-redundant --all >out &&
-+ sed -E -e "s#.*/pack-(.*)\.(idx|pack)#\1#" out | \
-+ sort -u | \
-+ while read p; do eval echo "\${P$p}"; done | \
-+ sort >actual && \
++ sed \
++ -e "s#.*/pack-\(.*\)\.idx#\1#" \
++ -e "s#.*/pack-\(.*\)\.pack#\1#" out |
++ sort -u |
++ while read p; do eval echo "\${P$p}"; done |
++ sort >actual &&
+ test_cmp expected actual
+'
+
-+test_expect_success 'clear loose objects' '
++test_expect_success 'clean loose objects' '
+ git prune-packed &&
+ find .git/objects -type f | sed -e "/objects\/pack\//d" >out &&
+ test_must_be_empty out
+'
+
-+test_expect_success 'remove redundant packs' '
++test_expect_success 'remove redundant packs and pass fsck' '
+ git pack-redundant --all | xargs rm &&
-+ git fsck &&
++ git fsck --no-progress &&
+ git pack-redundant --all >out &&
+ test_must_be_empty out
+'
2: 50cd5a5b47 ! 2: 51a9c2d8a5 pack-redundant: new algorithm to find min packs
@@ -67,7 +67,7 @@
Original PR and discussions: https://github.com/jiangxin/git/pull/25
Signed-off-by: Sun Chao [off-list ref]
- Signed-off-by: Jiang Xin [off-list ref]
+ Signed-off-by: Jiang Xin [off-list ref]
Signed-off-by: Junio C Hamano [off-list ref]
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
5: b7ccdea1ad ! 3: c5eb21c23c pack-redundant: remove unused functions
@@ -6,14 +6,14 @@
`pll_free`, etc.
Signed-off-by: Sun Chao [off-list ref]
- Signed-off-by: Jiang Xin [off-list ref]
+ Signed-off-by: Jiang Xin [off-list ref]
Signed-off-by: Junio C Hamano [off-list ref]
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@
- size_t all_objects_size;
+ struct llist *all_objects;
} *local_packs = NULL, *altodb_packs = NULL;
-struct pll {
@@ -105,7 +105,7 @@
- diff = llist_copy(list);
-
- while (pl) {
-- llist_sorted_difference_inplace(diff, pl->remaining_objects);
+- llist_sorted_difference_inplace(diff, pl->all_objects);
- if (diff->size == 0) { /* we're done */
- llist_free(diff);
- return 1;
3: 6338c6fad4 ! 4: 1acdd0af1e pack-redundant: rename pack_list.all_objects
@@ -18,16 +18,7 @@
+ struct llist *remaining_objects;
} *local_packs = NULL, *altodb_packs = NULL;
- struct pll {
-@@
- diff = llist_copy(list);
-
- while (pl) {
-- llist_sorted_difference_inplace(diff, pl->all_objects);
-+ llist_sorted_difference_inplace(diff, pl->remaining_objects);
- if (diff->size == 0) { /* we're done */
- llist_free(diff);
- return 1;
+ static struct llist_item *free_nodes;
@@
{
struct pack_list *pl_a = *((struct pack_list **)a);
4: 734f4d8a8b ! 5: 306d515cda pack-redundant: consistent sort method
@@ -26,7 +26,7 @@
+ size_t all_objects_size;
} *local_packs = NULL, *altodb_packs = NULL;
- struct pll {
+ static struct llist_item *free_nodes;
@@
return ret;
}
@@ -42,20 +42,24 @@
- if (sz_a == sz_b)
- return 0;
- else if (sz_a < sz_b)
-+ /* if have the same remaining_objects, big pack first */
-+ if (pl_a->remaining_objects->size == pl_b->remaining_objects->size)
++ if (pl_a->remaining_objects->size == pl_b->remaining_objects->size) {
++ /* have the same remaining_objects, big pack first */
+ if (pl_a->all_objects_size == pl_b->all_objects_size)
+ return 0;
+ else if (pl_a->all_objects_size < pl_b->all_objects_size)
+ return 1;
+ else
+ return -1;
-+
-+ /* sort according to remaining objects, more remaining objects first */
-+ if (pl_a->remaining_objects->size < pl_b->remaining_objects->size)
++ } else if (pl_a->remaining_objects->size < pl_b->remaining_objects->size) {
++ /* sort by remaining objects, more objects first */
return 1;
- else
+- else
++ } else {
return -1;
++ }
+ }
+
+ /* Sort pack_list, greater size of remaining_objects first */
@@
for (n = 0, p = *pl; p; p = p->next)
ary[n++] = p;
## This reroll has the following commits:
Jiang Xin (3):
t5323: test cases for git-pack-redundant
pack-redundant: rename pack_list.all_objects
pack-redundant: consistent sort method
Sun Chao (2):
pack-redundant: new algorithm to find min packs
pack-redundant: remove unused functions
builtin/pack-redundant.c | 221 +++++++++++++++-----------------------
t/t5323-pack-redundant.sh | 207 +++++++++++++++++++++++++++++++++++
2 files changed, 292 insertions(+), 136 deletions(-)
create mode 100755 t/t5323-pack-redundant.sh
--
2.20.0.3.gc45e608566
@@ -0,0 +1,207 @@+#!/bin/sh+#+# Copyright (c) 2018 Jiang Xin+#++test_description='git pack-redundant test'++../test-lib.sh++create_commits(){+parent=+fornameinABCDEFGHIJKLMNOPQR+do+test_tick&&+T=$(gitwrite-tree)&&+iftest-z"$parent"+then+oid=$(echo$name|gitcommit-tree$T)+else+oid=$(echo$name|gitcommit-tree-p$parent$T)+fi&&+eval$name=$oid&&+parent=$oid||+return1+done+gitupdate-refrefs/heads/master$R+}++create_pack_1(){+P1=$(git-C.git/objects/packpack-objects-qpack<<-EOF+$T+$A+$B+$C+$D+$E+$F+$R+EOF+)&&+evalP$P1=P1:$P1+}++create_pack_2(){+P2=$(git-C.git/objects/packpack-objects-qpack<<-EOF+$B+$C+$D+$E+$G+$H+$I+EOF+)&&+evalP$P2=P2:$P2+}++create_pack_3(){+P3=$(git-C.git/objects/packpack-objects-qpack<<-EOF+$F+$I+$J+$K+$L+$M+EOF+)&&+evalP$P3=P3:$P3+}++create_pack_4(){+P4=$(git-C.git/objects/packpack-objects-qpack<<-EOF+$J+$K+$L+$M+$P+EOF+)&&+evalP$P4=P4:$P4+}++create_pack_5(){+P5=$(git-C.git/objects/packpack-objects-qpack<<-EOF+$G+$H+$N+$O+EOF+)&&+evalP$P5=P5:$P5+}++create_pack_6(){+P6=$(git-C.git/objects/packpack-objects-qpack<<-EOF+$N+$O+$Q+EOF+)&&+evalP$P6=P6:$P6+}++create_pack_7(){+P7=$(git-C.git/objects/packpack-objects-qpack<<-EOF+$P+$Q+EOF+)&&+evalP$P7=P7:$P7+}++create_pack_8(){+P8=$(git-C.git/objects/packpack-objects-qpack<<-EOF+$A+EOF+)&&+evalP$P8=P8:$P8+}++test_expect_success'setup''+create_commits+'++test_expect_success'no redundant packs''+create_pack_1&&create_pack_2&&create_pack_3&&+gitpack-redundant--all>out&&+test_must_be_emptyout+'++test_expect_success'create pack 4, 5''+create_pack_4&&create_pack_5+'++cat>expected<<EOF+P2:$P2+EOF++test_expect_success'one of pack-2/pack-3 is redundant''+gitpack-redundant--all>out&&+sed\+-e"s#.*/pack-\(.*\)\.idx#\1#"\+-e"s#.*/pack-\(.*\)\.pack#\1#"out|+sort-u|+whilereadp;doevalecho"\${P$p}";done|+sort>actual&&+test_cmpexpectedactual+'++test_expect_success'create pack 6, 7''+create_pack_6&&create_pack_7+'++# Only after calling create_pack_6, we can use $P6 variable.+cat>expected<<EOF+P2:$P2+P4:$P4+P6:$P6+EOF++test_expect_success'pack 2, 4, and 6 are redundant''+gitpack-redundant--all>out&&+sed\+-e"s#.*/pack-\(.*\)\.idx#\1#"\+-e"s#.*/pack-\(.*\)\.pack#\1#"out|+sort-u|+whilereadp;doevalecho"\${P$p}";done|+sort>actual&&+test_cmpexpectedactual+'++test_expect_success'create pack 8''+create_pack_8+'++cat>expected<<EOF+P2:$P2+P4:$P4+P6:$P6+P8:$P8+EOF++test_expect_success'pack-8 (subset of pack-1) is also redundant''+gitpack-redundant--all>out&&+sed\+-e"s#.*/pack-\(.*\)\.idx#\1#"\+-e"s#.*/pack-\(.*\)\.pack#\1#"out|+sort-u|+whilereadp;doevalecho"\${P$p}";done|+sort>actual&&+test_cmpexpectedactual+'++test_expect_success'clean loose objects''+gitprune-packed&&+find.git/objects-typef|sed-e"/objects\/pack\//d">out&&+test_must_be_emptyout+'++test_expect_success'remove redundant packs and pass fsck''+gitpack-redundant--all|xargsrm&&+gitfsck--no-progress&&+gitpack-redundant--all>out&&+test_must_be_emptyout+'++test_done
From: Sun Chao <redacted>
When calling `git pack-redundant --all`, if there are too many local
packs and too many redundant objects within them, the too deep iteration
of `get_permutations` will exhaust all the resources, and the process of
`git pack-redundant` will be killed.
The following script could create a repository with too many redundant
packs, and running `git pack-redundant --all` in the `test.git` repo
will die soon.
#!/bin/sh
repo="$(pwd)/test.git"
work="$(pwd)/test"
i=1
max=199
if test -d "$repo" || test -d "$work"; then
echo >&2 "ERROR: '$repo' or '$work' already exist"
exit 1
fi
git init -q --bare "$repo"
git --git-dir="$repo" config gc.auto 0
git --git-dir="$repo" config transfer.unpackLimit 0
git clone -q "$repo" "$work" 2>/dev/null
while :; do
cd "$work"
echo "loop $i: $(date +%s)" >$i
git add $i
git commit -q -sm "loop $i"
git push -q origin HEAD:master
printf "\rCreate pack %4d/%d\t" $i $max
if test $i -ge $max; then break; fi
cd "$repo"
git repack -q
if test $(($i % 2)) -eq 0; then
git repack -aq
pack=$(ls -t $repo/objects/pack/*.pack | head -1)
touch "${pack%.pack}.keep"
fi
i=$((i+1))
done
printf "\ndone\n"
To get the `min` unique pack list, we can replace the iteration in
`minimize` function with a new algorithm, and this could solve this
issue:
1. Get the unique and non_uniqe packs, add the unique packs to the
`min` list.
2. Remove the objects of unique packs from non_unique packs, then each
object left in the non_unique packs will have at least two copies.
3. Sort the non_unique packs by the objects' size, more objects first,
and add the first non_unique pack to `min` list.
4. Drop the duplicated objects from other packs in the ordered
non_unique pack list, and repeat step 3.
Original PR and discussions: https://github.com/jiangxin/git/pull/25
Signed-off-by: Sun Chao <redacted>
Signed-off-by: Jiang Xin <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
builtin/pack-redundant.c | 109 ++++++++++++++++++++++++---------------
1 file changed, 68 insertions(+), 41 deletions(-)
@@ -421,14 +421,52 @@ static inline off_t pack_set_bytecount(struct pack_list *pl)returnret;}+staticintcmp_pack_list_reverse(constvoid*a,constvoid*b)+{+structpack_list*pl_a=*((structpack_list**)a);+structpack_list*pl_b=*((structpack_list**)b);+size_tsz_a=pl_a->all_objects->size;+size_tsz_b=pl_b->all_objects->size;++if(sz_a==sz_b)+return0;+elseif(sz_a<sz_b)+return1;+else+return-1;+}++/* Sort pack_list, greater size of all_objects first */+staticvoidsort_pack_list(structpack_list**pl)+{+structpack_list**ary,*p;+inti;+size_tn=pack_list_size(*pl);++if(n<2)+return;++/* prepare an array of packed_list for easier sorting */+ary=xcalloc(n,sizeof(structpack_list*));+for(n=0,p=*pl;p;p=p->next)+ary[n++]=p;++QSORT(ary,n,cmp_pack_list_reverse);++/* link them back again */+for(i=0;i<n-1;i++)+ary[i]->next=ary[i+1];+ary[n-1]->next=NULL;+*pl=ary[0];++free(ary);+}++staticvoidminimize(structpack_list**min){-structpack_list*pl,*unique=NULL,-*non_unique=NULL,*min_perm=NULL;-structpll*perm,*perm_all,*perm_ok=NULL,*new_perm;-structllist*missing;-off_tmin_perm_size=0,perm_size;-intn;+structpack_list*pl,*unique=NULL,*non_unique=NULL;+structllist*missing,*unique_pack_objects;pl=local_packs;while(pl){
@@ -446,49 +484,37 @@ static void minimize(struct pack_list **min)pl=pl->next;}+*min=unique;+/* return if there are no objects missing from the unique set */if(missing->size==0){-*min=unique;free(missing);return;}-/* find the permutations which contain all missing objects */-for(n=1;n<=pack_list_size(non_unique)&&!perm_ok;n++){-perm_all=perm=get_permutations(non_unique,n);-while(perm){-if(is_superset(perm->pl,missing)){-new_perm=xmalloc(sizeof(structpll));-memcpy(new_perm,perm,sizeof(structpll));-new_perm->next=perm_ok;-perm_ok=new_perm;-}-perm=perm->next;-}-if(perm_ok)-break;-pll_free(perm_all);-}-if(perm_ok==NULL)-die("Internal error: No complete sets found!");--/* find the permutation with the smallest size */-perm=perm_ok;-while(perm){-perm_size=pack_set_bytecount(perm->pl);-if(!min_perm_size||min_perm_size>perm_size){-min_perm_size=perm_size;-min_perm=perm->pl;-}-perm=perm->next;-}-*min=min_perm;-/* add the unique packs to the list */-pl=unique;+unique_pack_objects=llist_copy(all_objects);+llist_sorted_difference_inplace(unique_pack_objects,missing);++/* remove unique pack objects from the non_unique packs */+pl=non_unique;while(pl){-pack_list_insert(min,pl);+llist_sorted_difference_inplace(pl->all_objects,unique_pack_objects);pl=pl->next;}++while(non_unique){+/* sort the non_unique packs, greater size of all_objects first */+sort_pack_list(&non_unique);+if(non_unique->all_objects->size==0)+break;++pack_list_insert(min,non_unique);++for(pl=non_unique->next;pl&&pl->all_objects->size>0;pl=pl->next)+llist_sorted_difference_inplace(pl->all_objects,non_unique->all_objects);++non_unique=non_unique->next;+}}staticvoidload_all_objects(void)
From: Sun Chao <redacted>
Remove unused functions to find `min` packs, such as `get_permutations`,
`pll_free`, etc.
Signed-off-by: Sun Chao <redacted>
Signed-off-by: Jiang Xin <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
builtin/pack-redundant.c | 86 ----------------------------------------
1 file changed, 86 deletions(-)
@@ -285,78 +271,6 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)}}-staticvoidpll_free(structpll*l)-{-structpll*old;-structpack_list*opl;--while(l){-old=l;-while(l->pl){-opl=l->pl;-l->pl=opl->next;-free(opl);-}-l=l->next;-free(old);-}-}--/* all the permutations have to be free()d at the same time,-*sincetheyrefertoeachother-*/-staticstructpll*get_permutations(structpack_list*list,intn)-{-structpll*subset,*ret=NULL,*new_pll=NULL;--if(list==NULL||pack_list_size(list)<n||n==0)-returnNULL;--if(n==1){-while(list){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=NULL;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-list=list->next;-}-returnret;-}--while(list->next){-subset=get_permutations(list->next,n-1);-while(subset){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=subset->pl;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-subset=subset->next;-}-list=list->next;-}-returnret;-}--staticintis_superset(structpack_list*pl,structllist*list)-{-structllist*diff;--diff=llist_copy(list);--while(pl){-llist_sorted_difference_inplace(diff,pl->all_objects);-if(diff->size==0){/* we're done */-llist_free(diff);-return1;-}-pl=pl->next;-}-llist_free(diff);-return0;-}-staticsize_tsizeof_union(structpacked_git*p1,structpacked_git*p2){size_tret=0;
@@ -412,20 +412,20 @@ static void minimize(struct pack_list **min)/* remove unique pack objects from the non_unique packs */pl=non_unique;while(pl){-llist_sorted_difference_inplace(pl->all_objects,unique_pack_objects);+llist_sorted_difference_inplace(pl->remaining_objects,unique_pack_objects);pl=pl->next;}while(non_unique){-/* sort the non_unique packs, greater size of all_objects first */+/* sort the non_unique packs, greater size of remaining_objects first */sort_pack_list(&non_unique);-if(non_unique->all_objects->size==0)+if(non_unique->remaining_objects->size==0)break;pack_list_insert(min,non_unique);-for(pl=non_unique->next;pl&&pl->all_objects->size>0;pl=pl->next)-llist_sorted_difference_inplace(pl->all_objects,non_unique->all_objects);+for(pl=non_unique->next;pl&&pl->remaining_objects->size>0;pl=pl->next)+llist_sorted_difference_inplace(pl->remaining_objects,non_unique->remaining_objects);non_unique=non_unique->next;}
@@ -504,11 +504,11 @@ static struct pack_list * add_pack(struct packed_git *p)base+=256*4+((p->index_version<2)?4:8);step=the_hash_algo->rawsz+((p->index_version<2)?4:0);while(off<p->num_objects*step){-llist_insert_back(l.all_objects,(conststructobject_id*)(base+off));+llist_insert_back(l.remaining_objects,(conststructobject_id*)(base+off));off+=step;}/* this list will be pruned in cmp_two_packs later */-l.unique_objects=llist_copy(l.all_objects);+l.unique_objects=llist_copy(l.remaining_objects);if(p->pack_local)returnpack_list_insert(&local_packs,&l);else
From: Jiang Xin <redacted>
SZEDER reported that test case t5323 has different test result on MacOS.
This is because `cmp_pack_list_reverse` cannot give identical result
when two pack being sorted has the same size of remaining_objects.
Changes to the sorting function will make consistent test result for
t5323.
The new algorithm to find redundant packs is a trade-off to save memory
resources, and the result of it may be different with old one, and may
be not the best result sometimes. Update t5323 for the new algorithm.
Reported-by: SZEDER Gábor <redacted>
Signed-off-by: Jiang Xin <redacted>
---
builtin/pack-redundant.c | 24 ++++++++++++++++--------
t/t5323-pack-redundant.sh | 2 +-
2 files changed, 17 insertions(+), 9 deletions(-)
@@ -335,19 +336,25 @@ static inline off_t pack_set_bytecount(struct pack_list *pl)returnret;}-staticintcmp_pack_list_reverse(constvoid*a,constvoid*b)+staticintcmp_remaining_objects(constvoid*a,constvoid*b){structpack_list*pl_a=*((structpack_list**)a);structpack_list*pl_b=*((structpack_list**)b);-size_tsz_a=pl_a->remaining_objects->size;-size_tsz_b=pl_b->remaining_objects->size;-if(sz_a==sz_b)-return0;-elseif(sz_a<sz_b)+if(pl_a->remaining_objects->size==pl_b->remaining_objects->size){+/* have the same remaining_objects, big pack first */+if(pl_a->all_objects_size==pl_b->all_objects_size)+return0;+elseif(pl_a->all_objects_size<pl_b->all_objects_size)+return1;+else+return-1;+}elseif(pl_a->remaining_objects->size<pl_b->remaining_objects->size){+/* sort by remaining objects, more objects first */return1;-else+}else{return-1;+}}/* Sort pack_list, greater size of remaining_objects first */
@@ -365,7 +372,7 @@ static void sort_pack_list(struct pack_list **pl)for(n=0,p=*pl;p;p=p->next)ary[n++]=p;-QSORT(ary,n,cmp_pack_list_reverse);+QSORT(ary,n,cmp_remaining_objects);/* link them back again */for(i=0;i<n-1;i++)
@@ -507,6 +514,7 @@ static struct pack_list * add_pack(struct packed_git *p)llist_insert_back(l.remaining_objects,(conststructobject_id*)(base+off));off+=step;}+l.all_objects_size=l.remaining_objects->size;/* this list will be pruned in cmp_two_packs later */l.unique_objects=llist_copy(l.remaining_objects);if(p->pack_local)
From: Torsten Bögershausen <redacted>
From `man sed` (on a Mac OS X box):
The -E, -a and -i options are non-standard FreeBSD extensions and may not be available
on other operating systems.
From `man sed` on a Linux box:
REGULAR EXPRESSIONS
POSIX.2 BREs should be supported, but they aren't completely because of
performance problems. The \n sequence in a regular expression matches
the newline character, and similarly for \a, \t, and other sequences.
The -E option switches to using extended regular expressions instead;
the -E option has been supported for years by GNU sed, and is now
included in POSIX.
Well, there are still a lot of systems out there, which don't support it.
Beside that, see IEEE Std 1003.1TM-2017
http://pubs.opengroup.org/onlinepubs/9699919799/
does not mention -E either.
To be on the safe side, don't allow it.
Reported-by: SZEDER Gábor <redacted>
Signed-off-by: Torsten Bögershausen <redacted>
---
I am somewhat unsure if we should disable all options except -e -f -n
instead ?
/\bsed\s+-[^efn]/ and err 'Not portable option with sed. Only -n -e -f are portable';
That would cause a false positive in t9001 here:
"--cc-cmd=./cccmd-sed --suppress-cc=self"
which could either be fixed by an anchor:
/^\s*sed\s+-[^efn]/
Or by allowing '--' like this:
/\bsed\s+-[^-efn]/
Any thoughts, please ?
t/check-non-portable-shell.pl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
@@ -35,7 +35,7 @@ sub err {chomp;}-/\bsed\s+-i/anderr'sed -i is not portable';+/\bsed\s+-[Eail]/anderr'Not portable option with sed. Only -e -f -n are portable';/\becho\s+-[neE]/anderr'echo with option is not portable (use printf)';/^\s*declare\s+/anderr'arrays/declare not portable';/^\s*[^#]\s*which\s/anderr'which is not portable (use type)';--
From: Eric Sunshine <hidden> Date: 2019-01-15 21:09:35
On Tue, Jan 15, 2019 at 3:31 PM [off-list ref] wrote:
quoted hunk
From `man sed` (on a Mac OS X box):
The -E, -a and -i options are non-standard FreeBSD extensions and may not be available
on other operating systems.
[...]
To be on the safe side, don't allow it.
Signed-off-by: Torsten Bögershausen <redacted>
---
@@ -35,7 +35,7 @@ sub err {- /\bsed\s+-i/ and err 'sed -i is not portable';+ /\bsed\s+-[Eail]/ and err 'Not portable option with sed. Only -e -f -n are portable'; /\becho\s+-[neE]/ and err 'echo with option is not portable (use printf)'; /^\s*declare\s+/ and err 'arrays/declare not portable'; /^\s*[^#]\s*which\s/ and err 'which is not portable (use type)';
Please update the new message to be more consistent with existing
surrounding error messages. For instance:
err 'sed -i/-a/-l/-E not portable (use only -e/-f/-n)'
or something. Thanks.
From: Torsten Bögershausen <redacted>
From `man sed` (on a Mac OS X box):
The -E, -a and -i options are non-standard FreeBSD extensions and may not be available
on other operating systems.
From `man sed` on a Linux box:
REGULAR EXPRESSIONS
POSIX.2 BREs should be supported, but they aren't completely because of
performance problems. The \n sequence in a regular expression matches
the newline character, and similarly for \a, \t, and other sequences.
The -E option switches to using extended regular expressions instead;
the -E option has been supported for years by GNU sed, and is now
included in POSIX.
Well, there are still a lot of systems out there, which don't support it.
Beside that, see IEEE Std 1003.1TM-2017
http://pubs.opengroup.org/onlinepubs/9699919799/
does not mention -E either.
To be on the safe side, don't allow it.
Reported-by: SZEDER Gábor <redacted>
Signed-off-by: Torsten Bögershausen <redacted>
---
I am somewhat unsure if we should disable all options except -e -f -n
instead ?
/\bsed\s+-[^efn]/ and err 'Not portable option with sed. Only -n -e -f are portable';
That would cause a false positive in t9001 here:
"--cc-cmd=./cccmd-sed --suppress-cc=self"
which could either be fixed by an anchor:
/^\s*sed\s+-[^efn]/
Or by allowing '--' like this:
/\bsed\s+-[^-efn]/
Any thoughts, please ?
t/check-non-portable-shell.pl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
@@ -35,7 +35,7 @@ sub err {chomp;}-/\bsed\s+-i/anderr'sed -i is not portable';+/\bsed\s+-[Eail]/anderr'Not portable option with sed. Only -e -f -n are portable';/\becho\s+-[neE]/anderr'echo with option is not portable (use printf)';/^\s*declare\s+/anderr'arrays/declare not portable';/^\s*[^#]\s*which\s/anderr'which is not portable (use type)';
I'd just go for your /\bsed\s+-[^-efn]/ suggestion. Just a note if we do
go for the whitelist: According to GNU sed's manpage -E is also known as
-r, so /\bsed\s+-[Erail]/ would be better.
From: Torsten Bögershausen <redacted>
From `man sed` (on a Mac OS X box):
The -E, -a and -i options are non-standard FreeBSD extensions and may not be available
on other operating systems.
From `man sed` on a Linux box:
REGULAR EXPRESSIONS
POSIX.2 BREs should be supported, but they aren't completely because of
performance problems. The \n sequence in a regular expression matches the newline
character, and similarly for \a, \t, and other sequences.
The -E option switches to using extended regular expressions instead; the -E option
has been supported for years by GNU sed, and is now included in POSIX.
Well, there are still a lot of systems out there, which don't support it.
Beside that, IEEE Std 1003.1TM-2017, see
http://pubs.opengroup.org/onlinepubs/9699919799/
does not mention -E either.
To be on the safe side, don't allow -E (or -r, which is GNU).
Change check-non-portable-shell.pl to only accept the portable options:
sed [-n] [-e command] [-f command_file]
Reported-by: SZEDER Gábor <redacted>
Helped-by: Eric Sunshine [off-list ref]
Helped-by: Ævar Arnfjörð Bjarmason [off-list ref]
Signed-off-by: Torsten Bögershausen <redacted>
---
t/check-non-portable-shell.pl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
@@ -35,7 +35,7 @@ sub err {chomp;}-/\bsed\s+-i/anderr'sed -i is not portable';+/\bsed\s+-[^efn]\s+/anderr'Not portable option with sed (use only [-n] [-e command] [-f command_file])';/\becho\s+-[neE]/anderr'echo with option is not portable (use printf)';/^\s*declare\s+/anderr'arrays/declare not portable';/^\s*[^#]\s*which\s/anderr'which is not portable (use type)';--
Sun Chao (my former colleague at Huawei) found a bug of
git-pack-redundant. If there are too many packs and many of them
overlap each other, running `git pack-redundant --all` will
exhaust all memories and the process will be killed by kernel.
There is a script in commit log of commit 3/6, which can be used to
create a repository with lots of redundant packs. Running `git
pack-redundant --all` in it can reproduce this issue.
Derrick Stolee [off-list ref] 于2019年1月20日周日 上午9:08写道:
Here is today's test coverage report.
builtin/pack-redundant.c
a338d10395 builtin/pack-redundant.c 339) static int cmp_remaining_objects(const void *a, const void *b)
e4e2c2884e builtin/pack-redundant.c 341) struct pack_list *pl_a = *((struct pack_list **)a);
e4e2c2884e builtin/pack-redundant.c 342) struct pack_list *pl_b = *((struct pack_list **)b);
...
Add new test cases in t5323 for better test coverage.
## Changes since reroll v6
* Add new test cases in t5323.
* Add new patch 2/6 (pack-redundant: delay creation of unique_objects),
which will fix a bug which fail to find redundant packs if turn on alt-odb
searching with `--alt-odb` option. This is because alt-odb objects are
only remove in unique_objects fields but not in all_objects fields of
pack_list.
## Range diff
1: be6555ae60 ! 1: 799e804d5e t5323: test cases for git-pack-redundant
@@ -43,7 +43,7 @@
+}
+
+create_pack_1 () {
-+ P1=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ P1=$(git -C objects/pack pack-objects -q pack <<-EOF
+ $T
+ $A
+ $B
@@ -58,7 +58,7 @@
+}
+
+create_pack_2 () {
-+ P2=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ P2=$(git -C objects/pack pack-objects -q pack <<-EOF
+ $B
+ $C
+ $D
@@ -72,7 +72,7 @@
+}
+
+create_pack_3 () {
-+ P3=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ P3=$(git -C objects/pack pack-objects -q pack <<-EOF
+ $F
+ $I
+ $J
@@ -85,7 +85,7 @@
+}
+
+create_pack_4 () {
-+ P4=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ P4=$(git -C objects/pack pack-objects -q pack <<-EOF
+ $J
+ $K
+ $L
@@ -97,7 +97,7 @@
+}
+
+create_pack_5 () {
-+ P5=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ P5=$(git -C objects/pack pack-objects -q pack <<-EOF
+ $G
+ $H
+ $N
@@ -108,7 +108,7 @@
+}
+
+create_pack_6 () {
-+ P6=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ P6=$(git -C objects/pack pack-objects -q pack <<-EOF
+ $N
+ $O
+ $Q
@@ -118,7 +118,7 @@
+}
+
+create_pack_7 () {
-+ P7=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ P7=$(git -C objects/pack pack-objects -q pack <<-EOF
+ $P
+ $Q
+ EOF
@@ -127,18 +127,37 @@
+}
+
+create_pack_8 () {
-+ P8=$(git -C .git/objects/pack pack-objects -q pack <<-EOF
++ P8=$(git -C objects/pack pack-objects -q pack <<-EOF
+ $A
+ EOF
+ ) &&
+ eval P$P8=P8:$P8
+}
+
-+test_expect_success 'setup' '
++format_packfiles () {
++ sed \
++ -e "s#.*/pack-\(.*\)\.idx#\1#" \
++ -e "s#.*/pack-\(.*\)\.pack#\1#" |
++ sort -u |
++ while read p
++ do
++ if test -z "$(eval echo \${P$p})"
++ then
++ echo $p
++ else
++ eval echo "\${P$p}"
++ fi
++ done |
++ sort
++}
++
++test_expect_success 'setup master.git' '
++ git init --bare master.git &&
++ cd master.git &&
+ create_commits
+'
+
-+test_expect_success 'no redundant packs' '
++test_expect_success 'no redundant for pack 1, 2, 3' '
+ create_pack_1 && create_pack_2 && create_pack_3 &&
+ git pack-redundant --all >out &&
+ test_must_be_empty out
@@ -154,12 +173,7 @@
+
+test_expect_success 'one of pack-2/pack-3 is redundant' '
+ git pack-redundant --all >out &&
-+ sed \
-+ -e "s#.*/pack-\(.*\)\.idx#\1#" \
-+ -e "s#.*/pack-\(.*\)\.pack#\1#" out |
-+ sort -u |
-+ while read p; do eval echo "\${P$p}"; done |
-+ sort >actual &&
++ format_packfiles <out >actual &&
+ test_cmp expected actual
+'
+
@@ -176,12 +190,7 @@
+
+test_expect_success 'pack 2, 4, and 6 are redundant' '
+ git pack-redundant --all >out &&
-+ sed \
-+ -e "s#.*/pack-\(.*\)\.idx#\1#" \
-+ -e "s#.*/pack-\(.*\)\.pack#\1#" out |
-+ sort -u |
-+ while read p; do eval echo "\${P$p}"; done |
-+ sort >actual &&
++ format_packfiles <out >actual &&
+ test_cmp expected actual
+'
+
@@ -198,18 +207,13 @@
+
+test_expect_success 'pack-8 (subset of pack-1) is also redundant' '
+ git pack-redundant --all >out &&
-+ sed \
-+ -e "s#.*/pack-\(.*\)\.idx#\1#" \
-+ -e "s#.*/pack-\(.*\)\.pack#\1#" out |
-+ sort -u |
-+ while read p; do eval echo "\${P$p}"; done |
-+ sort >actual &&
++ format_packfiles <out >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'clean loose objects' '
+ git prune-packed &&
-+ find .git/objects -type f | sed -e "/objects\/pack\//d" >out &&
++ find objects -type f | sed -e "/objects\/pack\//d" >out &&
+ test_must_be_empty out
+'
+
@@ -220,4 +224,115 @@
+ test_must_be_empty out
+'
+
++test_expect_success 'setup shared.git' '
++ cd "$TRASH_DIRECTORY" &&
++ git clone -q --mirror master.git shared.git &&
++ cd shared.git &&
++ printf "../../master.git/objects" >objects/info/alternates
++'
++
++test_expect_success 'no redundant packs without --alt-odb' '
++ git pack-redundant --all >out &&
++ test_must_be_empty out
++'
++
++cat >expected <<EOF
++P1:$P1
++P3:$P3
++P5:$P5
++P7:$P7
++EOF
++
++test_expect_success 'pack-redundant --verbose: show duplicate packs in stderr' '
++ git pack-redundant --all --verbose >out 2>out.err &&
++ test_must_be_empty out &&
++ grep "pack$" out.err | format_packfiles >actual &&
++ test_cmp expected actual
++'
++
++cat >expected <<EOF
++fatal: Zero packs found!
++EOF
++
++test_expect_success 'remove redundant packs by alt-odb, no packs left' '
++ git pack-redundant --all --alt-odb | xargs rm &&
++ git fsck --no-progress &&
++ test_must_fail git pack-redundant --all --alt-odb >actual 2>&1 &&
++ test_cmp expected actual
++'
++
++create_commits_others () {
++ parent=$(git rev-parse HEAD)
++ for name in X Y Z
++ do
++ test_tick &&
++ T=$(git write-tree) &&
++ if test -z "$parent"
++ then
++ oid=$(echo $name | git commit-tree $T)
++ else
++ oid=$(echo $name | git commit-tree -p $parent $T)
++ fi &&
++ eval $name=$oid &&
++ parent=$oid ||
++ return 1
++ done
++ git update-ref refs/heads/master $Z
++}
++
++create_pack_x1 () {
++ Px1=$(git -C objects/pack pack-objects -q pack <<-EOF
++ $X
++ $Y
++ $Z
++ $A
++ $B
++ $C
++ EOF
++ ) &&
++ eval P${Px1}=Px1:${Px1}
++}
++
++create_pack_x2 () {
++ Px2=$(git -C objects/pack pack-objects -q pack <<-EOF
++ $X
++ $Y
++ $Z
++ $D
++ $E
++ $F
++ EOF
++ ) &&
++ eval P${Px2}=Px2:${Px2}
++}
++
++test_expect_success 'new objects and packs in shared.git' '
++ create_commits_others &&
++ create_pack_x1 &&
++ create_pack_x2 &&
++ git pack-redundant --all >out &&
++ test_must_be_empty out
++'
++
++test_expect_success 'one pack is redundant' '
++ git pack-redundant --all --alt-odb >out &&
++ format_packfiles <out >actual &&
++ test_line_count = 1 actual
++'
++
++cat >expected <<EOF
++Px1:$Px1
++Px2:$Px2
++EOF
++
++test_expect_success 'set ignore objects and all two packs are redundant' '
++ git pack-redundant --all --alt-odb >out <<-EOF &&
++ $X
++ $Y
++ $Z
++ EOF
++ format_packfiles <out >actual &&
++ test_cmp expected actual
++'
++
+test_done
-: ---------- > 2: 520f6277fb pack-redundant: delay creation of unique_objects
2: e4e2c2884e ! 3: ab1c2c4950 pack-redundant: new algorithm to find min packs
@@ -64,6 +64,9 @@
4. Drop the duplicated objects from other packs in the ordered
non_unique pack list, and repeat step 3.
+ Some test cases will fail on Mac OS X. Mark them and will resolve in
+ later commit.
+
Original PR and discussions: https://github.com/jiangxin/git/pull/25
Signed-off-by: Sun Chao [off-list ref]
@@ -213,11 +216,61 @@
struct llist *ignore;
struct object_id *oid;
char buf[GIT_MAX_HEXSZ + 2]; /* hex hash + \n + \0 */
+
+ diff --git a/t/t5323-pack-redundant.sh b/t/t5323-pack-redundant.sh
+ --- a/t/t5323-pack-redundant.sh
+ +++ b/t/t5323-pack-redundant.sh
@@
- pl = local_packs;
- while (pl) {
- llist_sorted_difference_inplace(pl->unique_objects, ignore);
-+ llist_sorted_difference_inplace(pl->all_objects, ignore);
- pl = pl->next;
- }
+ P2:$P2
+ EOF
+
+-test_expect_success 'one of pack-2/pack-3 is redundant' '
++test_expect_failure 'one of pack-2/pack-3 is redundant' '
+ git pack-redundant --all >out &&
+ format_packfiles <out >actual &&
+ test_cmp expected actual
+@@
+ P6:$P6
+ EOF
+
+-test_expect_success 'pack 2, 4, and 6 are redundant' '
++test_expect_failure 'pack 2, 4, and 6 are redundant' '
+ git pack-redundant --all >out &&
+ format_packfiles <out >actual &&
+ test_cmp expected actual
+@@
+ P8:$P8
+ EOF
+
+-test_expect_success 'pack-8 (subset of pack-1) is also redundant' '
++test_expect_failure 'pack-8 (subset of pack-1) is also redundant' '
+ git pack-redundant --all >out &&
+ format_packfiles <out >actual &&
+ test_cmp expected actual
+@@
+ test_must_be_empty out
+ '
+
+-test_expect_success 'remove redundant packs and pass fsck' '
++test_expect_failure 'remove redundant packs and pass fsck' '
+ git pack-redundant --all | xargs rm &&
+ git fsck --no-progress &&
+ git pack-redundant --all >out &&
+@@
+ printf "../../master.git/objects" >objects/info/alternates
+ '
+
+-test_expect_success 'no redundant packs without --alt-odb' '
++test_expect_failure 'no redundant packs without --alt-odb' '
+ git pack-redundant --all >out &&
+ test_must_be_empty out
+ '
+@@
+ P7:$P7
+ EOF
+-test_expect_success 'pack-redundant --verbose: show duplicate packs in stderr' '
++test_expect_failure 'pack-redundant --verbose: show duplicate packs in stderr' '
+ git pack-redundant --all --verbose >out 2>out.err &&
+ test_must_be_empty out &&
+ grep "pack$" out.err | format_packfiles >actual &&
3: e60b134e66 = 4: 3c3a7ea40f pack-redundant: remove unused functions
4: cb7e0336fc ! 5: bc4b681f40 pack-redundant: rename pack_list.all_objects
@@ -20,6 +20,18 @@
} *local_packs = NULL, *altodb_packs = NULL;
static struct llist_item *free_nodes;
+@@
+ const unsigned int hashsz = the_hash_algo->rawsz;
+
+ if (!p1->unique_objects)
+- p1->unique_objects = llist_copy(p1->all_objects);
++ p1->unique_objects = llist_copy(p1->remaining_objects);
+ if (!p2->unique_objects)
+- p2->unique_objects = llist_copy(p2->all_objects);
++ p2->unique_objects = llist_copy(p2->remaining_objects);
+
+ p1_base = p1->pack->index_data;
+ p2_base = p2->pack->index_data;
@@
{
struct pack_list *pl_a = *((struct pack_list **)a);
@@ -94,10 +106,12 @@
}
}
@@
+ while (alt) {
local = local_packs;
while (local) {
- llist_sorted_difference_inplace(local->unique_objects,
+- llist_sorted_difference_inplace(local->all_objects,
- alt->all_objects);
++ llist_sorted_difference_inplace(local->remaining_objects,
+ alt->remaining_objects);
local = local->next;
}
@@ -123,16 +137,11 @@
+ llist_insert_back(l.remaining_objects, (const struct object_id *)(base + off));
off += step;
}
- /* this list will be pruned in cmp_two_packs later */
-- l.unique_objects = llist_copy(l.all_objects);
-+ l.unique_objects = llist_copy(l.remaining_objects);
- if (p->pack_local)
- return pack_list_insert(&local_packs, &l);
- else
+ l.unique_objects = NULL;
@@
+ llist_sorted_difference_inplace(all_objects, ignore);
pl = local_packs;
while (pl) {
- llist_sorted_difference_inplace(pl->unique_objects, ignore);
- llist_sorted_difference_inplace(pl->all_objects, ignore);
+ llist_sorted_difference_inplace(pl->remaining_objects, ignore);
pl = pl->next;
5: a338d10395 ! 6: 6cfba5b4b2 pack-redundant: consistent sort method
@@ -75,9 +75,9 @@
off += step;
}
+ l.all_objects_size = l.remaining_objects->size;
- /* this list will be pruned in cmp_two_packs later */
- l.unique_objects = llist_copy(l.remaining_objects);
+ l.unique_objects = NULL;
if (p->pack_local)
+ return pack_list_insert(&local_packs, &l);
diff --git a/t/t5323-pack-redundant.sh b/t/t5323-pack-redundant.sh
--- a/t/t5323-pack-redundant.sh
@@ -90,4 +90,53 @@
+P3:$P3
EOF
- test_expect_success 'one of pack-2/pack-3 is redundant' '
+-test_expect_failure 'one of pack-2/pack-3 is redundant' '
++test_expect_success 'one of pack-2/pack-3 is redundant' '
+ git pack-redundant --all >out &&
+ format_packfiles <out >actual &&
+ test_cmp expected actual
+@@
+ P6:$P6
+ EOF
+
+-test_expect_failure 'pack 2, 4, and 6 are redundant' '
++test_expect_success 'pack 2, 4, and 6 are redundant' '
+ git pack-redundant --all >out &&
+ format_packfiles <out >actual &&
+ test_cmp expected actual
+@@
+ P8:$P8
+ EOF
+
+-test_expect_failure 'pack-8 (subset of pack-1) is also redundant' '
++test_expect_success 'pack-8 (subset of pack-1) is also redundant' '
+ git pack-redundant --all >out &&
+ format_packfiles <out >actual &&
+ test_cmp expected actual
+@@
+ test_must_be_empty out
+ '
+
+-test_expect_failure 'remove redundant packs and pass fsck' '
++test_expect_success 'remove redundant packs and pass fsck' '
+ git pack-redundant --all | xargs rm &&
+ git fsck --no-progress &&
+ git pack-redundant --all >out &&
+@@
+ printf "../../master.git/objects" >objects/info/alternates
+ '
+
+-test_expect_failure 'no redundant packs without --alt-odb' '
++test_expect_success 'no redundant packs without --alt-odb' '
+ git pack-redundant --all >out &&
+ test_must_be_empty out
+ '
+@@
+ P7:$P7
+ EOF
+
+-test_expect_failure 'pack-redundant --verbose: show duplicate packs in stderr' '
++test_expect_success 'pack-redundant --verbose: show duplicate packs in stderr' '
+ git pack-redundant --all --verbose >out 2>out.err &&
+ test_must_be_empty out &&
+ grep "pack$" out.err | format_packfiles >actual &&
--
Jiang Xin (4):
t5323: test cases for git-pack-redundant
pack-redundant: delay creation of unique_objects
pack-redundant: rename pack_list.all_objects
pack-redundant: consistent sort method
Sun Chao (2):
pack-redundant: new algorithm to find min packs
pack-redundant: remove unused functions
builtin/pack-redundant.c | 233 +++++++++++----------------
t/t5323-pack-redundant.sh | 322 ++++++++++++++++++++++++++++++++++++++
2 files changed, 415 insertions(+), 140 deletions(-)
create mode 100755 t/t5323-pack-redundant.sh
--
2.20.1.103.ged0fc2ca7b
@@ -0,0 +1,322 @@+#!/bin/sh+#+# Copyright (c) 2018 Jiang Xin+#++test_description='git pack-redundant test'++../test-lib.sh++create_commits(){+parent=+fornameinABCDEFGHIJKLMNOPQR+do+test_tick&&+T=$(gitwrite-tree)&&+iftest-z"$parent"+then+oid=$(echo$name|gitcommit-tree$T)+else+oid=$(echo$name|gitcommit-tree-p$parent$T)+fi&&+eval$name=$oid&&+parent=$oid||+return1+done+gitupdate-refrefs/heads/master$R+}++create_pack_1(){+P1=$(git-Cobjects/packpack-objects-qpack<<-EOF+$T+$A+$B+$C+$D+$E+$F+$R+EOF+)&&+evalP$P1=P1:$P1+}++create_pack_2(){+P2=$(git-Cobjects/packpack-objects-qpack<<-EOF+$B+$C+$D+$E+$G+$H+$I+EOF+)&&+evalP$P2=P2:$P2+}++create_pack_3(){+P3=$(git-Cobjects/packpack-objects-qpack<<-EOF+$F+$I+$J+$K+$L+$M+EOF+)&&+evalP$P3=P3:$P3+}++create_pack_4(){+P4=$(git-Cobjects/packpack-objects-qpack<<-EOF+$J+$K+$L+$M+$P+EOF+)&&+evalP$P4=P4:$P4+}++create_pack_5(){+P5=$(git-Cobjects/packpack-objects-qpack<<-EOF+$G+$H+$N+$O+EOF+)&&+evalP$P5=P5:$P5+}++create_pack_6(){+P6=$(git-Cobjects/packpack-objects-qpack<<-EOF+$N+$O+$Q+EOF+)&&+evalP$P6=P6:$P6+}++create_pack_7(){+P7=$(git-Cobjects/packpack-objects-qpack<<-EOF+$P+$Q+EOF+)&&+evalP$P7=P7:$P7+}++create_pack_8(){+P8=$(git-Cobjects/packpack-objects-qpack<<-EOF+$A+EOF+)&&+evalP$P8=P8:$P8+}++format_packfiles(){+sed\+-e"s#.*/pack-\(.*\)\.idx#\1#"\+-e"s#.*/pack-\(.*\)\.pack#\1#"|+sort-u|+whilereadp+do+iftest-z"$(evalecho\${P$p})"+then+echo$p+else+evalecho"\${P$p}"+fi+done|+sort+}++test_expect_success'setup master.git''+gitinit--baremaster.git&&+cdmaster.git&&+create_commits+'++test_expect_success'no redundant for pack 1, 2, 3''+create_pack_1&&create_pack_2&&create_pack_3&&+gitpack-redundant--all>out&&+test_must_be_emptyout+'++test_expect_success'create pack 4, 5''+create_pack_4&&create_pack_5+'++cat>expected<<EOF+P2:$P2+EOF++test_expect_success'one of pack-2/pack-3 is redundant''+gitpack-redundant--all>out&&+format_packfiles<out>actual&&+test_cmpexpectedactual+'++test_expect_success'create pack 6, 7''+create_pack_6&&create_pack_7+'++# Only after calling create_pack_6, we can use $P6 variable.+cat>expected<<EOF+P2:$P2+P4:$P4+P6:$P6+EOF++test_expect_success'pack 2, 4, and 6 are redundant''+gitpack-redundant--all>out&&+format_packfiles<out>actual&&+test_cmpexpectedactual+'++test_expect_success'create pack 8''+create_pack_8+'++cat>expected<<EOF+P2:$P2+P4:$P4+P6:$P6+P8:$P8+EOF++test_expect_success'pack-8 (subset of pack-1) is also redundant''+gitpack-redundant--all>out&&+format_packfiles<out>actual&&+test_cmpexpectedactual+'++test_expect_success'clean loose objects''+gitprune-packed&&+findobjects-typef|sed-e"/objects\/pack\//d">out&&+test_must_be_emptyout+'++test_expect_success'remove redundant packs and pass fsck''+gitpack-redundant--all|xargsrm&&+gitfsck--no-progress&&+gitpack-redundant--all>out&&+test_must_be_emptyout+'++test_expect_success'setup shared.git''+cd"$TRASH_DIRECTORY"&&+gitclone-q--mirrormaster.gitshared.git&&+cdshared.git&&+printf"../../master.git/objects">objects/info/alternates+'++test_expect_success'no redundant packs without --alt-odb''+gitpack-redundant--all>out&&+test_must_be_emptyout+'++cat>expected<<EOF+P1:$P1+P3:$P3+P5:$P5+P7:$P7+EOF++test_expect_success'pack-redundant --verbose: show duplicate packs in stderr''+gitpack-redundant--all--verbose>out2>out.err&&+test_must_be_emptyout&&+grep"pack$"out.err|format_packfiles>actual&&+test_cmpexpectedactual+'++cat>expected<<EOF+fatal:Zeropacksfound!+EOF++test_expect_success'remove redundant packs by alt-odb, no packs left''+gitpack-redundant--all--alt-odb|xargsrm&&+gitfsck--no-progress&&+test_must_failgitpack-redundant--all--alt-odb>actual2>&1&&+test_cmpexpectedactual+'++create_commits_others(){+parent=$(gitrev-parseHEAD)+fornameinXYZ+do+test_tick&&+T=$(gitwrite-tree)&&+iftest-z"$parent"+then+oid=$(echo$name|gitcommit-tree$T)+else+oid=$(echo$name|gitcommit-tree-p$parent$T)+fi&&+eval$name=$oid&&+parent=$oid||+return1+done+gitupdate-refrefs/heads/master$Z+}++create_pack_x1(){+Px1=$(git-Cobjects/packpack-objects-qpack<<-EOF+$X+$Y+$Z+$A+$B+$C+EOF+)&&+evalP${Px1}=Px1:${Px1}+}++create_pack_x2(){+Px2=$(git-Cobjects/packpack-objects-qpack<<-EOF+$X+$Y+$Z+$D+$E+$F+EOF+)&&+evalP${Px2}=Px2:${Px2}+}++test_expect_success'new objects and packs in shared.git''+create_commits_others&&+create_pack_x1&&+create_pack_x2&&+gitpack-redundant--all>out&&+test_must_be_emptyout+'++test_expect_success'one pack is redundant''+gitpack-redundant--all--alt-odb>out&&+format_packfiles<out>actual&&+test_line_count=1actual+'++cat>expected<<EOF+Px1:$Px1+Px2:$Px2+EOF++test_expect_success'set ignore objects and all two packs are redundant''+gitpack-redundant--all--alt-odb>out<<-EOF&&+$X+$Y+$Z+EOF+format_packfiles<out>actual&&+test_cmpexpectedactual+'++test_done
From: Jiang Xin <redacted>
Instead of initializing unique_objects in `add_pack()`, copy from
all_objects in `cmp_two_packs()`, when unwanted objects are removed from
all_objects.
This will save memory (no allocate memory for alt-odb packs), and run
`llist_sorted_difference_inplace()` only once when removing ignored
objects and removing objects in alt-odb in `scan_alt_odb_packs()`.
Signed-off-by: Jiang Xin <redacted>
---
builtin/pack-redundant.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
@@ -567,8 +572,7 @@ static struct pack_list * add_pack(struct packed_git *p)llist_insert_back(l.all_objects,(conststructobject_id*)(base+off));off+=step;}-/* this list will be pruned in cmp_two_packs later */-l.unique_objects=llist_copy(l.all_objects);+l.unique_objects=NULL;if(p->pack_local)returnpack_list_insert(&local_packs,&l);else
From: Sun Chao <redacted>
When calling `git pack-redundant --all`, if there are too many local
packs and too many redundant objects within them, the too deep iteration
of `get_permutations` will exhaust all the resources, and the process of
`git pack-redundant` will be killed.
The following script could create a repository with too many redundant
packs, and running `git pack-redundant --all` in the `test.git` repo
will die soon.
#!/bin/sh
repo="$(pwd)/test.git"
work="$(pwd)/test"
i=1
max=199
if test -d "$repo" || test -d "$work"; then
echo >&2 "ERROR: '$repo' or '$work' already exist"
exit 1
fi
git init -q --bare "$repo"
git --git-dir="$repo" config gc.auto 0
git --git-dir="$repo" config transfer.unpackLimit 0
git clone -q "$repo" "$work" 2>/dev/null
while :; do
cd "$work"
echo "loop $i: $(date +%s)" >$i
git add $i
git commit -q -sm "loop $i"
git push -q origin HEAD:master
printf "\rCreate pack %4d/%d\t" $i $max
if test $i -ge $max; then break; fi
cd "$repo"
git repack -q
if test $(($i % 2)) -eq 0; then
git repack -aq
pack=$(ls -t $repo/objects/pack/*.pack | head -1)
touch "${pack%.pack}.keep"
fi
i=$((i+1))
done
printf "\ndone\n"
To get the `min` unique pack list, we can replace the iteration in
`minimize` function with a new algorithm, and this could solve this
issue:
1. Get the unique and non_uniqe packs, add the unique packs to the
`min` list.
2. Remove the objects of unique packs from non_unique packs, then each
object left in the non_unique packs will have at least two copies.
3. Sort the non_unique packs by the objects' size, more objects first,
and add the first non_unique pack to `min` list.
4. Drop the duplicated objects from other packs in the ordered
non_unique pack list, and repeat step 3.
Some test cases will fail on Mac OS X. Mark them and will resolve in
later commit.
Original PR and discussions: https://github.com/jiangxin/git/pull/25
Signed-off-by: Sun Chao <redacted>
Signed-off-by: Jiang Xin <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
builtin/pack-redundant.c | 108 +++++++++++++++++++++++---------------
t/t5323-pack-redundant.sh | 12 ++---
2 files changed, 73 insertions(+), 47 deletions(-)
@@ -426,14 +426,52 @@ static inline off_t pack_set_bytecount(struct pack_list *pl)returnret;}+staticintcmp_pack_list_reverse(constvoid*a,constvoid*b)+{+structpack_list*pl_a=*((structpack_list**)a);+structpack_list*pl_b=*((structpack_list**)b);+size_tsz_a=pl_a->all_objects->size;+size_tsz_b=pl_b->all_objects->size;++if(sz_a==sz_b)+return0;+elseif(sz_a<sz_b)+return1;+else+return-1;+}++/* Sort pack_list, greater size of all_objects first */+staticvoidsort_pack_list(structpack_list**pl)+{+structpack_list**ary,*p;+inti;+size_tn=pack_list_size(*pl);++if(n<2)+return;++/* prepare an array of packed_list for easier sorting */+ary=xcalloc(n,sizeof(structpack_list*));+for(n=0,p=*pl;p;p=p->next)+ary[n++]=p;++QSORT(ary,n,cmp_pack_list_reverse);++/* link them back again */+for(i=0;i<n-1;i++)+ary[i]->next=ary[i+1];+ary[n-1]->next=NULL;+*pl=ary[0];++free(ary);+}++staticvoidminimize(structpack_list**min){-structpack_list*pl,*unique=NULL,-*non_unique=NULL,*min_perm=NULL;-structpll*perm,*perm_all,*perm_ok=NULL,*new_perm;-structllist*missing;-off_tmin_perm_size=0,perm_size;-intn;+structpack_list*pl,*unique=NULL,*non_unique=NULL;+structllist*missing,*unique_pack_objects;pl=local_packs;while(pl){
@@ -451,49 +489,37 @@ static void minimize(struct pack_list **min)pl=pl->next;}+*min=unique;+/* return if there are no objects missing from the unique set */if(missing->size==0){-*min=unique;free(missing);return;}-/* find the permutations which contain all missing objects */-for(n=1;n<=pack_list_size(non_unique)&&!perm_ok;n++){-perm_all=perm=get_permutations(non_unique,n);-while(perm){-if(is_superset(perm->pl,missing)){-new_perm=xmalloc(sizeof(structpll));-memcpy(new_perm,perm,sizeof(structpll));-new_perm->next=perm_ok;-perm_ok=new_perm;-}-perm=perm->next;-}-if(perm_ok)-break;-pll_free(perm_all);-}-if(perm_ok==NULL)-die("Internal error: No complete sets found!");--/* find the permutation with the smallest size */-perm=perm_ok;-while(perm){-perm_size=pack_set_bytecount(perm->pl);-if(!min_perm_size||min_perm_size>perm_size){-min_perm_size=perm_size;-min_perm=perm->pl;-}-perm=perm->next;-}-*min=min_perm;-/* add the unique packs to the list */-pl=unique;+unique_pack_objects=llist_copy(all_objects);+llist_sorted_difference_inplace(unique_pack_objects,missing);++/* remove unique pack objects from the non_unique packs */+pl=non_unique;while(pl){-pack_list_insert(min,pl);+llist_sorted_difference_inplace(pl->all_objects,unique_pack_objects);pl=pl->next;}++while(non_unique){+/* sort the non_unique packs, greater size of all_objects first */+sort_pack_list(&non_unique);+if(non_unique->all_objects->size==0)+break;++pack_list_insert(min,non_unique);++for(pl=non_unique->next;pl&&pl->all_objects->size>0;pl=pl->next)+llist_sorted_difference_inplace(pl->all_objects,non_unique->all_objects);++non_unique=non_unique->next;+}}staticvoidload_all_objects(void)
@@ -155,7 +155,7 @@ cat >expected <<EOF P2:$P2 EOF-test_expect_success'one of pack-2/pack-3 is redundant''+test_expect_failure'one of pack-2/pack-3 is redundant''gitpack-redundant--all>out&&format_packfiles<out>actual&&test_cmpexpectedactual
@@ -172,7 +172,7 @@ P4:$P4 P6:$P6 EOF-test_expect_success'pack 2, 4, and 6 are redundant''+test_expect_failure'pack 2, 4, and 6 are redundant''gitpack-redundant--all>out&&format_packfiles<out>actual&&test_cmpexpectedactual
@@ -189,7 +189,7 @@ P6:$P6 P8:$P8 EOF-test_expect_success'pack-8 (subset of pack-1) is also redundant''+test_expect_failure'pack-8 (subset of pack-1) is also redundant''gitpack-redundant--all>out&&format_packfiles<out>actual&&test_cmpexpectedactual
@@ -201,7 +201,7 @@ test_expect_success 'clean loose objects' 'test_must_be_emptyout'-test_expect_success'remove redundant packs and pass fsck''+test_expect_failure'remove redundant packs and pass fsck''gitpack-redundant--all|xargsrm&&gitfsck--no-progress&&gitpack-redundant--all>out&&
@@ -215,7 +215,7 @@ test_expect_success 'setup shared.git' 'printf"../../master.git/objects">objects/info/alternates'-test_expect_success'no redundant packs without --alt-odb''+test_expect_failure'no redundant packs without --alt-odb''gitpack-redundant--all>out&&test_must_be_emptyout'
@@ -227,7 +227,7 @@ P5:$P5 P7:$P7 EOF-test_expect_success'pack-redundant --verbose: show duplicate packs in stderr''+test_expect_failure'pack-redundant --verbose: show duplicate packs in stderr''gitpack-redundant--all--verbose>out2>out.err&&test_must_be_emptyout&&grep"pack$"out.err|format_packfiles>actual&&
From: Sun Chao <redacted>
Remove unused functions to find `min` packs, such as `get_permutations`,
`pll_free`, etc.
Signed-off-by: Sun Chao <redacted>
Signed-off-by: Jiang Xin <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
builtin/pack-redundant.c | 86 ----------------------------------------
1 file changed, 86 deletions(-)
@@ -290,78 +276,6 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)}}-staticvoidpll_free(structpll*l)-{-structpll*old;-structpack_list*opl;--while(l){-old=l;-while(l->pl){-opl=l->pl;-l->pl=opl->next;-free(opl);-}-l=l->next;-free(old);-}-}--/* all the permutations have to be free()d at the same time,-*sincetheyrefertoeachother-*/-staticstructpll*get_permutations(structpack_list*list,intn)-{-structpll*subset,*ret=NULL,*new_pll=NULL;--if(list==NULL||pack_list_size(list)<n||n==0)-returnNULL;--if(n==1){-while(list){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=NULL;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-list=list->next;-}-returnret;-}--while(list->next){-subset=get_permutations(list->next,n-1);-while(subset){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=subset->pl;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-subset=subset->next;-}-list=list->next;-}-returnret;-}--staticintis_superset(structpack_list*pl,structllist*list)-{-structllist*diff;--diff=llist_copy(list);--while(pl){-llist_sorted_difference_inplace(diff,pl->all_objects);-if(diff->size==0){/* we're done */-llist_free(diff);-return1;-}-pl=pl->next;-}-llist_free(diff);-return0;-}-staticsize_tsizeof_union(structpacked_git*p1,structpacked_git*p2){size_tret=0;
@@ -417,20 +417,20 @@ static void minimize(struct pack_list **min)/* remove unique pack objects from the non_unique packs */pl=non_unique;while(pl){-llist_sorted_difference_inplace(pl->all_objects,unique_pack_objects);+llist_sorted_difference_inplace(pl->remaining_objects,unique_pack_objects);pl=pl->next;}while(non_unique){-/* sort the non_unique packs, greater size of all_objects first */+/* sort the non_unique packs, greater size of remaining_objects first */sort_pack_list(&non_unique);-if(non_unique->all_objects->size==0)+if(non_unique->remaining_objects->size==0)break;pack_list_insert(min,non_unique);-for(pl=non_unique->next;pl&&pl->all_objects->size>0;pl=pl->next)-llist_sorted_difference_inplace(pl->all_objects,non_unique->all_objects);+for(pl=non_unique->next;pl&&pl->remaining_objects->size>0;pl=pl->next)+llist_sorted_difference_inplace(pl->remaining_objects,non_unique->remaining_objects);non_unique=non_unique->next;}
From: Jiang Xin <redacted>
SZEDER reported that test case t5323 has different test result on MacOS.
This is because `cmp_pack_list_reverse` cannot give identical result
when two pack being sorted has the same size of remaining_objects.
Changes to the sorting function will make consistent test result for
t5323.
The new algorithm to find redundant packs is a trade-off to save memory
resources, and the result of it may be different with old one, and may
be not the best result sometimes. Update t5323 for the new algorithm.
Reported-by: SZEDER Gábor <redacted>
Signed-off-by: Jiang Xin <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
builtin/pack-redundant.c | 24 ++++++++++++++++--------
t/t5323-pack-redundant.sh | 14 +++++++-------
2 files changed, 23 insertions(+), 15 deletions(-)
@@ -340,19 +341,25 @@ static inline off_t pack_set_bytecount(struct pack_list *pl)returnret;}-staticintcmp_pack_list_reverse(constvoid*a,constvoid*b)+staticintcmp_remaining_objects(constvoid*a,constvoid*b){structpack_list*pl_a=*((structpack_list**)a);structpack_list*pl_b=*((structpack_list**)b);-size_tsz_a=pl_a->remaining_objects->size;-size_tsz_b=pl_b->remaining_objects->size;-if(sz_a==sz_b)-return0;-elseif(sz_a<sz_b)+if(pl_a->remaining_objects->size==pl_b->remaining_objects->size){+/* have the same remaining_objects, big pack first */+if(pl_a->all_objects_size==pl_b->all_objects_size)+return0;+elseif(pl_a->all_objects_size<pl_b->all_objects_size)+return1;+else+return-1;+}elseif(pl_a->remaining_objects->size<pl_b->remaining_objects->size){+/* sort by remaining objects, more objects first */return1;-else+}else{return-1;+}}/* Sort pack_list, greater size of remaining_objects first */
@@ -370,7 +377,7 @@ static void sort_pack_list(struct pack_list **pl)for(n=0,p=*pl;p;p=p->next)ary[n++]=p;-QSORT(ary,n,cmp_pack_list_reverse);+QSORT(ary,n,cmp_remaining_objects);/* link them back again */for(i=0;i<n-1;i++)
@@ -152,10 +152,10 @@ test_expect_success 'create pack 4, 5' '' cat>expected<<EOF-P2:$P2+P3:$P3 EOF-test_expect_failure'one of pack-2/pack-3 is redundant''+test_expect_success'one of pack-2/pack-3 is redundant''gitpack-redundant--all>out&&format_packfiles<out>actual&&test_cmpexpectedactual
@@ -172,7 +172,7 @@ P4:$P4 P6:$P6 EOF-test_expect_failure'pack 2, 4, and 6 are redundant''+test_expect_success'pack 2, 4, and 6 are redundant''gitpack-redundant--all>out&&format_packfiles<out>actual&&test_cmpexpectedactual
@@ -189,7 +189,7 @@ P6:$P6 P8:$P8 EOF-test_expect_failure'pack-8 (subset of pack-1) is also redundant''+test_expect_success'pack-8 (subset of pack-1) is also redundant''gitpack-redundant--all>out&&format_packfiles<out>actual&&test_cmpexpectedactual
@@ -201,7 +201,7 @@ test_expect_success 'clean loose objects' 'test_must_be_emptyout'-test_expect_failure'remove redundant packs and pass fsck''+test_expect_success'remove redundant packs and pass fsck''gitpack-redundant--all|xargsrm&&gitfsck--no-progress&&gitpack-redundant--all>out&&
@@ -215,7 +215,7 @@ test_expect_success 'setup shared.git' 'printf"../../master.git/objects">objects/info/alternates'-test_expect_failure'no redundant packs without --alt-odb''+test_expect_success'no redundant packs without --alt-odb''gitpack-redundant--all>out&&test_must_be_emptyout'
@@ -227,7 +227,7 @@ P5:$P5 P7:$P7 EOF-test_expect_failure'pack-redundant --verbose: show duplicate packs in stderr''+test_expect_success'pack-redundant --verbose: show duplicate packs in stderr''gitpack-redundant--all--verbose>out2>out.err&&test_must_be_emptyout&&grep"pack$"out.err|format_packfiles>actual&&
From: Sun Chao <redacted>
The objects in alt-odb are removed from `all_objects` twice in `load_all_objects`
and `scan_alt_odb_packs`, remove it from the later function.
Signed-off-by: Sun Chao <redacted>
---
builtin/pack-redundant.c | 1 -
1 file changed, 1 deletion(-)
Move this outside loop, not for efficiency but for clarity. This
helper function creates a single empty tree and bunch of commits
that hold the same empty tree, arranged as a single strand of
pearls.
By the way, I had to draw a table like this to figure out ...
T A B C D E F G H I J K L M N O P Q R
1 x x x x x x x x
2 x x x x x x x
3 x x x x x x
4 x x x x x
5 x x x x
6 x x x
7 x x
8 x
... what is going on. Perhaps something like this would help other
readers near the top of the file (or in test_description)?
+format_packfiles () {
+ sed \
+ -e "s#.*/pack-\(.*\)\.idx#\1#" \
+ -e "s#.*/pack-\(.*\)\.pack#\1#" |
+ sort -u |
+ while read p
+ do
+ if test -z "$(eval echo \${P$p})"
+ then
+ echo $p
All the "expected output" below will expect P$n:${P$n} prepared by
various create_pack_$n helpers we saw earlier, so an unknown
packfile would be detected as a line that this emits. Is that the
idea?
Everything below will be done inside master.git? Avoid cd'ing
around in random places in the test script, as a failure in any of
the steps that does cd would start later tests in an unexpected
place, if you can.
+cat >expected <<EOF
+P2:$P2
+EOF
+
+test_expect_success 'one of pack-2/pack-3 is redundant' '
+ git pack-redundant --all >out &&
+ format_packfiles <out >actual &&
+ test_cmp expected actual
+'
Do the preparation of file "expect" (most of the tests compare
'expect' vs 'actual', not 'expected') _inside_ the next test that
uses it. i.e.
test_expect_success 'with 1 4 and 5, either 2 or 3 can be omitted' '
cat >expect <<-EOF &&
P2:$P2
EOF
git pack-redundant --all >out &&
format ... >actual &&
test_cmp expect actual
'
Again, I needed to draw this to see if the "one of ... is redundant"
in the title is a valid claim. Something like it would help future
readers.
T A B C D E F G H I J K L M N O P Q R
1245 x x x x x x x x x x x x x x x x x
3 x x x x x x
T A B C D E F G H I J K L M N O P Q R
1345 x x x x x x x x x x x x x x x x x
2 x x x x x x x
I won't repeat the same for tests that appear later in this file,
but they share the same issue.
Junio C Hamano [off-list ref] 于2019年2月1日周五 上午5:44写道:
quoted
+create_commits () {
+ parent=
+ for name in A B C D E F G H I J K L M N O P Q R
+ do
+ test_tick &&
+ T=$(git write-tree) &&
Move this outside loop, not for efficiency but for clarity. This
helper function creates a single empty tree and bunch of commits
that hold the same empty tree, arranged as a single strand of
pearls.
Will rewrite as:
create_commits () {
parent=
T=$(git write-tree) &&
for name in A B C D E F G H I J K L M N O P Q R
By the way, I had to draw a table like this to figure out ...
T A B C D E F G H I J K L M N O P Q R
1 x x x x x x x x
2 x x x x x x x
3 x x x x x x
4 x x x x x
5 x x x x
6 x x x
7 x x
8 x
... what is going on. Perhaps something like this would help other
readers near the top of the file (or in test_description)?
Nice chart, will edit test_description as follows:
test_description='git pack-redundant test
In order to test git-pack-redundant, we will create a number of
redundant
packs in the repository `master.git`. The relationship between
packs (P1-P8)
and objects (T,A-R) is show in the following chart:
| T A B C D E F G H I J K L M N O P Q R
---+--------------------------------------
P1 | x x x x x x x x
P2 | x x x x x x x
P3 | x x x x x x
P4 | x x x x x
P5 | x x x x
P6 | x x x
P7 | x x
P8 | x
Another repoisitory `shared.git` has unique objects (X-Z), while
share others
objects through alt-odb (of `master.git`). The relationship
between packs
and objects is as follows:
| T A B C D E F G H I J K L M N O P Q R X Y Z
---+----------------------------------------------
Px1| x x x x x x
Px2| x x x x x x
'
quoted
+format_packfiles () {
+ sed \
+ -e "s#.*/pack-\(.*\)\.idx#\1#" \
+ -e "s#.*/pack-\(.*\)\.pack#\1#" |
+ sort -u |
+ while read p
+ do
+ if test -z "$(eval echo \${P$p})"
+ then
+ echo $p
All the "expected output" below will expect P$n:${P$n} prepared by
various create_pack_$n helpers we saw earlier, so an unknown
packfile would be detected as a line that this emits. Is that the
idea?
Right. During the reroll, a typo makes an empty output, so I decide
to make this change.
Everything below will be done inside master.git? Avoid cd'ing
around in random places in the test script, as a failure in any of
the steps that does cd would start later tests in an unexpected
place, if you can.
The first 10 test cases will run inside master.git, and others will
run inside shared.git. Only run cd inside the two `setup` test cases.
quoted
+cat >expected <<EOF
+P2:$P2
+EOF
+
+test_expect_success 'one of pack-2/pack-3 is redundant' '
+ git pack-redundant --all >out &&
+ format_packfiles <out >actual &&
+ test_cmp expected actual
+'
Do the preparation of file "expect" (most of the tests compare
'expect' vs 'actual', not 'expected') _inside_ the next test that
uses it. i.e.
test_expect_success 'with 1 4 and 5, either 2 or 3 can be omitted' '
cat >expect <<-EOF &&
P2:$P2
EOF
git pack-redundant --all >out &&
format ... >actual &&
test_cmp expect actual
'
From: Eric Sunshine <hidden> Date: 2019-02-01 06:13:55
On Fri, Feb 1, 2019 at 12:44 AM Jiang Xin [off-list ref] wrote:
quoted
Junio C Hamano [off-list ref] 于2019年2月1日周五 上午5:44写道:
Move this outside loop, not for efficiency but for clarity. This
helper function creates a single empty tree and bunch of commits
that hold the same empty tree, arranged as a single strand of
pearls.
Will rewrite as:
create_commits () {
parent=
T=$(git write-tree) &&
for name in A B C D E F G H I J K L M N O P Q R
Don't forget the && at the end of the 'parent=' line to protect
against someone later adding code above that line. So:
create_commits () {
parent= &&
T=$(git write-tree) &&
...
Nice chart, will edit test_description as follows:
test_description='git pack-redundant test
In order to test git-pack-redundant, we will create a number of
redundant
packs in the repository `master.git`. The relationship between
packs (P1-P8)
and objects (T,A-R) is show in the following chart:
| T A B C D E F G H I J K L M N O P Q R
---+--------------------------------------
P1 | x x x x x x x x
P2 | x x x x x x x
P3 | x x x x x x
P4 | x x x x x
P5 | x x x x
P6 | x x x
P7 | x x
P8 | x
test_description should be a meaningful one-liner; it should not
contain this other information, but this information should appear as
comments in the test script.
Another repoisitory `shared.git` has unique objects (X-Z), while
share others
Everything below will be done inside master.git? Avoid cd'ing
around in random places in the test script, as a failure in any of
the steps that does cd would start later tests in an unexpected
place, if you can.
The first 10 test cases will run inside master.git, and others will
run inside shared.git. Only run cd inside the two `setup` test cases.
That's not what Junio meant. It's okay for tests to 'cd', but each
test which does so _must_ ensure that the 'cd' is undone at the end of
the test, even if the test fails. The correct way to do this within
each test is by using 'cd' in a subhsell, like this:
test_expect_success 'setup master.git' '
git init --bare master.git &&
(
cd master.git &&
create_commits
)
'
Then, each test which needs to use "master.git" would 'cd' itself, like this:
test_expect_success 'some test' '
(
cd master.git &&
...
)
'
What Junio really meant by asking that question was that you should
not do this. When something goes wrong with a test, we want as much
output as possible to help diagnose the problem, so suppressing output
is undesirable. To summarize, don't use -q, --no-progress, or any
other such option and don't redirect to /dev/null.
Eric Sunshine [off-list ref] 于2019年2月1日周五 下午2:11写道:
On Fri, Feb 1, 2019 at 12:44 AM Jiang Xin [off-list ref] wrote:
quoted
quoted
Junio C Hamano [off-list ref] 于2019年2月1日周五 上午5:44写道:
Move this outside loop, not for efficiency but for clarity. This
helper function creates a single empty tree and bunch of commits
that hold the same empty tree, arranged as a single strand of
pearls.
Will rewrite as:
create_commits () {
parent=
T=$(git write-tree) &&
for name in A B C D E F G H I J K L M N O P Q R
Don't forget the && at the end of the 'parent=' line to protect
against someone later adding code above that line. So:
create_commits () {
parent= &&
T=$(git write-tree) &&
...
Will do.
quoted
Nice chart, will edit test_description as follows:
test_description='git pack-redundant test
In order to test git-pack-redundant, we will create a number of
redundant
packs in the repository `master.git`. The relationship between
packs (P1-P8)
and objects (T,A-R) is show in the following chart:
| T A B C D E F G H I J K L M N O P Q R
---+--------------------------------------
P1 | x x x x x x x x
P2 | x x x x x x x
P3 | x x x x x x
P4 | x x x x x
P5 | x x x x
P6 | x x x
P7 | x x
P8 | x
test_description should be a meaningful one-liner; it should not
contain this other information, but this information should appear as
comments in the test script.
In 't/t0000-basic.sh', there is also a very long test_description.
After read 't/test-lib.sh', the only usage of test_description
is showing it as help, when runing:
sh ./t0000-basic.sh
So write a long test_description is ok, I think.
quoted
Another repoisitory `shared.git` has unique objects (X-Z), while
share others
Everything below will be done inside master.git? Avoid cd'ing
around in random places in the test script, as a failure in any of
the steps that does cd would start later tests in an unexpected
place, if you can.
The first 10 test cases will run inside master.git, and others will
run inside shared.git. Only run cd inside the two `setup` test cases.
That's not what Junio meant. It's okay for tests to 'cd', but each
test which does so _must_ ensure that the 'cd' is undone at the end of
the test, even if the test fails. The correct way to do this within
each test is by using 'cd' in a subhsell, like this:
test_expect_success 'setup master.git' '
git init --bare master.git &&
(
cd master.git &&
create_commits
)
'
Then, each test which needs to use "master.git" would 'cd' itself, like this:
test_expect_success 'some test' '
(
cd master.git &&
...
)
'
What Junio really meant by asking that question was that you should
not do this. When something goes wrong with a test, we want as much
output as possible to help diagnose the problem, so suppressing output
is undesirable. To summarize, don't use -q, --no-progress, or any
other such option and don't redirect to /dev/null.
Eric Sunshine [off-list ref] 于2019年2月1日周五 下午2:11写道:
quoted
quoted
Nice chart, will edit test_description as follows:
test_description='git pack-redundant test
In order to test git-pack-redundant, we will create a number of
redundant
packs in the repository `master.git`. The relationship between
packs (P1-P8)
and objects (T,A-R) is show in the following chart:
| T A B C D E F G H I J K L M N O P Q R
---+--------------------------------------
P1 | x x x x x x x x
P2 | x x x x x x x
P3 | x x x x x x
P4 | x x x x x
P5 | x x x x
P6 | x x x
P7 | x x
P8 | x
test_description should be a meaningful one-liner; it should not
contain this other information, but this information should appear as
comments in the test script.
In 't/t0000-basic.sh', there is also a very long test_description.
After read 't/test-lib.sh', the only usage of test_description
is showing it as help, when runing:
sh ./t0000-basic.sh
Eric Sunshine [off-list ref] 于2019年2月1日周五 下午2:11写道:
quoted
Everything below will be done inside master.git? Avoid cd'ing
quoted
around in random places in the test script, as a failure in any of
the steps that does cd would start later tests in an unexpected
place, if you can.
The first 10 test cases will run inside master.git, and others will
run inside shared.git. Only run cd inside the two `setup` test cases.
That's not what Junio meant. It's okay for tests to 'cd', but each
test which does so _must_ ensure that the 'cd' is undone at the end of
the test, even if the test fails. The correct way to do this within
each test is by using 'cd' in a subhsell, like this:
test_expect_success 'setup master.git' '
git init --bare master.git &&
(
cd master.git &&
create_commits
)
'
create_commits should not run in sub-shell, or variables set are lost.
I write a commit_commits_in function :
# Usage: create_commits_in <repo> A B C ...
# Note: DO NOT run it in sub shell, or variables are not set
create_commits_in () {
repo="$1" &&
parent=$(git -C "$repo" rev-parse HEAD^{} 2>/dev/null) || parent=
T=$(git -C "$repo" write-tree) &&
shift &&
while test $# -gt 0
do
name=$1 &&
test_tick &&
if test -z "$parent"
then
oid=$(echo $name | git -C "$repo" commit-tree $T)
else
oid=$(echo $name | git -C "$repo"
commit-tree -p $parent $T)
fi &&
eval $name=$oid &&
parent=$oid &&
shift ||
return 1
done
git -C "$repo" update-ref refs/heads/master $oid
}
and use it to create commits like:
create_commits_in master.git A B C D E F G ...
Sun Chao (my former colleague at Huawei) found a bug of
git-pack-redundant. If there are too many packs and many of them
overlap each other, running `git pack-redundant --all` will
exhaust all memories and the process will be killed by kernel.
There is a script in commit log of commit 3/6, which can be used to
create a repository with lots of redundant packs. Running `git
pack-redundant --all` in it can reproduce this issue.
## Changes since reroll v7
1. Rewrite [PATCH v9 1/6] (t5323: test cases for git-pack-redundant)
* Add many tables for relationship of packs and objects.
* Change dir in subshell and fixed other issues.
2. New patch file from Sun Chao: [PATCH v9 3/6] (pack-redundant: delete redundant code)
3. Squash patches (remove unused functions) to patch 4/6 (new algorithm to find min packs).
## Range diff
1: 799e804d5e < -: ---------- t5323: test cases for git-pack-redundant
-: ---------- > 1: c8dbf8cef2 t5323: test cases for git-pack-redundant
2: 520f6277fb = 2: a6300516d7 pack-redundant: delay creation of unique_objects
-: ---------- > 3: fb71973df5 pack-redundant: delete redundant code
3: ab1c2c4950 ! 4: 9963d1c49f pack-redundant: new algorithm to find min packs
@@ -76,6 +76,113 @@
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
+@@
+ struct llist *all_objects;
+ } *local_packs = NULL, *altodb_packs = NULL;
+
+-struct pll {
+- struct pll *next;
+- struct pack_list *pl;
+-};
+-
+ static struct llist_item *free_nodes;
+
+ static inline void llist_item_put(struct llist_item *item)
+@@
+ return new_item;
+ }
+
+-static void llist_free(struct llist *list)
+-{
+- while ((list->back = list->front)) {
+- list->front = list->front->next;
+- llist_item_put(list->back);
+- }
+- free(list);
+-}
+-
+ static inline void llist_init(struct llist **list)
+ {
+ *list = xmalloc(sizeof(struct llist));
+@@
+ }
+ }
+
+-static void pll_free(struct pll *l)
+-{
+- struct pll *old;
+- struct pack_list *opl;
+-
+- while (l) {
+- old = l;
+- while (l->pl) {
+- opl = l->pl;
+- l->pl = opl->next;
+- free(opl);
+- }
+- l = l->next;
+- free(old);
+- }
+-}
+-
+-/* all the permutations have to be free()d at the same time,
+- * since they refer to each other
+- */
+-static struct pll * get_permutations(struct pack_list *list, int n)
+-{
+- struct pll *subset, *ret = NULL, *new_pll = NULL;
+-
+- if (list == NULL || pack_list_size(list) < n || n == 0)
+- return NULL;
+-
+- if (n == 1) {
+- while (list) {
+- new_pll = xmalloc(sizeof(*new_pll));
+- new_pll->pl = NULL;
+- pack_list_insert(&new_pll->pl, list);
+- new_pll->next = ret;
+- ret = new_pll;
+- list = list->next;
+- }
+- return ret;
+- }
+-
+- while (list->next) {
+- subset = get_permutations(list->next, n - 1);
+- while (subset) {
+- new_pll = xmalloc(sizeof(*new_pll));
+- new_pll->pl = subset->pl;
+- pack_list_insert(&new_pll->pl, list);
+- new_pll->next = ret;
+- ret = new_pll;
+- subset = subset->next;
+- }
+- list = list->next;
+- }
+- return ret;
+-}
+-
+-static int is_superset(struct pack_list *pl, struct llist *list)
+-{
+- struct llist *diff;
+-
+- diff = llist_copy(list);
+-
+- while (pl) {
+- llist_sorted_difference_inplace(diff, pl->all_objects);
+- if (diff->size == 0) { /* we're done */
+- llist_free(diff);
+- return 1;
+- }
+- pl = pl->next;
+- }
+- llist_free(diff);
+- return 0;
+-}
+-
+ static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
+ {
+ size_t ret = 0;
@@
return ret;
}
@@ -221,56 +328,56 @@
--- a/t/t5323-pack-redundant.sh
+++ b/t/t5323-pack-redundant.sh
@@
- P2:$P2
- EOF
-
+ # ALL | x x x x x x x x x x x x x x x x x x
+ #
+ #############################################################################
-test_expect_success 'one of pack-2/pack-3 is redundant' '
-+test_expect_failure 'one of pack-2/pack-3 is redundant' '
- git pack-redundant --all >out &&
- format_packfiles <out >actual &&
- test_cmp expected actual
++test_expect_failure 'one of pack-2/pack-3 is redundant (failed on Mac)' '
+ (
+ cd "$master_repo" &&
+ cat >expect <<-EOF &&
@@
- P6:$P6
- EOF
-
+ # ALL | x x x x x x x x x x x x x x x x x x x
+ #
+ #############################################################################
-test_expect_success 'pack 2, 4, and 6 are redundant' '
-+test_expect_failure 'pack 2, 4, and 6 are redundant' '
- git pack-redundant --all >out &&
- format_packfiles <out >actual &&
- test_cmp expected actual
++test_expect_failure 'pack 2, 4, and 6 are redundant (failed on Mac)' '
+ (
+ cd "$master_repo" &&
+ cat >expect <<-EOF &&
@@
- P8:$P8
- EOF
-
+ # ALL | x x x x x x x x x x x x x x x x x x x
+ #
+ #############################################################################
-test_expect_success 'pack-8 (subset of pack-1) is also redundant' '
-+test_expect_failure 'pack-8 (subset of pack-1) is also redundant' '
- git pack-redundant --all >out &&
- format_packfiles <out >actual &&
- test_cmp expected actual
++test_expect_failure 'pack-8 (subset of pack-1) is also redundant (failed on Mac)' '
+ (
+ cd "$master_repo" &&
+ cat >expect <<-EOF &&
@@
- test_must_be_empty out
+ )
'
-test_expect_success 'remove redundant packs and pass fsck' '
-+test_expect_failure 'remove redundant packs and pass fsck' '
- git pack-redundant --all | xargs rm &&
- git fsck --no-progress &&
- git pack-redundant --all >out &&
++test_expect_failure 'remove redundant packs and pass fsck (failed on Mac)' '
+ (
+ cd "$master_repo" &&
+ git pack-redundant --all | xargs rm &&
@@
- printf "../../master.git/objects" >objects/info/alternates
+ )
'
-test_expect_success 'no redundant packs without --alt-odb' '
-+test_expect_failure 'no redundant packs without --alt-odb' '
- git pack-redundant --all >out &&
- test_must_be_empty out
- '
++test_expect_failure 'no redundant packs without --alt-odb (failed on Mac)' '
+ (
+ cd "$shared_repo" &&
+ git pack-redundant --all >out &&
@@
- P7:$P7
- EOF
-
+ # ALL | x x x x x x x x x x x x x x x x x x x
+ #
+ #############################################################################
-test_expect_success 'pack-redundant --verbose: show duplicate packs in stderr' '
-+test_expect_failure 'pack-redundant --verbose: show duplicate packs in stderr' '
- git pack-redundant --all --verbose >out 2>out.err &&
- test_must_be_empty out &&
- grep "pack$" out.err | format_packfiles >actual &&
++test_expect_failure 'pack-redundant --verbose: show duplicate packs in stderr (failed on Mac)' '
+ (
+ cd "$shared_repo" &&
+ cat >expect <<-EOF &&
4: 3c3a7ea40f < -: ---------- pack-redundant: remove unused functions
5: bc4b681f40 ! 5: b8f80ad454 pack-redundant: rename pack_list.all_objects
@@ -115,11 +115,7 @@
+ alt->remaining_objects);
local = local->next;
}
-- llist_sorted_difference_inplace(all_objects, alt->all_objects);
-+ llist_sorted_difference_inplace(all_objects, alt->remaining_objects);
alt = alt->next;
- }
- }
@@
return NULL;
6: 6cfba5b4b2 ! 6: 8a12ad699e pack-redundant: consistent sort method
@@ -83,60 +83,71 @@
--- a/t/t5323-pack-redundant.sh
+++ b/t/t5323-pack-redundant.sh
@@
- '
-
- cat >expected <<EOF
--P2:$P2
-+P3:$P3
- EOF
-
--test_expect_failure 'one of pack-2/pack-3 is redundant' '
+ # | T A B C D E F G H I J K L M N O P Q R
+ # ----+--------------------------------------
+ # P1 | x x x x x x x x
+-# P2* | ! ! ! ! ! ! !
+-# P3 | x x x x x x
++# P2 | x x x x x x x
++# P3* | ! ! ! ! ! !
+ # P4 | x x x x x
+ # P5 | x x x x
+ # ----+--------------------------------------
+ # ALL | x x x x x x x x x x x x x x x x x x
+ #
+ #############################################################################
+-test_expect_failure 'one of pack-2/pack-3 is redundant (failed on Mac)' '
+test_expect_success 'one of pack-2/pack-3 is redundant' '
- git pack-redundant --all >out &&
- format_packfiles <out >actual &&
- test_cmp expected actual
+ (
+ cd "$master_repo" &&
+ cat >expect <<-EOF &&
+- P2:$P2
++ P3:$P3
+ EOF
+ git pack-redundant --all >out &&
+ format_packfiles <out >actual &&
@@
- P6:$P6
- EOF
-
--test_expect_failure 'pack 2, 4, and 6 are redundant' '
+ # ALL | x x x x x x x x x x x x x x x x x x x
+ #
+ #############################################################################
+-test_expect_failure 'pack 2, 4, and 6 are redundant (failed on Mac)' '
+test_expect_success 'pack 2, 4, and 6 are redundant' '
- git pack-redundant --all >out &&
- format_packfiles <out >actual &&
- test_cmp expected actual
+ (
+ cd "$master_repo" &&
+ cat >expect <<-EOF &&
@@
- P8:$P8
- EOF
-
--test_expect_failure 'pack-8 (subset of pack-1) is also redundant' '
+ # ALL | x x x x x x x x x x x x x x x x x x x
+ #
+ #############################################################################
+-test_expect_failure 'pack-8 (subset of pack-1) is also redundant (failed on Mac)' '
+test_expect_success 'pack-8 (subset of pack-1) is also redundant' '
- git pack-redundant --all >out &&
- format_packfiles <out >actual &&
- test_cmp expected actual
+ (
+ cd "$master_repo" &&
+ cat >expect <<-EOF &&
@@
- test_must_be_empty out
+ )
'
--test_expect_failure 'remove redundant packs and pass fsck' '
+-test_expect_failure 'remove redundant packs and pass fsck (failed on Mac)' '
+test_expect_success 'remove redundant packs and pass fsck' '
- git pack-redundant --all | xargs rm &&
- git fsck --no-progress &&
- git pack-redundant --all >out &&
+ (
+ cd "$master_repo" &&
+ git pack-redundant --all | xargs rm &&
@@
- printf "../../master.git/objects" >objects/info/alternates
+ )
'
--test_expect_failure 'no redundant packs without --alt-odb' '
+-test_expect_failure 'no redundant packs without --alt-odb (failed on Mac)' '
+test_expect_success 'no redundant packs without --alt-odb' '
- git pack-redundant --all >out &&
- test_must_be_empty out
- '
+ (
+ cd "$shared_repo" &&
+ git pack-redundant --all >out &&
@@
- P7:$P7
- EOF
-
--test_expect_failure 'pack-redundant --verbose: show duplicate packs in stderr' '
+ # ALL | x x x x x x x x x x x x x x x x x x x
+ #
+ #############################################################################
+-test_expect_failure 'pack-redundant --verbose: show duplicate packs in stderr (failed on Mac)' '
+test_expect_success 'pack-redundant --verbose: show duplicate packs in stderr' '
- git pack-redundant --all --verbose >out 2>out.err &&
- test_must_be_empty out &&
- grep "pack$" out.err | format_packfiles >actual &&
+ (
+ cd "$shared_repo" &&
+ cat >expect <<-EOF &&
Jiang Xin (4):
t5323: test cases for git-pack-redundant
pack-redundant: delay creation of unique_objects
pack-redundant: rename pack_list.all_objects
pack-redundant: consistent sort method
Sun Chao (2):
pack-redundant: delete redundant code
pack-redundant: new algorithm to find min packs
builtin/pack-redundant.c | 232 +++++++----------
t/t5323-pack-redundant.sh | 510 ++++++++++++++++++++++++++++++++++++++
2 files changed, 602 insertions(+), 140 deletions(-)
create mode 100755 t/t5323-pack-redundant.sh
--
2.20.1.103.ged0fc2ca7b
@@ -0,0 +1,510 @@+#!/bin/sh+#+# Copyright (c) 2018 Jiang Xin+#++test_description='Testgitpack-redundant++Inordertotestgit-pack-redundant,wewillcreateanumberofobjectsand+packsintherepository`master.git`.Therelationshipbetweenpacks(P1-P8)+andobjects(T,A-R)isshowedinthefollowingchart.Objectsofapackwill+bemarkedwithletterx,whileobjectsofredundantpackswillbemarkedwith+exclamationpoint,andredundantpackitselfwillbemarkedwithasterisk.++|TABCDEFGHIJKLMNOPQR+----+--------------------------------------+P1|xxxxxxxx+P2*|!!!!!!!+P3|xxxxxx+P4*|!!!!!+P5|xxxx+P6*|!!!+P7|xx+P8*|!+----+--------------------------------------+ALL|xxxxxxxxxxxxxxxxxxx++Anotherrepository`shared.git`hasuniqueobjects(X-Z),whileotherobjects+(markedwithletters)aresharedthroughalt-odb(of`master.git`).The+relationshipbetweenpacksandobjectsisasfollows:++|TABCDEFGHIJKLMNOPQRXYZ+----+----------------------------------------------+Px1|sssxxx+Px2|sssxxx+'++../test-lib.sh++master_repo=master.git+shared_repo=shared.git++# Note: DO NOT run it in a subshell, otherwise the variables will not be set+# Usage: create_commits_in <repo> A B C ...+create_commits_in(){+repo="$1"&&+parent=$(git-C"$repo"rev-parseHEAD^{}2>/dev/null)||parent=+T=$(git-C"$repo"write-tree)&&+shift&&+whiletest$#-gt0+do+name=$1&&+test_tick&&+iftest-z"$parent"+then+oid=$(echo$name|git-C"$repo"commit-tree$T)+else+oid=$(echo$name|git-C"$repo"commit-tree-p$parent$T)+fi&&+eval$name=$oid&&+parent=$oid&&+shift||+return1+done+git-C"$repo"update-refrefs/heads/master$oid+}++# Note: DO NOT run it in a subshell, otherwise the variables will not be set+create_pack_1(){+P1=$(git-C"$master_repo/objects/pack"pack-objects-qpack<<-EOF+$T+$A+$B+$C+$D+$E+$F+$R+EOF+)&&+evalP$P1=P1:$P1+}++create_pack_2(){+P2=$(git-C"$master_repo/objects/pack"pack-objects-qpack<<-EOF+$B+$C+$D+$E+$G+$H+$I+EOF+)&&+evalP$P2=P2:$P2+}++create_pack_3(){+P3=$(git-C"$master_repo/objects/pack"pack-objects-qpack<<-EOF+$F+$I+$J+$K+$L+$M+EOF+)&&+evalP$P3=P3:$P3+}++create_pack_4(){+P4=$(git-C"$master_repo/objects/pack"pack-objects-qpack<<-EOF+$J+$K+$L+$M+$P+EOF+)&&+evalP$P4=P4:$P4+}++create_pack_5(){+P5=$(git-C"$master_repo/objects/pack"pack-objects-qpack<<-EOF+$G+$H+$N+$O+EOF+)&&+evalP$P5=P5:$P5+}++create_pack_6(){+P6=$(git-C"$master_repo/objects/pack"pack-objects-qpack<<-EOF+$N+$O+$Q+EOF+)&&+evalP$P6=P6:$P6+}++create_pack_7(){+P7=$(git-C"$master_repo/objects/pack"pack-objects-qpack<<-EOF+$P+$Q+EOF+)&&+evalP$P7=P7:$P7+}++create_pack_8(){+P8=$(git-C"$master_repo/objects/pack"pack-objects-qpack<<-EOF+$A+EOF+)&&+evalP$P8=P8:$P8+}++format_packfiles(){+sed\+-e"s#.*/pack-\(.*\)\.idx#\1#"\+-e"s#.*/pack-\(.*\)\.pack#\1#"|+sort-u|+whilereadp+do+iftest-z"$(evalecho\${P$p})"+then+echo$p+else+evalecho"\${P$p}"+fi+done|+sort+}++test_expect_success'setup master repo''+gitinit--bare"$master_repo"&&+create_commits_in"$master_repo"ABCDEFGHIJKLMNOPQR+'++#############################################################################+# Chart of packs and objects for this test case+#+# | T A B C D E F G H I J K L M N O P Q R+# ----+--------------------------------------+# P1 | x x x x x x x x+# P2 | x x x x x x x+# P3 | x x x x x x+# ----+--------------------------------------+# ALL | x x x x x x x x x x x x x x x+#+#############################################################################+test_expect_success'no redundant for pack 1, 2, 3''+create_pack_1&&create_pack_2&&create_pack_3&&+(+cd"$master_repo"&&+gitpack-redundant--all>out&&+test_must_be_emptyout+)+'++test_expect_success'create pack 4, 5''+create_pack_4&&create_pack_5+'++#############################################################################+# Chart of packs and objects for this test case+#+# | T A B C D E F G H I J K L M N O P Q R+# ----+--------------------------------------+# P1 | x x x x x x x x+# P2* | ! ! ! ! ! ! !+# P3 | x x x x x x+# P4 | x x x x x+# P5 | x x x x+# ----+--------------------------------------+# ALL | x x x x x x x x x x x x x x x x x x+#+#############################################################################+test_expect_success'one of pack-2/pack-3 is redundant''+(+cd"$master_repo"&&+cat>expect<<-EOF&&+P2:$P2+EOF+gitpack-redundant--all>out&&+format_packfiles<out>actual&&+test_cmpexpectactual+)+'++test_expect_success'create pack 6, 7''+create_pack_6&&create_pack_7+'++#############################################################################+# Chart of packs and objects for this test case+#+# | T A B C D E F G H I J K L M N O P Q R+# ----+--------------------------------------+# P1 | x x x x x x x x+# P2* | ! ! ! ! ! ! !+# P3 | x x x x x x+# P4* | ! ! ! ! !+# P5 | x x x x+# P6* | ! ! !+# P7 | x x+# ----+--------------------------------------+# ALL | x x x x x x x x x x x x x x x x x x x+#+#############################################################################+test_expect_success'pack 2, 4, and 6 are redundant''+(+cd"$master_repo"&&+cat>expect<<-EOF&&+P2:$P2+P4:$P4+P6:$P6+EOF+gitpack-redundant--all>out&&+format_packfiles<out>actual&&+test_cmpexpectactual+)+'++test_expect_success'create pack 8''+create_pack_8+'++#############################################################################+# Chart of packs and objects for this test case+#+# | T A B C D E F G H I J K L M N O P Q R+# ----+--------------------------------------+# P1 | x x x x x x x x+# P2* | ! ! ! ! ! ! !+# P3 | x x x x x x+# P4* | ! ! ! ! !+# P5 | x x x x+# P6* | ! ! !+# P7 | x x+# P8* | !+# ----+--------------------------------------+# ALL | x x x x x x x x x x x x x x x x x x x+#+#############################################################################+test_expect_success'pack-8 (subset of pack-1) is also redundant''+(+cd"$master_repo"&&+cat>expect<<-EOF&&+P2:$P2+P4:$P4+P6:$P6+P8:$P8+EOF+gitpack-redundant--all>out&&+format_packfiles<out>actual&&+test_cmpexpectactual+)+'++test_expect_success'clean loose objects''+(+cd"$master_repo"&&+gitprune-packed&&+findobjects-typef|sed-e"/objects\/pack\//d">out&&+test_must_be_emptyout+)+'++test_expect_success'remove redundant packs and pass fsck''+(+cd"$master_repo"&&+gitpack-redundant--all|xargsrm&&+gitfsck&&+gitpack-redundant--all>out&&+test_must_be_emptyout+)+'++# The following test cases will execute inside `shared.git`, instead of+# inside `master.git`.+test_expect_success'setup shared.git''+gitclone--mirror"$master_repo""$shared_repo"&&+(+cd"$shared_repo"&&+printf"../../$master_repo/objects\n">objects/info/alternates+)+'++test_expect_success'no redundant packs without --alt-odb''+(+cd"$shared_repo"&&+gitpack-redundant--all>out&&+test_must_be_emptyout+)+'++#############################################################################+# Chart of packs and objects for this test case+#+# ================ master.git ===============+# | T A B C D E F G H I J K L M N O P Q R <----------++# ----+-------------------------------------- |+# P1 | x x x x x x x x |+# P3 | x x x x x x |+# P5 | x x x x |+# P7 | x x |+# ----+-------------------------------------- |+# ALL | x x x x x x x x x x x x x x x x x x x |+# |+# |+# ================ shared.git =============== |+# | T A B C D E F G H I J K L M N O P Q R <objects/info/alternates>+# ----+--------------------------------------+# P1* | s s s s s s s s+# P3* | s s s s s s+# P5* | s s s s+# P7* | s s+# ----+--------------------------------------+# ALL | x x x x x x x x x x x x x x x x x x x+#+#############################################################################+test_expect_success'pack-redundant --verbose: show duplicate packs in stderr''+(+cd"$shared_repo"&&+cat>expect<<-EOF&&+P1:$P1+P3:$P3+P5:$P5+P7:$P7+EOF+gitpack-redundant--all--verbose>out2>out.err&&+test_must_be_emptyout&&+grep"pack$"out.err|format_packfiles>actual&&+test_cmpexpectactual+)+'++test_expect_success'remove redundant packs by alt-odb, no packs left''+(+cd"$shared_repo"&&+cat>expect<<-EOF&&+fatal:Zeropacksfound!+EOF+gitpack-redundant--all--alt-odb|xargsrm&&+gitfsck&&+test_must_failgitpack-redundant--all--alt-odb>actual2>&1&&+test_cmpexpectactual+)+'++# Note: DO NOT run function `create_pack_*` in sub shell, or variables are not set+create_pack_x1_in(){+repo="$1"&&+Px1=$(git-C"$repo/objects/pack"pack-objects-qpack<<-EOF+$X+$Y+$Z+$A+$B+$C+EOF+)&&+evalP${Px1}=Px1:${Px1}+}++create_pack_x2_in(){+repo="$1"&&+Px2=$(git-C"$repo/objects/pack"pack-objects-qpack<<-EOF+$X+$Y+$Z+$D+$E+$F+EOF+)&&+evalP${Px2}=Px2:${Px2}+}++test_expect_success'create new objects and packs in shared.git''+create_commits_in"$shared_repo"XYZ&&+create_pack_x1_in"$shared_repo"&&+create_pack_x2_in"$shared_repo"+'++test_expect_success'no redundant without --alt-odb''+(+cd"$shared_repo"&&+gitpack-redundant--all>out&&+test_must_be_emptyout+)+'++#############################################################################+# Chart of packs and objects for this test case+#+# ================ master.git ===============+# | T A B C D E F G H I J K L M N O P Q R <----------------++# ----+-------------------------------------- |+# P1 | x x x x x x x x |+# P3 | x x x x x x |+# P5 | x x x x |+# P7 | x x |+# ----+-------------------------------------- |+# ALL | x x x x x x x x x x x x x x x x x x x |+# |+# |+# ================ shared.git ======================= |+# | T A B C D E F G H I J K L M N O P Q R X Y Z <objects/info/alternates>+# ----+----------------------------------------------+# Px1 | s s s x x x+# Px2*| s s s ! ! !+# ----+----------------------------------------------+# ALL | s s s s s s s s s s s s s s s s s s s x x x+#+#############################################################################+test_expect_success'one pack is redundant''+(+cd"$shared_repo"&&+gitpack-redundant--all--alt-odb>out&&+format_packfiles<out>actual&&+test_line_count=1actual+)+'++#############################################################################+# Chart of packs and objects for this test case+#+# ================ master.git ===============+# | T A B C D E F G H I J K L M N O P Q R <----------------++# ----+-------------------------------------- |+# P1 | x x x x x x x x |+# P3 | x x x x x x |+# P5 | x x x x |+# P7 | x x |+# ----+-------------------------------------- |+# ALL | x x x x x x x x x x x x x x x x x x x |+# |+# |+# ================ shared.git ======================= |+# | T A B C D E F G H I J K L M N O P Q R X Y Z <objects/info/alternates>+# ----+----------------------------------------------+# Px1*| s s s i i i+# Px2*| s s s i i i+# ----+----------------------------------------------+# ALL | s s s s s s s s s s s s s s s s s s s i i i+# (ignored objects, marked with i)+#+#############################################################################+test_expect_success'set ignore objects and all two packs are redundant''+(+cd"$shared_repo"&&+cat>expect<<-EOF&&+Px1:$Px1+Px2:$Px2+EOF+gitpack-redundant--all--alt-odb>out<<-EOF&&+$X+$Y+$Z+EOF+format_packfiles<out>actual&&+test_cmpexpectactual+)+'++test_done
From: Jiang Xin <redacted>
Instead of initializing unique_objects in `add_pack()`, copy from
all_objects in `cmp_two_packs()`, when unwanted objects are removed from
all_objects.
This will save memory (no allocate memory for alt-odb packs), and run
`llist_sorted_difference_inplace()` only once when removing ignored
objects and removing objects in alt-odb in `scan_alt_odb_packs()`.
Signed-off-by: Jiang Xin <redacted>
---
builtin/pack-redundant.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
@@ -567,8 +572,7 @@ static struct pack_list * add_pack(struct packed_git *p)llist_insert_back(l.all_objects,(conststructobject_id*)(base+off));off+=step;}-/* this list will be pruned in cmp_two_packs later */-l.unique_objects=llist_copy(l.all_objects);+l.unique_objects=NULL;if(p->pack_local)returnpack_list_insert(&local_packs,&l);else
From: Sun Chao <redacted>
The objects in alt-odb are removed from `all_objects` twice in `load_all_objects`
and `scan_alt_odb_packs`, remove it from the later function.
Signed-off-by: Sun Chao <redacted>
Signed-off-by: Jiang Xin <redacted>
---
builtin/pack-redundant.c | 1 -
1 file changed, 1 deletion(-)
From: Sun Chao <redacted>
When calling `git pack-redundant --all`, if there are too many local
packs and too many redundant objects within them, the too deep iteration
of `get_permutations` will exhaust all the resources, and the process of
`git pack-redundant` will be killed.
The following script could create a repository with too many redundant
packs, and running `git pack-redundant --all` in the `test.git` repo
will die soon.
#!/bin/sh
repo="$(pwd)/test.git"
work="$(pwd)/test"
i=1
max=199
if test -d "$repo" || test -d "$work"; then
echo >&2 "ERROR: '$repo' or '$work' already exist"
exit 1
fi
git init -q --bare "$repo"
git --git-dir="$repo" config gc.auto 0
git --git-dir="$repo" config transfer.unpackLimit 0
git clone -q "$repo" "$work" 2>/dev/null
while :; do
cd "$work"
echo "loop $i: $(date +%s)" >$i
git add $i
git commit -q -sm "loop $i"
git push -q origin HEAD:master
printf "\rCreate pack %4d/%d\t" $i $max
if test $i -ge $max; then break; fi
cd "$repo"
git repack -q
if test $(($i % 2)) -eq 0; then
git repack -aq
pack=$(ls -t $repo/objects/pack/*.pack | head -1)
touch "${pack%.pack}.keep"
fi
i=$((i+1))
done
printf "\ndone\n"
To get the `min` unique pack list, we can replace the iteration in
`minimize` function with a new algorithm, and this could solve this
issue:
1. Get the unique and non_uniqe packs, add the unique packs to the
`min` list.
2. Remove the objects of unique packs from non_unique packs, then each
object left in the non_unique packs will have at least two copies.
3. Sort the non_unique packs by the objects' size, more objects first,
and add the first non_unique pack to `min` list.
4. Drop the duplicated objects from other packs in the ordered
non_unique pack list, and repeat step 3.
Some test cases will fail on Mac OS X. Mark them and will resolve in
later commit.
Original PR and discussions: https://github.com/jiangxin/git/pull/25
Signed-off-by: Sun Chao <redacted>
Signed-off-by: Jiang Xin <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
builtin/pack-redundant.c | 194 +++++++++++++-------------------------
t/t5323-pack-redundant.sh | 12 +--
2 files changed, 73 insertions(+), 133 deletions(-)
@@ -290,78 +276,6 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)}}-staticvoidpll_free(structpll*l)-{-structpll*old;-structpack_list*opl;--while(l){-old=l;-while(l->pl){-opl=l->pl;-l->pl=opl->next;-free(opl);-}-l=l->next;-free(old);-}-}--/* all the permutations have to be free()d at the same time,-*sincetheyrefertoeachother-*/-staticstructpll*get_permutations(structpack_list*list,intn)-{-structpll*subset,*ret=NULL,*new_pll=NULL;--if(list==NULL||pack_list_size(list)<n||n==0)-returnNULL;--if(n==1){-while(list){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=NULL;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-list=list->next;-}-returnret;-}--while(list->next){-subset=get_permutations(list->next,n-1);-while(subset){-new_pll=xmalloc(sizeof(*new_pll));-new_pll->pl=subset->pl;-pack_list_insert(&new_pll->pl,list);-new_pll->next=ret;-ret=new_pll;-subset=subset->next;-}-list=list->next;-}-returnret;-}--staticintis_superset(structpack_list*pl,structllist*list)-{-structllist*diff;--diff=llist_copy(list);--while(pl){-llist_sorted_difference_inplace(diff,pl->all_objects);-if(diff->size==0){/* we're done */-llist_free(diff);-return1;-}-pl=pl->next;-}-llist_free(diff);-return0;-}-staticsize_tsizeof_union(structpacked_git*p1,structpacked_git*p2){size_tret=0;
@@ -426,14 +340,52 @@ static inline off_t pack_set_bytecount(struct pack_list *pl)returnret;}+staticintcmp_pack_list_reverse(constvoid*a,constvoid*b)+{+structpack_list*pl_a=*((structpack_list**)a);+structpack_list*pl_b=*((structpack_list**)b);+size_tsz_a=pl_a->all_objects->size;+size_tsz_b=pl_b->all_objects->size;++if(sz_a==sz_b)+return0;+elseif(sz_a<sz_b)+return1;+else+return-1;+}++/* Sort pack_list, greater size of all_objects first */+staticvoidsort_pack_list(structpack_list**pl)+{+structpack_list**ary,*p;+inti;+size_tn=pack_list_size(*pl);++if(n<2)+return;++/* prepare an array of packed_list for easier sorting */+ary=xcalloc(n,sizeof(structpack_list*));+for(n=0,p=*pl;p;p=p->next)+ary[n++]=p;++QSORT(ary,n,cmp_pack_list_reverse);++/* link them back again */+for(i=0;i<n-1;i++)+ary[i]->next=ary[i+1];+ary[n-1]->next=NULL;+*pl=ary[0];++free(ary);+}++staticvoidminimize(structpack_list**min){-structpack_list*pl,*unique=NULL,-*non_unique=NULL,*min_perm=NULL;-structpll*perm,*perm_all,*perm_ok=NULL,*new_perm;-structllist*missing;-off_tmin_perm_size=0,perm_size;-intn;+structpack_list*pl,*unique=NULL,*non_unique=NULL;+structllist*missing,*unique_pack_objects;pl=local_packs;while(pl){
@@ -451,49 +403,37 @@ static void minimize(struct pack_list **min)pl=pl->next;}+*min=unique;+/* return if there are no objects missing from the unique set */if(missing->size==0){-*min=unique;free(missing);return;}-/* find the permutations which contain all missing objects */-for(n=1;n<=pack_list_size(non_unique)&&!perm_ok;n++){-perm_all=perm=get_permutations(non_unique,n);-while(perm){-if(is_superset(perm->pl,missing)){-new_perm=xmalloc(sizeof(structpll));-memcpy(new_perm,perm,sizeof(structpll));-new_perm->next=perm_ok;-perm_ok=new_perm;-}-perm=perm->next;-}-if(perm_ok)-break;-pll_free(perm_all);-}-if(perm_ok==NULL)-die("Internal error: No complete sets found!");--/* find the permutation with the smallest size */-perm=perm_ok;-while(perm){-perm_size=pack_set_bytecount(perm->pl);-if(!min_perm_size||min_perm_size>perm_size){-min_perm_size=perm_size;-min_perm=perm->pl;-}-perm=perm->next;-}-*min=min_perm;-/* add the unique packs to the list */-pl=unique;+unique_pack_objects=llist_copy(all_objects);+llist_sorted_difference_inplace(unique_pack_objects,missing);++/* remove unique pack objects from the non_unique packs */+pl=non_unique;while(pl){-pack_list_insert(min,pl);+llist_sorted_difference_inplace(pl->all_objects,unique_pack_objects);pl=pl->next;}++while(non_unique){+/* sort the non_unique packs, greater size of all_objects first */+sort_pack_list(&non_unique);+if(non_unique->all_objects->size==0)+break;++pack_list_insert(min,non_unique);++for(pl=non_unique->next;pl&&pl->all_objects->size>0;pl=pl->next)+llist_sorted_difference_inplace(pl->all_objects,non_unique->all_objects);++non_unique=non_unique->next;+}}staticvoidload_all_objects(void)
@@ -218,7 +218,7 @@ test_expect_success 'create pack 4, 5' '# ALL | x x x x x x x x x x x x x x x x x x##############################################################################-test_expect_success'one of pack-2/pack-3 is redundant''+test_expect_failure'one of pack-2/pack-3 is redundant (failed on Mac)''(cd"$master_repo"&&cat>expect<<-EOF&&
@@ -250,7 +250,7 @@ test_expect_success 'create pack 6, 7' '# ALL | x x x x x x x x x x x x x x x x x x x##############################################################################-test_expect_success'pack 2, 4, and 6 are redundant''+test_expect_failure'pack 2, 4, and 6 are redundant (failed on Mac)''(cd"$master_repo"&&cat>expect<<-EOF&&
@@ -285,7 +285,7 @@ test_expect_success 'create pack 8' '# ALL | x x x x x x x x x x x x x x x x x x x##############################################################################-test_expect_success'pack-8 (subset of pack-1) is also redundant''+test_expect_failure'pack-8 (subset of pack-1) is also redundant (failed on Mac)''(cd"$master_repo"&&cat>expect<<-EOF&&
@@ -309,7 +309,7 @@ test_expect_success 'clean loose objects' ')'-test_expect_success'remove redundant packs and pass fsck''+test_expect_failure'remove redundant packs and pass fsck (failed on Mac)''(cd"$master_repo"&&gitpack-redundant--all|xargsrm&&
@@ -329,7 +329,7 @@ test_expect_success 'setup shared.git' ')'-test_expect_success'no redundant packs without --alt-odb''+test_expect_failure'no redundant packs without --alt-odb (failed on Mac)''(cd"$shared_repo"&&gitpack-redundant--all>out&&
@@ -362,7 +362,7 @@ test_expect_success 'no redundant packs without --alt-odb' '# ALL | x x x x x x x x x x x x x x x x x x x##############################################################################-test_expect_success'pack-redundant --verbose: show duplicate packs in stderr''+test_expect_failure'pack-redundant --verbose: show duplicate packs in stderr (failed on Mac)''(cd"$shared_repo"&&cat>expect<<-EOF&&
@@ -417,20 +417,20 @@ static void minimize(struct pack_list **min)/* remove unique pack objects from the non_unique packs */pl=non_unique;while(pl){-llist_sorted_difference_inplace(pl->all_objects,unique_pack_objects);+llist_sorted_difference_inplace(pl->remaining_objects,unique_pack_objects);pl=pl->next;}while(non_unique){-/* sort the non_unique packs, greater size of all_objects first */+/* sort the non_unique packs, greater size of remaining_objects first */sort_pack_list(&non_unique);-if(non_unique->all_objects->size==0)+if(non_unique->remaining_objects->size==0)break;pack_list_insert(min,non_unique);-for(pl=non_unique->next;pl&&pl->all_objects->size>0;pl=pl->next)-llist_sorted_difference_inplace(pl->all_objects,non_unique->all_objects);+for(pl=non_unique->next;pl&&pl->remaining_objects->size>0;pl=pl->next)+llist_sorted_difference_inplace(pl->remaining_objects,non_unique->remaining_objects);non_unique=non_unique->next;}
From: Jiang Xin <redacted>
SZEDER reported that test case t5323 has different test result on MacOS.
This is because `cmp_pack_list_reverse` cannot give identical result
when two pack being sorted has the same size of remaining_objects.
Changes to the sorting function will make consistent test result for
t5323.
The new algorithm to find redundant packs is a trade-off to save memory
resources, and the result of it may be different with old one, and may
be not the best result sometimes. Update t5323 for the new algorithm.
Reported-by: SZEDER Gábor <redacted>
Signed-off-by: Jiang Xin <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
builtin/pack-redundant.c | 24 ++++++++++++++++--------
t/t5323-pack-redundant.sh | 18 +++++++++---------
2 files changed, 25 insertions(+), 17 deletions(-)
@@ -340,19 +341,25 @@ static inline off_t pack_set_bytecount(struct pack_list *pl)returnret;}-staticintcmp_pack_list_reverse(constvoid*a,constvoid*b)+staticintcmp_remaining_objects(constvoid*a,constvoid*b){structpack_list*pl_a=*((structpack_list**)a);structpack_list*pl_b=*((structpack_list**)b);-size_tsz_a=pl_a->remaining_objects->size;-size_tsz_b=pl_b->remaining_objects->size;-if(sz_a==sz_b)-return0;-elseif(sz_a<sz_b)+if(pl_a->remaining_objects->size==pl_b->remaining_objects->size){+/* have the same remaining_objects, big pack first */+if(pl_a->all_objects_size==pl_b->all_objects_size)+return0;+elseif(pl_a->all_objects_size<pl_b->all_objects_size)+return1;+else+return-1;+}elseif(pl_a->remaining_objects->size<pl_b->remaining_objects->size){+/* sort by remaining objects, more objects first */return1;-else+}else{return-1;+}}/* Sort pack_list, greater size of remaining_objects first */
@@ -370,7 +377,7 @@ static void sort_pack_list(struct pack_list **pl)for(n=0,p=*pl;p;p=p->next)ary[n++]=p;-QSORT(ary,n,cmp_pack_list_reverse);+QSORT(ary,n,cmp_remaining_objects);/* link them back again */for(i=0;i<n-1;i++)
@@ -210,19 +210,19 @@ test_expect_success 'create pack 4, 5' '# | T A B C D E F G H I J K L M N O P Q R# ----+--------------------------------------# P1 | x x x x x x x x-# P2* | ! ! ! ! ! ! !-# P3 | x x x x x x+# P2 | x x x x x x x+# P3* | ! ! ! ! ! !# P4 | x x x x x# P5 | x x x x# ----+--------------------------------------# ALL | x x x x x x x x x x x x x x x x x x##############################################################################-test_expect_failure'one of pack-2/pack-3 is redundant (failed on Mac)''+test_expect_success'one of pack-2/pack-3 is redundant''(cd"$master_repo"&&cat>expect<<-EOF&&-P2:$P2+P3:$P3EOFgitpack-redundant--all>out&&format_packfiles<out>actual&&
@@ -250,7 +250,7 @@ test_expect_success 'create pack 6, 7' '# ALL | x x x x x x x x x x x x x x x x x x x##############################################################################-test_expect_failure'pack 2, 4, and 6 are redundant (failed on Mac)''+test_expect_success'pack 2, 4, and 6 are redundant''(cd"$master_repo"&&cat>expect<<-EOF&&
@@ -285,7 +285,7 @@ test_expect_success 'create pack 8' '# ALL | x x x x x x x x x x x x x x x x x x x##############################################################################-test_expect_failure'pack-8 (subset of pack-1) is also redundant (failed on Mac)''+test_expect_success'pack-8 (subset of pack-1) is also redundant''(cd"$master_repo"&&cat>expect<<-EOF&&
@@ -309,7 +309,7 @@ test_expect_success 'clean loose objects' ')'-test_expect_failure'remove redundant packs and pass fsck (failed on Mac)''+test_expect_success'remove redundant packs and pass fsck''(cd"$master_repo"&&gitpack-redundant--all|xargsrm&&
@@ -329,7 +329,7 @@ test_expect_success 'setup shared.git' ')'-test_expect_failure'no redundant packs without --alt-odb (failed on Mac)''+test_expect_success'no redundant packs without --alt-odb''(cd"$shared_repo"&&gitpack-redundant--all>out&&
@@ -362,7 +362,7 @@ test_expect_failure 'no redundant packs without --alt-odb (failed on Mac)' '# ALL | x x x x x x x x x x x x x x x x x x x##############################################################################-test_expect_failure'pack-redundant --verbose: show duplicate packs in stderr (failed on Mac)''+test_expect_success'pack-redundant --verbose: show duplicate packs in stderr''(cd"$shared_repo"&&cat>expect<<-EOF&&
@@ -0,0 +1,510 @@+# Note: DO NOT run it in a subshell, otherwise the variables will not be set
Which variables won't be set? It's not clear what this restriction is about.
+# Usage: create_commits_in <repo> A B C ...
+create_commits_in () {
+ repo="$1" &&
+ parent=$(git -C "$repo" rev-parse HEAD^{} 2>/dev/null) || parent=
Broken &&-chain. Instead, perhaps:
if ! parent=$(git -C "$repo" rev-parse HEAD^{} 2>/dev/null)
then
parent=
fi &&
or something simpler.
+ T=$(git -C "$repo" write-tree) &&
+ shift &&
+ while test $# -gt 0
+ do
+ name=$1 &&
+ test_tick &&
+ if test -z "$parent"
+ then
+ oid=$(echo $name | git -C "$repo" commit-tree $T)
+ else
+ oid=$(echo $name | git -C "$repo" commit-tree -p $parent $T)
+ fi &&
+ eval $name=$oid &&
+ parent=$oid &&
+ shift ||
+ return 1
+ done
Broken &&-chain. Use:
done &&
+ git -C "$repo" update-ref refs/heads/master $oid
+}
+
+# Note: DO NOT run it in a subshell, otherwise the variables will not be set
+create_pack_1 () {
+ P1=$(git -C "$master_repo/objects/pack" pack-objects -q pack <<-EOF
Which variables? Note that you can capture output of a subshell into a
variable, if necessary.