1+ package hudson .plugins .scm_sync_configuration .scms .customproviders .git .gitexe ;
2+
3+ import java .io .File ;
4+ import java .io .IOException ;
5+ import java .util .ArrayList ;
6+ import java .util .List ;
7+
8+ import org .apache .commons .io .FilenameUtils ;
9+ import org .apache .maven .scm .ScmException ;
10+ import org .apache .maven .scm .ScmFile ;
11+ import org .apache .maven .scm .ScmFileSet ;
12+ import org .apache .maven .scm .ScmFileStatus ;
13+ import org .apache .maven .scm .ScmVersion ;
14+ import org .apache .maven .scm .command .checkin .CheckInScmResult ;
15+ import org .apache .maven .scm .log .ScmLogger ;
16+ import org .apache .maven .scm .provider .ScmProviderRepository ;
17+ import org .apache .maven .scm .provider .git .gitexe .command .GitCommandLineUtils ;
18+ import org .apache .maven .scm .provider .git .gitexe .command .add .GitAddCommand ;
19+ import org .apache .maven .scm .provider .git .gitexe .command .branch .GitBranchCommand ;
20+ import org .apache .maven .scm .provider .git .gitexe .command .checkin .GitCheckInCommand ;
21+ import org .apache .maven .scm .provider .git .gitexe .command .status .GitStatusCommand ;
22+ import org .apache .maven .scm .provider .git .gitexe .command .status .GitStatusConsumer ;
23+ import org .apache .maven .scm .provider .git .repository .GitScmProviderRepository ;
24+ import org .codehaus .plexus .util .FileUtils ;
25+ import org .codehaus .plexus .util .cli .CommandLineUtils ;
26+ import org .codehaus .plexus .util .cli .Commandline ;
27+
28+ /**
29+ * @author fcamblor
30+ * Crappy hack because for the moment, in maven-scmprovider-gitext 1.8.1, when we checkIn file,
31+ * checkin is originally made by passing pushUrl instead of a local reference (such as "origin")
32+ * Problem is passing pushUrl doesn't update current local reference once pushed => after push,
33+ * origin/<targetBranch> will not be updated to latest commit => on next push, there will be an
34+ * error saying some pull is needed.
35+ * This workaround could be betterly handled when something like "checkinAndFetch" could be
36+ * implemented generically in maven-scm-api
37+ * (see http://maven.40175.n5.nabble.com/SCM-GitExe-no-fetch-after-push-td5745064.html)
38+ */
39+ public class ScmSyncGitCheckInCommand extends GitCheckInCommand {
40+ // Retrieved implementation from GitCheckInCommande v1.8.1, only overriding call to createPushCommandLine()
41+ // by a *custom* implementation
42+ @ Override
43+ protected CheckInScmResult executeCheckInCommand (
44+ ScmProviderRepository repo , ScmFileSet fileSet , String message , ScmVersion version ) throws ScmException {
45+
46+ GitScmProviderRepository repository = (GitScmProviderRepository ) repo ;
47+
48+ CommandLineUtils .StringStreamConsumer stderr = new CommandLineUtils .StringStreamConsumer ();
49+ CommandLineUtils .StringStreamConsumer stdout = new CommandLineUtils .StringStreamConsumer ();
50+
51+ int exitCode ;
52+
53+ File messageFile = FileUtils .createTempFile ("maven-scm-" , ".commit" , null );
54+ try
55+ {
56+ FileUtils .fileWrite ( messageFile .getAbsolutePath (), message );
57+ }
58+ catch ( IOException ex )
59+ {
60+ return new CheckInScmResult ( null , "Error while making a temporary file for the commit message: "
61+ + ex .getMessage (), null , false );
62+ }
63+
64+ try
65+ {
66+ if ( !fileSet .getFileList ().isEmpty () )
67+ {
68+ // if specific fileSet is given, we have to git-add them first
69+ // otherwise we will use 'git-commit -a' later
70+
71+ Commandline clAdd = GitAddCommand .createCommandLine (fileSet .getBasedir (), fileSet .getFileList ());
72+
73+ exitCode = GitCommandLineUtils .execute (clAdd , stdout , stderr , getLogger ());
74+
75+ if ( exitCode != 0 )
76+ {
77+ return new CheckInScmResult ( clAdd .toString (), "The git-add command failed." , stderr .getOutput (),
78+ false );
79+ }
80+
81+ }
82+
83+ // git-commit doesn't show single files, but only summary :/
84+ // so we must run git-status and consume the output
85+ // borrow a few things from the git-status command
86+ Commandline clStatus = GitStatusCommand .createCommandLine (repository , fileSet );
87+
88+ GitStatusConsumer statusConsumer = new GitStatusConsumer ( getLogger (), fileSet .getBasedir () );
89+ exitCode = GitCommandLineUtils .execute ( clStatus , statusConsumer , stderr , getLogger () );
90+ if ( exitCode != 0 )
91+ {
92+ // git-status returns non-zero if nothing to do
93+ if ( getLogger ().isInfoEnabled () )
94+ {
95+ getLogger ().info ( "nothing added to commit but untracked files present (use \" git add\" to " +
96+ "track)" );
97+ }
98+ }
99+
100+ if ( statusConsumer .getChangedFiles ().isEmpty () )
101+ {
102+ return new CheckInScmResult ( null , statusConsumer .getChangedFiles () );
103+ }
104+
105+ Commandline clCommit = createCommitCommandLine ( repository , fileSet , messageFile );
106+
107+ exitCode = GitCommandLineUtils .execute ( clCommit , stdout , stderr , getLogger () );
108+ if ( exitCode != 0 )
109+ {
110+ return new CheckInScmResult ( clCommit .toString (), "The git-commit command failed." , stderr .getOutput (),
111+ false );
112+ }
113+
114+ if ( repo .isPushChanges () )
115+ {
116+ Commandline cl = createSpecificPushCommandLine ( getLogger (), repository , fileSet , version );
117+
118+ exitCode = GitCommandLineUtils .execute ( cl , stdout , stderr , getLogger () );
119+ if ( exitCode != 0 )
120+ {
121+ return new CheckInScmResult ( cl .toString (), "The git-push command failed." , stderr .getOutput (), false );
122+ }
123+ }
124+
125+ List <ScmFile > checkedInFiles = new ArrayList <ScmFile >( statusConsumer .getChangedFiles ().size () );
126+
127+ // rewrite all detected files to now have status 'checked_in'
128+ for ( ScmFile changedFile : statusConsumer .getChangedFiles () )
129+ {
130+ ScmFile scmfile = new ScmFile ( changedFile .getPath (), ScmFileStatus .CHECKED_IN );
131+
132+ if ( fileSet .getFileList ().isEmpty () )
133+ {
134+ checkedInFiles .add ( scmfile );
135+ }
136+ else
137+ {
138+ // if a specific fileSet is given, we have to check if the file is really tracked
139+ for ( File f : fileSet .getFileList () )
140+ {
141+ if ( FilenameUtils .separatorsToUnix (f .getPath ()).equals ( scmfile .getPath () ) )
142+ {
143+ checkedInFiles .add ( scmfile );
144+ }
145+
146+ }
147+ }
148+ }
149+
150+ return new CheckInScmResult ( clCommit .toString (), checkedInFiles );
151+ }
152+ finally
153+ {
154+ try
155+ {
156+ FileUtils .forceDelete ( messageFile );
157+ }
158+ catch ( IOException ex )
159+ {
160+ // ignore
161+ }
162+ }
163+
164+ }
165+
166+ // ----------------------------------------------------------------------
167+ //
168+ // ----------------------------------------------------------------------
169+
170+ public static Commandline createSpecificPushCommandLine ( ScmLogger logger , GitScmProviderRepository repository ,
171+ ScmFileSet fileSet , ScmVersion version )
172+ throws ScmException
173+ {
174+ Commandline cl = GitCommandLineUtils .getBaseGitCommandLine ( fileSet .getBasedir (), "push" );
175+
176+ String branch = GitBranchCommand .getCurrentBranch (logger , repository , fileSet );
177+
178+ if ( branch == null || branch .length () == 0 )
179+ {
180+ throw new ScmException ( "Could not detect the current branch. Don't know where I should push to!" );
181+ }
182+
183+ // Overloaded branch name here : if repository.getUrl() is kept, during checkin(), current *local* branch
184+ // reference is not updated, whereas by using origin, it will be done !
185+ cl .createArg ().setValue ( "origin" );
186+
187+ cl .createArg ().setValue ( branch + ":" + branch );
188+
189+ return cl ;
190+ }
191+
192+ }
0 commit comments