1 package org.apache.maven.scm.provider.git.jgit.command;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.scm.ScmFile;
23 import org.apache.maven.scm.ScmFileSet;
24 import org.apache.maven.scm.ScmFileStatus;
25 import org.apache.maven.scm.log.ScmLogger;
26 import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
27 import org.apache.maven.scm.util.FilenameUtils;
28 import org.codehaus.plexus.util.StringUtils;
29 import org.eclipse.jgit.api.AddCommand;
30 import org.eclipse.jgit.api.Git;
31 import org.eclipse.jgit.api.PushCommand;
32 import org.eclipse.jgit.api.Status;
33 import org.eclipse.jgit.api.errors.GitAPIException;
34 import org.eclipse.jgit.api.errors.InvalidRemoteException;
35 import org.eclipse.jgit.api.errors.NoFilepatternException;
36 import org.eclipse.jgit.api.errors.TransportException;
37 import org.eclipse.jgit.diff.DiffEntry;
38 import org.eclipse.jgit.diff.DiffEntry.ChangeType;
39 import org.eclipse.jgit.diff.DiffFormatter;
40 import org.eclipse.jgit.diff.RawTextComparator;
41 import org.eclipse.jgit.errors.CorruptObjectException;
42 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
43 import org.eclipse.jgit.errors.MissingObjectException;
44 import org.eclipse.jgit.errors.StopWalkException;
45 import org.eclipse.jgit.lib.Constants;
46 import org.eclipse.jgit.lib.ObjectId;
47 import org.eclipse.jgit.lib.ProgressMonitor;
48 import org.eclipse.jgit.lib.Repository;
49 import org.eclipse.jgit.lib.RepositoryBuilder;
50 import org.eclipse.jgit.lib.StoredConfig;
51 import org.eclipse.jgit.lib.TextProgressMonitor;
52 import org.eclipse.jgit.revwalk.RevCommit;
53 import org.eclipse.jgit.revwalk.RevFlag;
54 import org.eclipse.jgit.revwalk.RevSort;
55 import org.eclipse.jgit.revwalk.RevWalk;
56 import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
57 import org.eclipse.jgit.revwalk.filter.RevFilter;
58 import org.eclipse.jgit.transport.CredentialsProvider;
59 import org.eclipse.jgit.transport.PushResult;
60 import org.eclipse.jgit.transport.RefSpec;
61 import org.eclipse.jgit.transport.RemoteRefUpdate;
62 import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
63 import org.eclipse.jgit.util.io.DisabledOutputStream;
64
65 import java.io.File;
66 import java.io.IOException;
67 import java.io.UnsupportedEncodingException;
68 import java.net.URI;
69 import java.net.URLEncoder;
70 import java.util.ArrayList;
71 import java.util.Collection;
72 import java.util.Date;
73 import java.util.HashSet;
74 import java.util.Iterator;
75 import java.util.List;
76 import java.util.Set;
77
78
79
80
81
82
83
84
85 public class JGitUtils
86 {
87
88 private JGitUtils()
89 {
90
91 }
92
93
94
95
96
97
98 public static Git openRepo( File basedir ) throws IOException
99 {
100 return new Git( new RepositoryBuilder().readEnvironment().findGitDir( basedir ).setMustExist( true ).build() );
101 }
102
103
104
105
106
107 public static void closeRepo( Git git )
108 {
109 if ( git != null && git.getRepository() != null )
110 {
111 git.getRepository().close();
112 }
113 }
114
115
116
117
118
119
120
121 public static ProgressMonitor getMonitor( ScmLogger logger )
122 {
123
124 return new TextProgressMonitor();
125 }
126
127
128
129
130
131
132
133
134
135
136
137
138
139 public static CredentialsProvider prepareSession( ScmLogger logger, Git git, GitScmProviderRepository repository )
140 {
141 StoredConfig config = git.getRepository().getConfig();
142 config.setString( "remote", "origin", "url", repository.getFetchUrl() );
143 config.setString( "remote", "origin", "pushURL", repository.getPushUrl() );
144
145
146 String password =
147 StringUtils.isNotBlank( repository.getPassword() ) ? repository.getPassword().trim() : "no-pwd-defined";
148
149
150 try
151 {
152 password = URLEncoder.encode( password, "UTF-8" );
153 }
154 catch ( UnsupportedEncodingException e )
155 {
156
157
158 System.out.println( "Ignore UnsupportedEncodingException when trying to encode password" );
159 }
160 logger.info( "fetch url: " + repository.getFetchUrl().replace( password, "******" ) );
161 logger.info( "push url: " + repository.getPushUrl().replace( password, "******" ) );
162 return getCredentials( repository );
163 }
164
165
166
167
168
169
170
171
172
173
174
175 public static CredentialsProvider getCredentials( GitScmProviderRepository repository )
176 {
177 if ( StringUtils.isNotBlank( repository.getUser() ) && StringUtils.isNotBlank( repository.getPassword() ) )
178 {
179 return new UsernamePasswordCredentialsProvider( repository.getUser().trim(),
180 repository.getPassword().trim() );
181 }
182
183
184 return null;
185 }
186
187 public static Iterable<PushResult> push( ScmLogger logger, Git git, GitScmProviderRepository repo, RefSpec refSpec )
188 throws GitAPIException, InvalidRemoteException, TransportException
189 {
190 CredentialsProvider credentials = JGitUtils.prepareSession( logger, git, repo );
191 PushCommand command = git.push().setRefSpecs( refSpec ).setCredentialsProvider( credentials )
192 .setTransportConfigCallback( new JGitTransportConfigCallback( repo, logger ) );
193
194 Iterable<PushResult> pushResultList = command.call();
195 for ( PushResult pushResult : pushResultList )
196 {
197 Collection<RemoteRefUpdate> ru = pushResult.getRemoteUpdates();
198 for ( RemoteRefUpdate remoteRefUpdate : ru )
199 {
200 logger.info( remoteRefUpdate.getStatus() + " - " + remoteRefUpdate.toString() );
201 }
202 }
203 return pushResultList;
204 }
205
206
207
208
209
210
211
212 public static boolean hasCommits( Repository repo )
213 {
214 if ( repo != null && repo.getDirectory().exists() )
215 {
216 return ( new File( repo.getDirectory(), "objects" ).list().length > 2 ) || (
217 new File( repo.getDirectory(), "objects/pack" ).list().length > 0 );
218 }
219 return false;
220 }
221
222
223
224
225
226
227
228
229
230
231
232
233 public static List<ScmFile> getFilesInCommit( Repository repository, RevCommit commit )
234 throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException, IOException
235 {
236 List<ScmFile> list = new ArrayList<ScmFile>();
237 if ( JGitUtils.hasCommits( repository ) )
238 {
239
240 try ( RevWalk rw = new RevWalk( repository );
241 DiffFormatter df = new DiffFormatter( DisabledOutputStream.INSTANCE ) )
242 {
243 RevCommit realParent = commit.getParentCount() > 0 ? commit.getParent( 0 ) : commit;
244 RevCommit parent = rw.parseCommit( realParent.getId() );
245 df.setRepository( repository );
246 df.setDiffComparator( RawTextComparator.DEFAULT );
247 df.setDetectRenames( true );
248 List<DiffEntry> diffs = df.scan( parent.getTree(), commit.getTree() );
249 for ( DiffEntry diff : diffs )
250 {
251 list.add( new ScmFile( diff.getNewPath(), ScmFileStatus.CHECKED_IN ) );
252 }
253 }
254 }
255 return list;
256 }
257
258
259
260
261
262
263
264 public static ScmFileStatus getScmFileStatus( ChangeType changeType )
265 {
266 switch ( changeType )
267 {
268 case ADD:
269 return ScmFileStatus.ADDED;
270 case MODIFY:
271 return ScmFileStatus.MODIFIED;
272 case DELETE:
273 return ScmFileStatus.DELETED;
274 case RENAME:
275 return ScmFileStatus.RENAMED;
276 case COPY:
277 return ScmFileStatus.COPIED;
278 default:
279 return ScmFileStatus.UNKNOWN;
280 }
281 }
282
283
284
285
286
287
288
289
290
291
292
293 public static List<ScmFile> addAllFiles( Git git, ScmFileSet fileSet )
294 throws GitAPIException, NoFilepatternException
295 {
296 URI baseUri = fileSet.getBasedir().toURI();
297 AddCommand add = git.add();
298 for ( File file : fileSet.getFileList() )
299 {
300 if ( !file.isAbsolute() )
301 {
302 file = new File( fileSet.getBasedir().getPath(), file.getPath() );
303 }
304
305 if ( file.exists() )
306 {
307 String path = relativize( baseUri, file );
308 add.addFilepattern( path );
309 add.addFilepattern( file.getAbsolutePath() );
310 }
311 }
312 add.call();
313
314 Status status = git.status().call();
315
316 Set<String> allInIndex = new HashSet<String>();
317 allInIndex.addAll( status.getAdded() );
318 allInIndex.addAll( status.getChanged() );
319
320
321
322 List<ScmFile> addedFiles = new ArrayList<ScmFile>( allInIndex.size() );
323
324
325 for ( String entry : allInIndex )
326 {
327 ScmFile scmfile = new ScmFile( entry, ScmFileStatus.ADDED );
328
329
330
331 for ( Iterator<File> itfl = fileSet.getFileList().iterator(); itfl.hasNext(); )
332 {
333 String path = FilenameUtils.normalizeFilename( relativize( baseUri, itfl.next() ) );
334 if ( path.equals( FilenameUtils.normalizeFilename( scmfile.getPath() ) ) )
335 {
336 addedFiles.add( scmfile );
337 }
338 }
339 }
340 return addedFiles;
341 }
342
343 private static String relativize( URI baseUri, File f )
344 {
345 String path = f.getPath();
346 if ( f.isAbsolute() )
347 {
348 path = baseUri.relativize( new File( path ).toURI() ).getPath();
349 }
350 return path;
351 }
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368 public static List<RevCommit> getRevCommits( Repository repo, RevSort[] sortings, String fromRev, String toRev,
369 final Date fromDate, final Date toDate, int maxLines )
370 throws IOException, MissingObjectException, IncorrectObjectTypeException
371 {
372
373 List<RevCommit> revs = new ArrayList<RevCommit>();
374
375 ObjectId fromRevId = fromRev != null ? repo.resolve( fromRev ) : null;
376 ObjectId toRevId = toRev != null ? repo.resolve( toRev ) : null;
377
378 if ( sortings == null || sortings.length == 0 )
379 {
380 sortings = new RevSort[]{ RevSort.TOPO, RevSort.COMMIT_TIME_DESC };
381 }
382
383 try ( RevWalk walk = new RevWalk( repo ) )
384 {
385 for ( final RevSort s : sortings )
386 {
387 walk.sort( s, true );
388 }
389
390 if ( fromDate != null && toDate != null )
391 {
392
393 walk.setRevFilter( new RevFilter()
394 {
395 @Override
396 public boolean include( RevWalk walker, RevCommit cmit )
397 throws StopWalkException, MissingObjectException, IncorrectObjectTypeException, IOException
398 {
399 int cmtTime = cmit.getCommitTime();
400
401 return ( cmtTime >= ( fromDate.getTime() / 1000 ) )
402 && ( cmtTime <= ( toDate.getTime() / 1000 ) );
403 }
404
405 @Override
406 public RevFilter clone()
407 {
408 return this;
409 }
410 } );
411 }
412 else
413 {
414 if ( fromDate != null )
415 {
416 walk.setRevFilter( CommitTimeRevFilter.after( fromDate ) );
417 }
418 if ( toDate != null )
419 {
420 walk.setRevFilter( CommitTimeRevFilter.before( toDate ) );
421 }
422 }
423
424 if ( fromRevId != null )
425 {
426 RevCommit c = walk.parseCommit( fromRevId );
427 c.add( RevFlag.UNINTERESTING );
428 RevCommit real = walk.parseCommit( c );
429 walk.markUninteresting( real );
430 }
431
432 if ( toRevId != null )
433 {
434 RevCommit c = walk.parseCommit( toRevId );
435 c.remove( RevFlag.UNINTERESTING );
436 RevCommit real = walk.parseCommit( c );
437 walk.markStart( real );
438 }
439 else
440 {
441 final ObjectId head = repo.resolve( Constants.HEAD );
442 if ( head == null )
443 {
444 throw new RuntimeException( "Cannot resolve " + Constants.HEAD );
445 }
446 RevCommit real = walk.parseCommit( head );
447 walk.markStart( real );
448 }
449
450 int n = 0;
451 for ( final RevCommit c : walk )
452 {
453 n++;
454 if ( maxLines != -1 && n > maxLines )
455 {
456 break;
457 }
458
459 revs.add( c );
460 }
461 return revs;
462 }
463 }
464
465 }