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
45 package org.eclipse.jgit.api;
46
47 import java.io.IOException;
48 import java.text.MessageFormat;
49
50 import org.eclipse.jgit.annotations.Nullable;
51 import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
52 import org.eclipse.jgit.api.MergeCommand.FastForwardMode.Merge;
53 import org.eclipse.jgit.api.RebaseCommand.Operation;
54 import org.eclipse.jgit.api.errors.CanceledException;
55 import org.eclipse.jgit.api.errors.GitAPIException;
56 import org.eclipse.jgit.api.errors.InvalidConfigurationException;
57 import org.eclipse.jgit.api.errors.InvalidRemoteException;
58 import org.eclipse.jgit.api.errors.JGitInternalException;
59 import org.eclipse.jgit.api.errors.NoHeadException;
60 import org.eclipse.jgit.api.errors.RefNotAdvertisedException;
61 import org.eclipse.jgit.api.errors.RefNotFoundException;
62 import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
63 import org.eclipse.jgit.dircache.DirCacheCheckout;
64 import org.eclipse.jgit.internal.JGitText;
65 import org.eclipse.jgit.lib.AnyObjectId;
66 import org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode;
67 import org.eclipse.jgit.lib.Config;
68 import org.eclipse.jgit.lib.ConfigConstants;
69 import org.eclipse.jgit.lib.Constants;
70 import org.eclipse.jgit.lib.NullProgressMonitor;
71 import org.eclipse.jgit.lib.ObjectId;
72 import org.eclipse.jgit.lib.ProgressMonitor;
73 import org.eclipse.jgit.lib.Ref;
74 import org.eclipse.jgit.lib.RefUpdate;
75 import org.eclipse.jgit.lib.RefUpdate.Result;
76 import org.eclipse.jgit.lib.Repository;
77 import org.eclipse.jgit.lib.RepositoryState;
78 import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode;
79 import org.eclipse.jgit.merge.MergeStrategy;
80 import org.eclipse.jgit.revwalk.RevCommit;
81 import org.eclipse.jgit.revwalk.RevWalk;
82 import org.eclipse.jgit.transport.FetchResult;
83 import org.eclipse.jgit.transport.TagOpt;
84
85
86
87
88
89
90
91 public class PullCommand extends TransportCommand<PullCommand, PullResult> {
92
93 private final static String DOT = ".";
94
95 private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
96
97 private BranchRebaseMode pullRebaseMode = null;
98
99 private String remote;
100
101 private String remoteBranchName;
102
103 private MergeStrategy strategy = MergeStrategy.RECURSIVE;
104
105 private TagOpt tagOption;
106
107 private FastForwardMode fastForwardMode;
108
109 private FetchRecurseSubmodulesMode submoduleRecurseMode = null;
110
111
112
113
114
115
116
117 protected PullCommand(Repository repo) {
118 super(repo);
119 }
120
121
122
123
124
125
126
127
128 public PullCommand setProgressMonitor(ProgressMonitor monitor) {
129 if (monitor == null) {
130 monitor = NullProgressMonitor.INSTANCE;
131 }
132 this.monitor = monitor;
133 return this;
134 }
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155 public PullCommand setRebase(boolean useRebase) {
156 checkCallable();
157 pullRebaseMode = useRebase ? BranchRebaseMode.REBASE
158 : BranchRebaseMode.NONE;
159 return this;
160 }
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196 public PullCommand setRebase(BranchRebaseMode rebaseMode) {
197 checkCallable();
198 pullRebaseMode = rebaseMode;
199 return this;
200 }
201
202
203
204
205
206
207
208
209
210
211 @Override
212 public PullResult call() throws GitAPIException,
213 WrongRepositoryStateException, InvalidConfigurationException,
214 InvalidRemoteException, CanceledException,
215 RefNotFoundException, RefNotAdvertisedException, NoHeadException,
216 org.eclipse.jgit.api.errors.TransportException {
217 checkCallable();
218
219 monitor.beginTask(JGitText.get().pullTaskName, 2);
220 Config repoConfig = repo.getConfig();
221
222 String branchName = null;
223 try {
224 String fullBranch = repo.getFullBranch();
225 if (fullBranch != null
226 && fullBranch.startsWith(Constants.R_HEADS)) {
227 branchName = fullBranch.substring(Constants.R_HEADS.length());
228 }
229 } catch (IOException e) {
230 throw new JGitInternalException(
231 JGitText.get().exceptionCaughtDuringExecutionOfPullCommand,
232 e);
233 }
234 if (remoteBranchName == null && branchName != null) {
235
236
237 remoteBranchName = repoConfig.getString(
238 ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
239 ConfigConstants.CONFIG_KEY_MERGE);
240 }
241 if (remoteBranchName == null) {
242 remoteBranchName = branchName;
243 }
244 if (remoteBranchName == null) {
245 throw new NoHeadException(
246 JGitText.get().cannotCheckoutFromUnbornBranch);
247 }
248
249 if (!repo.getRepositoryState().equals(RepositoryState.SAFE))
250 throw new WrongRepositoryStateException(MessageFormat.format(
251 JGitText.get().cannotPullOnARepoWithState, repo
252 .getRepositoryState().name()));
253
254 if (remote == null && branchName != null) {
255
256
257 remote = repoConfig.getString(
258 ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
259 ConfigConstants.CONFIG_KEY_REMOTE);
260 }
261 if (remote == null) {
262
263 remote = Constants.DEFAULT_REMOTE_NAME;
264 }
265
266
267 if (pullRebaseMode == null && branchName != null) {
268 pullRebaseMode = getRebaseMode(branchName, repoConfig);
269 }
270
271
272 final boolean isRemote = !remote.equals(".");
273 String remoteUri;
274 FetchResult fetchRes;
275 if (isRemote) {
276 remoteUri = repoConfig.getString(
277 ConfigConstants.CONFIG_REMOTE_SECTION, remote,
278 ConfigConstants.CONFIG_KEY_URL);
279 if (remoteUri == null) {
280 String missingKey = ConfigConstants.CONFIG_REMOTE_SECTION + DOT
281 + remote + DOT + ConfigConstants.CONFIG_KEY_URL;
282 throw new InvalidConfigurationException(MessageFormat.format(
283 JGitText.get().missingConfigurationForKey, missingKey));
284 }
285
286 if (monitor.isCancelled())
287 throw new CanceledException(MessageFormat.format(
288 JGitText.get().operationCanceled,
289 JGitText.get().pullTaskName));
290
291 FetchCommand fetch = new FetchCommand(repo).setRemote(remote)
292 .setProgressMonitor(monitor).setTagOpt(tagOption)
293 .setRecurseSubmodules(submoduleRecurseMode);
294 configure(fetch);
295
296 fetchRes = fetch.call();
297 } else {
298
299 remoteUri = JGitText.get().localRepository;
300 fetchRes = null;
301 }
302
303 monitor.update(1);
304
305 if (monitor.isCancelled())
306 throw new CanceledException(MessageFormat.format(
307 JGitText.get().operationCanceled,
308 JGitText.get().pullTaskName));
309
310
311
312
313 AnyObjectId commitToMerge;
314 if (isRemote) {
315 Ref r = null;
316 if (fetchRes != null) {
317 r = fetchRes.getAdvertisedRef(remoteBranchName);
318 if (r == null) {
319 r = fetchRes.getAdvertisedRef(Constants.R_HEADS
320 + remoteBranchName);
321 }
322 }
323 if (r == null) {
324 throw new RefNotAdvertisedException(MessageFormat.format(
325 JGitText.get().couldNotGetAdvertisedRef, remote,
326 remoteBranchName));
327 }
328 commitToMerge = r.getObjectId();
329 } else {
330 try {
331 commitToMerge = repo.resolve(remoteBranchName);
332 if (commitToMerge == null) {
333 throw new RefNotFoundException(MessageFormat.format(
334 JGitText.get().refNotResolved, remoteBranchName));
335 }
336 } catch (IOException e) {
337 throw new JGitInternalException(
338 JGitText.get().exceptionCaughtDuringExecutionOfPullCommand,
339 e);
340 }
341 }
342
343 String upstreamName = MessageFormat.format(
344 JGitText.get().upstreamBranchName,
345 Repository.shortenRefName(remoteBranchName), remoteUri);
346
347 PullResult result;
348 if (pullRebaseMode != BranchRebaseMode.NONE) {
349 try {
350 Ref head = repo.exactRef(Constants.HEAD);
351 if (head == null) {
352 throw new NoHeadException(JGitText
353 .get().commitOnRepoWithoutHEADCurrentlyNotSupported);
354 }
355 ObjectId headId = head.getObjectId();
356 if (headId == null) {
357
358 try (RevWalklk.html#RevWalk">RevWalk revWalk = new RevWalk(repo)) {
359 RevCommit srcCommit = revWalk
360 .parseCommit(commitToMerge);
361 DirCacheCheckout dco = new DirCacheCheckout(repo,
362 repo.lockDirCache(), srcCommit.getTree());
363 dco.setFailOnConflict(true);
364 dco.setProgressMonitor(monitor);
365 dco.checkout();
366 RefUpdate refUpdate = repo
367 .updateRef(head.getTarget().getName());
368 refUpdate.setNewObjectId(commitToMerge);
369 refUpdate.setExpectedOldObjectId(null);
370 refUpdate.setRefLogMessage("initial pull", false);
371 if (refUpdate.update() != Result.NEW) {
372 throw new NoHeadException(JGitText
373 .get().commitOnRepoWithoutHEADCurrentlyNotSupported);
374 }
375 monitor.endTask();
376 return new PullResult(fetchRes, remote,
377 RebaseResult.result(
378 RebaseResult.Status.FAST_FORWARD,
379 srcCommit));
380 }
381 }
382 } catch (NoHeadException e) {
383 throw e;
384 } catch (IOException e) {
385 throw new JGitInternalException(JGitText
386 .get().exceptionCaughtDuringExecutionOfPullCommand, e);
387 }
388 RebaseCommand rebase = new RebaseCommand(repo);
389 RebaseResult rebaseRes = rebase.setUpstream(commitToMerge)
390 .setUpstreamName(upstreamName).setProgressMonitor(monitor)
391 .setOperation(Operation.BEGIN).setStrategy(strategy)
392 .setPreserveMerges(
393 pullRebaseMode == BranchRebaseMode.PRESERVE)
394 .call();
395 result = new PullResult(fetchRes, remote, rebaseRes);
396 } else {
397 MergeCommand merge = new MergeCommand(repo);
398 MergeResult mergeRes = merge.include(upstreamName, commitToMerge)
399 .setStrategy(strategy).setProgressMonitor(monitor)
400 .setFastForward(getFastForwardMode()).call();
401 monitor.update(1);
402 result = new PullResult(fetchRes, remote, mergeRes);
403 }
404 monitor.endTask();
405 return result;
406 }
407
408
409
410
411
412
413
414
415
416
417
418
419
420 public PullCommand setRemote(String remote) {
421 checkCallable();
422 this.remote = remote;
423 return this;
424 }
425
426
427
428
429
430
431
432
433
434
435
436
437 public PullCommand setRemoteBranchName(String remoteBranchName) {
438 checkCallable();
439 this.remoteBranchName = remoteBranchName;
440 return this;
441 }
442
443
444
445
446
447
448
449 public String getRemote() {
450 return remote;
451 }
452
453
454
455
456
457
458
459
460 public String getRemoteBranchName() {
461 return remoteBranchName;
462 }
463
464
465
466
467
468
469
470
471
472 public PullCommand setStrategy(MergeStrategy strategy) {
473 this.strategy = strategy;
474 return this;
475 }
476
477
478
479
480
481
482
483
484
485 public PullCommand setTagOpt(TagOpt tagOpt) {
486 checkCallable();
487 this.tagOption = tagOpt;
488 return this;
489 }
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505 public PullCommand setFastForward(
506 @Nullable FastForwardMode fastForwardMode) {
507 checkCallable();
508 this.fastForwardMode = fastForwardMode;
509 return this;
510 }
511
512
513
514
515
516
517
518
519
520
521
522
523 public PullCommand setRecurseSubmodules(
524 @Nullable FetchRecurseSubmodulesMode recurse) {
525 this.submoduleRecurseMode = recurse;
526 return this;
527 }
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543 public static BranchRebaseMode getRebaseMode(String branchName,
544 Config config) {
545 BranchRebaseMode mode = config.getEnum(BranchRebaseMode.values(),
546 ConfigConstants.CONFIG_BRANCH_SECTION,
547 branchName, ConfigConstants.CONFIG_KEY_REBASE, null);
548 if (mode == null) {
549 mode = config.getEnum(BranchRebaseMode.values(),
550 ConfigConstants.CONFIG_PULL_SECTION, null,
551 ConfigConstants.CONFIG_KEY_REBASE, BranchRebaseMode.NONE);
552 }
553 return mode;
554 }
555
556 private FastForwardMode getFastForwardMode() {
557 if (fastForwardMode != null) {
558 return fastForwardMode;
559 }
560 Config config = repo.getConfig();
561 Merge ffMode = config.getEnum(Merge.values(),
562 ConfigConstants.CONFIG_PULL_SECTION, null,
563 ConfigConstants.CONFIG_KEY_FF, null);
564 return ffMode != null ? FastForwardMode.valueOf(ffMode) : null;
565 }
566 }