 26c0d09bfd
			
		
	
	26c0d09bfd
	
	
	
		
			
			bash may not be installed in /bin, so don't make that assumption. Change-Id: I04ae786c7558f829ddf9649c127cbba1457dcfd3
		
			
				
	
	
		
			567 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			567 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
| #!/usr/bin/env bash
 | |
| # Copyright (c) 2012, Code Aurora Forum. All rights reserved.
 | |
| #
 | |
| # Redistribution and use in source and binary forms, with or without
 | |
| # modification, are permitted provided that the following conditions are
 | |
| # met:
 | |
| #    # Redistributions of source code must retain the above copyright
 | |
| #       notice, this list of conditions and the following disclaimer.
 | |
| #    # Redistributions in binary form must reproduce the above
 | |
| #       copyright notice, this list of conditions and the following
 | |
| #       disclaimer in the documentation and/or other materials provided
 | |
| #       with the distribution.
 | |
| #    # Neither the name of Code Aurora Forum, Inc. nor the names of its
 | |
| #       contributors may be used to endorse or promote products derived
 | |
| #       from this software without specific prior written permission.
 | |
| #
 | |
| # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 | |
| # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | |
| # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 | |
| # ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 | |
| # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | |
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | |
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 | |
| # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 | |
| # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 | |
| # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 | |
| # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
| 
 | |
