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