@@ -15,11 +15,12 @@ GIT_DIR=$(git rev-parse --git-dir)
15
15
16
16
DO_RECOVER=0
17
17
DO_FULL=0
18
+ DO_INTERACTIVE=0
18
19
BLOBS=()
19
20
FILENAMES=()
20
21
21
22
function die_usage {
22
- echo " usage: $PROGNAME [-a] [--full] [<id> [-f <filename>] ...]" >&2
23
+ echo " usage: $PROGNAME [-a] [-i] [- -full] [<id> [-f <filename>] ...]" >&2
23
24
exit 1
24
25
}
25
26
@@ -28,6 +29,9 @@ while [[ $# -gt 0 ]]; do
28
29
-a|--all)
29
30
DO_RECOVER=1
30
31
;;
32
+ -i|--interactive)
33
+ DO_INTERACTIVE=1
34
+ ;;
31
35
--full)
32
36
DO_FULL=1
33
37
;;
@@ -96,10 +100,9 @@ function find_unreachable() {
96
100
" ${FULLNESS} " --no-progress | sed -ne ' s/^unreachable blob //p' ) )
97
101
}
98
102
99
- function write_one_file {
103
+ function read_one_file {
100
104
BLOB=$1
101
105
FILTER_NAME=$2
102
- OUTPUT_NAME=$3
103
106
ARGS=()
104
107
105
108
if [ -z " $FILTER_NAME " ]; then
@@ -108,9 +111,19 @@ function write_one_file {
108
111
ARGS+=(" --filters" " --path=$FILTER_NAME " )
109
112
fi
110
113
111
- echo -n " Writing $BLOB : "
112
- git cat-file " ${ARGS[@]} " " $BLOB " > " $OUTPUT_NAME "
113
- echo " $OUTPUT_NAME "
114
+ git cat-file " ${ARGS[@]} " " $BLOB "
115
+ }
116
+
117
+ function write_one_file {
118
+ BLOB=$1
119
+ FILTER_NAME=$2
120
+ OUTPUT_NAME=$3
121
+
122
+ ABBREV=$( git rev-parse --short " ${BLOB} " )
123
+
124
+ echo -n " Writing $ABBREV : "
125
+ read_one_file " $BLOB " " $FILTER_NAME " > " $OUTPUT_NAME "
126
+ echo " $OUTPUT_NAME ."
114
127
}
115
128
116
129
function write_recoverable {
@@ -139,17 +152,20 @@ function timestamp_to_s {
139
152
fi
140
153
}
141
154
142
- function print_recoverable {
155
+ function sort_by_timestamp {
143
156
# sort blobs in loose objects by their timestamp (packed blobs last)
144
157
BLOB_AND_TIMESTAMPS=($( for BLOB in " ${BLOBS[@]} " ; do
145
158
LOOSE=" ${BLOB:: 2} /${BLOB: 2} "
146
159
TIME=$( file_time " $GIT_DIR /objects/$LOOSE " 2> /dev/null || true)
147
160
echo " $BLOB $TIME "
148
161
done | sort -k2 -r) )
162
+ }
149
163
164
+ function print_recoverable {
150
165
echo " Recoverable orphaned git blobs:"
151
166
echo " "
152
167
168
+ sort_by_timestamp
153
169
for BLOB_AND_TIMESTAMP in " ${BLOB_AND_TIMESTAMPS[@]} " ; do
154
170
BLOB=${BLOB_AND_TIMESTAMP:: 40}
155
171
TIME=${BLOB_AND_TIMESTAMP: 41}
@@ -159,6 +175,53 @@ function print_recoverable {
159
175
done
160
176
}
161
177
178
+ function show_summary {
179
+ FILETYPE=$( read_one_file " ${BLOB} " | file -b -)
180
+ IS_TEXT=$( echo " ${FILETYPE} " | grep -c ' text$' 2> /dev/null || true)
181
+
182
+ if [ " $IS_TEXT " == " 1" ]; then
183
+ read_one_file " ${BLOB} "
184
+ else
185
+ read_one_file " ${BLOB} " | hexdump -C
186
+ fi
187
+ }
188
+
189
+ function interactive {
190
+ echo " Recoverable orphaned git blobs:"
191
+
192
+ sort_by_timestamp
193
+ for BLOB_AND_TIMESTAMP in " ${BLOB_AND_TIMESTAMPS[@]} " ; do
194
+ echo
195
+
196
+ BLOB=${BLOB_AND_TIMESTAMP:: 40}
197
+ TIME=${BLOB_AND_TIMESTAMP: 41}
198
+ DATE=$( [ ! -z " $TIME " ] && timestamp_to_s " $TIME " || echo " (Unknown)" )
199
+
200
+ echo " $BLOB ($DATE )"
201
+ show_summary " ${BLOB} " | head -4 | sed -e ' s/^/> /'
202
+ echo
203
+
204
+ while true
205
+ do
206
+ echo -n " Recover this file? [y,n,q]: "
207
+ read -r ans || return 1
208
+
209
+ case " $ans " in
210
+ [yY]* )
211
+ write_one_file " ${BLOB} " " " " ${BLOB} "
212
+ break
213
+ ;;
214
+ [nN]* )
215
+ break
216
+ ;;
217
+ [qQ]* )
218
+ return 0
219
+ ;;
220
+ esac
221
+ done
222
+ done
223
+ }
224
+
162
225
163
226
if [ ${# BLOBS[@]} != 0 ]; then
164
227
expand_given_blobs
@@ -171,7 +234,9 @@ if [ ${#BLOBS[@]} == 0 ]; then
171
234
exit
172
235
fi
173
236
174
- if [ $DO_RECOVER == 1 ]; then
237
+ if [ $DO_INTERACTIVE == 1 ]; then
238
+ interactive
239
+ elif [ $DO_RECOVER == 1 ]; then
175
240
write_recoverable
176
241
else
177
242
print_recoverable
0 commit comments