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