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