Skip to content

Commit f0674a8

Browse files
committed
git-recover: introduce interactive mode
1 parent 477c39e commit f0674a8

File tree

2 files changed

+77
-9
lines changed

2 files changed

+77
-9
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,15 @@ repository (but haven't yet been garbage collected), you can run:
4141

4242
Options
4343
-------
44-
git-recover [-a] [--full] [<id> [-f <filename>] ...]
44+
git-recover [-a] [-i] [--full] [<id> [-f <filename>] ...]
4545

4646
`-a`, `--all`
4747
Write all orphaned blobs to the current working directory. Each file will
4848
be named using its 40 character object ID.
4949

50+
`-i`, `--interactive`
51+
Display information about each orphaned blob and prompt to recover it.
52+
5053
`--full`
5154
List or recover all orphaned blobs, even those that are in packfiles. By
5255
default, `git-recover` will only look at loose object files, which limits

git-recover

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ GIT_DIR=$(git rev-parse --git-dir)
1515

1616
DO_RECOVER=0
1717
DO_FULL=0
18+
DO_INTERACTIVE=0
1819
BLOBS=()
1920
FILENAMES=()
2021

2122
function die_usage {
22-
echo "usage: $PROGNAME [-a] [--full] [<id> [-f <filename>] ...]" >&2
23+
echo "usage: $PROGNAME [-a] [-i] [--full] [<id> [-f <filename>] ...]" >&2
2324
exit 1
2425
}
2526

@@ -28,6 +29,9 @@ while [[ $# -gt 0 ]]; do
2829
-a|--all)
2930
DO_RECOVER=1
3031
;;
32+
-i|--interactive)
33+
DO_INTERACTIVE=1
34+
;;
3135
--full)
3236
DO_FULL=1
3337
;;
@@ -96,10 +100,9 @@ function find_unreachable() {
96100
"${FULLNESS}" --no-progress | sed -ne 's/^unreachable blob //p'))
97101
}
98102

99-
function write_one_file {
103+
function read_one_file {
100104
BLOB=$1
101105
FILTER_NAME=$2
102-
OUTPUT_NAME=$3
103106
ARGS=()
104107

105108
if [ -z "$FILTER_NAME" ]; then
@@ -108,9 +111,19 @@ function write_one_file {
108111
ARGS+=("--filters" "--path=$FILTER_NAME")
109112
fi
110113

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."
114127
}
115128

116129
function write_recoverable {
@@ -139,17 +152,20 @@ function timestamp_to_s {
139152
fi
140153
}
141154

142-
function print_recoverable {
155+
function sort_by_timestamp {
143156
# sort blobs in loose objects by their timestamp (packed blobs last)
144157
BLOB_AND_TIMESTAMPS=($(for BLOB in "${BLOBS[@]}"; do
145158
LOOSE="${BLOB::2}/${BLOB:2}"
146159
TIME=$(file_time "$GIT_DIR/objects/$LOOSE" 2>/dev/null || true)
147160
echo "$BLOB $TIME"
148161
done | sort -k2 -r))
162+
}
149163

164+
function print_recoverable {
150165
echo "Recoverable orphaned git blobs:"
151166
echo ""
152167

168+
sort_by_timestamp
153169
for BLOB_AND_TIMESTAMP in "${BLOB_AND_TIMESTAMPS[@]}"; do
154170
BLOB=${BLOB_AND_TIMESTAMP::40}
155171
TIME=${BLOB_AND_TIMESTAMP:41}
@@ -159,6 +175,53 @@ function print_recoverable {
159175
done
160176
}
161177

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+
162225

163226
if [ ${#BLOBS[@]} != 0 ]; then
164227
expand_given_blobs
@@ -171,7 +234,9 @@ if [ ${#BLOBS[@]} == 0 ]; then
171234
exit
172235
fi
173236

174-
if [ $DO_RECOVER == 1 ]; then
237+
if [ $DO_INTERACTIVE == 1 ]; then
238+
interactive
239+
elif [ $DO_RECOVER == 1 ]; then
175240
write_recoverable
176241
else
177242
print_recoverable

0 commit comments

Comments
 (0)