Improved pack format documentation.
[git] / git-applypatch.sh
1 #!/bin/sh
2 ##
3 ## applypatch takes four file arguments, and uses those to
4 ## apply the unpacked patch (surprise surprise) that they
5 ## represent to the current tree.
6 ##
7 ## The arguments are:
8 ##      $1 - file with commit message
9 ##      $2 - file with the actual patch
10 ##      $3 - "info" file with Author, email and subject
11 ##      $4 - optional file containing signoff to add
12 ##
13
14 USAGE='<msg> <patch> <info> [<signoff>]'
15 . git-sh-setup
16
17 case "$#" in 3|4) ;; *) usage ;; esac
18
19 final=.dotest/final-commit
20 ##
21 ## If this file exists, we ask before applying
22 ##
23 query_apply=.dotest/.query_apply
24
25 ## We do not munge the first line of the commit message too much
26 ## if this file exists.
27 keep_subject=.dotest/.keep_subject
28
29 ## We do not attempt the 3-way merge fallback unless this file exists.
30 fall_back_3way=.dotest/.3way
31
32 MSGFILE=$1
33 PATCHFILE=$2
34 INFO=$3
35 SIGNOFF=$4
36 EDIT=${VISUAL:-${EDITOR:-vi}}
37
38 export GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$INFO")"
39 export GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$INFO")"
40 export GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$INFO")"
41 export SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$INFO")"
42
43 if test '' != "$SIGNOFF"
44 then
45         if test -f "$SIGNOFF"
46         then
47                 SIGNOFF=`cat "$SIGNOFF"` || exit
48         elif case "$SIGNOFF" in yes | true | me | please) : ;; *) false ;; esac
49         then
50                 SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e '
51                                 s/>.*/>/
52                                 s/^/Signed-off-by: /'
53                 `
54         else
55                 SIGNOFF=
56         fi
57         if test '' != "$SIGNOFF"
58         then
59                 LAST_SIGNED_OFF_BY=`
60                         sed -ne '/^Signed-off-by: /p' "$MSGFILE" |
61                         tail -n 1
62                 `
63                 test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
64                     test '' = "$LAST_SIGNED_OFF_BY" && echo
65                     echo "$SIGNOFF"
66                 } >>"$MSGFILE"
67         fi
68 fi
69
70 patch_header=
71 test -f "$keep_subject" || patch_header='[PATCH] '
72
73 {
74         echo "$patch_header$SUBJECT"
75         if test -s "$MSGFILE"
76         then
77                 echo
78                 cat "$MSGFILE"
79         fi
80 } >"$final"
81
82 interactive=yes
83 test -f "$query_apply" || interactive=no
84
85 while [ "$interactive" = yes ]; do
86         echo "Commit Body is:"
87         echo "--------------------------"
88         cat "$final"
89         echo "--------------------------"
90         printf "Apply? [y]es/[n]o/[e]dit/[a]ccept all "
91         read reply
92         case "$reply" in
93                 y|Y) interactive=no;;
94                 n|N) exit 2;;   # special value to tell dotest to keep going
95                 e|E) "$EDIT" "$final";;
96                 a|A) rm -f "$query_apply"
97                      interactive=no ;;
98         esac
99 done
100
101 if test -x "$GIT_DIR"/hooks/applypatch-msg
102 then
103         "$GIT_DIR"/hooks/applypatch-msg "$final" || exit
104 fi
105
106 echo
107 echo Applying "'$SUBJECT'"
108 echo
109
110 git-apply --index "$PATCHFILE" || {
111
112         # git-apply exits with status 1 when the patch does not apply,
113         # but it die()s with other failures, most notably upon corrupt
114         # patch.  In the latter case, there is no point to try applying
115         # it to another tree and do 3-way merge.
116         test $? = 1 || exit 1
117
118         test -f "$fall_back_3way" || exit 1
119
120         # Here if we know which revision the patch applies to,
121         # we create a temporary working tree and index, apply the
122         # patch, and attempt 3-way merge with the resulting tree.
123
124         O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
125         rm -fr .patch-merge-*
126
127         if git-apply -z --index-info "$PATCHFILE" \
128                 >.patch-merge-index-info 2>/dev/null &&
129                 GIT_INDEX_FILE=.patch-merge-tmp-index \
130                 git-update-index -z --index-info <.patch-merge-index-info &&
131                 GIT_INDEX_FILE=.patch-merge-tmp-index \
132                 git-write-tree >.patch-merge-tmp-base &&
133                 (
134                         mkdir .patch-merge-tmp-dir &&
135                         cd .patch-merge-tmp-dir &&
136                         GIT_INDEX_FILE="../.patch-merge-tmp-index" \
137                         GIT_OBJECT_DIRECTORY="$O_OBJECT" \
138                         git-apply $binary --index
139                 ) <"$PATCHFILE"
140         then
141                 echo Using index info to reconstruct a base tree...
142                 mv .patch-merge-tmp-base .patch-merge-base
143                 mv .patch-merge-tmp-index .patch-merge-index
144         else
145         (
146                 N=10
147
148                 # Otherwise, try nearby trees that can be used to apply the
149                 # patch.
150                 git-rev-list --max-count=$N HEAD
151
152                 # or hoping the patch is against known tags...
153                 git-ls-remote --tags .
154         ) |
155             while read base junk
156             do
157                 # Try it if we have it as a tree.
158                 git-cat-file tree "$base" >/dev/null 2>&1 || continue
159
160                 rm -fr .patch-merge-tmp-* &&
161                 mkdir .patch-merge-tmp-dir || break
162                 (
163                         cd .patch-merge-tmp-dir &&
164                         GIT_INDEX_FILE=../.patch-merge-tmp-index &&
165                         GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
166                         export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
167                         git-read-tree "$base" &&
168                         git-apply --index &&
169                         mv ../.patch-merge-tmp-index ../.patch-merge-index &&
170                         echo "$base" >../.patch-merge-base
171                 ) <"$PATCHFILE"  2>/dev/null && break
172             done
173         fi
174
175         test -f .patch-merge-index &&
176         his_tree=$(GIT_INDEX_FILE=.patch-merge-index git-write-tree) &&
177         orig_tree=$(cat .patch-merge-base) &&
178         rm -fr .patch-merge-* || exit 1
179
180         echo Falling back to patching base and 3-way merge using $orig_tree...
181
182         # This is not so wrong.  Depending on which base we picked,
183         # orig_tree may be wildly different from ours, but his_tree
184         # has the same set of wildly different changes in parts the
185         # patch did not touch, so resolve ends up cancelling them,
186         # saying that we reverted all those changes.
187
188         if git-merge-resolve $orig_tree -- HEAD $his_tree
189         then
190                 echo Done.
191         else
192                 echo Failed to merge in the changes.
193                 exit 1
194         fi
195 }
196
197 if test -x "$GIT_DIR"/hooks/pre-applypatch
198 then
199         "$GIT_DIR"/hooks/pre-applypatch || exit
200 fi
201
202 tree=$(git-write-tree) || exit 1
203 echo Wrote tree $tree
204 parent=$(git-rev-parse --verify HEAD) &&
205 commit=$(git-commit-tree $tree -p $parent <"$final") || exit 1
206 echo Committed: $commit
207 git-update-ref HEAD $commit $parent || exit
208
209 if test -x "$GIT_DIR"/hooks/post-applypatch
210 then
211         "$GIT_DIR"/hooks/post-applypatch
212 fi