Fix the window icon and title for "Git Bash" when launched via shell extension
[msysgit/mtrensch.git] / share / WinGit / install.iss
blob0dd91f14e77c53527ef8d44170a6ad9e89242cb5
1 #define APP_NAME 'Git'
2 #define APP_VERSION '%APPVERSION%'
3 #define APP_URL 'http://msysgit.googlecode.com/'
4 #define APP_BUILTINS 'etc\fileList-builtins.txt'
6 [Setup]
7 ; Compiler-related
8 InternalCompressLevel=max
9 OutputBaseFilename={#emit APP_NAME+'-'+APP_VERSION}
10 OutputDir=%OUTPUTDIR%
11 SolidCompression=yes
13 ; Installer-related
14 AllowNoIcons=yes
15 AppName={#emit APP_NAME}
16 AppPublisherURL={#emit APP_URL}
17 AppVersion={#emit APP_VERSION}
18 AppVerName={#emit APP_NAME+' '+APP_VERSION}
19 ChangesEnvironment=yes
20 DefaultDirName={pf}\{#emit APP_NAME}
21 DefaultGroupName={#emit APP_NAME}
22 DisableReadyPage=yes
23 InfoBeforeFile=gpl-2.0.rtf
24 PrivilegesRequired=none
25 UninstallDisplayIcon=etc\git.ico
27 ; Cosmetic
28 SetupIconFile=etc\git.ico
29 WizardImageBackColor=clWhite
30 WizardImageStretch=no
31 WizardImageFile=git.bmp
32 WizardSmallImageFile=gitsmall.bmp
34 [Tasks]
35 Name: quicklaunchicon; Description: Create a &Quick Launch icon; GroupDescription: Additional icons:; Flags: checkedonce
36 Name: desktopicon; Description: Create a &Desktop icon; GroupDescription: Additional icons:; Flags: checkedonce
37 Name: shellextension; Description: "Add ""Git Ba&sh Here"""; GroupDescription: Windows Explorer integration:; Flags: checkedonce
38 Name: guiextension; Description: "Add ""Git &GUI Here"""; GroupDescription: Windows Explorer integration:; Flags: checkedonce
40 [Files]
41 Source: *; DestDir: {app}; Excludes: \*.bmp, gpl-2.0.rtf, \install.*, \tmp.*, \bin\*install*; Flags: recursesubdirs replacesameversion
42 Source: ReleaseNotes.rtf; DestDir: {app}; Flags: isreadme replacesameversion
44 [Icons]
45 Name: {group}\Git GUI; Filename: {app}\bin\wish.exe; Parameters: """{app}\libexec\git-core\git-gui"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico
46 Name: {group}\Git Bash; Filename: {syswow64}\cmd.exe; Parameters: "/c """"{app}\bin\sh.exe"" --login -i"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico
47 Name: {group}\Uninstall Git; Filename: {uninstallexe}
48 Name: {userappdata}\Microsoft\Internet Explorer\Quick Launch\Git Bash; Filename: {syswow64}\cmd.exe; Parameters: "/c """"{app}\bin\sh.exe"" --login -i"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico; Tasks: quicklaunchicon
49 Name: {code:GetShellFolder|desktop}\Git Bash; Filename: {syswow64}\cmd.exe; Parameters: "/c """"{app}\bin\sh.exe"" --login -i"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico; Tasks: desktopicon
51 ; Create a special shortcut that does not set a working directory. This is used by "Git Bash.vbs", which in turn is run by the "Git Bash Here" shell extension.
52 Name: {app}\Git Bash; Filename: {syswow64}\cmd.exe; Parameters: "/c """"{app}\bin\sh.exe"" --login -i"""; IconFilename: {app}\etc\git.ico; Tasks: shellextension
54 [Messages]
55 BeveledLabel={#emit APP_URL}
56 SetupAppTitle={#emit APP_NAME} Setup
57 SetupWindowTitle={#emit APP_NAME} Setup
59 [UninstallDelete]
60 Type: files; Name: {app}\bin\git-*.exe
61 Type: files; Name: {app}\libexec\git-core\git-*.exe
62 Type: dirifempty; Name: {app}\home\{username}
63 Type: dirifempty; Name: {app}\home
65 [Code]
67 Helper methods
70 function GetShellFolder(Param:string):string;
71 begin
72 if IsAdminLoggedOn then begin
73 Param:='{common'+Param+'}';
74 end else begin
75 Param:='{user'+Param+'}';
76 end;
77 Result:=ExpandConstant(Param);
78 end;
80 function CreateHardLink(lpFileName,lpExistingFileName:string;lpSecurityAttributes:Integer):Boolean;
81 external 'CreateHardLinkA@Kernel32.dll';
83 function GetEnvStrings(VarName:string;AllUsers:Boolean):TArrayOfString;
84 var
85 Path:string;
86 i:Longint;
87 p:Integer;
88 begin
89 Path:='';
91 // See http://www.jrsoftware.org/isfaq.php#env
92 if AllUsers then begin
93 // We ignore errors here. The resulting array of strings will be empty.
94 RegQueryStringValue(HKEY_LOCAL_MACHINE,'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',VarName,Path);
95 end else begin
96 // We ignore errors here. The resulting array of strings will be empty.
97 RegQueryStringValue(HKEY_CURRENT_USER,'Environment',VarName,Path);
98 end;
100 // Make sure we have at least one semicolon.
101 Path:=Path+';';
103 // Split the directories in PATH into an array of strings.
104 i:=0;
105 SetArrayLength(Result,0);
107 p:=Pos(';',Path);
108 while p>0 do begin
109 SetArrayLength(Result,i+1);
110 if p>1 then begin
111 Result[i]:=Copy(Path,1,p-1);
112 i:=i+1;
113 end;
114 Path:=Copy(Path,p+1,Length(Path));
115 p:=Pos(';',Path);
116 end;
117 end;
119 function SetEnvStrings(VarName:string;AllUsers,DeleteIfEmpty:Boolean;DirStrings:TArrayOfString):Boolean;
121 Path,KeyName:string;
122 i:Longint;
123 begin
124 // Merge all non-empty directory strings into a PATH variable.
125 Path:='';
126 for i:=0 to GetArrayLength(DirStrings)-1 do begin
127 if Length(DirStrings[i])>0 then begin
128 if Length(Path)>0 then begin
129 Path:=Path+';'+DirStrings[i];
130 end else begin
131 Path:=DirStrings[i];
132 end;
133 end;
134 end;
136 // See http://www.jrsoftware.org/isfaq.php#env
137 if AllUsers then begin
138 KeyName:='SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
139 if DeleteIfEmpty and (Length(Path)=0) then begin
140 Result:=(not RegValueExists(HKEY_LOCAL_MACHINE,KeyName,VarName)) or
141 RegDeleteValue(HKEY_LOCAL_MACHINE,KeyName,VarName);
142 end else begin
143 Result:=RegWriteStringValue(HKEY_LOCAL_MACHINE,KeyName,VarName,Path);
144 end;
145 end else begin
146 KeyName:='Environment';
147 if DeleteIfEmpty and (Length(Path)=0) then begin
148 Result:=(not RegValueExists(HKEY_CURRENT_USER,KeyName,VarName)) or
149 RegDeleteValue(HKEY_CURRENT_USER,KeyName,VarName);
150 end else begin
151 Result:=RegWriteStringValue(HKEY_CURRENT_USER,KeyName,VarName,Path);
152 end;
153 end;
154 end;
156 const
157 TortoiseSVNInstallKey='SOFTWARE\TortoiseSVN';
158 TortoiseCVSUninstallKey='SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\TortoiseCVS_is1';
159 PuTTYUninstallKey='SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\PuTTY_is1';
160 PuTTYPrivateKeyAssoc='PuTTYPrivateKey\shell\open\command';
162 function GetPuTTYLocation:string;
163 begin
164 // Prefer TortoisePlink over vanilla Plink for its GUI dialog to accept host keys.
165 if (IsWin64 and RegQueryStringValue(HKEY_LOCAL_MACHINE_64,TortoiseSVNInstallKey,'Directory',Result)) or
166 RegQueryStringValue(HKEY_LOCAL_MACHINE_32,TortoiseSVNInstallKey,'Directory',Result) then begin
167 // C:\Program Files\TortoiseSVN\
168 Result:=Result+'bin\';
169 // C:\Program Files\TortoiseSVN\bin\
170 end else begin
171 if not (IsWin64 and RegQueryStringValue(HKEY_LOCAL_MACHINE_64,TortoiseCVSUninstallKey,'InstallLocation',Result)) then begin
172 RegQueryStringValue(HKEY_LOCAL_MACHINE_32,TortoiseCVSUninstallKey,'InstallLocation',Result);
173 end;
174 // C:\Program Files\TortoiseCVS\
175 end;
177 if DirExists(Result) then begin
178 Result:=Result+'TortoisePlink.exe'
179 Exit;
180 end;
182 if not RegQueryStringValue(HKEY_LOCAL_MACHINE,PuTTYUninstallKey,'InstallLocation',Result) then begin
183 if RegQueryStringValue(HKEY_CLASSES_ROOT,PuTTYPrivateKeyAssoc,'',Result) then begin
184 // "C:\Program Files\PuTTY\pageant.exe" "%1"
185 Result:=RemoveQuotes(Result);
186 // C:\Program Files\PuTTY\pageant.exe" "%1
187 Result:=ExtractFilePath(Result);
188 end;
189 end;
190 // C:\Program Files\PuTTY\
192 if not DirExists(Result) then begin
193 // Guess something.
194 Result:=ExpandConstant('{pf}\PuTTY\');
195 end;
197 Result:=Result+'plink.exe'
198 end;
200 const
201 // Git Path options.
202 GP_BashOnly = 1;
203 GP_Cmd = 2;
204 GP_CmdTools = 3;
206 // Git SSH options.
207 GS_OpenSSH = 1;
208 GS_Plink = 2;
210 // Git CRLF options.
211 GC_LFOnly = 1;
212 GC_CRLFAlways = 2;
213 GC_CRLFCommitAsIs = 3;
216 // Wizard page and variables for the Path options.
217 PathPage:TWizardPage;
218 RdbPath:array[GP_BashOnly..GP_CmdTools] of TRadioButton;
220 // Wizard page and variables for the SSH options.
221 PuTTYPage:TWizardPage;
222 RdbSSH:array[GS_OpenSSH..GS_Plink] of TRadioButton;
223 EdtPlink:TEdit;
225 // Wizard page and variables for the CR/LF options.
226 CRLFPage:TWizardPage;
227 RdbCRLF:array[GC_LFOnly..GC_CRLFCommitAsIs] of TRadioButton;
229 procedure BrowseForPuTTYFolder(Sender:TObject);
231 Path:string;
232 begin
233 Path:=ExtractFilePath(EdtPlink.Text);
234 BrowseForFolder('Please select the PuTTY folder:',Path,False);
235 if FileExists(Path+'\TortoisePlink.exe') then begin
236 EdtPlink.Text:=Path+'\TortoisePlink.exe';
237 RdbSSH[GS_Plink].Checked:=True;
238 end else if FileExists(Path+'\plink.exe') then begin
239 EdtPlink.Text:=Path+'\plink.exe';
240 RdbSSH[GS_Plink].Checked:=True;
241 end else begin
242 MsgBox('Please enter a valid path to "TortoisePlink.exe" or "plink.exe".',mbError,MB_OK);
243 end;
244 end;
247 Installer code
250 procedure InitializeWizard;
252 PrevPageID:Integer;
253 LblGitBash,LblGitCmd,LblGitCmdTools,LblGitCmdToolsWarn:TLabel;
254 LblOpenSSH,LblPlink:TLabel;
255 PuTTYSessions:TArrayOfString;
256 LblLFOnly,LblCRLFAlways,LblCRLFCommitAsIs:TLabel;
257 BtnPlink:TButton;
258 Data:String;
259 begin
260 PrevPageID:=wpSelectTasks;
263 * Create a custom page for modifying the environment.
266 PathPage:=CreateCustomPage(
267 PrevPageID,
268 'Adjusting your PATH environment',
269 'How would you like to use Git from the command line?'
271 PrevPageID:=PathPage.ID;
273 // 1st choice
274 RdbPath[GP_BashOnly]:=TRadioButton.Create(PathPage);
275 with RdbPath[GP_BashOnly] do begin
276 Parent:=PathPage.Surface;
277 Caption:='Use Git Bash only';
278 Left:=ScaleX(4);
279 Top:=ScaleY(8);
280 Width:=ScaleX(129);
281 Height:=ScaleY(17);
282 Font.Style:=[fsBold];
283 TabOrder:=0;
284 Checked:=True;
285 end;
286 LblGitBash:=TLabel.Create(PathPage);
287 with LblGitBash do begin
288 Parent:=PathPage.Surface;
289 Caption:=
290 'This is the most conservative choice if you are concerned about the stability' + #13 +
291 'of your system. Your PATH will not be modified.';
292 Left:=ScaleX(28);
293 Top:=ScaleY(32);
294 Width:=ScaleX(405);
295 Height:=ScaleY(26);
296 end;
298 // 2nd choice
299 RdbPath[GP_Cmd]:=TRadioButton.Create(PathPage);
300 with RdbPath[GP_Cmd] do begin
301 Parent:=PathPage.Surface;
302 Caption:='Run Git from the Windows Command Prompt';
303 Left:=ScaleX(4);
304 Top:=ScaleY(76);
305 Width:=ScaleX(281);
306 Height:=ScaleY(17);
307 Font.Style:=[fsBold];
308 TabOrder:=1;
309 end;
310 LblGitCmd:=TLabel.Create(PathPage);
311 with LblGitCmd do begin
312 Parent:=PathPage.Surface;
313 Caption:=
314 'This option is considered safe and no conflicts with other tools are known.' + #13 +
315 'Only Git will be added to your PATH. Use this option if you want to use Git' + #13 +
316 'from a Cygwin Prompt (make sure to not have Cygwin''s Git installed).';
317 Left:=ScaleX(28);
318 Top:=ScaleY(100);
319 Width:=ScaleX(405);
320 Height:=ScaleY(39);
321 end;
323 // 3rd choice
324 RdbPath[GP_CmdTools]:=TRadioButton.Create(PathPage);
325 with RdbPath[GP_CmdTools] do begin
326 Parent:=PathPage.Surface;
327 Caption:='Run Git and included Unix tools from the Windows Command Prompt';
328 Left:=ScaleX(4);
329 Top:=ScaleY(152);
330 Width:=ScaleX(405);
331 Height:=ScaleY(17);
332 Font.Style:=[fsBold];
333 TabOrder:=2;
334 end;
335 LblGitCmdTools:=TLabel.Create(PathPage);
336 with LblGitCmdTools do begin
337 Parent:=PathPage.Surface;
338 Caption:='Both Git and its accompanying Unix tools will be added to your PATH.';
339 Left:=ScaleX(28);
340 Top:=ScaleY(176);
341 Width:=ScaleX(405);
342 Height:=ScaleY(13);
343 end;
344 LblGitCmdToolsWarn:=TLabel.Create(PathPage);
345 with LblGitCmdToolsWarn do begin
346 Parent:=PathPage.Surface;
347 Caption:=
348 'Warning: This will override Windows tools like find.exe and' + #13 +
349 'sort.exe. Select this option only if you understand the implications.';
350 Left:=ScaleX(28);
351 Top:=ScaleY(192);
352 Width:=ScaleX(376);
353 Height:=ScaleY(26);
354 Font.Color:=255;
355 Font.Style:=[fsBold];
356 end;
358 // Restore the setting chosen during a previous install.
359 Data:=GetPreviousData('Path Option','BashOnly');
360 if Data='BashOnly' then begin
361 RdbPath[GP_BashOnly].Checked:=True;
362 end else if Data='Cmd' then begin
363 RdbPath[GP_Cmd].Checked:=True;
364 end else if Data='CmdTools' then begin
365 RdbPath[GP_CmdTools].Checked:=True;
366 end;
369 * Create a custom page for using (Tortoise)Plink instead of OpenSSH
370 * if at least one PuTTY session is found in the Registry.
373 if RegGetSubkeyNames(HKEY_CURRENT_USER,'Software\SimonTatham\PuTTY\Sessions',PuTTYSessions) and (GetArrayLength(PuTTYSessions)>0) then begin
374 PuTTYPage:=CreateCustomPage(
375 PrevPageID,
376 'Choosing the SSH executable',
377 'Which Secure Shell client program would you like Git to use?'
379 PrevPageID:=PuTTYPage.ID;
381 // 1st choice
382 RdbSSH[GS_OpenSSH]:=TRadioButton.Create(PuTTYPage);
383 with RdbSSH[GS_OpenSSH] do begin
384 Parent:=PuTTYPage.Surface;
385 Caption:='Use OpenSSH';
386 Left:=ScaleX(4);
387 Top:=ScaleY(8);
388 Width:=ScaleX(129);
389 Height:=ScaleY(17);
390 Font.Style:=[fsBold];
391 TabOrder:=0;
392 Checked:=True;
393 end;
394 LblOpenSSH:=TLabel.Create(PuTTYPage);
395 with LblOpenSSH do begin
396 Parent:=PuTTYPage.Surface;
397 Caption:=
398 'This uses ssh.exe that comes with Git. The GIT_SSH and SVN_SSH' + #13 +
399 'environment variables will not be modified.';
400 Left:=ScaleX(28);
401 Top:=ScaleY(32);
402 Width:=ScaleX(324);
403 Height:=ScaleY(26);
404 end;
406 // 2nd choice
407 RdbSSH[GS_Plink]:=TRadioButton.Create(PuTTYPage);
408 with RdbSSH[GS_Plink] do begin
409 Parent:=PuTTYPage.Surface;
410 Caption:='Use (Tortoise)Plink';
411 Left:=ScaleX(4);
412 Top:=ScaleY(76);
413 Width:=ScaleX(281);
414 Height:=ScaleY(17);
415 Font.Style:=[fsBold];
416 TabOrder:=1;
417 end;
418 LblPlink:=TLabel.Create(PuTTYPage);
419 with LblPlink do begin
420 Parent:=PuTTYPage.Surface;
421 Caption:=
422 'PuTTY sessions were found in your Registry. You may specify the path' + #13 +
423 'to an existing copy of (Tortoise)Plink.exe from the TortoiseSVN/CVS' + #13 +
424 'or PuTTY applications. The GIT_SSH and SVN_SSH environment' + #13 +
425 'variables will be adjusted to point to the following executable:';
426 Left:=ScaleX(28);
427 Top:=ScaleY(100);
428 Width:=ScaleX(340);
429 Height:=ScaleY(52);
430 end;
431 EdtPlink:=TEdit.Create(PuTTYPage);
432 with EdtPlink do begin
433 Parent:=PuTTYPage.Surface;
434 Text:=GetPuTTYLocation;
435 if not FileExists(Text) then begin
436 Text:='';
437 end;
438 Left:=ScaleX(28);
439 Top:=ScaleY(161);
440 Width:=ScaleX(316);
441 Height:=ScaleY(13);
442 end;
443 BtnPlink:=TButton.Create(PuTTYPage);
444 with BtnPlink do begin
445 Parent:=PuTTYPage.Surface;
446 Caption:='...';
447 OnClick:=@BrowseForPuTTYFolder;
448 Left:=ScaleX(348);
449 Top:=ScaleY(161);
450 Width:=ScaleX(21);
451 Height:=ScaleY(21);
452 end;
454 // Restore the setting chosen during a previous install.
455 Data:=GetPreviousData('SSH Option','OpenSSH');
456 if Data='OpenSSH' then begin
457 RdbSSH[GS_OpenSSH].Checked:=True;
458 end else if Data='Plink' then begin
459 RdbSSH[GS_Plink].Checked:=True;
460 end;
461 end else begin
462 PuTTYPage:=NIL;
463 end;
466 * Create a custom page for the autoCRLF setting.
469 CRLFPage:=CreateCustomPage(
470 PrevPageID,
471 'Choosing CR/LF behavior',
472 'Which CR/LF behavior would you like Git to have?'
474 PrevPageID:=CRLFPage.ID;
476 // 1st choice
477 RdbCRLF[GC_LFOnly]:=TRadioButton.Create(CRLFPage);
478 with RdbCRLF[GC_LFOnly] do begin
479 Parent:=CRLFPage.Surface;
480 Caption:='Use Unix style line endings';
481 Left:=ScaleX(4);
482 Top:=ScaleY(8);
483 Width:=ScaleX(329);
484 Height:=ScaleY(17);
485 Font.Style:=[fsBold];
486 TabOrder:=0;
487 Checked:=False;
488 end;
489 LblLFOnly:=TLabel.Create(CRLFPage);
490 with LblLFOnly do begin
491 Parent:=CRLFPage.Surface;
492 Caption:=
493 'Choose this if a single Line Feed character ends your lines. Most Windows' + #13 +
494 'programs can cope with these line endings. However, some editors, like' + #13 +
495 'Notepad, will show everything in one line with this mode.';
496 Left:=ScaleX(28);
497 Top:=ScaleY(32);
498 Width:=ScaleX(364);
499 Height:=ScaleY(47);
500 end;
502 // 2nd choice
503 RdbCRLF[GC_CRLFAlways]:=TRadioButton.Create(CRLFPage);
504 with RdbCRLF[GC_CRLFAlways] do begin
505 Parent:=CRLFPage.Surface;
506 Caption:='Use Windows style line endings';
507 Left:=ScaleX(4);
508 Top:=ScaleY(76);
509 Width:=ScaleX(329);
510 Height:=ScaleY(17);
511 Font.Style:=[fsBold];
512 TabOrder:=1;
513 Checked:=True;
514 end;
515 LblCRLFAlways:=TLabel.Create(CRLFPage);
516 with LblCRLFAlways do begin
517 Parent:=CRLFPage.Surface;
518 Caption:=
519 'Choose this if your source code uses a Carriage Return and a Line Feed' + #13 +
520 'character to end lines. This is the DOS convention; your checked-out files' + #13 +
521 'might not be handled gracefully by MSYS / Cygwin command line utilities.';
522 Left:=ScaleX(28);
523 Top:=ScaleY(100);
524 Width:=ScaleX(364);
525 Height:=ScaleY(47);
526 end;
528 // 3rd choice
529 RdbCRLF[GC_CRLFCommitAsIs]:=TRadioButton.Create(CRLFPage);
530 with RdbCRLF[GC_CRLFCommitAsIs] do begin
531 Parent:=CRLFPage.Surface;
532 Caption:='Commit line endings as they are';
533 Left:=ScaleX(4);
534 Top:=ScaleY(152);
535 Width:=ScaleX(329);
536 Height:=ScaleY(17);
537 Font.Style:=[fsBold];
538 TabOrder:=2;
539 Checked:=False;
540 end;
541 LblCRLFCommitAsIs:=TLabel.Create(CRLFPage);
542 with LblCRLFCommitAsIs do begin
543 Parent:=CRLFPage.Surface;
544 Caption:=
545 'Choose this if you know what you are doing and want to track the files with' + #13 +
546 'the line endings exactly as they appear in the files. This option might' + #13 +
547 'cause your projects to be hard to use on other platforms.';
548 Left:=ScaleX(28);
549 Top:=ScaleY(176);
550 Width:=ScaleX(364);
551 Height:=ScaleY(47);
552 end;
554 // Restore the setting chosen during a previous install.
555 Data:=GetPreviousData('CRLF Option','CRLFAlways');
556 if Data='LFOnly' then begin
557 RdbCRLF[GC_LFOnly].Checked:=True;
558 end else if Data='CRLFAlways' then begin
559 RdbCRLF[GC_CRLFAlways].Checked:=True;
560 end else if Data='CRLFCommitAsIs' then begin
561 RdbCRLF[GC_CRLFCommitAsIs].Checked:=True;
562 end;
563 end;
565 function NextButtonClick(CurPageID:Integer):Boolean;
566 begin
567 if (PuTTYPage=NIL) or (CurPageID<>PuTTYPage.ID) then begin
568 Result:=True;
569 Exit;
570 end;
572 Result:=RdbSSH[GS_OpenSSH].Checked or
573 (RdbSSH[GS_Plink].Checked and FileExists(EdtPlink.Text));
575 if not Result then begin
576 MsgBox('Please enter a valid path to (Tortoise)Plink.exe.',mbError,MB_OK);
577 end;
578 end;
580 procedure CurStepChanged(CurStep:TSetupStep);
582 AppDir,FileName,Cmd,Msg:string;
583 BuiltIns,EnvPath,EnvHome,EnvSSH:TArrayOfString;
584 Count,i:Longint;
585 IsNTFS:Boolean;
586 FindRec:TFindRec;
587 RootKey:Integer;
588 begin
589 if CurStep<>ssPostInstall then begin
590 Exit;
591 end;
593 AppDir:=ExpandConstant('{app}');
596 Create the built-ins
599 // Load the built-ins from a text file.
600 FileName:=ExpandConstant('{app}\{#emit APP_BUILTINS}');
601 if LoadStringsFromFile(FileName,BuiltIns) then begin
602 // Check if we are running on NTFS.
603 IsNTFS:=False;
604 if SetNTFSCompression(AppDir+'\bin\git.exe',true) then begin
605 IsNTFS:=SetNTFSCompression(AppDir+'\bin\git.exe',false);
606 end;
608 Count:=GetArrayLength(BuiltIns)-1;
610 // Delete those scripts from "bin" which have been replaced by built-ins in "libexec\git-core".
611 for i:=0 to Count do begin
612 FileName:=AppDir+'\bin\'+ChangeFileExt(ExtractFileName(BuiltIns[i]),'');
613 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
614 Log('Line {#emit __LINE__}: Unable to delete script "'+FileName+'", ignoring.');
615 end;
616 end;
618 // Map the built-ins to git.exe.
619 if IsNTFS then begin
620 Log('Line {#emit __LINE__}: Partition seems to be NTFS, trying to create built-in aliases as hard links.');
622 for i:=0 to Count do begin
623 FileName:=AppDir+'\'+BuiltIns[i];
625 // On non-NTFS partitions, create hard links.
626 if (FileExists(FileName) and (not DeleteFile(FileName))) or
627 (not CreateHardLink(FileName,AppDir+'\bin\git.exe',0)) then begin
628 Log('Line {#emit __LINE__}: Unable to create hard link "'+FileName+'", will try to copy files.');
629 IsNTFS:=False;
630 Break;
631 end;
632 end;
634 Log('Line {#emit __LINE__}: Successfully created built-in aliases as hard links.');
635 end;
637 // The fallback is to copy the files.
638 if not IsNTFS then begin
639 Log('Line {#emit __LINE__}: Trying to create built-in aliases as file copies.');
641 for i:=0 to Count do begin
642 FileName:=AppDir+'\'+BuiltIns[i];
644 // On non-NTFS partitions, copy simply the files (overwriting existing ones).
645 if not FileCopy(AppDir+'\bin\git.exe',FileName,false) then begin
646 Msg:='Line {#emit __LINE__}: Unable to create file copy "'+FileName+'".';
647 MsgBox(Msg,mbError,MB_OK);
648 Log(Msg);
649 // This is not a critical error, Git could basically be used without the
650 // aliases for built-ins, so we continue.
651 end;
652 end;
654 Log('Line {#emit __LINE__}: Successfully created built-in aliases as file copies.');
655 end;
657 // Delete any duplicate files in case we are updating from a non-libexec to a libexec directory layout.
658 if FindFirst(AppDir+'\libexec\git-core\*',FindRec) then begin
659 repeat
660 if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY)=0 then begin
661 FileName:=AppDir+'\bin\'+FindRec.name;
662 if (Pos(FindRec.name,'git.exe')<>1) and FileExists(FileName) and (not DeleteFile(FileName)) then begin
663 Log('Line {#emit __LINE__}: Unable to delete dupe "'+FileName+'", ignoring.');
664 end;
665 end;
666 until not FindNext(FindRec);
667 FindClose(FindRec);
668 end;
669 end else begin
670 Msg:='Line {#emit __LINE__}: Unable to read file "{#emit APP_BUILTINS}".';
671 MsgBox(Msg,mbError,MB_OK);
672 Log(Msg);
673 // This is not a critical error, Git could basically be used without the
674 // aliases for built-ins, so we continue.
675 end;
678 Adapt core.autocrlf
681 if RdbCRLF[GC_LFOnly].checked then begin
682 Cmd:='core.autoCRLF input';
683 end else if RdbCRLF[GC_CRLFAlways].checked then begin
684 Cmd:='core.autoCRLF true';
685 end else begin
686 Cmd:='core.autoCRLF false';
687 end;
688 if not Exec(AppDir + '\bin\git.exe', 'config -f gitconfig ' + Cmd,
689 AppDir + '\etc', SW_HIDE, ewWaitUntilTerminated, i) then begin
690 Msg:='Could not set CR/LF behavior: ' + Cmd;
691 MsgBox(Msg,mbError,MB_OK);
692 Log(Msg);
693 end;
696 Modify the environment
698 This must happen no later than ssPostInstall to make
699 "ChangesEnvironment=yes" not happend before the change!
702 FileName:=AppDir+'\setup.ini';
704 // Delete GIT_SSH and SVN_SSH if a previous installation set them (this is required for the GS_OpenSSH case).
705 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
706 if (GetArrayLength(EnvSSH)=1) and
707 (CompareStr(EnvSSH[0],GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
708 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
709 Msg:='Line {#emit __LINE__}: Unable to reset GIT_SSH prior to install.';
710 MsgBox(Msg,mbError,MB_OK);
711 Log(Msg);
712 // This is not a critical error, the user can probably fix it manually,
713 // so we continue.
714 end;
715 end;
717 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
718 if (GetArrayLength(EnvSSH)=1) and
719 (CompareStr(EnvSSH[0],GetIniString('Environment','SVN_SSH','',FileName))=0) then begin
720 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
721 Msg:='Line {#emit __LINE__}: Unable to reset SVN_SSH prior to install.';
722 MsgBox(Msg,mbError,MB_OK);
723 Log(Msg);
724 // This is not a critical error, the user can probably fix it manually,
725 // so we continue.
726 end;
727 end;
729 if (PuTTYPage<>NIL) and RdbSSH[GS_Plink].Checked then begin
730 SetArrayLength(EnvSSH,1);
731 EnvSSH[0]:=EdtPlink.Text;
733 // Set GIT_SSH as specified by the user.
734 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
735 Msg:='Line {#emit __LINE__}: Unable to set the GIT_SSH environment variable.';
736 MsgBox(Msg,mbError,MB_OK);
737 Log(Msg);
738 // This is not a critical error, the user can probably fix it manually,
739 // so we continue.
740 end;
742 // Mark that we have changed GIT_SSH by writing its value to a file.
743 if not SetIniString('Environment','GIT_SSH',EnvSSH[0],FileName) then begin
744 Msg:='Line {#emit __LINE__}: Unable to write to file "'+FileName+'".';
745 MsgBox(Msg,mbError,MB_OK);
746 Log(Msg);
747 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
748 // so we continue.
749 end;
751 // Set SVN_SSH as specified by the user, but with escaped backslashes and quotes.
752 StringChangeEx(EnvSSH[0],'\','\\',True);
753 EnvSSH[0]:=AddQuotes(EnvSSH[0]);
755 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
756 Msg:='Line {#emit __LINE__}: Unable to set the SVN_SSH environment variable.';
757 MsgBox(Msg,mbError,MB_OK);
758 Log(Msg);
759 // This is not a critical error, the user can probably fix it manually,
760 // so we continue.
761 end;
763 // Mark that we have changed SVN_SSH by writing its value to a file.
764 if not SetIniString('Environment','SVN_SSH',EnvSSH[0],FileName) then begin
765 Msg:='Line {#emit __LINE__}: Unable to write to file "'+FileName+'".';
766 MsgBox(Msg,mbError,MB_OK);
767 Log(Msg);
768 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
769 // so we continue.
770 end;
771 end;
773 // Get the current user's directories in PATH.
774 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
776 // First, remove the installation directory from PATH in any case.
777 for i:=0 to GetArrayLength(EnvPath)-1 do begin
778 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
779 EnvPath[i]:='';
780 end;
781 end;
783 // Delete HOME if a previous installation modified it.
784 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
785 if (GetArrayLength(EnvHome)=1) and
786 (CompareStr(EnvHome[0],GetIniString('Environment','HOME','',FileName))=0) then begin
787 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
788 Msg:='Line {#emit __LINE__}: Unable to reset HOME prior to install.';
789 MsgBox(Msg,mbError,MB_OK);
790 Log(Msg);
791 // This is not a critical error, the user can probably fix it manually,
792 // so we continue.
793 end;
794 end;
796 // Modify the PATH variable as requested by the user.
797 if RdbPath[GP_Cmd].Checked or RdbPath[GP_CmdTools].Checked then begin
798 i:=GetArrayLength(EnvPath);
799 SetArrayLength(EnvPath,i+1);
801 // List \cmd before \bin so \cmd has higher priority and programs in
802 // there will be called in favor of those in \bin.
803 EnvPath[i]:=ExpandConstant('{app}\cmd');
805 if RdbPath[GP_CmdTools].Checked then begin
806 SetArrayLength(EnvPath,i+2);
807 EnvPath[i+1]:=ExpandConstant('{app}\bin');
809 // Set HOME for the Windows Command Prompt, but only if it has not been set manually before.
810 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
811 i:=GetArrayLength(EnvHome);
812 if (i=0) or ((i=1) and (Length(EnvHome[0])=0)) then begin
813 SetArrayLength(EnvHome,1);
814 EnvHome[0]:=ExpandConstant('{%HOMEDRIVE}{%HOMEPATH}');
815 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,EnvHome) then begin
816 Msg:='Line {#emit __LINE__}: Unable to set the HOME environment variable.';
817 MsgBox(Msg,mbError,MB_OK);
818 Log(Msg);
819 // This is not a critical error, the user can probably fix it manually,
820 // so we continue.
821 end;
823 // Mark that we have changed HOME.
824 if not SetIniString('Environment','HOME',EnvHome[0],FileName) then begin
825 Msg:='Line {#emit __LINE__}: Unable to write to file "'+FileName+'".';
826 MsgBox(Msg,mbError,MB_OK);
827 Log(Msg);
828 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
829 // so we continue.
830 end;
831 end;
832 end;
833 end;
835 // Set the current user's PATH directories.
836 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
837 Msg:='Line {#emit __LINE__}: Unable to set the PATH environment variable.';
838 MsgBox(Msg,mbError,MB_OK);
839 Log(Msg);
840 // This is not a critical error, the user can probably fix it manually,
841 // so we continue.
842 end;
845 Create the Windows Explorer shell extensions
848 if IsAdminLoggedOn then begin
849 RootKey:=HKEY_LOCAL_MACHINE;
850 end else begin
851 RootKey:=HKEY_CURRENT_USER;
852 end;
854 if IsTaskSelected('shellextension') then begin
855 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell','','Git Ba&sh Here')) or
856 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','','wscript "'+AppDir+'\Git Bash.vbs" "%1"')) then begin
857 Msg:='Line {#emit __LINE__}: Unable to create "Git Bash Here" shell extension.';
858 MsgBox(Msg,mbError,MB_OK);
859 Log(Msg);
860 // This is not a critical error, the user can probably fix it manually,
861 // so we continue.
862 end;
863 end;
865 if IsTaskSelected('guiextension') then begin
866 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui','','Git &GUI Here')) or
867 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui\command','','"'+AppDir+'\bin\wish.exe" "'+AppDir+'\libexec\git-core\git-gui" "--working-dir" "%1"')) then begin
868 Msg:='Line {#emit __LINE__}: Unable to create "Git GUI Here" shell extension.';
869 MsgBox(Msg,mbError,MB_OK);
870 Log(Msg);
871 // This is not a critical error, the user can probably fix it manually,
872 // so we continue.
873 end;
874 end;
875 end;
877 procedure RegisterPreviousData(PreviousDataKey:Integer);
879 Data:string;
880 begin
881 // Git Path options.
882 Data:='';
883 if RdbPath[GP_BashOnly].Checked then begin
884 Data:='BashOnly';
885 end else if RdbPath[GP_Cmd].Checked then begin
886 Data:='Cmd';
887 end else if RdbPath[GP_CmdTools].Checked then begin
888 Data:='CmdTools';
889 end;
890 SetPreviousData(PreviousDataKey,'Path Option',Data);
892 // Git SSH options.
893 Data:='';
894 if (PuTTYPage=NIL) or RdbSSH[GS_OpenSSH].Checked then begin
895 Data:='OpenSSH';
896 end else if RdbSSH[GS_Plink].Checked then begin
897 Data:='Plink';
898 end;
899 SetPreviousData(PreviousDataKey,'SSH Option',Data);
901 // Git CR/LF options.
902 Data:='';
903 if RdbCRLF[GC_LFOnly].Checked then begin
904 Data:='LFOnly';
905 end else if RdbCRLF[GC_CRLFAlways].Checked then begin
906 Data:='CRLFAlways';
907 end else if RdbCRLF[GC_CRLFCommitAsIs].Checked then begin
908 Data:='CRLFCommitAsIs';
909 end;
910 SetPreviousData(PreviousDataKey,'CRLF Option',Data);
911 end;
914 Uninstaller code
917 function InitializeUninstall:Boolean;
919 FileName,NewName,Msg:string;
920 begin
921 FileName:=ExpandConstant('{app}\bin\ssh-agent.exe');
922 if FileExists(FileName) then begin
923 // Create a temporary copy of the file we try to delete.
924 NewName:=FileName+'.'+IntToStr(1000+Random(9000));
925 Result:=FileCopy(FileName,NewName,True) and DeleteFile(FileName);
927 if not Result then begin
928 Msg:='Line {#emit __LINE__}: Please stop all ssh-agent processes and run uninstall again.';
929 MsgBox(Msg,mbError,MB_OK);
930 Log(Msg);
932 // Clean-up the temporary copy (ignoring any errors).
933 DeleteFile(NewName);
934 end else begin
935 // Clean-up the temporary copy (ignoring any errors).
936 RenameFile(NewName,FileName);
937 end;
938 end else begin
939 Result:=True;
940 end;
941 end;
943 procedure CurUninstallStepChanged(CurUninstallStep:TUninstallStep);
945 AppDir,Command,Msg:string;
946 EnvPath,EnvHome,EnvSSH:TArrayOfString;
947 i:Longint;
948 RootKey:Integer;
949 begin
950 if CurUninstallStep<>usUninstall then begin
951 Exit;
952 end;
955 Modify the environment
957 This must happen no later than usUninstall to make
958 "ChangesEnvironment=yes" not happend before the change!
961 AppDir:=ExpandConstant('{app}');
962 Command:=AppDir+'\setup.ini';
964 // Delete the current user's GIT_SSH and SVN_SSH if we set it.
965 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
966 if (GetArrayLength(EnvSSH)=1) and
967 (CompareStr(EnvSSH[0],GetIniString('Environment','GIT_SSH','',Command))=0) then begin
968 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
969 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to GIT_SSH.';
970 MsgBox(Msg,mbError,MB_OK);
971 Log(Msg);
972 // This is not a critical error, the user can probably fix it manually,
973 // so we continue.
974 end;
975 end;
977 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
978 if (GetArrayLength(EnvSSH)=1) and
979 (CompareStr(EnvSSH[0],GetIniString('Environment','SVN_SSH','',Command))=0) then begin
980 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
981 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to SVN_SSH.';
982 MsgBox(Msg,mbError,MB_OK);
983 Log(Msg);
984 // This is not a critical error, the user can probably fix it manually,
985 // so we continue.
986 end;
987 end;
989 // Get the current user's directories in PATH.
990 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
992 // Remove the installation directory from PATH in any case, even if it
993 // was not added by the installer.
994 for i:=0 to GetArrayLength(EnvPath)-1 do begin
995 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
996 EnvPath[i]:='';
997 end;
998 end;
1000 // Reset the current user's directories in PATH.
1001 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
1002 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to PATH.';
1003 MsgBox(Msg,mbError,MB_OK);
1004 Log(Msg);
1005 // This is not a critical error, the user can probably fix it manually,
1006 // so we continue.
1007 end;
1009 // Reset the current user's HOME if we modified it.
1010 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
1011 if (GetArrayLength(EnvHome)=1) and
1012 (CompareStr(EnvHome[0],GetIniString('Environment','HOME','',Command))=0) then begin
1013 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
1014 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to HOME.';
1015 MsgBox(Msg,mbError,MB_OK);
1016 Log(Msg);
1017 // This is not a critical error, the user can probably fix it manually,
1018 // so we continue.
1019 end;
1020 end;
1022 if FileExists(Command) and (not DeleteFile(Command)) then begin
1023 Msg:='Line {#emit __LINE__}: Unable to delete file "'+Command+'".';
1024 MsgBox(Msg,mbError,MB_OK);
1025 Log(Msg);
1026 // This is not a critical error, the user can probably fix it manually,
1027 // so we continue.
1028 end;
1031 Delete the Windows Explorer shell extensions
1034 if IsAdminLoggedOn then begin
1035 RootKey:=HKEY_LOCAL_MACHINE;
1036 end else begin
1037 RootKey:=HKEY_CURRENT_USER;
1038 end;
1040 Command:='';
1041 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','',Command);
1042 if Pos(AppDir,Command)>0 then begin
1043 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell') then begin
1044 Msg:='Line {#emit __LINE__}: Unable to remove "Git Bash Here" shell extension.';
1045 MsgBox(Msg,mbError,MB_OK);
1046 Log(Msg);
1047 // This is not a critical error, the user can probably fix it manually,
1048 // so we continue.
1049 end;
1050 end;
1052 Command:='';
1053 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui\command','',Command);
1054 if Pos(AppDir,Command)>0 then begin
1055 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui') then begin
1056 Msg:='Line {#emit __LINE__}: Unable to remove "Git GUI Here" shell extension.';
1057 MsgBox(Msg,mbError,MB_OK);
1058 Log(Msg);
1059 // This is not a critical error, the user can probably fix it manually,
1060 // so we continue.
1061 end;
1062 end;
1063 end;