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