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