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