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<>();
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 @Override
138 public DirCache call() throws GitAPIException, NoFilepatternException {
139
140 if (filepatterns.isEmpty())
141 throw new NoFilepatternException(JGitText.get().atLeastOnePatternIsRequired);
142 checkCallable();
143 DirCache dc = null;
144 boolean addAll = filepatterns.contains(".");
145
146 try (ObjectInserter inserter = repo.newObjectInserter();
147 NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
148 tw.setOperationType(OperationType.CHECKIN_OP);
149 dc = repo.lockDirCache();
150
151 DirCacheBuilder builder = dc.builder();
152 tw.addTree(new DirCacheBuildIterator(builder));
153 if (workingTreeIterator == null)
154 workingTreeIterator = new FileTreeIterator(repo);
155 workingTreeIterator.setDirCacheIterator(tw, 0);
156 tw.addTree(workingTreeIterator);
157 if (!addAll)
158 tw.setFilter(PathFilterGroup.createFromStrings(filepatterns));
159
160 byte[] lastAdded = null;
161
162 while (tw.next()) {
163 DirCacheIterator c = tw.getTree(0, DirCacheIterator.class);
164 WorkingTreeIterator f = tw.getTree(1, WorkingTreeIterator.class);
165 if (c == null && f != null && f.isEntryIgnored()) {
166
167 continue;
168 } else if (c == null && update) {
169
170 continue;
171 }
172
173 DirCacheEntry entry = c != null ? c.getDirCacheEntry() : null;
174 if (entry != null && entry.getStage() > 0
175 && lastAdded != null
176 && lastAdded.length == tw.getPathLength()
177 && tw.isPathPrefix(lastAdded, lastAdded.length) == 0) {
178
179
180
181
182 continue;
183 }
184
185 if (tw.isSubtree() && !tw.isDirectoryFileConflict()) {
186 tw.enterSubtree();
187 continue;
188 }
189
190 if (f == null) {
191 if (entry != null
192 && (!update || GITLINK == entry.getFileMode())) {
193 builder.add(entry);
194 }
195 continue;
196 }
197
198 if (entry != null && entry.isAssumeValid()) {
199
200
201
202 builder.add(entry);
203 continue;
204 }
205
206 if ((f.getEntryRawMode() == TYPE_TREE
207 && f.getIndexFileMode(c) != FileMode.GITLINK) ||
208 (f.getEntryRawMode() == TYPE_GITLINK
209 && f.getIndexFileMode(c) == FileMode.TREE)) {
210
211
212
213 tw.enterSubtree();
214 continue;
215 }
216
217 byte[] path = tw.getRawPath();
218 if (entry == null || entry.getStage() > 0) {
219 entry = new DirCacheEntry(path);
220 }
221 FileMode mode = f.getIndexFileMode(c);
222 entry.setFileMode(mode);
223
224 if (GITLINK != mode) {
225 entry.setLength(f.getEntryLength());
226 entry.setLastModified(f.getEntryLastModified());
227 long len = f.getEntryContentLength();
228
229
230
231
232
233 try (InputStream in = f.openEntryStream()) {
234 ObjectId id = inserter.insert(OBJ_BLOB, len, in);
235 entry.setObjectId(id);
236 }
237 } else {
238 entry.setLength(0);
239 entry.setLastModified(0);
240 entry.setObjectId(f.getEntryObjectId());
241 }
242 builder.add(entry);
243 lastAdded = path;
244 }
245 inserter.flush();
246 builder.commit();
247 setCallable(false);
248 } catch (IOException e) {
249 Throwable cause = e.getCause();
250 if (cause != null && cause instanceof FilterFailedException)
251 throw (FilterFailedException) cause;
252 throw new JGitInternalException(
253 JGitText.get().exceptionCaughtDuringExecutionOfAddCommand, e);
254 } finally {
255 if (dc != null)
256 dc.unlock();
257 }
258
259 return dc;
260 }
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276 public AddCommand setUpdate(boolean update) {
277 this.update = update;
278 return this;
279 }
280
281
282
283
284 public boolean isUpdate() {
285 return update;
286 }
287 }