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.RebaseCommand.Operation;
50 import org.eclipse.jgit.api.errors.CanceledException;
51 import org.eclipse.jgit.api.errors.DetachedHeadException;
52 import org.eclipse.jgit.api.errors.GitAPIException;
53 import org.eclipse.jgit.api.errors.InvalidConfigurationException;
54 import org.eclipse.jgit.api.errors.InvalidRemoteException;
55 import org.eclipse.jgit.api.errors.JGitInternalException;
56 import org.eclipse.jgit.api.errors.NoHeadException;
57 import org.eclipse.jgit.api.errors.RefNotAdvertisedException;
58 import org.eclipse.jgit.api.errors.RefNotFoundException;
59 import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
60 import org.eclipse.jgit.internal.JGitText;
61 import org.eclipse.jgit.lib.AnyObjectId;
62 import org.eclipse.jgit.lib.Config;
63 import org.eclipse.jgit.lib.ConfigConstants;
64 import org.eclipse.jgit.lib.Constants;
65 import org.eclipse.jgit.lib.NullProgressMonitor;
66 import org.eclipse.jgit.lib.ProgressMonitor;
67 import org.eclipse.jgit.lib.Ref;
68 import org.eclipse.jgit.lib.Repository;
69 import org.eclipse.jgit.lib.RepositoryState;
70 import org.eclipse.jgit.merge.MergeStrategy;
71 import org.eclipse.jgit.transport.FetchResult;
72
73
74
75
76
77
78
79 public class PullCommand extends TransportCommand<PullCommand, PullResult> {
80
81 private final static String DOT = ".";
82
83 private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
84
85 private PullRebaseMode pullRebaseMode = null;
86
87 private String remote;
88
89 private String remoteBranchName;
90
91 private MergeStrategy strategy = MergeStrategy.RECURSIVE;
92
93 private enum PullRebaseMode implements Config.ConfigEnum {
94 REBASE_PRESERVE("preserve", true, true),
95 REBASE("true", true, false),
96 NO_REBASE("false", false, false);
97
98 private final String configValue;
99
100 private final boolean rebase;
101
102 private final boolean preserveMerges;
103
104 PullRebaseMode(String configValue, boolean rebase,
105 boolean preserveMerges) {
106 this.configValue = configValue;
107 this.rebase = rebase;
108 this.preserveMerges = preserveMerges;
109 }
110
111 public String toConfigValue() {
112 return configValue;
113 }
114
115 public boolean matchConfigValue(String in) {
116 return in.equals(configValue);
117 }
118 }
119
120
121
122
123 protected PullCommand(Repository repo) {
124 super(repo);
125 }
126
127
128
129
130
131
132 public PullCommand setProgressMonitor(ProgressMonitor monitor) {
133 this.monitor = monitor;
134 return this;
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 ? PullRebaseMode.REBASE : PullRebaseMode.NO_REBASE;
158 return this;
159 }
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180 public PullResult call() throws GitAPIException,
181 WrongRepositoryStateException, InvalidConfigurationException,
182 DetachedHeadException, InvalidRemoteException, CanceledException,
183 RefNotFoundException, RefNotAdvertisedException, NoHeadException,
184 org.eclipse.jgit.api.errors.TransportException {
185 checkCallable();
186
187 monitor.beginTask(JGitText.get().pullTaskName, 2);
188
189 String branchName;
190 try {
191 String fullBranch = repo.getFullBranch();
192 if (fullBranch == null)
193 throw new NoHeadException(
194 JGitText.get().pullOnRepoWithoutHEADCurrentlyNotSupported);
195 if (!fullBranch.startsWith(Constants.R_HEADS)) {
196
197
198 throw new DetachedHeadException();
199 }
200 branchName = fullBranch.substring(Constants.R_HEADS.length());
201 } catch (IOException e) {
202 throw new JGitInternalException(
203 JGitText.get().exceptionCaughtDuringExecutionOfPullCommand,
204 e);
205 }
206
207 if (!repo.getRepositoryState().equals(RepositoryState.SAFE))
208 throw new WrongRepositoryStateException(MessageFormat.format(
209 JGitText.get().cannotPullOnARepoWithState, repo
210 .getRepositoryState().name()));
211
212 Config repoConfig = repo.getConfig();
213 if (remote == null) {
214
215
216 remote = repoConfig.getString(
217 ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
218 ConfigConstants.CONFIG_KEY_REMOTE);
219 }
220 if (remote == null)
221
222 remote = Constants.DEFAULT_REMOTE_NAME;
223
224 if (remoteBranchName == null)
225
226
227 remoteBranchName = repoConfig.getString(
228 ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
229 ConfigConstants.CONFIG_KEY_MERGE);
230
231
232 if (pullRebaseMode == null) {
233 pullRebaseMode = getRebaseMode(branchName, repoConfig);
234 }
235
236 if (remoteBranchName == null)
237 remoteBranchName = branchName;
238
239 final boolean isRemote = !remote.equals(".");
240 String remoteUri;
241 FetchResult fetchRes;
242 if (isRemote) {
243 remoteUri = repoConfig.getString(
244 ConfigConstants.CONFIG_REMOTE_SECTION, remote,
245 ConfigConstants.CONFIG_KEY_URL);
246 if (remoteUri == null) {
247 String missingKey = ConfigConstants.CONFIG_REMOTE_SECTION + DOT
248 + remote + DOT + ConfigConstants.CONFIG_KEY_URL;
249 throw new InvalidConfigurationException(MessageFormat.format(
250 JGitText.get().missingConfigurationForKey, missingKey));
251 }
252
253 if (monitor.isCancelled())
254 throw new CanceledException(MessageFormat.format(
255 JGitText.get().operationCanceled,
256 JGitText.get().pullTaskName));
257
258 FetchCommand fetch = new FetchCommand(repo);
259 fetch.setRemote(remote);
260 fetch.setProgressMonitor(monitor);
261 configure(fetch);
262
263 fetchRes = fetch.call();
264 } else {
265
266 remoteUri = JGitText.get().localRepository;
267 fetchRes = null;
268 }
269
270 monitor.update(1);
271
272 if (monitor.isCancelled())
273 throw new CanceledException(MessageFormat.format(
274 JGitText.get().operationCanceled,
275 JGitText.get().pullTaskName));
276
277
278
279
280 AnyObjectId commitToMerge;
281 if (isRemote) {
282 Ref r = null;
283 if (fetchRes != null) {
284 r = fetchRes.getAdvertisedRef(remoteBranchName);
285 if (r == null)
286 r = fetchRes.getAdvertisedRef(Constants.R_HEADS
287 + remoteBranchName);
288 }
289 if (r == null) {
290 throw new RefNotAdvertisedException(MessageFormat.format(
291 JGitText.get().couldNotGetAdvertisedRef, remote,
292 remoteBranchName));
293 } else {
294 commitToMerge = r.getObjectId();
295 }
296 } else {
297 try {
298 commitToMerge = repo.resolve(remoteBranchName);
299 if (commitToMerge == null)
300 throw new RefNotFoundException(MessageFormat.format(
301 JGitText.get().refNotResolved, remoteBranchName));
302 } catch (IOException e) {
303 throw new JGitInternalException(
304 JGitText.get().exceptionCaughtDuringExecutionOfPullCommand,
305 e);
306 }
307 }
308
309 String upstreamName = MessageFormat.format(
310 JGitText.get().upstreamBranchName,
311 Repository.shortenRefName(remoteBranchName), remoteUri);
312
313 PullResult result;
314 if (pullRebaseMode.rebase) {
315 RebaseCommand rebase = new RebaseCommand(repo);
316 RebaseResult rebaseRes = rebase.setUpstream(commitToMerge)
317 .setUpstreamName(upstreamName).setProgressMonitor(monitor)
318 .setOperation(Operation.BEGIN).setStrategy(strategy)
319 .setPreserveMerges(pullRebaseMode.preserveMerges)
320 .call();
321 result = new PullResult(fetchRes, remote, rebaseRes);
322 } else {
323 MergeCommand merge = new MergeCommand(repo);
324 merge.include(upstreamName, commitToMerge);
325 merge.setStrategy(strategy);
326 MergeResult mergeRes = merge.call();
327 monitor.update(1);
328 result = new PullResult(fetchRes, remote, mergeRes);
329 }
330 monitor.endTask();
331 return result;
332 }
333
334
335
336
337
338
339
340
341
342
343
344
345 public PullCommand setRemote(String remote) {
346 checkCallable();
347 this.remote = remote;
348 return this;
349 }
350
351
352
353
354
355
356
357
358
359
360
361 public PullCommand setRemoteBranchName(String remoteBranchName) {
362 checkCallable();
363 this.remoteBranchName = remoteBranchName;
364 return this;
365 }
366
367
368
369
370
371 public String getRemote() {
372 return remote;
373 }
374
375
376
377
378
379
380 public String getRemoteBranchName() {
381 return remoteBranchName;
382 }
383
384
385
386
387
388
389
390 public PullCommand setStrategy(MergeStrategy strategy) {
391 this.strategy = strategy;
392 return this;
393 }
394
395 private static PullRebaseMode getRebaseMode(String branchName, Config config) {
396 PullRebaseMode mode = config.getEnum(PullRebaseMode.values(),
397 ConfigConstants.CONFIG_PULL_SECTION, null,
398 ConfigConstants.CONFIG_KEY_REBASE, PullRebaseMode.NO_REBASE);
399 mode = config.getEnum(PullRebaseMode.values(),
400 ConfigConstants.CONFIG_BRANCH_SECTION,
401 branchName, ConfigConstants.CONFIG_KEY_REBASE, mode);
402 return mode;
403 }
404 }