Skip to content

Commit 7345013

Browse files
committed
git-recover: recover deleted files in your repository
0 parents  commit 7345013

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed

git-recover

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
#!/bin/bash
2+
#
3+
# git-recover: recover deleted files in your repo
4+
# Copyright (c) Edward Thomson. All rights reserved.
5+
# Available under the MIT license; see the included LICENSE file.
6+
#
7+
# vim: set expandtab:ts=4:sw=4:number
8+
9+
set -e
10+
11+
IFS=$'\n'
12+
13+
PROGNAME=$(echo "$0" | sed -e 's/.*\///')
14+
GIT_DIR=$(git rev-parse --git-dir)
15+
16+
DO_RECOVER=0
17+
DO_FULL=0
18+
BLOBS=()
19+
FILENAMES=()
20+
21+
function die_usage {
22+
echo "usage: $PROGNAME [-a] [--full] [<id> [-f <filename>] ...]" >&2
23+
exit 1
24+
}
25+
26+
while [[ $# -gt 0 ]]; do
27+
case "$1" in
28+
-a|--all)
29+
DO_RECOVER=1
30+
;;
31+
--full)
32+
DO_FULL=1
33+
;;
34+
*)
35+
if [ "${1:0:1}" == "-" ]; then
36+
echo "$PROGNAME: unknown argument: $1" >&2
37+
die_usage
38+
fi
39+
BLOBS+=("$1")
40+
41+
shift
42+
if [ "$1" == "-f" ] || [ "$1" == "--filename" ]; then
43+
shift
44+
if [ $# == 0 ]; then
45+
die_usage
46+
fi
47+
FILENAMES+=("$1")
48+
shift
49+
else
50+
FILENAMES+=("")
51+
fi
52+
continue
53+
;;
54+
esac
55+
shift
56+
done
57+
58+
if [ ${#BLOBS[@]} != 0 ] && [ $DO_RECOVER == 1 ]; then
59+
die_usage
60+
elif [ ${#BLOBS[@]} != 0 ]; then
61+
DO_RECOVER=1
62+
fi
63+
64+
case "$OSTYPE" in
65+
darwin*|freebsd*) IS_BSD=1 ;;
66+
*) IS_BSD=0 ;;
67+
esac
68+
69+
function expand_given_blobs() {
70+
for i in "${!BLOBS[@]}"; do
71+
ID=$(git rev-parse --verify "${BLOBS[$i]}" 2>/dev/null || true)
72+
73+
if [ -z "$ID" ]; then
74+
echo "$PROGNAME: ${BLOBS[$i]} is not a valid object." 1>&2
75+
exit 1
76+
fi
77+
78+
TYPE=$(git cat-file -t "${ID}" 2>/dev/null || true)
79+
80+
if [ "$TYPE" != "blob" ]; then
81+
echo "$PROGNAME: ${BLOBS[$i]} is not a blob." 1>&2
82+
exit
83+
fi
84+
85+
BLOBS[$i]=$ID
86+
done
87+
}
88+
89+
# find all the unreachable blobs
90+
function find_unreachable() {
91+
FULLNESS="--no-full"
92+
93+
if [ $DO_FULL == 1 ]; then FULLNESS="--full"; fi
94+
95+
BLOBS=($(git fsck --unreachable --no-reflogs \
96+
"${FULLNESS}" --no-progress | sed -ne 's/^unreachable blob //p'))
97+
}
98+
99+
function write_one_file {
100+
BLOB=$1
101+
FILTER_NAME=$2
102+
OUTPUT_NAME=$3
103+
ARGS=()
104+
105+
if [ -z "$FILTER_NAME" ]; then
106+
ARGS+=("blob")
107+
else
108+
ARGS+=("--filters" "--path=$FILTER_NAME")
109+
fi
110+
111+
echo -n "Writing $BLOB: "
112+
git cat-file "${ARGS[@]}" "$BLOB" > "$OUTPUT_NAME"
113+
echo "$OUTPUT_NAME"
114+
}
115+
116+
function write_recoverable {
117+
for i in "${!BLOBS[@]}"; do
118+
BLOB=${BLOBS[$i]}
119+
FILTER_NAME=${FILENAMES[$i]}
120+
OUTPUT_NAME=${FILENAMES[$i]:-$BLOB}
121+
122+
write_one_file "$BLOB" "$FILTER_NAME" "$OUTPUT_NAME"
123+
done
124+
}
125+
126+
function file_time {
127+
if [ $IS_BSD == 1 ]; then
128+
stat -f %c "$1"
129+
else
130+
stat -c %Y "$1"
131+
fi
132+
}
133+
134+
function timestamp_to_s {
135+
if [ $IS_BSD == 1 ]; then
136+
date -r "$1"
137+
else
138+
date -d @"$1"
139+
fi
140+
}
141+
142+
function print_recoverable {
143+
# sort blobs in loose objects by their timestamp (packed blobs last)
144+
BLOB_AND_TIMESTAMPS=($(for BLOB in "${BLOBS[@]}"; do
145+
LOOSE="${BLOB::2}/${BLOB:2}"
146+
TIME=$(file_time "$GIT_DIR/objects/$LOOSE" 2>/dev/null || true)
147+
echo "$BLOB $TIME"
148+
done | sort -k2 -r))
149+
150+
echo "Recoverable orphaned git blobs:"
151+
echo ""
152+
153+
for BLOB_AND_TIMESTAMP in "${BLOB_AND_TIMESTAMPS[@]}"; do
154+
BLOB=${BLOB_AND_TIMESTAMP::40}
155+
TIME=${BLOB_AND_TIMESTAMP:41}
156+
DATE=$([ ! -z "$TIME" ] && timestamp_to_s "$TIME" || echo "(Unknown)")
157+
158+
echo "$BLOB $DATE"
159+
done
160+
}
161+
162+
163+
if [ ${#BLOBS[@]} != 0 ]; then
164+
expand_given_blobs
165+
else
166+
find_unreachable
167+
fi
168+
169+
if [ ${#BLOBS[@]} == 0 ]; then
170+
echo "$PROGNAME: no recoverable orphaned blobs."
171+
exit
172+
fi
173+
174+
if [ $DO_RECOVER == 1 ]; then
175+
write_recoverable
176+
else
177+
print_recoverable
178+
fi
179+

0 commit comments

Comments
 (0)