| usage() { # error_message
 | |
| 
 | |
|     cat <<-EOF
 | |
| 		usage: $(basename $0) [-unvt] [--noref] [--nolosse] [-r|--ratio number]
 | |
| 		                      [git gc option...] git.repo
 | |
| 
 | |
| 		-u|-h                usage/help
 | |
| 		-v verbose
 | |
| 		-n dry-run           don't actually repack anything
 | |
| 		-t touch             treat repo as if it had been touched
 | |
| 		--noref              avoid extra ref packing timestamp checking
 | |
| 		--noloose            do not run just because there are loose object dirs
 | |
| 		                     (repacking may still run if they are referenced)
 | |
| 		-r ratio <number>    packfile ratio to aim for (default 10)
 | |
| 
 | |
| 		git gc option        will be passed as args to git gc
 | |
| 
 | |
| 		git.repo             to run gc against
 | |
| 
 | |
| 		Garbage collect using a pseudo logarithmic packfile maintenance
 | |
| 		approach.  This approach attempts to minimize packfile churn
 | |
| 		by keeping several generations of varying sized packfiles around
 | |
| 		and only consolidating packfiles (or loose objects) which are
 | |
| 		either new packfiles, or packfiles close to the same size as
 | |
| 		another packfile.
 | |
| 
 | |
| 		An estimate is used to predict when rollups (one consolidation
 | |
| 		would cause another consolidation) would occur so that this
 | |
| 		rollup can be done all at once via a single repack.  This reduces
 | |
| 		both the runtime and the pack file churn in rollup cases.
 | |
| 
 | |
| 		Approach: plan each consolidation by creating a table like this:
 | |
| 
 | |
| 		Id Keep Size           Sha1(or consolidation list)      Actions(repack down up note)
 | |
| 		1     - 11356          9052edfb7392646cd4e5f362b953675985f01f96 y - - New
 | |
| 		2     - 429088         010904d5c11cd26a79fda91b01ab454d1001b402 y - - New
 | |
| 		c1    - 440444         [1,2]                                    - - -
 | |
| 
 | |
| 		Id:    numbers preceded by a c are estimated "c pack" files
 | |
| 		Keep:  - none, k private keep, o our keep
 | |
| 		Size:  in disk blocks (default du output)
 | |
| 		Sha1:  of packfile, or consolidation list of packfile ids
 | |
| 		Actions
 | |
| 		repack: - n no, y yes
 | |
| 		down:   - noop, ^ consolidate with a file above
 | |
| 		up:     - noop, v consolidate with a file below
 | |
| 		note:   Human description of script decisions:
 | |
| 		         New (file is a new packfile)
 | |
| 		         Consolidate with:<list of packfile ids>
 | |
| 		         (too far from:<list of packfile ids>)
 | |
| 
 | |
| 		On the first pass, always consolidate any new packfiles along
 | |
| 		with loose objects and along with any packfiles which are within
 | |
| 		the ratio size of their predecessors (note, the list is ordered
 | |
| 		by increasing size).  After each consolidation, insert a fake
 | |
| 		consolidation, or "c pack", to naively represent the size and
 | |
| 		ordered positioning of the anticipated new consolidated pack.
 | |
| 		Every time a new pack is planned, rescan the list in case the
 | |
| 		new "c pack" would cause more consolidation...
 | |
| 
 | |
| 		Once the packfiles which need consolidation are determined, the
 | |
| 		packfiles which will not be consolidated are marked with a .keep
 | |
| 		file, and those which will be consolidated will have their .keep
 | |
| 		removed if they have one.  Thus, the packfiles with a .keep will
 | |
| 		not get repacked.
 | |
| 
 | |
| 		Packfile consolidation is determined by the --ratio parameter
 | |
| 		(default is 10).  This ratio is somewhat of a tradeoff.  The
 | |
| 		smaller the number, the more packfiles will be kept on average;
 | |
| 		this increases disk utilization somewhat.  However, a larger
 | |
| 		ratio causes greater churn and may increase disk utilization due
 | |
| 		to deleted packfiles not being reclaimed since they may still be
 | |
| 		kept open by long running applications such as Gerrit.  Sane
 | |
| 		ratio values are probably between 2 and 10.  Since most
 | |
| 		consolidations actually end up smaller than the estimated
 | |
| 		consolidated packfile size (due to compression), the true ratio
 | |
| 		achieved will likely be 1 to 2 greater than the target ratio.
 | |
| 		The smaller the target ratio, the greater this discrepancy.
 | |
| 
 | |
| 		Finally, attempt to skip garbage collection entirely on untouched
 | |
| 		repos.  In order to determine if a repo has been touched, use the
 | |
| 		timestamp on the script's keep files, if any relevant file/dir
 | |
| 		is newer than a keep marker file, assume that the repo has been
 | |
| 		touched and gc needs to run.  Also assume gc needs to run whenever
 | |
| 		there are loose object dirs since they may contain untouched
 | |
| 		unreferenced loose objects which need to be pruned (once they
 | |
| 		expire).
 | |
| 
 | |
| 		In order to allow the keep files to be an effective timestamp
 | |
| 		marker to detect relevant changes in a repo since the last run,
 | |
| 		all relevant files and directories which may be modified during a
 | |
| 		gc run (even during a noop gc run), must have their timestamps
 | |
| 		reset to the same time as the keep files or gc will always run
 | |
| 		even on untouched repos.  The relevant files/dirs are all those
 | |
| 		files and directories which garbage collection, object packing,
 | |
| 		ref packing and pruning might change during noop actions.
 | |
| EOF
 | |
| 
 | |
|     [ -n "$1" ] && info "ERROR $1"
 | |
| 
 | |
|     exit 128
 | |
| }
 | |
| 
 | |
| debug() { [ -n "$SW_V" ] && info "$1" ; }
 | |
| info() { echo "$1" >&2 ; }
 | |
| 
 | |
