Installer: Move msysGit unspecific helper functions to a separate file
[msysgit.git] / share / WinGit / install.iss
blob94b6ae6218a4c33f1e1fb29e388cec08076576b5
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
39 Name: consolefont; Description: Use TrueType font (required for proper character encoding); GroupDescription: Console properties:; Flags: checkedonce
41 [Files]
42 Source: *; DestDir: {app}; Excludes: \*.bmp, gpl-2.0.rtf, \*.iss, \tmp.*, \bin\*install*; Flags: recursesubdirs replacesameversion
43 Source: ReleaseNotes.rtf; DestDir: {app}; Flags: isreadme replacesameversion
45 [Icons]
46 Name: {group}\Git GUI; Filename: {app}\bin\wish.exe; Parameters: """{app}\libexec\git-core\git-gui"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico
47 Name: {group}\Git Bash; Filename: {syswow64}\cmd.exe; Parameters: "/c """"{app}\bin\sh.exe"" --login -i"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico
48 Name: {group}\Uninstall Git; Filename: {uninstallexe}
49 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
50 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
52 ; 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.
53 Name: {app}\Git Bash; Filename: {syswow64}\cmd.exe; Parameters: "/c """"{app}\bin\sh.exe"" --login -i"""; IconFilename: {app}\etc\git.ico; Tasks: shellextension
55 [Messages]
56 BeveledLabel={#emit APP_URL}
57 SetupAppTitle={#emit APP_NAME} Setup
58 SetupWindowTitle={#emit APP_NAME} Setup
60 [Registry]
61 Root: HKCU; Subkey: Console; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Tasks: consolefont
62 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Tasks: consolefont
63 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Tasks: consolefont
64 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Tasks: consolefont
66 Root: HKCU; Subkey: Console\Git Bash; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: createvalueifdoesntexist
67 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Flags: createvalueifdoesntexist
68 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Flags: createvalueifdoesntexist
69 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Flags: createvalueifdoesntexist
71 [UninstallDelete]
72 Type: files; Name: {app}\bin\git-*.exe
73 Type: files; Name: {app}\libexec\git-core\git-*.exe
74 Type: dirifempty; Name: {app}\home\{username}
75 Type: dirifempty; Name: {app}\home
77 [Code]
78 #include "helpers.inc.iss"
80 function CreateHardLink(lpFileName,lpExistingFileName:string;lpSecurityAttributes:Integer):Boolean;
81 external 'CreateHardLinkA@Kernel32.dll';
83 const
84 TortoiseSVNInstallKey='SOFTWARE\TortoiseSVN';
85 TortoiseCVSUninstallKey='SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\TortoiseCVS_is1';
86 PuTTYUninstallKey='SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\PuTTY_is1';
87 PuTTYPrivateKeyAssoc='PuTTYPrivateKey\shell\open\command';
89 function GetPuTTYLocation:string;
90 begin
91 // Prefer TortoisePlink over vanilla Plink for its GUI dialog to accept host keys.
92 if (IsWin64 and RegQueryStringValue(HKEY_LOCAL_MACHINE_64,TortoiseSVNInstallKey,'Directory',Result)) or
93 RegQueryStringValue(HKEY_LOCAL_MACHINE_32,TortoiseSVNInstallKey,'Directory',Result) then begin
94 // C:\Program Files\TortoiseSVN\
95 Result:=Result+'bin\';
96 // C:\Program Files\TortoiseSVN\bin\
97 end else begin
98 if not (IsWin64 and RegQueryStringValue(HKEY_LOCAL_MACHINE_64,TortoiseCVSUninstallKey,'InstallLocation',Result)) then begin
99 RegQueryStringValue(HKEY_LOCAL_MACHINE_32,TortoiseCVSUninstallKey,'InstallLocation',Result);
100 end;
101 // C:\Program Files\TortoiseCVS\
102 end;
104 if DirExists(Result) then begin
105 Result:=Result+'TortoisePlink.exe'
106 Exit;
107 end;
109 if not RegQueryStringValue(HKEY_LOCAL_MACHINE,PuTTYUninstallKey,'InstallLocation',Result) then begin
110 if RegQueryStringValue(HKEY_CLASSES_ROOT,PuTTYPrivateKeyAssoc,'',Result) then begin
111 // "C:\Program Files\PuTTY\pageant.exe" "%1"
112 Result:=RemoveQuotes(Result);
113 // C:\Program Files\PuTTY\pageant.exe" "%1
114 Result:=ExtractFilePath(Result);
115 end;
116 end;
117 // C:\Program Files\PuTTY\
119 if not DirExists(Result) then begin
120 // Guess something.
121 Result:=ExpandConstant('{pf}\PuTTY\');
122 end;
124 Result:=Result+'plink.exe'
125 end;
127 const
128 // Git Path options.
129 GP_BashOnly = 1;
130 GP_Cmd = 2;
131 GP_CmdTools = 3;
133 // Git SSH options.
134 GS_OpenSSH = 1;
135 GS_Plink = 2;
137 // Git line ending conversion options.
138 GC_LFOnly = 1;
139 GC_CRLFAlways = 2;
140 GC_CRLFCommitAsIs = 3;
143 // Wizard page and variables for the Path options.
144 PathPage:TWizardPage;
145 RdbPath:array[GP_BashOnly..GP_CmdTools] of TRadioButton;
147 // Wizard page and variables for the SSH options.
148 PuTTYPage:TWizardPage;
149 RdbSSH:array[GS_OpenSSH..GS_Plink] of TRadioButton;
150 EdtPlink:TEdit;
152 // Wizard page and variables for the line ending conversion options.
153 CRLFPage:TWizardPage;
154 RdbCRLF:array[GC_LFOnly..GC_CRLFCommitAsIs] of TRadioButton;
156 procedure BrowseForPuTTYFolder(Sender:TObject);
158 Path:string;
159 begin
160 Path:=ExtractFilePath(EdtPlink.Text);
161 BrowseForFolder('Please select the PuTTY folder:',Path,False);
162 if FileExists(Path+'\TortoisePlink.exe') then begin
163 EdtPlink.Text:=Path+'\TortoisePlink.exe';
164 RdbSSH[GS_Plink].Checked:=True;
165 end else if FileExists(Path+'\plink.exe') then begin
166 EdtPlink.Text:=Path+'\plink.exe';
167 RdbSSH[GS_Plink].Checked:=True;
168 end else begin
169 MsgBox('Please enter a valid path to "TortoisePlink.exe" or "plink.exe".',mbError,MB_OK);
170 end;
171 end;
174 Installer code
177 procedure InitializeWizard;
179 PrevPageID:Integer;
180 LblGitBash,LblGitCmd,LblGitCmdTools,LblGitCmdToolsWarn:TLabel;
181 LblOpenSSH,LblPlink:TLabel;
182 PuTTYSessions:TArrayOfString;
183 LblLFOnly,LblCRLFAlways,LblCRLFCommitAsIs:TLabel;
184 BtnPlink:TButton;
185 Data:String;
186 begin
187 PrevPageID:=wpSelectTasks;
190 * Create a custom page for modifying the environment.
193 PathPage:=CreateCustomPage(
194 PrevPageID,
195 'Adjusting your PATH environment',
196 'How would you like to use Git from the command line?'
198 PrevPageID:=PathPage.ID;
200 // 1st choice
201 RdbPath[GP_BashOnly]:=TRadioButton.Create(PathPage);
202 with RdbPath[GP_BashOnly] do begin
203 Parent:=PathPage.Surface;
204 Caption:='Use Git Bash only';
205 Left:=ScaleX(4);
206 Top:=ScaleY(8);
207 Width:=ScaleX(129);
208 Height:=ScaleY(17);
209 Font.Style:=[fsBold];
210 TabOrder:=0;
211 Checked:=True;
212 end;
213 LblGitBash:=TLabel.Create(PathPage);
214 with LblGitBash do begin
215 Parent:=PathPage.Surface;
216 Caption:=
217 'This is the most conservative choice if you are concerned about the stability' + #13 +
218 'of your system. Your PATH will not be modified.';
219 Left:=ScaleX(28);
220 Top:=ScaleY(32);
221 Width:=ScaleX(405);
222 Height:=ScaleY(26);
223 end;
225 // 2nd choice
226 RdbPath[GP_Cmd]:=TRadioButton.Create(PathPage);
227 with RdbPath[GP_Cmd] do begin
228 Parent:=PathPage.Surface;
229 Caption:='Run Git from the Windows Command Prompt';
230 Left:=ScaleX(4);
231 Top:=ScaleY(76);
232 Width:=ScaleX(281);
233 Height:=ScaleY(17);
234 Font.Style:=[fsBold];
235 TabOrder:=1;
236 end;
237 LblGitCmd:=TLabel.Create(PathPage);
238 with LblGitCmd do begin
239 Parent:=PathPage.Surface;
240 Caption:=
241 'This option is considered safe and no conflicts with other tools are known.' + #13 +
242 'Only Git will be added to your PATH. Use this option if you want to use Git' + #13 +
243 'from a Cygwin Prompt (make sure to not have Cygwin''s Git installed).';
244 Left:=ScaleX(28);
245 Top:=ScaleY(100);
246 Width:=ScaleX(405);
247 Height:=ScaleY(39);
248 end;
250 // 3rd choice
251 RdbPath[GP_CmdTools]:=TRadioButton.Create(PathPage);
252 with RdbPath[GP_CmdTools] do begin
253 Parent:=PathPage.Surface;
254 Caption:='Run Git and included Unix tools from the Windows Command Prompt';
255 Left:=ScaleX(4);
256 Top:=ScaleY(152);
257 Width:=ScaleX(405);
258 Height:=ScaleY(17);
259 Font.Style:=[fsBold];
260 TabOrder:=2;
261 end;
262 LblGitCmdTools:=TLabel.Create(PathPage);
263 with LblGitCmdTools do begin
264 Parent:=PathPage.Surface;
265 Caption:='Both Git and its accompanying Unix tools will be added to your PATH.';
266 Left:=ScaleX(28);
267 Top:=ScaleY(176);
268 Width:=ScaleX(405);
269 Height:=ScaleY(13);
270 end;
271 LblGitCmdToolsWarn:=TLabel.Create(PathPage);
272 with LblGitCmdToolsWarn do begin
273 Parent:=PathPage.Surface;
274 Caption:=
275 'Warning: This will override Windows tools like find.exe and' + #13 +
276 'sort.exe. Select this option only if you understand the implications.';
277 Left:=ScaleX(28);
278 Top:=ScaleY(192);
279 Width:=ScaleX(376);
280 Height:=ScaleY(26);
281 Font.Color:=255;
282 Font.Style:=[fsBold];
283 end;
285 // Restore the setting chosen during a previous install.
286 Data:=GetPreviousData('Path Option','BashOnly');
287 if Data='BashOnly' then begin
288 RdbPath[GP_BashOnly].Checked:=True;
289 end else if Data='Cmd' then begin
290 RdbPath[GP_Cmd].Checked:=True;
291 end else if Data='CmdTools' then begin
292 RdbPath[GP_CmdTools].Checked:=True;
293 end;
296 * Create a custom page for using (Tortoise)Plink instead of OpenSSH
297 * if at least one PuTTY session is found in the Registry.
300 if RegGetSubkeyNames(HKEY_CURRENT_USER,'Software\SimonTatham\PuTTY\Sessions',PuTTYSessions) and (GetArrayLength(PuTTYSessions)>0) then begin
301 PuTTYPage:=CreateCustomPage(
302 PrevPageID,
303 'Choosing the SSH executable',
304 'Which Secure Shell client program would you like Git to use?'
306 PrevPageID:=PuTTYPage.ID;
308 // 1st choice
309 RdbSSH[GS_OpenSSH]:=TRadioButton.Create(PuTTYPage);
310 with RdbSSH[GS_OpenSSH] do begin
311 Parent:=PuTTYPage.Surface;
312 Caption:='Use OpenSSH';
313 Left:=ScaleX(4);
314 Top:=ScaleY(8);
315 Width:=ScaleX(129);
316 Height:=ScaleY(17);
317 Font.Style:=[fsBold];
318 TabOrder:=0;
319 Checked:=True;
320 end;
321 LblOpenSSH:=TLabel.Create(PuTTYPage);
322 with LblOpenSSH do begin
323 Parent:=PuTTYPage.Surface;
324 Caption:=
325 'This uses ssh.exe that comes with Git. The GIT_SSH and SVN_SSH' + #13 +
326 'environment variables will not be modified.';
327 Left:=ScaleX(28);
328 Top:=ScaleY(32);
329 Width:=ScaleX(324);
330 Height:=ScaleY(26);
331 end;
333 // 2nd choice
334 RdbSSH[GS_Plink]:=TRadioButton.Create(PuTTYPage);
335 with RdbSSH[GS_Plink] do begin
336 Parent:=PuTTYPage.Surface;
337 Caption:='Use (Tortoise)Plink';
338 Left:=ScaleX(4);
339 Top:=ScaleY(76);
340 Width:=ScaleX(281);
341 Height:=ScaleY(17);
342 Font.Style:=[fsBold];
343 TabOrder:=1;
344 end;
345 LblPlink:=TLabel.Create(PuTTYPage);
346 with LblPlink do begin
347 Parent:=PuTTYPage.Surface;
348 Caption:=
349 'PuTTY sessions were found in your Registry. You may specify the path' + #13 +
350 'to an existing copy of (Tortoise)Plink.exe from the TortoiseSVN/CVS' + #13 +
351 'or PuTTY applications. The GIT_SSH and SVN_SSH environment' + #13 +
352 'variables will be adjusted to point to the following executable:';
353 Left:=ScaleX(28);
354 Top:=ScaleY(100);
355 Width:=ScaleX(340);
356 Height:=ScaleY(52);
357 end;
358 EdtPlink:=TEdit.Create(PuTTYPage);
359 with EdtPlink do begin
360 Parent:=PuTTYPage.Surface;
361 Text:=GetPuTTYLocation;
362 if not FileExists(Text) then begin
363 Text:='';
364 end;
365 Left:=ScaleX(28);
366 Top:=ScaleY(161);
367 Width:=ScaleX(316);
368 Height:=ScaleY(13);
369 end;
370 BtnPlink:=TButton.Create(PuTTYPage);
371 with BtnPlink do begin
372 Parent:=PuTTYPage.Surface;
373 Caption:='...';
374 OnClick:=@BrowseForPuTTYFolder;
375 Left:=ScaleX(348);
376 Top:=ScaleY(161);
377 Width:=ScaleX(21);
378 Height:=ScaleY(21);
379 end;
381 // Restore the setting chosen during a previous install.
382 Data:=GetPreviousData('SSH Option','OpenSSH');
383 if Data='OpenSSH' then begin
384 RdbSSH[GS_OpenSSH].Checked:=True;
385 end else if Data='Plink' then begin
386 RdbSSH[GS_Plink].Checked:=True;
387 end;
388 end else begin
389 PuTTYPage:=NIL;
390 end;
393 * Create a custom page for the core.autocrlf setting.
396 CRLFPage:=CreateCustomPage(
397 PrevPageID,
398 'Configuring the line ending conversions',
399 'How should Git treat line endings in text files?'
401 PrevPageID:=CRLFPage.ID;
403 // 1st choice
404 RdbCRLF[GC_CRLFAlways]:=TRadioButton.Create(CRLFPage);
405 with RdbCRLF[GC_CRLFAlways] do begin
406 Parent:=CRLFPage.Surface;
407 Caption:='Checkout Windows-style, commit Unix-style line endings';
408 Left:=ScaleX(4);
409 Top:=ScaleY(8);
410 Width:=ScaleX(340);
411 Height:=ScaleY(17);
412 Font.Style:=[fsBold];
413 TabOrder:=0;
414 Checked:=True;
415 end;
416 LblCRLFAlways:=TLabel.Create(CRLFPage);
417 with LblCRLFAlways do begin
418 Parent:=CRLFPage.Surface;
419 Caption:=
420 'Git will convert LF to CRLF when checking out text files. When committing' + #13 +
421 'text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
422 'this is the recommended setting on Windows ("core.autocrlf" is set to "true").';
423 Left:=ScaleX(28);
424 Top:=ScaleY(32);
425 Width:=ScaleX(372);
426 Height:=ScaleY(47);
427 end;
429 // 2nd choice
430 RdbCRLF[GC_LFOnly]:=TRadioButton.Create(CRLFPage);
431 with RdbCRLF[GC_LFOnly] do begin
432 Parent:=CRLFPage.Surface;
433 Caption:='Checkout as-is, commit Unix-style line endings';
434 Left:=ScaleX(4);
435 Top:=ScaleY(80);
436 Width:=ScaleX(340);
437 Height:=ScaleY(17);
438 Font.Style:=[fsBold];
439 TabOrder:=1;
440 Checked:=False;
441 end;
442 LblLFOnly:=TLabel.Create(CRLFPage);
443 with LblLFOnly do begin
444 Parent:=CRLFPage.Surface;
445 Caption:=
446 'Git will not perform any conversion when checking out text files. When' + #13 +
447 'committing text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
448 'this is the recommended setting on Unix ("core.autocrlf" is set to "input").';
449 Left:=ScaleX(28);
450 Top:=ScaleY(104);
451 Width:=ScaleX(372);
452 Height:=ScaleY(47);
453 end;
455 // 3rd choice
456 RdbCRLF[GC_CRLFCommitAsIs]:=TRadioButton.Create(CRLFPage);
457 with RdbCRLF[GC_CRLFCommitAsIs] do begin
458 Parent:=CRLFPage.Surface;
459 Caption:='Checkout as-is, commit as-is';
460 Left:=ScaleX(4);
461 Top:=ScaleY(152);
462 Width:=ScaleX(340);
463 Height:=ScaleY(17);
464 Font.Style:=[fsBold];
465 TabOrder:=2;
466 Checked:=False;
467 end;
468 LblCRLFCommitAsIs:=TLabel.Create(CRLFPage);
469 with LblCRLFCommitAsIs do begin
470 Parent:=CRLFPage.Surface;
471 Caption:=
472 'Git will not perform any conversions when checking out or committing' + #13 +
473 'text files. Choosing this option is not recommended for cross-platform' + #13 +
474 'projects ("core.autocrlf" is set to "false").';
475 Left:=ScaleX(28);
476 Top:=ScaleY(176);
477 Width:=ScaleX(372);
478 Height:=ScaleY(47);
479 end;
481 // Restore the setting chosen during a previous install.
482 Data:=GetPreviousData('CRLF Option','CRLFAlways');
483 if Data='LFOnly' then begin
484 RdbCRLF[GC_LFOnly].Checked:=True;
485 end else if Data='CRLFAlways' then begin
486 RdbCRLF[GC_CRLFAlways].Checked:=True;
487 end else if Data='CRLFCommitAsIs' then begin
488 RdbCRLF[GC_CRLFCommitAsIs].Checked:=True;
489 end;
490 end;
492 function NextButtonClick(CurPageID:Integer):Boolean;
493 begin
494 if (PuTTYPage=NIL) or (CurPageID<>PuTTYPage.ID) then begin
495 Result:=True;
496 Exit;
497 end;
499 Result:=RdbSSH[GS_OpenSSH].Checked or
500 (RdbSSH[GS_Plink].Checked and FileExists(EdtPlink.Text));
502 if not Result then begin
503 MsgBox('Please enter a valid path to (Tortoise)Plink.exe.',mbError,MB_OK);
504 end;
505 end;
507 procedure CurStepChanged(CurStep:TSetupStep);
509 AppDir,FileName,Cmd,Msg:string;
510 BuiltIns,EnvPath,EnvHome,EnvSSH:TArrayOfString;
511 Count,i:Longint;
512 IsNTFS:Boolean;
513 FindRec:TFindRec;
514 RootKey:Integer;
515 begin
516 if CurStep<>ssPostInstall then begin
517 Exit;
518 end;
520 AppDir:=ExpandConstant('{app}');
523 Create the built-ins
526 // Load the built-ins from a text file.
527 FileName:=ExpandConstant('{app}\{#emit APP_BUILTINS}');
528 if LoadStringsFromFile(FileName,BuiltIns) then begin
529 // Check if we are running on NTFS.
530 IsNTFS:=False;
531 if SetNTFSCompression(AppDir+'\bin\git.exe',true) then begin
532 IsNTFS:=SetNTFSCompression(AppDir+'\bin\git.exe',false);
533 end;
535 Count:=GetArrayLength(BuiltIns)-1;
537 // Delete those scripts from "bin" which have been replaced by built-ins in "libexec\git-core".
538 for i:=0 to Count do begin
539 FileName:=AppDir+'\bin\'+ChangeFileExt(ExtractFileName(BuiltIns[i]),'');
540 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
541 Log('Line {#emit __LINE__}: Unable to delete script "'+FileName+'", ignoring.');
542 end;
543 end;
545 // Map the built-ins to git.exe.
546 if IsNTFS then begin
547 Log('Line {#emit __LINE__}: Partition seems to be NTFS, trying to create built-in aliases as hard links.');
549 for i:=0 to Count do begin
550 FileName:=AppDir+'\'+BuiltIns[i];
552 // On non-NTFS partitions, create hard links.
553 if (FileExists(FileName) and (not DeleteFile(FileName))) or
554 (not CreateHardLink(FileName,AppDir+'\bin\git.exe',0)) then begin
555 Log('Line {#emit __LINE__}: Unable to create hard link "'+FileName+'", will try to copy files.');
556 IsNTFS:=False;
557 Break;
558 end;
559 end;
561 Log('Line {#emit __LINE__}: Successfully created built-in aliases as hard links.');
562 end;
564 // The fallback is to copy the files.
565 if not IsNTFS then begin
566 Log('Line {#emit __LINE__}: Trying to create built-in aliases as file copies.');
568 for i:=0 to Count do begin
569 FileName:=AppDir+'\'+BuiltIns[i];
571 // On non-NTFS partitions, copy simply the files (overwriting existing ones).
572 if not FileCopy(AppDir+'\bin\git.exe',FileName,false) then begin
573 Msg:='Line {#emit __LINE__}: Unable to create file copy "'+FileName+'".';
574 MsgBox(Msg,mbError,MB_OK);
575 Log(Msg);
576 // This is not a critical error, Git could basically be used without the
577 // aliases for built-ins, so we continue.
578 end;
579 end;
581 Log('Line {#emit __LINE__}: Successfully created built-in aliases as file copies.');
582 end;
584 // Delete any duplicate files in case we are updating from a non-libexec to a libexec directory layout.
585 if FindFirst(AppDir+'\libexec\git-core\*',FindRec) then begin
586 repeat
587 if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY)=0 then begin
588 FileName:=AppDir+'\bin\'+FindRec.name;
589 if (Pos(FindRec.name,'git.exe')<>1) and FileExists(FileName) and (not DeleteFile(FileName)) then begin
590 Log('Line {#emit __LINE__}: Unable to delete dupe "'+FileName+'", ignoring.');
591 end;
592 end;
593 until not FindNext(FindRec);
594 FindClose(FindRec);
595 end;
596 end else begin
597 Msg:='Line {#emit __LINE__}: Unable to read file "{#emit APP_BUILTINS}".';
598 MsgBox(Msg,mbError,MB_OK);
599 Log(Msg);
600 // This is not a critical error, Git could basically be used without the
601 // aliases for built-ins, so we continue.
602 end;
605 Adapt core.autocrlf
608 if RdbCRLF[GC_LFOnly].checked then begin
609 Cmd:='core.autocrlf input';
610 end else if RdbCRLF[GC_CRLFAlways].checked then begin
611 Cmd:='core.autocrlf true';
612 end else begin
613 Cmd:='core.autocrlf false';
614 end;
615 if not Exec(AppDir + '\bin\git.exe', 'config -f gitconfig ' + Cmd,
616 AppDir + '\etc', SW_HIDE, ewWaitUntilTerminated, i) then begin
617 Msg:='Unable to configure the line ending conversion: ' + Cmd;
618 MsgBox(Msg,mbError,MB_OK);
619 Log(Msg);
620 end;
623 Modify the environment
625 This must happen no later than ssPostInstall to make
626 "ChangesEnvironment=yes" not happend before the change!
629 FileName:=AppDir+'\setup.ini';
631 // Delete GIT_SSH and SVN_SSH if a previous installation set them (this is required for the GS_OpenSSH case).
632 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
633 if (GetArrayLength(EnvSSH)=1) and
634 (CompareStr(EnvSSH[0],GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
635 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
636 Msg:='Line {#emit __LINE__}: Unable to reset GIT_SSH prior to install.';
637 MsgBox(Msg,mbError,MB_OK);
638 Log(Msg);
639 // This is not a critical error, the user can probably fix it manually,
640 // so we continue.
641 end;
642 end;
644 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
645 if (GetArrayLength(EnvSSH)=1) and
646 (CompareStr(EnvSSH[0],GetIniString('Environment','SVN_SSH','',FileName))=0) then begin
647 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
648 Msg:='Line {#emit __LINE__}: Unable to reset SVN_SSH prior to install.';
649 MsgBox(Msg,mbError,MB_OK);
650 Log(Msg);
651 // This is not a critical error, the user can probably fix it manually,
652 // so we continue.
653 end;
654 end;
656 if (PuTTYPage<>NIL) and RdbSSH[GS_Plink].Checked then begin
657 SetArrayLength(EnvSSH,1);
658 EnvSSH[0]:=EdtPlink.Text;
660 // Set GIT_SSH as specified by the user.
661 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
662 Msg:='Line {#emit __LINE__}: Unable to set the GIT_SSH environment variable.';
663 MsgBox(Msg,mbError,MB_OK);
664 Log(Msg);
665 // This is not a critical error, the user can probably fix it manually,
666 // so we continue.
667 end;
669 // Mark that we have changed GIT_SSH by writing its value to a file.
670 if not SetIniString('Environment','GIT_SSH',EnvSSH[0],FileName) then begin
671 Msg:='Line {#emit __LINE__}: Unable to write to file "'+FileName+'".';
672 MsgBox(Msg,mbError,MB_OK);
673 Log(Msg);
674 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
675 // so we continue.
676 end;
678 // Set SVN_SSH as specified by the user, but with escaped backslashes and quotes.
679 StringChangeEx(EnvSSH[0],'\','\\',True);
680 EnvSSH[0]:=AddQuotes(EnvSSH[0]);
682 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
683 Msg:='Line {#emit __LINE__}: Unable to set the SVN_SSH environment variable.';
684 MsgBox(Msg,mbError,MB_OK);
685 Log(Msg);
686 // This is not a critical error, the user can probably fix it manually,
687 // so we continue.
688 end;
690 // Mark that we have changed SVN_SSH by writing its value to a file.
691 if not SetIniString('Environment','SVN_SSH',EnvSSH[0],FileName) then begin
692 Msg:='Line {#emit __LINE__}: Unable to write to file "'+FileName+'".';
693 MsgBox(Msg,mbError,MB_OK);
694 Log(Msg);
695 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
696 // so we continue.
697 end;
698 end;
700 // Get the current user's directories in PATH.
701 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
703 // First, remove the installation directory from PATH in any case.
704 for i:=0 to GetArrayLength(EnvPath)-1 do begin
705 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
706 EnvPath[i]:='';
707 end;
708 end;
710 // Delete HOME if a previous installation modified it.
711 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
712 if (GetArrayLength(EnvHome)=1) and
713 (CompareStr(EnvHome[0],GetIniString('Environment','HOME','',FileName))=0) then begin
714 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
715 Msg:='Line {#emit __LINE__}: Unable to reset HOME prior to install.';
716 MsgBox(Msg,mbError,MB_OK);
717 Log(Msg);
718 // This is not a critical error, the user can probably fix it manually,
719 // so we continue.
720 end;
721 end;
723 // Modify the PATH variable as requested by the user.
724 if RdbPath[GP_Cmd].Checked or RdbPath[GP_CmdTools].Checked then begin
725 i:=GetArrayLength(EnvPath);
726 SetArrayLength(EnvPath,i+1);
728 // List \cmd before \bin so \cmd has higher priority and programs in
729 // there will be called in favor of those in \bin.
730 EnvPath[i]:=ExpandConstant('{app}\cmd');
732 if RdbPath[GP_CmdTools].Checked then begin
733 SetArrayLength(EnvPath,i+2);
734 EnvPath[i+1]:=ExpandConstant('{app}\bin');
736 // Set HOME for the Windows Command Prompt, but only if it has not been set manually before.
737 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
738 i:=GetArrayLength(EnvHome);
739 if (i=0) or ((i=1) and (Length(EnvHome[0])=0)) then begin
740 SetArrayLength(EnvHome,1);
741 EnvHome[0]:=ExpandConstant('{%HOMEDRIVE}{%HOMEPATH}');
742 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,EnvHome) then begin
743 Msg:='Line {#emit __LINE__}: Unable to set the HOME environment variable.';
744 MsgBox(Msg,mbError,MB_OK);
745 Log(Msg);
746 // This is not a critical error, the user can probably fix it manually,
747 // so we continue.
748 end;
750 // Mark that we have changed HOME.
751 if not SetIniString('Environment','HOME',EnvHome[0],FileName) then begin
752 Msg:='Line {#emit __LINE__}: Unable to write to file "'+FileName+'".';
753 MsgBox(Msg,mbError,MB_OK);
754 Log(Msg);
755 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
756 // so we continue.
757 end;
758 end;
759 end;
760 end;
762 // Set the current user's PATH directories.
763 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
764 Msg:='Line {#emit __LINE__}: Unable to set the PATH environment variable.';
765 MsgBox(Msg,mbError,MB_OK);
766 Log(Msg);
767 // This is not a critical error, the user can probably fix it manually,
768 // so we continue.
769 end;
772 Create the Windows Explorer shell extensions
775 if IsAdminLoggedOn then begin
776 RootKey:=HKEY_LOCAL_MACHINE;
777 end else begin
778 RootKey:=HKEY_CURRENT_USER;
779 end;
781 if IsTaskSelected('shellextension') then begin
782 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell','','Git Ba&sh Here')) or
783 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','','wscript "'+AppDir+'\Git Bash.vbs" "%1"')) then begin
784 Msg:='Line {#emit __LINE__}: Unable to create "Git Bash Here" shell extension.';
785 MsgBox(Msg,mbError,MB_OK);
786 Log(Msg);
787 // This is not a critical error, the user can probably fix it manually,
788 // so we continue.
789 end;
790 end;
792 if IsTaskSelected('guiextension') then begin
793 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui','','Git &GUI Here')) or
794 (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
795 Msg:='Line {#emit __LINE__}: Unable to create "Git GUI Here" shell extension.';
796 MsgBox(Msg,mbError,MB_OK);
797 Log(Msg);
798 // This is not a critical error, the user can probably fix it manually,
799 // so we continue.
800 end;
801 end;
802 end;
804 procedure RegisterPreviousData(PreviousDataKey:Integer);
806 Data:string;
807 begin
808 // Git Path options.
809 Data:='';
810 if RdbPath[GP_BashOnly].Checked then begin
811 Data:='BashOnly';
812 end else if RdbPath[GP_Cmd].Checked then begin
813 Data:='Cmd';
814 end else if RdbPath[GP_CmdTools].Checked then begin
815 Data:='CmdTools';
816 end;
817 SetPreviousData(PreviousDataKey,'Path Option',Data);
819 // Git SSH options.
820 Data:='';
821 if (PuTTYPage=NIL) or RdbSSH[GS_OpenSSH].Checked then begin
822 Data:='OpenSSH';
823 end else if RdbSSH[GS_Plink].Checked then begin
824 Data:='Plink';
825 end;
826 SetPreviousData(PreviousDataKey,'SSH Option',Data);
828 // Line ending conversion options.
829 Data:='';
830 if RdbCRLF[GC_LFOnly].Checked then begin
831 Data:='LFOnly';
832 end else if RdbCRLF[GC_CRLFAlways].Checked then begin
833 Data:='CRLFAlways';
834 end else if RdbCRLF[GC_CRLFCommitAsIs].Checked then begin
835 Data:='CRLFCommitAsIs';
836 end;
837 SetPreviousData(PreviousDataKey,'CRLF Option',Data);
838 end;
841 Uninstaller code
844 function InitializeUninstall:Boolean;
846 FileName,NewName,Msg:string;
847 begin
848 FileName:=ExpandConstant('{app}\bin\ssh-agent.exe');
849 if FileExists(FileName) then begin
850 // Create a temporary copy of the file we try to delete.
851 NewName:=FileName+'.'+IntToStr(1000+Random(9000));
852 Result:=FileCopy(FileName,NewName,True) and DeleteFile(FileName);
854 if not Result then begin
855 Msg:='Line {#emit __LINE__}: Please stop all ssh-agent processes and run uninstall again.';
856 MsgBox(Msg,mbError,MB_OK);
857 Log(Msg);
859 // Clean-up the temporary copy (ignoring any errors).
860 DeleteFile(NewName);
861 end else begin
862 // Clean-up the temporary copy (ignoring any errors).
863 RenameFile(NewName,FileName);
864 end;
865 end else begin
866 Result:=True;
867 end;
868 end;
870 procedure CurUninstallStepChanged(CurUninstallStep:TUninstallStep);
872 AppDir,Command,Msg:string;
873 EnvPath,EnvHome,EnvSSH:TArrayOfString;
874 i:Longint;
875 RootKey:Integer;
876 begin
877 if CurUninstallStep<>usUninstall then begin
878 Exit;
879 end;
882 Modify the environment
884 This must happen no later than usUninstall to make
885 "ChangesEnvironment=yes" not happend before the change!
888 AppDir:=ExpandConstant('{app}');
889 Command:=AppDir+'\setup.ini';
891 // Delete the current user's GIT_SSH and SVN_SSH if we set it.
892 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
893 if (GetArrayLength(EnvSSH)=1) and
894 (CompareStr(EnvSSH[0],GetIniString('Environment','GIT_SSH','',Command))=0) then begin
895 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
896 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to GIT_SSH.';
897 MsgBox(Msg,mbError,MB_OK);
898 Log(Msg);
899 // This is not a critical error, the user can probably fix it manually,
900 // so we continue.
901 end;
902 end;
904 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
905 if (GetArrayLength(EnvSSH)=1) and
906 (CompareStr(EnvSSH[0],GetIniString('Environment','SVN_SSH','',Command))=0) then begin
907 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
908 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to SVN_SSH.';
909 MsgBox(Msg,mbError,MB_OK);
910 Log(Msg);
911 // This is not a critical error, the user can probably fix it manually,
912 // so we continue.
913 end;
914 end;
916 // Get the current user's directories in PATH.
917 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
919 // Remove the installation directory from PATH in any case, even if it
920 // was not added by the installer.
921 for i:=0 to GetArrayLength(EnvPath)-1 do begin
922 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
923 EnvPath[i]:='';
924 end;
925 end;
927 // Reset the current user's directories in PATH.
928 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
929 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to PATH.';
930 MsgBox(Msg,mbError,MB_OK);
931 Log(Msg);
932 // This is not a critical error, the user can probably fix it manually,
933 // so we continue.
934 end;
936 // Reset the current user's HOME if we modified it.
937 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
938 if (GetArrayLength(EnvHome)=1) and
939 (CompareStr(EnvHome[0],GetIniString('Environment','HOME','',Command))=0) then begin
940 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
941 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to HOME.';
942 MsgBox(Msg,mbError,MB_OK);
943 Log(Msg);
944 // This is not a critical error, the user can probably fix it manually,
945 // so we continue.
946 end;
947 end;
949 if FileExists(Command) and (not DeleteFile(Command)) then begin
950 Msg:='Line {#emit __LINE__}: Unable to delete file "'+Command+'".';
951 MsgBox(Msg,mbError,MB_OK);
952 Log(Msg);
953 // This is not a critical error, the user can probably fix it manually,
954 // so we continue.
955 end;
958 Delete the Windows Explorer shell extensions
961 if IsAdminLoggedOn then begin
962 RootKey:=HKEY_LOCAL_MACHINE;
963 end else begin
964 RootKey:=HKEY_CURRENT_USER;
965 end;
967 Command:='';
968 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','',Command);
969 if Pos(AppDir,Command)>0 then begin
970 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell') then begin
971 Msg:='Line {#emit __LINE__}: Unable to remove "Git Bash Here" shell extension.';
972 MsgBox(Msg,mbError,MB_OK);
973 Log(Msg);
974 // This is not a critical error, the user can probably fix it manually,
975 // so we continue.
976 end;
977 end;
979 Command:='';
980 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui\command','',Command);
981 if Pos(AppDir,Command)>0 then begin
982 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui') then begin
983 Msg:='Line {#emit __LINE__}: Unable to remove "Git GUI Here" shell extension.';
984 MsgBox(Msg,mbError,MB_OK);
985 Log(Msg);
986 // This is not a critical error, the user can probably fix it manually,
987 // so we continue.
988 end;
989 end;
990 end;