-
Notifications
You must be signed in to change notification settings - Fork 0
/
gitapply.sh
executable file
·77 lines (67 loc) · 1.85 KB
/
gitapply.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/bin/sh
#
# Apply a diff generated by git diff.
# Copyright (c) Petr Baudis, 2005
#
# This is basically just a smart patch wrapper. It handles stuff like
# mode changes, removal of files vs. zero-size files etc.
#
# Takes the diff on stdin.
gonefile=$(mktemp -t gitapply.XXXXXX)
todo=$(mktemp -t gitapply.XXXXXX)
patchfifo=$(mktemp -t gitapply.XXXXXX)
rm $patchfifo && mkfifo -m 600 $patchfifo
show-files --deleted >$gonefile
# patch file removal behaviour cannot be sensibly controlled, so we
# just handle it all ourselves.
patch -p1 -N <$patchfifo &
tee $patchfifo | egrep '^[+-]\{3\}' | {
victim=
origmode=
while read sign file attrs; do
echo $sign $file $attrs ... >&2
case $sign in
"+++")
victim=file
mode=$(echo $attrs | sed 's/.*mode:[0-7]*\([0-7]\{3\}\).*/\1/')
origmode=
[ "$mode" != "$attrs" ] && origmode=$mode
;;
"---")
if [ "$file" = "/dev/null" ]; then
torm=$(echo "$victim" | sed 's/[^\/]*\///') #-p1
echo -ne "rm\0$torm\0"
continue
elif [ "$victim" = "/dev/null" ]; then
echo -ne "add\0$file\0"
fi
mode=$(echo $attrs | sed 's/.*mode:[0-7]*\([0-7]\{3\}\).*/\1/')
if [ "$mode" ] && [ "$mode" != "$attrs" ] && [ "$origmode" != "$mode" ]; then
echo -ne "cm\0$mode\0$file\0"
fi
;;
*)
echo "invalid operation: $sign" >&2
;;
esac
done
} >$todo
wait
# Now we just recreate all the supposedly deleted files and
# kill only those who really are gone.
#
# This is done on the assumption that we are never going to have
# too many files deleted in the first place anyway.
touchfiles="$(show-files --deleted | join -a 2 $gonefile -)"
[ "$touchfiles" ] && touch $touchfiles
cat $todo | xargs -0 sh -c '
while [ "$1" ]; do
op="$1"; shift;
case "$op" in
"add") git add "$1"; shift;;
"rm") rm "$1" && git rm "$1"; shift;;
"cm") chmod "$1" "$2"; shift; shift;;
esac
done
'
rm $pathfifo $todo $gonefile