mirror of
https://github.com/git/git.git
synced 2024-10-29 21:37:53 +01:00
3e6b1d0abc
Some platforms refuse to rename a file that is open. When repacking an already packed repository without adding any new object, the resulting pack will contain the same set of objects as an existing pack, and on such platforms, a newly created packfile cannot replace the existing one. The logic detected this issue but did not try hard enough to recover from it. Especially because the files that needs renaming come in pairs, there potentially are different failure modes that one can be renamed but the others cannot. Asking manual recovery to end users were error prone. This patch tries to make it more robust by first making sure all the existing files that need to be renamed have been renamed before continuing, and attempts to roll back if some failed to rename. This is based on an initial patch by Robin Rosenberg. Signed-off-by: Junio C Hamano <gitster@pobox.com>
185 lines
4.4 KiB
Bash
Executable file
185 lines
4.4 KiB
Bash
Executable file
#!/bin/sh
|
|
#
|
|
# Copyright (c) 2005 Linus Torvalds
|
|
#
|
|
|
|
OPTIONS_KEEPDASHDASH=
|
|
OPTIONS_SPEC="\
|
|
git repack [options]
|
|
--
|
|
a pack everything in a single pack
|
|
A same as -a, and turn unreachable objects loose
|
|
d remove redundant packs, and run git-prune-packed
|
|
f pass --no-reuse-object to git-pack-objects
|
|
n do not run git-update-server-info
|
|
q,quiet be quiet
|
|
l pass --local to git-pack-objects
|
|
Packing constraints
|
|
window= size of the window used for delta compression
|
|
window-memory= same as the above, but limit memory size instead of entries count
|
|
depth= limits the maximum delta depth
|
|
max-pack-size= maximum size of each packfile
|
|
"
|
|
SUBDIRECTORY_OK='Yes'
|
|
. git-sh-setup
|
|
|
|
no_update_info= all_into_one= remove_redundant= unpack_unreachable=
|
|
local= quiet= no_reuse= extra=
|
|
while test $# != 0
|
|
do
|
|
case "$1" in
|
|
-n) no_update_info=t ;;
|
|
-a) all_into_one=t ;;
|
|
-A) all_into_one=t
|
|
unpack_unreachable=--unpack-unreachable ;;
|
|
-d) remove_redundant=t ;;
|
|
-q) quiet=-q ;;
|
|
-f) no_reuse=--no-reuse-object ;;
|
|
-l) local=--local ;;
|
|
--max-pack-size|--window|--window-memory|--depth)
|
|
extra="$extra $1=$2"; shift ;;
|
|
--) shift; break;;
|
|
*) usage ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
case "`git config --bool repack.usedeltabaseoffset || echo true`" in
|
|
true)
|
|
extra="$extra --delta-base-offset" ;;
|
|
esac
|
|
|
|
PACKDIR="$GIT_OBJECT_DIRECTORY/pack"
|
|
PACKTMP="$GIT_OBJECT_DIRECTORY/.tmp-$$-pack"
|
|
rm -f "$PACKTMP"-*
|
|
trap 'rm -f "$PACKTMP"-*' 0 1 2 3 15
|
|
|
|
# There will be more repacking strategies to come...
|
|
case ",$all_into_one," in
|
|
,,)
|
|
args='--unpacked --incremental'
|
|
;;
|
|
,t,)
|
|
if [ -d "$PACKDIR" ]; then
|
|
for e in `cd "$PACKDIR" && find . -type f -name '*.pack' \
|
|
| sed -e 's/^\.\///' -e 's/\.pack$//'`
|
|
do
|
|
if [ -e "$PACKDIR/$e.keep" ]; then
|
|
: keep
|
|
else
|
|
args="$args --unpacked=$e.pack"
|
|
existing="$existing $e"
|
|
fi
|
|
done
|
|
if test -n "$args" -a -n "$unpack_unreachable" -a \
|
|
-n "$remove_redundant"
|
|
then
|
|
args="$args $unpack_unreachable"
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
args="$args $local $quiet $no_reuse$extra"
|
|
names=$(git pack-objects --honor-pack-keep --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
|
|
exit 1
|
|
if [ -z "$names" ]; then
|
|
if test -z "$quiet"; then
|
|
echo Nothing new to pack.
|
|
fi
|
|
fi
|
|
|
|
# Ok we have prepared all new packfiles.
|
|
mkdir -p "$PACKDIR" || exit
|
|
|
|
# First see if there are packs of the same name and if so
|
|
# if we can move them out of the way (this can happen if we
|
|
# repacked immediately after packing fully.
|
|
rollback=
|
|
failed=
|
|
for name in $names
|
|
do
|
|
for sfx in pack idx
|
|
do
|
|
file=pack-$name.$sfx
|
|
test -f "$PACKDIR/$file" || continue
|
|
rm -f "$PACKDIR/old-$file" &&
|
|
mv "$PACKDIR/$file" "$PACKDIR/old-$file" || {
|
|
failed=t
|
|
break
|
|
}
|
|
rollback="$rollback $file"
|
|
done
|
|
test -z "$failed" || break
|
|
done
|
|
|
|
# If renaming failed for any of them, roll the ones we have
|
|
# already renamed back to their original names.
|
|
if test -n "$failed"
|
|
then
|
|
rollback_failure=
|
|
for file in $rollback
|
|
do
|
|
mv "$PACKDIR/old-$file" "$PACKDIR/$file" ||
|
|
rollback_failure="$rollback_failure $file"
|
|
done
|
|
if test -n "$rollback_failure"
|
|
then
|
|
echo >&2 "WARNING: Some packs in use have been renamed by"
|
|
echo >&2 "WARNING: prefixing old- to their name, in order to"
|
|
echo >&2 "WARNING: replace them with the new version of the"
|
|
echo >&2 "WARNING: file. But the operation failed, and"
|
|
echo >&2 "WARNING: attempt to rename them back to their"
|
|
echo >&2 "WARNING: original names also failed."
|
|
echo >&2 "WARNING: Please rename them in $PACKDIR manually:"
|
|
for file in $rollback_failure
|
|
do
|
|
echo >&2 "WARNING: old-$file -> $file"
|
|
done
|
|
fi
|
|
exit 1
|
|
fi
|
|
|
|
# Now the ones with the same name are out of the way...
|
|
fullbases=
|
|
for name in $names
|
|
do
|
|
fullbases="$fullbases pack-$name"
|
|
chmod a-w "$PACKTMP-$name.pack"
|
|
chmod a-w "$PACKTMP-$name.idx"
|
|
mv -f "$PACKTMP-$name.pack" "$PACKDIR/pack-$name.pack" &&
|
|
mv -f "$PACKTMP-$name.idx" "$PACKDIR/pack-$name.idx" ||
|
|
exit
|
|
done
|
|
|
|
# Remove the "old-" files
|
|
for name in $names
|
|
do
|
|
rm -f "$PACKDIR/old-pack-$name.idx"
|
|
rm -f "$PACKDIR/old-pack-$name.pack"
|
|
done
|
|
|
|
# End of pack replacement.
|
|
|
|
if test "$remove_redundant" = t
|
|
then
|
|
# We know $existing are all redundant.
|
|
if [ -n "$existing" ]
|
|
then
|
|
( cd "$PACKDIR" &&
|
|
for e in $existing
|
|
do
|
|
case " $fullbases " in
|
|
*" $e "*) ;;
|
|
*) rm -f "$e.pack" "$e.idx" "$e.keep" ;;
|
|
esac
|
|
done
|
|
)
|
|
fi
|
|
git prune-packed $quiet
|
|
fi
|
|
|
|
case "$no_update_info" in
|
|
t) : ;;
|
|
*) git-update-server-info ;;
|
|
esac
|