66using System . Text . RegularExpressions ;
77using System . Threading ;
88using System . Threading . Tasks ;
9+ using System . IO ;
910
1011namespace Microsoft . VisualStudio . Services . Agent . Worker . Build
1112{
1213 [ ServiceLocator ( Default = typeof ( GitCommandManager ) ) ]
1314 public interface IGitCommandManager : IAgentService
1415 {
15- string GitPath { get ; set ; }
16-
17- Version Version { get ; set ; }
18-
19- string GitHttpUserAgent { get ; set ; }
16+ // setup git execution info, git location, version, useragent, execpath
17+ Task LoadGitExecutionInfo ( IExecutionContext context ) ;
2018
2119 // git init <LocalDir>
2220 Task < int > GitInit ( IExecutionContext context , string repositoryPath ) ;
@@ -56,7 +54,7 @@ public interface IGitCommandManager : IAgentService
5654
5755 // git config --unset-all <key>
5856 Task < int > GitConfigUnset ( IExecutionContext context , string repositoryPath , string configKey ) ;
59-
57+
6058 // git config gc.auto 0
6159 Task < int > GitDisableAutoGC ( IExecutionContext context , string repositoryPath ) ;
6260
@@ -66,20 +64,36 @@ public interface IGitCommandManager : IAgentService
6664
6765 public class GitCommandManager : AgentService , IGitCommandManager
6866 {
69- private readonly Dictionary < string , Dictionary < Version , string > > _gitCommands = new Dictionary < string , Dictionary < Version , string > > ( StringComparer . OrdinalIgnoreCase )
67+ private string _gitHttpUserAgentEnv = null ;
68+ private string _gitPath = null ;
69+ private Version _version = null ;
70+ private string _gitExecPathEnv = null ;
71+
72+ public async Task LoadGitExecutionInfo ( IExecutionContext context )
7073 {
74+ #if OS_WINDOWS
75+ _gitPath = Path . Combine ( IOUtil . GetExternalsPath ( ) , "git" , "cmd" , $ "git{ IOUtil . ExeExtension } ") ;
76+ #else
77+ _gitPath = Path . Combine ( IOUtil . GetExternalsPath ( ) , "git" , "bin" , $ "git{ IOUtil . ExeExtension } ") ;
78+ #endif
79+ if ( string . IsNullOrEmpty ( _gitPath ) || ! File . Exists ( _gitPath ) )
7180 {
72- "checkout" , new Dictionary < Version , string > ( )
73- {
74- { new Version ( 1 , 8 ) , "--force {0}" } ,
75- { new Version ( 2 , 7 ) , "--progress --force {0}" }
76- }
81+ throw new Exception ( StringUtil . Loc ( "GitNotFound" ) ) ;
7782 }
78- } ;
7983
80- public string GitPath { get ; set ; }
81- public Version Version { get ; set ; }
82- public string GitHttpUserAgent { get ; set ; }
84+ context . Debug ( $ "Find git from agent's external directory: { _gitPath } .") ;
85+
86+ _version = await GitVersion ( context ) ;
87+ context . Debug ( $ "Detect git version: { _version . ToString ( ) } .") ;
88+
89+ _gitHttpUserAgentEnv = $ "git/{ _version . ToString ( ) } (vsts-agent-git/{ Constants . Agent . Version } )";
90+ context . Debug ( $ "Set git useragent to: { _gitHttpUserAgentEnv } .") ;
91+
92+ #if ! OS_WINDOWS
93+ _gitExecPathEnv = Path . Combine ( IOUtil . GetExternalsPath ( ) , "git" , "libexec" , "git-core" ) ;
94+ context . Debug ( $ "Set git execpath to: { _gitExecPathEnv } ") ;
95+ #endif
96+ }
8397
8498 // git init <LocalDir>
8599 public async Task < int > GitInit ( IExecutionContext context , string repositoryPath )
@@ -105,8 +119,7 @@ public async Task<int> GitFetch(IExecutionContext context, string repositoryPath
105119 public async Task < int > GitCheckout ( IExecutionContext context , string repositoryPath , string committishOrBranchSpec , CancellationToken cancellationToken )
106120 {
107121 context . Debug ( $ "Checkout { committishOrBranchSpec } .") ;
108- string checkoutOption = GetCommandOption ( "checkout" ) ;
109- return await ExecuteGitCommandAsync ( context , repositoryPath , "checkout" , StringUtil . Format ( checkoutOption , committishOrBranchSpec ) , cancellationToken ) ;
122+ return await ExecuteGitCommandAsync ( context , repositoryPath , "checkout" , StringUtil . Format ( "--progress --force {0}" , committishOrBranchSpec ) , cancellationToken ) ;
110123 }
111124
112125 // git clean -fdx
@@ -225,6 +238,7 @@ public async Task<Version> GitVersion(IExecutionContext context)
225238 Version version = null ;
226239 List < string > outputStrings = new List < string > ( ) ;
227240 int exitCode = await ExecuteGitCommandAsync ( context , IOUtil . GetWorkPath ( HostContext ) , "version" , null , outputStrings ) ;
241+ context . Debug ( $ "git version ouput: { string . Join ( Environment . NewLine , outputStrings ) } ") ;
228242 if ( exitCode == 0 )
229243 {
230244 // remove any empty line.
@@ -248,32 +262,6 @@ public async Task<Version> GitVersion(IExecutionContext context)
248262 return version ;
249263 }
250264
251- private string GetCommandOption ( string command )
252- {
253- if ( string . IsNullOrEmpty ( command ) )
254- {
255- throw new ArgumentNullException ( "command" ) ;
256- }
257-
258- if ( ! _gitCommands . ContainsKey ( command ) )
259- {
260- throw new NotSupportedException ( $ "Unsupported git command: { command } ") ;
261- }
262-
263- Dictionary < Version , string > options = _gitCommands [ command ] ;
264- foreach ( var versionOption in options . OrderByDescending ( o => o . Key ) )
265- {
266- if ( Version >= versionOption . Key )
267- {
268- return versionOption . Value ;
269- }
270- }
271-
272- var earliestVersion = options . OrderByDescending ( o => o . Key ) . Last ( ) ;
273- Trace . Info ( $ "Fallback to version { earliestVersion . Key . ToString ( ) } command option for git { command } .") ;
274- return earliestVersion . Value ;
275- }
276-
277265 private async Task < int > ExecuteGitCommandAsync ( IExecutionContext context , string repoRoot , string command , string options , CancellationToken cancellationToken = default ( CancellationToken ) )
278266 {
279267 string arg = StringUtil . Format ( $ "{ command } { options } ") . Trim ( ) ;
@@ -290,13 +278,18 @@ private string GetCommandOption(string command)
290278 context . Output ( message . Data ) ;
291279 } ;
292280
293- Dictionary < string , string > _userAgentEnv = new Dictionary < string , string > ( ) ;
294- if ( ! string . IsNullOrEmpty ( GitHttpUserAgent ) )
281+ Dictionary < string , string > _gitEnv = new Dictionary < string , string > ( ) ;
282+ if ( ! string . IsNullOrEmpty ( _gitHttpUserAgentEnv ) )
295283 {
296- _userAgentEnv [ "GIT_HTTP_USER_AGENT" ] = GitHttpUserAgent ;
284+ _gitEnv [ "GIT_HTTP_USER_AGENT" ] = _gitHttpUserAgentEnv ;
297285 }
298286
299- return await processInvoker . ExecuteAsync ( repoRoot , GitPath , arg , _userAgentEnv , cancellationToken ) ;
287+ if ( ! string . IsNullOrEmpty ( _gitExecPathEnv ) )
288+ {
289+ _gitEnv [ "GIT_EXEC_PATH" ] = _gitExecPathEnv ;
290+ }
291+
292+ return await processInvoker . ExecuteAsync ( repoRoot , _gitPath , arg , _gitEnv , cancellationToken ) ;
300293 }
301294
302295 private async Task < int > ExecuteGitCommandAsync ( IExecutionContext context , string repoRoot , string command , string options , IList < string > output )
@@ -327,13 +320,18 @@ private async Task<int> ExecuteGitCommandAsync(IExecutionContext context, string
327320 }
328321 } ;
329322
330- Dictionary < string , string > _userAgentEnv = new Dictionary < string , string > ( ) ;
331- if ( ! string . IsNullOrEmpty ( GitHttpUserAgent ) )
323+ Dictionary < string , string > _gitEnv = new Dictionary < string , string > ( ) ;
324+ if ( ! string . IsNullOrEmpty ( _gitHttpUserAgentEnv ) )
325+ {
326+ _gitEnv [ "GIT_HTTP_USER_AGENT" ] = _gitHttpUserAgentEnv ;
327+ }
328+
329+ if ( ! string . IsNullOrEmpty ( _gitExecPathEnv ) )
332330 {
333- _userAgentEnv [ "GIT_HTTP_USER_AGENT "] = GitHttpUserAgent ;
331+ _gitEnv [ "GIT_EXEC_PATH "] = _gitExecPathEnv ;
334332 }
335333
336- return await processInvoker . ExecuteAsync ( repoRoot , GitPath , arg , _userAgentEnv , default ( CancellationToken ) ) ;
334+ return await processInvoker . ExecuteAsync ( repoRoot , _gitPath , arg , _gitEnv , default ( CancellationToken ) ) ;
337335 }
338336
339337 private async Task < int > ExecuteGitCommandAsync ( IExecutionContext context , string repoRoot , string command , string options , string additionalCommandLine , CancellationToken cancellationToken )
@@ -352,13 +350,18 @@ private async Task<int> ExecuteGitCommandAsync(IExecutionContext context, string
352350 context . Output ( message . Data ) ;
353351 } ;
354352
355- Dictionary < string , string > _userAgentEnv = new Dictionary < string , string > ( ) ;
356- if ( ! string . IsNullOrEmpty ( GitHttpUserAgent ) )
353+ Dictionary < string , string > _gitEnv = new Dictionary < string , string > ( ) ;
354+ if ( ! string . IsNullOrEmpty ( _gitHttpUserAgentEnv ) )
355+ {
356+ _gitEnv [ "GIT_HTTP_USER_AGENT" ] = _gitHttpUserAgentEnv ;
357+ }
358+
359+ if ( ! string . IsNullOrEmpty ( _gitExecPathEnv ) )
357360 {
358- _userAgentEnv [ "GIT_HTTP_USER_AGENT "] = GitHttpUserAgent ;
361+ _gitEnv [ "GIT_EXEC_PATH "] = _gitExecPathEnv ;
359362 }
360363
361- return await processInvoker . ExecuteAsync ( repoRoot , GitPath , arg , _userAgentEnv , cancellationToken ) ;
364+ return await processInvoker . ExecuteAsync ( repoRoot , _gitPath , arg , _gitEnv , cancellationToken ) ;
362365 }
363366 }
364367}
0 commit comments