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 if (monitor == null) {
134 monitor = NullProgressMonitor.INSTANCE;
135 }
136 this.monitor = monitor;
137 return this;
138 }
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158 public PullCommand setRebase(boolean useRebase) {
159 checkCallable();
160 pullRebaseMode = useRebase ? PullRebaseMode.REBASE : PullRebaseMode.NO_REBASE;
161 return this;
162 }
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183 public PullResult call() throws GitAPIException,
184 WrongRepositoryStateException, InvalidConfigurationException,
185 DetachedHeadException, InvalidRemoteException, CanceledException,
186 RefNotFoundException, RefNotAdvertisedException, NoHeadException,
187 org.eclipse.jgit.api.errors.TransportException {
188 checkCallable();
189
190 monitor.beginTask(JGitText.get().pullTaskName, 2);
191
192 String branchName;
193 try {
194 String fullBranch = repo.getFullBranch();
195 if (fullBranch == null)
196 throw new NoHeadException(
197 JGitText.get().pullOnRepoWithoutHEADCurrentlyNotSupported);
198 if (!fullBranch.startsWith(Constants.R_HEADS)) {
199
200
201 throw new DetachedHeadException();
202 }
203 branchName = fullBranch.substring(Constants.R_HEADS.length());
204 } catch (IOException e) {
205 throw new JGitInternalException(
206 JGitText.get().exceptionCaughtDuringExecutionOfPullCommand,
207 e);
208 }
209
210 if (!repo.getRepositoryState().equals(RepositoryState.SAFE))
211 throw new WrongRepositoryStateException(MessageFormat.format(
212 JGitText.get().cannotPullOnARepoWithState, repo
213 .getRepositoryState().name()));
214
215 Config repoConfig = repo.getConfig();
216 if (remote == null) {
217
218
219 remote = repoConfig.getString(
220 ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
221 ConfigConstants.CONFIG_KEY_REMOTE);
222 }
223 if (remote == null)
224
225 remote = Constants.DEFAULT_REMOTE_NAME;
226
227 if (remoteBranchName == null)
228
229
230 remoteBranchName = repoConfig.getString(
231 ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
232 ConfigConstants.CONFIG_KEY_MERGE);
233
234
235 if (pullRebaseMode == null) {
236 pullRebaseMode = getRebaseMode(branchName, repoConfig);
237 }
238
239 if (remoteBranchName == null)
240 remoteBranchName = branchName;
241
242 final boolean isRemote = !remote.equals(".");
243 String remoteUri;
244 FetchResult fetchRes;
245 if (isRemote) {
246 remoteUri = repoConfig.getString(
247 ConfigConstants.CONFIG_REMOTE_SECTION, remote,
248 ConfigConstants.CONFIG_KEY_URL);
249 if (remoteUri == null) {
250 String missingKey = ConfigConstants.CONFIG_REMOTE_SECTION + DOT
251 + remote + DOT + ConfigConstants.CONFIG_KEY_URL;
252 throw new InvalidConfigurationException(MessageFormat.format(
253 JGitText.get().missingConfigurationForKey, missingKey));
254 }
255
256 if (monitor.isCancelled())
257 throw new CanceledException(MessageFormat.format(
258 JGitText.get().operationCanceled,
259 JGitText.get().pullTaskName));
260
261 FetchCommand fetch = new FetchCommand(repo);
262 fetch.setRemote(remote);
263 fetch.setProgressMonitor(monitor);
264 configure(fetch);
265
266 fetchRes = fetch.call();
267 } else {
268
269 remoteUri = JGitText.get().localRepository;
270 fetchRes = null;
271 }
272
273 monitor.update(1);
274
275 if (monitor.isCancelled())
276 throw new CanceledException(MessageFormat.format(
277 JGitText.get().operationCanceled,
278 JGitText.get().pullTaskName));
279
280
281
282
283 AnyObjectId commitToMerge;
284 if (isRemote) {
285 Ref r = null;
286 if (fetchRes != null) {
287 r = fetchRes.getAdvertisedRef(remoteBranchName);
288 if (r == null)
289 r = fetchRes.getAdvertisedRef(Constants.R_HEADS
290 + remoteBranchName);
291 }
292 if (r == null) {
293 throw new RefNotAdvertisedException(MessageFormat.format(
294 JGitText.get().couldNotGetAdvertisedRef, remote,
295 remoteBranchName));
296 } else {
297 commitToMerge = r.getObjectId();
298 }
299 } else {
300 try {
301 commitToMerge = repo.resolve(remoteBranchName);
302 if (commitToMerge == null)
303 throw new RefNotFoundException(MessageFormat.format(
304 JGitText.get().refNotResolved, remoteBranchName));
305 } catch (IOException e) {
306 throw new JGitInternalException(
307 JGitText.get().exceptionCaughtDuringExecutionOfPullCommand,
308 e);
309 }
310 }
311
312 String upstreamName = MessageFormat.format(
313 JGitText.get().upstreamBranchName,
314 Repository.shortenRefName(remoteBranchName), remoteUri);
315
316 PullResult result;
317 if (pullRebaseMode.rebase) {
318 RebaseCommand rebase = new RebaseCommand(repo);
319 RebaseResult rebaseRes = rebase.setUpstream(commitToMerge)
320 .setUpstreamName(upstreamName).setProgressMonitor(monitor)
321 .setOperation(Operation.BEGIN).setStrategy(strategy)
322 .setPreserveMerges(pullRebaseMode.preserveMerges)
323 .call();
324 result = new PullResult(fetchRes, remote, rebaseRes);
325 } else {
326 MergeCommand merge = new MergeCommand(repo);
327 merge.include(upstreamName, commitToMerge);
328 merge.setStrategy(strategy);
329 MergeResult mergeRes = merge.call();
330 monitor.update(1);
331 result = new PullResult(fetchRes, remote, mergeRes);
332 }
333 monitor.endTask();
334 return result;
335 }
336
337
338
339
340
341
342
343
344
345
346
347
348 public PullCommand setRemote(String remote) {
349 checkCallable();
350 this.remote = remote;
351 return this;
352 }
353
354
355
356
357
358
359
360
361
362
363
364 public PullCommand setRemoteBranchName(String remoteBranchName) {
365 checkCallable();
366 this.remoteBranchName = remoteBranchName;
367 return this;
368 }
369
370
371
372
373
374 public String getRemote() {
375 return remote;
376 }
377
378
379
380
381
382
383 public String getRemoteBranchName() {
384 return remoteBranchName;
385 }
386
387
388
389
390
391
392
393 public PullCommand setStrategy(MergeStrategy strategy) {
394 this.strategy = strategy;
395 return this;
396 }
397
398 private static PullRebaseMode getRebaseMode(String branchName, Config config) {
399 PullRebaseMode mode = config.getEnum(PullRebaseMode.values(),
400 ConfigConstants.CONFIG_PULL_SECTION, null,
401 ConfigConstants.CONFIG_KEY_REBASE, PullRebaseMode.NO_REBASE);
402 mode = config.getEnum(PullRebaseMode.values(),
403 ConfigConstants.CONFIG_BRANCH_SECTION,
404 branchName, ConfigConstants.CONFIG_KEY_REBASE, mode);
405 return mode;
406 }
407 }