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