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.HEAD;
47 import static org.eclipse.jgit.lib.Constants.R_HEADS;
48
49 import java.io.IOException;
50 import java.text.MessageFormat;
51
52 import org.eclipse.jgit.api.errors.GitAPIException;
53 import org.eclipse.jgit.api.errors.InvalidRefNameException;
54 import org.eclipse.jgit.api.errors.JGitInternalException;
55 import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
56 import org.eclipse.jgit.api.errors.RefNotFoundException;
57 import org.eclipse.jgit.errors.AmbiguousObjectException;
58 import org.eclipse.jgit.internal.JGitText;
59 import org.eclipse.jgit.lib.ConfigConstants;
60 import org.eclipse.jgit.lib.Constants;
61 import org.eclipse.jgit.lib.ObjectId;
62 import org.eclipse.jgit.lib.Ref;
63 import org.eclipse.jgit.lib.RefUpdate;
64 import org.eclipse.jgit.lib.RefUpdate.Result;
65 import org.eclipse.jgit.lib.Repository;
66 import org.eclipse.jgit.lib.StoredConfig;
67 import org.eclipse.jgit.revwalk.RevCommit;
68 import org.eclipse.jgit.revwalk.RevWalk;
69
70
71
72
73
74
75
76
77 public class CreateBranchCommand extends GitCommand<Ref> {
78 private String name;
79
80 private boolean force = false;
81
82 private SetupUpstreamMode upstreamMode;
83
84 private String startPoint = HEAD;
85
86 private RevCommit startCommit;
87
88
89
90
91
92
93 public enum SetupUpstreamMode {
94
95
96
97 TRACK,
98
99
100
101 NOTRACK,
102
103
104
105 SET_UPSTREAM;
106 }
107
108
109
110
111
112
113
114 protected CreateBranchCommand(Repository repo) {
115 super(repo);
116 }
117
118
119 @Override
120 public Ref call() throws GitAPIException, RefAlreadyExistsException,
121 RefNotFoundException, InvalidRefNameException {
122 checkCallable();
123 processOptions();
124 try (RevWalklk.html#RevWalk">RevWalk revWalk = new RevWalk(repo)) {
125 Ref refToCheck = repo.findRef(name);
126 boolean exists = refToCheck != null
127 && refToCheck.getName().startsWith(R_HEADS);
128 if (!force && exists)
129 throw new RefAlreadyExistsException(MessageFormat.format(
130 JGitText.get().refAlreadyExists1, name));
131
132 ObjectId startAt = getStartPointObjectId();
133 String startPointFullName = null;
134 if (startPoint != null) {
135 Ref baseRef = repo.findRef(startPoint);
136 if (baseRef != null)
137 startPointFullName = baseRef.getName();
138 }
139
140
141
142 String refLogMessage;
143 String baseBranch = "";
144 if (startPointFullName == null) {
145 String baseCommit;
146 if (startCommit != null)
147 baseCommit = startCommit.getShortMessage();
148 else {
149 RevCommit commit = revWalk.parseCommit(repo
150 .resolve(getStartPointOrHead()));
151 baseCommit = commit.getShortMessage();
152 }
153 if (exists)
154 refLogMessage = "branch: Reset start-point to commit "
155 + baseCommit;
156 else
157 refLogMessage = "branch: Created from commit " + baseCommit;
158
159 } else if (startPointFullName.startsWith(R_HEADS)
160 || startPointFullName.startsWith(Constants.R_REMOTES)) {
161 baseBranch = startPointFullName;
162 if (exists)
163 refLogMessage = "branch: Reset start-point to branch "
164 + startPointFullName;
165 else
166 refLogMessage = "branch: Created from branch " + baseBranch;
167 } else {
168 startAt = revWalk.peel(revWalk.parseAny(startAt));
169 if (exists)
170 refLogMessage = "branch: Reset start-point to tag "
171 + startPointFullName;
172 else
173 refLogMessage = "branch: Created from tag "
174 + startPointFullName;
175 }
176
177 RefUpdate updateRef = repo.updateRef(R_HEADS + name);
178 updateRef.setNewObjectId(startAt);
179 updateRef.setRefLogMessage(refLogMessage, false);
180 Result updateResult;
181 if (exists && force)
182 updateResult = updateRef.forceUpdate();
183 else
184 updateResult = updateRef.update();
185
186 setCallable(false);
187
188 boolean ok = false;
189 switch (updateResult) {
190 case NEW:
191 ok = !exists;
192 break;
193 case NO_CHANGE:
194 case FAST_FORWARD:
195 case FORCED:
196 ok = exists;
197 break;
198 default:
199 break;
200 }
201
202 if (!ok)
203 throw new JGitInternalException(MessageFormat.format(JGitText
204 .get().createBranchUnexpectedResult, updateResult
205 .name()));
206
207 Ref result = repo.findRef(name);
208 if (result == null)
209 throw new JGitInternalException(
210 JGitText.get().createBranchFailedUnknownReason);
211
212 if (baseBranch.length() == 0) {
213 return result;
214 }
215
216
217
218
219 boolean doConfigure;
220 if (upstreamMode == SetupUpstreamMode.SET_UPSTREAM
221 || upstreamMode == SetupUpstreamMode.TRACK)
222
223 doConfigure = true;
224 else if (upstreamMode == SetupUpstreamMode.NOTRACK)
225
226 doConfigure = false;
227 else {
228
229 String autosetupflag = repo.getConfig().getString(
230 ConfigConstants.CONFIG_BRANCH_SECTION, null,
231 ConfigConstants.CONFIG_KEY_AUTOSETUPMERGE);
232 if ("false".equals(autosetupflag)) {
233 doConfigure = false;
234 } else if ("always".equals(autosetupflag)) {
235 doConfigure = true;
236 } else {
237
238
239 doConfigure = baseBranch.startsWith(Constants.R_REMOTES);
240 }
241 }
242
243 if (doConfigure) {
244 StoredConfig config = repo.getConfig();
245
246 String remoteName = repo.getRemoteName(baseBranch);
247 if (remoteName != null) {
248 String branchName = repo
249 .shortenRemoteBranchName(baseBranch);
250 config
251 .setString(ConfigConstants.CONFIG_BRANCH_SECTION,
252 name, ConfigConstants.CONFIG_KEY_REMOTE,
253 remoteName);
254 config.setString(ConfigConstants.CONFIG_BRANCH_SECTION,
255 name, ConfigConstants.CONFIG_KEY_MERGE,
256 Constants.R_HEADS + branchName);
257 } else {
258
259 config.setString(ConfigConstants.CONFIG_BRANCH_SECTION,
260 name, ConfigConstants.CONFIG_KEY_REMOTE, ".");
261 config.setString(ConfigConstants.CONFIG_BRANCH_SECTION,
262 name, ConfigConstants.CONFIG_KEY_MERGE, baseBranch);
263 }
264 config.save();
265 }
266 return result;
267 } catch (IOException ioe) {
268 throw new JGitInternalException(ioe.getMessage(), ioe);
269 }
270 }
271
272 private ObjectId getStartPointObjectId() throws AmbiguousObjectException,
273 RefNotFoundException, IOException {
274 if (startCommit != null)
275 return startCommit.getId();
276 String startPointOrHead = getStartPointOrHead();
277 ObjectId result = repo.resolve(startPointOrHead);
278 if (result == null)
279 throw new RefNotFoundException(MessageFormat.format(
280 JGitText.get().refNotResolved, startPointOrHead));
281 return result;
282 }
283
284 private String getStartPointOrHead() {
285 return startPoint != null ? startPoint : HEAD;
286 }
287
288 private void processOptions() throws InvalidRefNameException {
289 if (name == null
290 || !Repository.isValidRefName(R_HEADS + name)
291 || !isValidBranchName(name))
292 throw new InvalidRefNameException(MessageFormat.format(JGitText
293 .get().branchNameInvalid, name == null ? "<null>" : name));
294 }
295
296
297
298
299
300
301
302
303
304
305 public static boolean isValidBranchName(String branchName) {
306 if (HEAD.equals(branchName)) {
307 return false;
308 }
309 return !branchName.startsWith("-");
310 }
311
312
313
314
315
316
317
318
319 public CreateBranchCommand setName(String name) {
320 checkCallable();
321 this.name = name;
322 return this;
323 }
324
325
326
327
328
329
330
331
332
333
334
335 public CreateBranchCommand setForce(boolean force) {
336 checkCallable();
337 this.force = force;
338 return this;
339 }
340
341
342
343
344
345
346
347
348
349 public CreateBranchCommand setStartPoint(String startPoint) {
350 checkCallable();
351 this.startPoint = startPoint;
352 this.startCommit = null;
353 return this;
354 }
355
356
357
358
359
360
361
362
363
364 public CreateBranchCommand setStartPoint(RevCommit startPoint) {
365 checkCallable();
366 this.startCommit = startPoint;
367 this.startPoint = null;
368 return this;
369 }
370
371
372
373
374
375
376
377
378
379 public CreateBranchCommand setUpstreamMode(SetupUpstreamMode mode) {
380 checkCallable();
381 this.upstreamMode = mode;
382 return this;
383 }
384 }