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 package org.eclipse.jgit.api;
44
45 import java.io.File;
46 import java.io.FileOutputStream;
47 import java.io.FileWriter;
48 import java.io.IOException;
49 import java.io.InputStream;
50 import java.nio.file.StandardCopyOption;
51 import java.text.MessageFormat;
52 import java.util.ArrayList;
53 import java.util.List;
54
55 import org.eclipse.jgit.api.errors.GitAPIException;
56 import org.eclipse.jgit.api.errors.PatchApplyException;
57 import org.eclipse.jgit.api.errors.PatchFormatException;
58 import org.eclipse.jgit.diff.DiffEntry.ChangeType;
59 import org.eclipse.jgit.diff.RawText;
60 import org.eclipse.jgit.internal.JGitText;
61 import org.eclipse.jgit.lib.FileMode;
62 import org.eclipse.jgit.lib.Repository;
63 import org.eclipse.jgit.patch.FileHeader;
64 import org.eclipse.jgit.patch.HunkHeader;
65 import org.eclipse.jgit.patch.Patch;
66 import org.eclipse.jgit.util.FileUtils;
67 import org.eclipse.jgit.util.IO;
68
69
70
71
72
73
74
75
76 public class ApplyCommand extends GitCommand<ApplyResult> {
77
78 private InputStream in;
79
80
81
82
83
84
85 ApplyCommand(Repository repo) {
86 super(repo);
87 }
88
89
90
91
92
93
94 public ApplyCommand setPatch(InputStream in) {
95 checkCallable();
96 this.in = in;
97 return this;
98 }
99
100
101
102
103
104
105
106
107
108
109
110
111
112 @Override
113 public ApplyResult call() throws GitAPIException, PatchFormatException,
114 PatchApplyException {
115 checkCallable();
116 ApplyResult r = new ApplyResult();
117 try {
118 final Patch p = new Patch();
119 try {
120 p.parse(in);
121 } finally {
122 in.close();
123 }
124 if (!p.getErrors().isEmpty())
125 throw new PatchFormatException(p.getErrors());
126 for (FileHeader fh : p.getFiles()) {
127 ChangeType type = fh.getChangeType();
128 File f = null;
129 switch (type) {
130 case ADD:
131 f = getFile(fh.getNewPath(), true);
132 apply(f, fh);
133 break;
134 case MODIFY:
135 f = getFile(fh.getOldPath(), false);
136 apply(f, fh);
137 break;
138 case DELETE:
139 f = getFile(fh.getOldPath(), false);
140 if (!f.delete())
141 throw new PatchApplyException(MessageFormat.format(
142 JGitText.get().cannotDeleteFile, f));
143 break;
144 case RENAME:
145 f = getFile(fh.getOldPath(), false);
146 File dest = getFile(fh.getNewPath(), false);
147 try {
148 FileUtils.rename(f, dest,
149 StandardCopyOption.ATOMIC_MOVE);
150 } catch (IOException e) {
151 throw new PatchApplyException(MessageFormat.format(
152 JGitText.get().renameFileFailed, f, dest), e);
153 }
154 break;
155 case COPY:
156 f = getFile(fh.getOldPath(), false);
157 byte[] bs = IO.readFully(f);
158 FileOutputStream fos = new FileOutputStream(getFile(
159 fh.getNewPath(),
160 true));
161 try {
162 fos.write(bs);
163 } finally {
164 fos.close();
165 }
166 }
167 r.addUpdatedFile(f);
168 }
169 } catch (IOException e) {
170 throw new PatchApplyException(MessageFormat.format(
171 JGitText.get().patchApplyException, e.getMessage()), e);
172 }
173 setCallable(false);
174 return r;
175 }
176
177 private File getFile(String path, boolean create)
178 throws PatchApplyException {
179 File f = new File(getRepository().getWorkTree(), path);
180 if (create)
181 try {
182 File parent = f.getParentFile();
183 FileUtils.mkdirs(parent, true);
184 FileUtils.createNewFile(f);
185 } catch (IOException e) {
186 throw new PatchApplyException(MessageFormat.format(
187 JGitText.get().createNewFileFailed, f), e);
188 }
189 return f;
190 }
191
192
193
194
195
196
197
198 private void apply(File f, FileHeader fh)
199 throws IOException, PatchApplyException {
200 RawText rt = new RawText(f);
201 List<String> oldLines = new ArrayList<>(rt.size());
202 for (int i = 0; i < rt.size(); i++)
203 oldLines.add(rt.getString(i));
204 List<String> newLines = new ArrayList<>(oldLines);
205 for (HunkHeader hh : fh.getHunks()) {
206
207 byte[] b = new byte[hh.getEndOffset() - hh.getStartOffset()];
208 System.arraycopy(hh.getBuffer(), hh.getStartOffset(), b, 0,
209 b.length);
210 RawText hrt = new RawText(b);
211
212 List<String> hunkLines = new ArrayList<>(hrt.size());
213 for (int i = 0; i < hrt.size(); i++)
214 hunkLines.add(hrt.getString(i));
215 int pos = 0;
216 for (int j = 1; j < hunkLines.size(); j++) {
217 String hunkLine = hunkLines.get(j);
218 switch (hunkLine.charAt(0)) {
219 case ' ':
220 if (!newLines.get(hh.getNewStartLine() - 1 + pos).equals(
221 hunkLine.substring(1))) {
222 throw new PatchApplyException(MessageFormat.format(
223 JGitText.get().patchApplyException, hh));
224 }
225 pos++;
226 break;
227 case '-':
228 if (hh.getNewStartLine() == 0) {
229 newLines.clear();
230 } else {
231 if (!newLines.get(hh.getNewStartLine() - 1 + pos)
232 .equals(hunkLine.substring(1))) {
233 throw new PatchApplyException(MessageFormat.format(
234 JGitText.get().patchApplyException, hh));
235 }
236 newLines.remove(hh.getNewStartLine() - 1 + pos);
237 }
238 break;
239 case '+':
240 newLines.add(hh.getNewStartLine() - 1 + pos,
241 hunkLine.substring(1));
242 pos++;
243 break;
244 }
245 }
246 }
247 if (!isNoNewlineAtEndOfFile(fh))
248 newLines.add("");
249 if (!rt.isMissingNewlineAtEnd())
250 oldLines.add("");
251 if (!isChanged(oldLines, newLines))
252 return;
253 StringBuilder sb = new StringBuilder();
254 for (String l : newLines) {
255
256
257 sb.append(l).append('\n');
258 }
259 if (sb.length() > 0) {
260 sb.deleteCharAt(sb.length() - 1);
261 }
262 FileWriter fw = new FileWriter(f);
263 fw.write(sb.toString());
264 fw.close();
265
266 getRepository().getFS().setExecute(f, fh.getNewMode() == FileMode.EXECUTABLE_FILE);
267 }
268
269 private static boolean isChanged(List<String> ol, List<String> nl) {
270 if (ol.size() != nl.size())
271 return true;
272 for (int i = 0; i < ol.size(); i++)
273 if (!ol.get(i).equals(nl.get(i)))
274 return true;
275 return false;
276 }
277
278 private boolean isNoNewlineAtEndOfFile(FileHeader fh) {
279 HunkHeader lastHunk = fh.getHunks().get(fh.getHunks().size() - 1);
280 RawText lhrt = new RawText(lastHunk.getBuffer());
281 return lhrt.getString(lhrt.size() - 1).equals(
282 "\\ No newline at end of file");
283 }
284 }