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