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
44 package org.eclipse.jgit.api;
45
46 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
47 import static org.eclipse.jgit.lib.FileMode.GITLINK;
48 import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK;
49 import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
50
51 import java.io.IOException;
52 import java.io.InputStream;
53 import java.util.Collection;
54 import java.util.LinkedList;
55
56 import org.eclipse.jgit.api.errors.FilterFailedException;
57 import org.eclipse.jgit.api.errors.GitAPIException;
58 import org.eclipse.jgit.api.errors.JGitInternalException;
59 import org.eclipse.jgit.api.errors.NoFilepatternException;
60 import org.eclipse.jgit.dircache.DirCache;
61 import org.eclipse.jgit.dircache.DirCacheBuildIterator;
62 import org.eclipse.jgit.dircache.DirCacheBuilder;
63 import org.eclipse.jgit.dircache.DirCacheEntry;
64 import org.eclipse.jgit.dircache.DirCacheIterator;
65 import org.eclipse.jgit.internal.JGitText;
66 import org.eclipse.jgit.lib.FileMode;
67 import org.eclipse.jgit.lib.ObjectId;
68 import org.eclipse.jgit.lib.ObjectInserter;
69 import org.eclipse.jgit.lib.Repository;
70 import org.eclipse.jgit.treewalk.FileTreeIterator;
71 import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
72 import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
73 import org.eclipse.jgit.treewalk.WorkingTreeIterator;
74 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
75
76
77
78
79
80
81
82
83
84
85 public class AddCommand extends GitCommand<DirCache> {
86
87 private Collection<String> filepatterns;
88
89 private WorkingTreeIterator workingTreeIterator;
90
91 private boolean update = false;
92
93
94
95
96
97 public AddCommand(Repository repo) {
98 super(repo);
99 filepatterns = new LinkedList<String>();
100 }
101
102
103
104
105
106
107
108
109
110
111
112
113
114 public AddCommand addFilepattern(String filepattern) {
115 checkCallable();
116 filepatterns.add(filepattern);
117 return this;
118 }
119
120
121
122
123
124
125 public AddCommand setWorkingTreeIterator(WorkingTreeIterator f) {
126 workingTreeIterator = f;
127 return this;
128 }
129
130
131
132
133
134
135
136
137 public DirCache call() throws GitAPIException, NoFilepatternException {
138
139 if (filepatterns.isEmpty())
140 throw new NoFilepatternException(JGitText.get().atLeastOnePatternIsRequired);
141 checkCallable();
142 DirCache dc = null;
143 boolean addAll = filepatterns.contains(".");
144
145 try (ObjectInserter inserter = repo.newObjectInserter();
146 NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
147 tw.setOperationType(OperationType.CHECKIN_OP);
148 dc = repo.lockDirCache();
149
150 DirCacheBuilder builder = dc.builder();
151 tw.addTree(new DirCacheBuildIterator(builder));
152 if (workingTreeIterator == null)
153 workingTreeIterator = new FileTreeIterator(repo);
154 workingTreeIterator.setDirCacheIterator(tw, 0);
155 tw.addTree(workingTreeIterator);
156 if (!addAll)
157 tw.setFilter(PathFilterGroup.createFromStrings(filepatterns));
158
159 byte[] lastAdded = null;
160
161 while (tw.next()) {
162 DirCacheIterator c = tw.getTree(0, DirCacheIterator.class);
163 WorkingTreeIterator f = tw.getTree(1, WorkingTreeIterator.class);
164 if (c == null && f != null && f.isEntryIgnored()) {
165
166 continue;
167 } else if (c == null && update) {
168
169 continue;
170 }
171
172 DirCacheEntry entry = c != null ? c.getDirCacheEntry() : null;
173 if (entry != null && entry.getStage() > 0
174 && lastAdded != null
175 && lastAdded.length == tw.getPathLength()
176 && tw.isPathPrefix(lastAdded, lastAdded.length) == 0) {
177
178
179
180
181 continue;
182 }
183
184 if (tw.isSubtree() && !tw.isDirectoryFileConflict()) {
185 tw.enterSubtree();
186 continue;
187 }
188
189 if (f == null) {
190 if (entry != null
191 && (!update || GITLINK == entry.getFileMode())) {
192 builder.add(entry);
193 }
194 continue;
195 }
196
197 if (entry != null && entry.isAssumeValid()) {
198
199
200
201 builder.add(entry);
202 continue;
203 }
204
205 if ((f.getEntryRawMode() == TYPE_TREE
206 && f.getIndexFileMode(c) != FileMode.GITLINK) ||
207 (f.getEntryRawMode() == TYPE_GITLINK
208 && f.getIndexFileMode(c) == FileMode.TREE)) {
209
210
211
212 tw.enterSubtree();
213 continue;
214 }
215
216 byte[] path = tw.getRawPath();
217 if (entry == null || entry.getStage() > 0) {
218 entry = new DirCacheEntry(path);
219 }
220 FileMode mode = f.getIndexFileMode(c);
221 entry.setFileMode(mode);
222
223 if (GITLINK != mode) {
224 entry.setLength(f.getEntryLength());
225 entry.setLastModified(f.getEntryLastModified());
226 long len = f.getEntryContentLength();
227
228
229
230
231
232 try (InputStream in = f.openEntryStream()) {
233 ObjectId id = inserter.insert(OBJ_BLOB, len, in);
234 entry.setObjectId(id);
235 }
236 } else {
237 entry.setLength(0);
238 entry.setLastModified(0);
239 entry.setObjectId(f.getEntryObjectId());
240 }
241 builder.add(entry);
242 lastAdded = path;
243 }
244 inserter.flush();
245 builder.commit();
246 setCallable(false);
247 } catch (IOException e) {
248 Throwable cause = e.getCause();
249 if (cause != null && cause instanceof FilterFailedException)
250 throw (FilterFailedException) cause;
251 throw new JGitInternalException(
252 JGitText.get().exceptionCaughtDuringExecutionOfAddCommand, e);
253 } finally {
254 if (dc != null)
255 dc.unlock();
256 }
257
258 return dc;
259 }
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275 public AddCommand setUpdate(boolean update) {
276 this.update = update;
277 return this;
278 }
279
280
281
282
283 public boolean isUpdate() {
284 return update;
285 }
286 }