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