| array_copy() { #v2 # array_src array_dst
 | |
|     local src=$1 dst=$2
 | |
|     local s i=0
 | |
|     eval s=\${#$src[@]}
 | |
|     while [ $i -lt $s ] ; do
 | |
|         eval $dst[$i]=\"\${$src[$i]}\"
 | |
|         i=$(($i + 1))
 | |
|     done
 | |
| }
 | |
| 
 | |
| array_equals() { #v2 # array_name [vals...]
 | |
|     local a=$1 ; shift
 | |
|     local s=0 t=() val
 | |
|     array_copy "$a" t
 | |
|     for s in "${!t[@]}" ; do s=$((s+1)) ; done
 | |
|     [ "$s" -ne "$#" ] && return 1
 | |
|     for val in "${t[@]}" ; do
 | |
|         [ "$val" = "$1" ] || return 2
 | |
|         shift
 | |
|     done
 | |
|     return 0
 | |
| }
 | |
| 
 | |
| packs_sizes() { # git.repo > "size pack"...
 | |
|     du -s "$1"/objects/pack/pack-$SHA1.pack | sort -n 2> /dev/null
 | |
| }
 | |
| 
 | |
| is_ourkeep() { grep -q "$KEEP" "$1" 2> /dev/null ; } # keep
 | |
| has_ourkeep() { is_ourkeep "$(keep_for "$1")" ; } # pack
 | |
| has_keep() { [ -f "$(keep_for "$1")" ] ; } # pack
 | |
| is_repo() { [ -d "$1/objects" ] && [ -d "$1/refs/heads" ] ; } # git.repo
 | |
| 
 | |
| keep() { # pack   # returns true if we added our keep
 | |
|     keep=$(keep_for "$1")
 | |
|     [ -f "$keep" ] && return 1
 | |
|     echo "$KEEP" > "$keep"
 | |
|     return 0
 | |
| }
 | |
| 
 | |
| keep_for() { # packfile > keepfile
 | |
|     local keep=$(echo "$1" | sed -es'/\.pack$/.keep/')
 | |
|     [ "${keep/.keep}" = "$keep" ] && return 1
 | |
|     echo "$keep"
 | |
| }
 | |
| 
 | |
| idx_for() { # packfile > idxfile
 | |
|     local idx=$(echo "$1" | sed -es'/\.pack$/.idx/')
 | |
|     [ "${idx/.idx}" = "$idx" ] && return 1
 | |
|     echo "$idx"
 | |
| }
 | |
| 
 | |
| # pack_or_keep_file > sha
 | |
| sha_for() { echo "$1" | sed -es'|\(.*/\)*pack-\([^.]*\)\..*$|\2|' ; }
 | |
| 
 | |
| private_keeps() { # git.repo -> sets pkeeps
 | |
|     local repo=$1 ary=$2
 | |
|     local keep keeps=("$repo"/objects/pack/pack-$SHA1.keep)
 | |
|     pkeeps=()
 | |
|     for keep in "${keeps[@]}" ; do
 | |
|         is_ourkeep "$keep" || pkeeps=("${pkeeps[@]}" "$keep")
 | |
|     done
 | |
| }
 | |
| 
 | |
| is_tooclose() { [ "$(($1 * $RATIO))" -gt "$2" ] ; } # smaller larger
 | |
| 
 | |
| unique() { # [args...] > unique_words
 | |
|     local lines=$(while [ $# -gt 0 ] ; do echo "$1" ; shift ; done)
 | |
|     lines=$(echo "$lines" | sort -u)
 | |
|     echo $lines  # as words
 | |
| }
 | |
| 
 | |
| outfs() { # fs [args...] > argfs...
 | |
|     local fs=$1 ; shift
 | |
|     [ $# -gt 0 ] && echo -n "$1" ; shift
 | |
|     while [ $# -gt 0 ] ; do echo -n "$fs$1" ; shift ; done
 | |
| }
 | |
| 
 | |
| sort_list() { # < list > formatted_list
 | |
|     # n has_keep size sha repack down up note
 | |
|     awk '{ note=$8; for(i=8;i<NF;i++) note=note " "$(i+1)
 | |
|            printf("%-5s %s %-14s %-40s %s %s %s %s\n", \
 | |
|                      $1,$2,   $3,  $4, $5,$6,$7,note)}' |\
 | |
|         sort -k 3,3n -k 1,1n
 | |
| }
 | |
| 
 | |
| is_touched() { # git.repo
 | |
|     local repo=$1
 | |
|     local loose keep ours newer
 | |
|     [ -n "$SW_T" ] && { debug "$SW_T -> treat as touched" ; return 0 ; }
 | |
| 
 | |
|     if [ -z "$SW_LOOSE" ] ; then
 | |
|         # If there are loose objects, they may need to be pruned,
 | |
|         # run even if nothing has really been touched.
 | |
|         loose=$(find "$repo/objects" -type d \
 | |
|                       -wholename "$repo/objects/[0-9][0-9]"
 | |
|                       -print -quit 2>/dev/null)
 | |
|         [ -n "$loose" ] && { info "There are loose object directories" ; return 0 ; }
 | |
|     fi
 | |
| 
 | |
|     # If we don't have a keep, the current packfiles may not have been
 | |
|     # compressed with the current gc policy (gc may never have been run),
 | |
|     # so run at least once to repack everything.  Also, we need a marker
 | |
|     # file for timestamp tracking (a dir needs to detect changes within
 | |
|     # it, so it cannot be a marker) and our keeps are something we control,
 | |
|     # use them.
 | |
|     for keep in "$repo"/objects/pack/pack-$SHA1.keep ; do
 | |
|         is_ourkeep "$keep" && { ours=$keep ; break ; }
 | |
|     done
 | |
|     [ -z "$ours" ] && { info 'We have no keep (we have never run?): run' ; return 0 ; }
 | |
| 
 | |
|     debug "Our timestamp keep: $ours"
 | |
|     # The wholename stuff seems to get touched by a noop git gc
 | |
|     newer=$(find "$repo/objects" "$repo/refs" "$repo/packed-refs" \
 | |
|                   '!' -wholename "$repo/objects/info" \
 | |
|                   '!' -wholename "$repo/objects/info/*" \
 | |
|                   -newer "$ours" \
 | |
|                   -print -quit 2>/dev/null)
 | |
|     [ -z "$newer" ] && return 1
 | |
| 
 | |
|     info "Touched since last run: $newer"
 | |
|     return 0
 | |
| }
 | |
| 
 | |
| touch_refs() { # git.repo start_date refs
 | |
|     local repo=$1 start_date=$2 refs=$3
 | |
|     (
 | |
|         debug "Setting start date($start_date) on unpacked refs:"
 | |
|         debug "$refs"
 | |
|         cd "$repo/refs" || return
 | |
|         # safe to assume no newlines in a ref name
 | |
|         echo "$refs" | xargs -d '\n' -n 1 touch -c -d "$start_date"
 | |
|     )
 | |
| }
 | |
| 
 | |
| set_start_date() { # git.repo start_date refs refdirs packedrefs [packs]
 | |
|     local repo=$1 start_date=$2 refs=$3 refdirs=$4 packedrefs=$5 ; shift 5
 | |
|     local pack keep idx repacked
 | |
| 
 | |
|     # This stuff is touched during object packs
 | |
|     while [ $# -gt 0 ] ; do
 | |
|         pack=$1 ; shift
 | |
|         keep="$(keep_for "$pack")"
 | |
|         idx="$(idx_for "$pack")"
 | |
|         touch -c -d "$start_date" "$pack" "$keep" "$idx"
 | |
|         debug "Setting start date on: $pack $keep $idx"
 | |
|     done
 | |
|     # This will prevent us from detecting any deletes in the pack dir
 | |
|     # since gc ran, except for private keeps which we are checking
 | |
|     # manually.  But there really shouldn't be any other relevant deletes
 | |
|     # in this dir which should cause us to rerun next time, deleting a
 | |
|     # pack or index file by anything but gc would be bad!
 | |
|     debug "Setting start date on pack dir: $start_date"
 | |
|     touch -c -d "$start_date" "$repo/objects/pack"
 | |
| 
 | |
| 
 | |
|     if [ -z "$SW_REFS" ] ; then
 | |
|         repacked=$(find "$repo/packed-refs" -newer "$repo/objects/pack"
 | |
|                       -print -quit 2>/dev/null)
 | |
|         if [ -n "$repacked" ] ; then
 | |
|             # The ref dirs and packed-ref files seem to get touched even on
 | |
|             # a noop refpacking
 | |
|             debug "Setting start date on packed-refs"
 | |
|             touch -c -d "$start_date" "$repo/packed-refs"
 | |
|             touch_refs "$repo" "$start_date" "$refdirs"
 | |
| 
 | |
|             # A ref repack does not imply a ref change, but since it is
 | |
|             # hard to tell, simply assume so
 | |
|             if [ "$refs" != "$(cd "$repo/refs" ; find -depth)" ] || \
 | |
|                [ "$packedrefs" != "$(<"$repo/packed-refs")" ] ; then
 | |
|                 # We retouch if needed (instead of simply checking then
 | |
|                 # touching) to avoid a race between the check and the set.
 | |
|                 debug "  but refs actually got packed, so retouch packed-refs"
 | |
|                 touch -c "$repo/packed-refs"
 | |
|             fi
 | |
|         fi
 | |
|     fi
 | |
| }
 | |
| 
 | |
| note_consolidate() { # note entry > note (no duplicated consolidated entries)
 | |
|     local note=$1 entry=$2
 | |
|     local entries=() ifs=$IFS
 | |
|     if  echo "$note" | grep -q 'Consolidate with:[0-9,c]' ; then
 | |
|         IFS=,
 | |
|         entries=( $(echo "$note" | sed -es'/^.*Consolidate with:\([0-9,c]*\).*$/\1/') )
 | |
|         note=( $(echo "$note" | sed -es'/Consolidate with:[0-9,c]*//') )
 | |
|         IFS=$ifs
 | |
|     fi
 | |
|     entries=( $(unique "${entries[@]}" "$entry") )
 | |
|     echo "$note Consolidate with:$(outfs , "${entries[@]}")"
 | |
| }
 | |
| 
 | |
| note_toofar() { # note entry > note (no duplicated "too far" entries)
 | |
|     local note=$1 entry=$2
 | |
|     local entries=() ifs=$IFS
 | |
|     if  echo "$note" | grep -q '(too far from:[0-9,c]*)' ; then
 | |
|         IFS=,
 | |
|         entries=( $(echo "$note" | sed -es'/^.*(too far from:\([0-9,c]*\)).*$/\1/') )
 | |
|         note=( $(echo "$note" | sed -es'/(too far from:[0-9,c]*)//') )
 | |
|         IFS=$ifs
 | |
|     fi
 | |
|     entries=( $(unique "${entries[@]}" "$entry") )
 | |
|     echo "$note (too far from:$(outfs , "${entries[@]}"))"
 | |
| }
 | |
| 
 | |
| last_entry() { # isRepack pline repackline > last_rows_entry
 | |
|     local size_hit=$1 pline=$2 repackline=$3
 | |
|     if [ -n "$pline" ] ; then
 | |
|         if [ -n "$size_hit" ] ; then
 | |
|             echo "$repack_line"
 | |
|         else
 | |
|             echo "$pline"
 | |
|         fi
 | |
|     fi
 | |
| }
 | |
| 
 | |
| init_list() { # git.repo > shortlist
 | |
|     local repo=$1
 | |
|     local file
 | |
|     local n has_keep size sha repack
 | |
| 
 | |
|     packs_sizes "$1" | {
 | |
|         while read size file ; do
 | |
|             n=$((n+1))
 | |
|             repack=n
 | |
|             has_keep=-
 | |
|             if has_keep "$file" ; then
 | |
|                 has_keep=k
 | |
|                 has_ourkeep "$file" && has_keep=o
 | |
|             fi
 | |
|             sha=$(sha_for "$file")
 | |
|             echo "$n $has_keep $size $sha $repack"
 | |
|         done
 | |
|     } | sort_list
 | |
| }
 | |
| 
 | |
| consolidate_list() { # run < list > list
 | |
|     local run=$1
 | |
|     local sum=0 psize=0 sum_size=0 size_hit pn clist pline repackline
 | |
|     local n has_keep size sha repack down up note
 | |
| 
 | |
|     {
 | |
|         while read n has_keep size sha repack down up note; do
 | |
|             [ -z "$up" ] && up='-'
 | |
|             [ -z "$down" ] && down="-"
 | |
| 
 | |
|             if [ "$has_keep" = "k" ] ; then
 | |
|                 echo "$n $has_keep $size $sha $repack - - Private"
 | |
|                 continue
 | |
|             fi
 | |
| 
 | |
|             if [ "$repack" = "n" ] ; then
 | |
|                 if is_tooclose $psize $size ; then
 | |
|                     size_hit=y
 | |
|                     repack=y
 | |
|                     sum=$(($sum + $sum_size + $size))
 | |
|                     sum_size=0 # Prevents double summing this entry
 | |
|                     clist=($(unique "${clist[@]}" $pn $n))
 | |
|                     down="^"
 | |
|                     [ "$has_keep" = "-" ] && note="$note New +"
 | |
|                     note=$(note_consolidate "$note" "$pn")
 | |
|                 elif [ "$has_keep" = "-" ] ; then
 | |
|                     repack=y
 | |
|                     sum=$(($sum + $size))
 | |
|                     sum_size=0 # Prevents double summing this entry
 | |
|                     clist=($(unique "${clist[@]}" $n))
 | |
|                     note="$note New"
 | |
|                 elif [ $psize -ne 0 ] ; then
 | |
|                     sum_size=$size
 | |
|                     down="!"
 | |
|                     note=$(note_toofar "$note" "$pn")
 | |
|                 else
 | |
|                     sum_size=$size
 | |
|                 fi
 | |
|             else
 | |
|                 sum_size=$size
 | |
|             fi
 | |
| 
 | |
|             # By preventing "c files" (consolidated) from being marked
 | |
|             # "repack" they won't get keeps
 | |
|             repack2=y
 | |
|             [ "${n/c}" != "$n" ] && { repack=- ; repack2=- ; }
 | |
| 
 | |
|             last_entry "$size_hit" "$pline" "$repack_line"
 | |
|             # Delay the printout until we know whether we are
 | |
|             # being consolidated with the entry following us
 | |
|             # (we won't know until the next iteration).
 | |
|             # size_hit is used to determine which of the lines
 | |
|             # below will actually get printed above on the next
 | |
|             # iteration.
 | |
|             pline="$n $has_keep $size $sha $repack $down $up $note"
 | |
|             repack_line="$n $has_keep $size $sha $repack2 $down v $note"
 | |
| 
 | |
|             pn=$n ; psize=$size # previous entry data
 | |
|             size_hit='' # will not be consolidated up
 | |
| 
 | |
|         done
 | |
|         last_entry "$size_hit" "$pline" "$repack_line"
 | |
| 
 | |
|         [ $sum -gt 0 ] && echo "c$run - $sum [$(outfs , "${clist[@]}")] - - -"
 | |
| 
 | |
|     } | sort_list
 | |
| }
 | |
| 
 | |
| process_list() { # git.repo > list
 | |
|     local list=$(init_list "$1")  plist run=0
 | |
| 
 | |
|     while true ; do
 | |
|         plist=$list
 | |
|         run=$((run +1))
 | |
|         list=$(echo "$list" | consolidate_list "$run")
 | |
|         if [ "$plist" != "$list" ] ; then
 | |
|             debug "------------------------------------------------------------------------------------"
 | |
|             debug "$HEADER"
 | |
|             debug "$list"
 | |
|         else
 | |
|             break
 | |
|         fi
 | |
|     done
 | |
|     debug "------------------------------------------------------------------------------------"
 | |
|     echo "$list"
 | |
| }
 | |
| 
 | |
| repack_list() { # git.repo < list
 | |
|     local repo=$1
 | |
|     local start_date newpacks=0 pkeeps keeps=1 refs refdirs rtn
 | |
|     local packedrefs=$(<"$repo/packed-refs")
 | |
| 
 | |
|     # so they don't appear touched after a noop refpacking
 | |
|     if [ -z "$SW_REFS" ] ; then
 | |
|         refs=$(cd "$repo/refs" ; find -depth)
 | |
|         refdirs=$(cd "$repo/refs" ; find -type d -depth)
 | |
|         debug "Before refs:"
 | |
|         debug "$refs"
 | |
|     fi
 | |
| 
 | |
|     # Find a private keep snapshot which has not changed from
 | |
|     # before our start_date so private keep deletions during gc
 | |
|     # can be detected
 | |
|     while ! array_equals pkeeps "${keeps[@]}" ; do
 | |
|        debug "Getting a private keep snapshot"
 | |
|        private_keeps "$repo"
 | |
|        keeps=("${pkeeps[@]}")
 | |
|        debug "before keeps: ${keeps[*]}"
 | |
|        start_date=$(date)
 | |
|        private_keeps "$repo"
 | |
|        debug "after keeps: ${pkeeps[*]}"
 | |
|     done
 | |
| 
 | |
|     while read n has_keep size sha repack down up note; do
 | |
|         if [ "$repack" = "y" ] ; then
 | |
|             keep="$repo/objects/pack/pack-$sha.keep"
 | |
|             info "Repacking $repo/objects/pack/pack-$sha.pack"
 | |
|             [ -f "$keep" ] && rm -f "$keep"
 | |
|         fi
 | |
|     done
 | |
| 
 | |
|     ( cd "$repo" && git gc "${GC_OPTS[@]}" ) ; rtn=$?
 | |
| 
 | |
|     # Mark any files withoug a .keep with our .keep
 | |
|     packs=("$repo"/objects/pack/pack-$SHA1.pack)
 | |
|     for pack in "${packs[@]}" ; do
 | |
|         if keep "$pack" ; then
 | |
|             info "New pack: $pack"
 | |
|             newpacks=$((newpacks+1))
 | |
|         fi
 | |
|     done
 | |
| 
 | |
|     # Record start_time.  If there is more than 1 new packfile, we
 | |
|     # don't want to risk touching it with an older date since that
 | |
|     # would prevent consolidation on the next run.  If the private
 | |
|     # keeps have changed, then we should run next time no matter what.
 | |
|     if [ $newpacks -le 1 ] || ! array_equals pkeeps "${keeps[@]}" ; then
 | |
|         set_start_date "$repo" "$start_date" "$refs" "$refdirs" "$packedrefs" "${packs[@]}"
 | |
|     fi
 | |
| 
 | |
|     return $rtn # we really only care about the gc error code
 | |
| }
 | |
| 
 | |
| git_gc() { # git.repo
 | |
|     local list=$(process_list "$1")
 | |
|     if [ -z "$SW_V" ] ; then
 | |
|         info "Running $PROG on $1.  git gc options: ${GC_OPTS[@]}"
 | |
|         echo "$HEADER" >&2
 | |
|         echo "$list" >&2 ;
 | |
|     fi
 | |
|     echo "$list" | repack_list "$1"
 | |
| }
 | |
| 
 | |
| 
 | |
| PROG=$(basename "$0")
 | |
| HEADER="Id Keep Size           Sha1(or consolidation list)      Actions(repack down up note)"
 | |
| KEEP=git-exproll
 | |
| HEX='[0-9a-f]'
 | |
| HEX10=$HEX$HEX$HEX$HEX$HEX$HEX$HEX$HEX$HEX$HEX
 | |
| SHA1=$HEX10$HEX10$HEX10$HEX10
 | |
| 
 | |
| RATIO=10
 | |
| SW_N='' ; SW_V='' ; SW_T='' ; SW_REFS='' ; SW_LOOSE='' ; GC_OPTS=()
 | |
| while [ $# -gt 0 ] ; do
 | |
|     case "$1" in
 | |
|         -u|-h)  usage ;;
 | |
|         -n)  SW_N="$1" ;;
 | |
|         -v)  SW_V="$1" ;;
 | |
| 
 | |
|         -t)  SW_T="$1" ;;
 | |
|         --norefs)  SW_REFS="$1" ;;
 | |
|         --noloose) SW_LOOSE="$1" ;;
 | |
| 
 | |
|         -r|--ratio)  shift ; RATIO="$1" ;;
 | |
| 
 | |
|         *)  [ $# -le 1 ] && break
 | |
|             GC_OPTS=( "${GC_OPTS[@]}" "$1" )
 | |
|             ;;
 | |
|     esac
 | |
|     shift
 | |
| done
 | |
| 
 | |
| 
 | |
| REPO="$1"
 | |
| if ! is_repo "$REPO" ; then
 | |
|     REPO=$REPO/.git
 | |
|     is_repo "$REPO" || usage "($1) is not likely a git repo"
 | |
| fi
 | |
| 
 | |
| 
 | |
| if [ -z "$SW_N" ] ; then
 | |
|     is_touched "$REPO" || { info "Repo untouched since last run" ; exit ; }
 | |
|     git_gc "$REPO"
 | |
| else
 | |
|     is_touched "$REPO" || info "Repo untouched since last run, analyze anyway."
 | |
|     process_list "$REPO" >&2
 | |
| fi
 |