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 static org.eclipse.jgit.util.FileUtils.RECURSIVE;
46
47 import java.io.File;
48 import java.io.IOException;
49 import java.text.MessageFormat;
50 import java.util.ArrayList;
51 import java.util.Collection;
52 import java.util.Collections;
53 import java.util.List;
54
55 import org.eclipse.jgit.api.errors.GitAPIException;
56 import org.eclipse.jgit.api.errors.JGitInternalException;
57 import org.eclipse.jgit.api.errors.NoHeadException;
58 import org.eclipse.jgit.internal.JGitText;
59 import org.eclipse.jgit.lib.ConfigConstants;
60 import org.eclipse.jgit.lib.ObjectId;
61 import org.eclipse.jgit.lib.Ref;
62 import org.eclipse.jgit.lib.Repository;
63 import org.eclipse.jgit.lib.StoredConfig;
64 import org.eclipse.jgit.revwalk.RevCommit;
65 import org.eclipse.jgit.revwalk.RevTree;
66 import org.eclipse.jgit.revwalk.RevWalk;
67 import org.eclipse.jgit.submodule.SubmoduleWalk;
68 import org.eclipse.jgit.treewalk.filter.PathFilter;
69 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
70 import org.eclipse.jgit.treewalk.filter.TreeFilter;
71 import org.eclipse.jgit.util.FileUtils;
72
73
74
75
76
77
78
79
80
81
82
83
84 public class SubmoduleDeinitCommand
85 extends GitCommand<Collection<SubmoduleDeinitResult>> {
86
87 private final Collection<String> paths;
88
89 private boolean force;
90
91
92
93
94
95
96 public SubmoduleDeinitCommand(Repository repo) {
97 super(repo);
98 paths = new ArrayList<>();
99 }
100
101
102
103
104
105
106
107
108
109
110 @Override
111 public Collection<SubmoduleDeinitResult> call() throws GitAPIException {
112 checkCallable();
113 try {
114 if (paths.isEmpty()) {
115 return Collections.emptyList();
116 }
117 for (String path : paths) {
118 if (!submoduleExists(path)) {
119 throw new NoSuchSubmoduleException(path);
120 }
121 }
122 List<SubmoduleDeinitResult> results = new ArrayList<>(paths.size());
123 try (RevWalklk.html#RevWalk">RevWalk revWalk = new RevWalk(repo);
124 SubmoduleWalk generator = SubmoduleWalk.forIndex(repo)) {
125 generator.setFilter(PathFilterGroup.createFromStrings(paths));
126 StoredConfig config = repo.getConfig();
127 while (generator.next()) {
128 String path = generator.getPath();
129 String name = generator.getModuleName();
130 SubmoduleDeinitStatus status = checkDirty(revWalk, path);
131 switch (status) {
132 case SUCCESS:
133 deinit(path);
134 break;
135 case ALREADY_DEINITIALIZED:
136 break;
137 case DIRTY:
138 if (force) {
139 deinit(path);
140 status = SubmoduleDeinitStatus.FORCED;
141 }
142 break;
143 default:
144 throw new JGitInternalException(MessageFormat.format(
145 JGitText.get().unexpectedSubmoduleStatus,
146 status));
147 }
148
149 config.unsetSection(
150 ConfigConstants.CONFIG_SUBMODULE_SECTION, name);
151 results.add(new SubmoduleDeinitResult(path, status));
152 }
153 }
154 return results;
155 } catch (IOException e) {
156 throw new JGitInternalException(e.getMessage(), e);
157 }
158 }
159
160
161
162
163
164
165
166
167
168 private void deinit(String path) throws IOException {
169 File dir = new File(repo.getWorkTree(), path);
170 if (!dir.isDirectory()) {
171 throw new JGitInternalException(MessageFormat.format(
172 JGitText.get().expectedDirectoryNotSubmodule, path));
173 }
174 final File[] ls = dir.listFiles();
175 if (ls != null) {
176 for (int i = 0; i < ls.length; i++) {
177 FileUtils.delete(ls[i], RECURSIVE);
178 }
179 }
180 }
181
182
183
184
185
186
187
188
189
190
191
192
193
194 private SubmoduleDeinitStatus checkDirty(RevWalk revWalk, String path)
195 throws GitAPIException, IOException {
196 Ref head = repo.exactRef("HEAD");
197 if (head == null) {
198 throw new NoHeadException(
199 JGitText.get().invalidRepositoryStateNoHead);
200 }
201 RevCommit headCommit = revWalk.parseCommit(head.getObjectId());
202 RevTree tree = headCommit.getTree();
203
204 ObjectId submoduleHead;
205 try (SubmoduleWalk w = SubmoduleWalk.forPath(repo, tree, path)) {
206 submoduleHead = w.getHead();
207 if (submoduleHead == null) {
208
209 return SubmoduleDeinitStatus.ALREADY_DEINITIALIZED;
210 }
211 if (!submoduleHead.equals(w.getObjectId())) {
212
213
214 return SubmoduleDeinitStatus.DIRTY;
215 }
216 }
217
218 try (SubmoduleWalk w = SubmoduleWalk.forIndex(repo)) {
219 if (!w.next()) {
220
221
222 return SubmoduleDeinitStatus.DIRTY;
223 }
224 if (!submoduleHead.equals(w.getObjectId())) {
225
226
227 return SubmoduleDeinitStatus.DIRTY;
228 }
229
230 try (Repository submoduleRepo = w.getRepository()) {
231 Status status = Git.wrap(submoduleRepo).status().call();
232 return status.isClean() ? SubmoduleDeinitStatus.SUCCESS
233 : SubmoduleDeinitStatus.DIRTY;
234 }
235 }
236 }
237
238
239
240
241
242
243
244
245
246
247
248
249 private boolean submoduleExists(String path) throws IOException {
250 TreeFilter filter = PathFilter.create(path);
251 try (SubmoduleWalk w = SubmoduleWalk.forIndex(repo)) {
252 return w.setFilter(filter).next();
253 }
254 }
255
256
257
258
259
260
261
262
263 public SubmoduleDeinitCommand addPath(String path) {
264 paths.add(path);
265 return this;
266 }
267
268
269
270
271
272
273
274
275 public SubmoduleDeinitCommand setForce(boolean force) {
276 this.force = force;
277 return this;
278 }
279
280
281
282
283
284 public static class NoSuchSubmoduleException extends GitAPIException {
285 private static final long serialVersionUID = 1L;
286
287
288
289
290
291
292
293 public NoSuchSubmoduleException(String path) {
294 super(MessageFormat.format(JGitText.get().noSuchSubmodule, path));
295 }
296 }
297
298
299
300
301 public enum SubmoduleDeinitStatus {
302
303
304
305 ALREADY_DEINITIALIZED,
306
307
308
309 SUCCESS,
310
311
312
313 FORCED,
314
315
316
317 DIRTY,
318 }
319 }