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