Git installer: do not delete bin\git.exe blindly
[msysgit.git] / share / WinGit / install.iss
blob9a68340c6929782a1b7161029fa444e116db2bc9
1 #define APP_NAME 'Git'
2 #define APP_VERSION '%APPVERSION%'
3 #define APP_URL 'http://code.google.com/p/msysgit/'
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 WizardSmallImageFile=git.bmp
31 [Tasks]
32 Name: quicklaunchicon; Description: "Create a &Quick Launch icon"; GroupDescription: "Additional icons:"; Flags: checkedonce
33 Name: desktopicon; Description: "Create a &Desktop icon"; GroupDescription: "Additional icons:"; Flags: checkedonce
34 Name: shellextension; Description: "Add ""Git Ba&sh Here"""; GroupDescription: "Windows Explorer integration:"; Flags: checkedonce
35 Name: guiextension; Description: "Add ""Git &GUI Here"""; GroupDescription: "Windows Explorer integration:"; Flags: checkedonce
37 [Files]
38 Source: "*"; DestDir: "{app}"; Excludes: "\*.bmp, gpl-2.0.rtf, \install.*, \tmp.*, \bin\*install*"; Flags: recursesubdirs replacesameversion
39 Source: ReleaseNotes.rtf; DestDir: "{app}"; Flags: isreadme replacesameversion
41 [Icons]
42 Name: "{group}\Git GUI"; Filename: "{app}\bin\wish.exe"; Parameters: """{app}\libexec\git-core\git-gui"""; WorkingDir: "%HOMEDRIVE%%HOMEPATH%"; IconFilename: "{app}\etc\git.ico"
43 Name: "{group}\Git Bash"; Filename: "{syswow64}\cmd.exe"; Parameters: "/c """"{app}\bin\sh.exe"" --login -i"""; WorkingDir: "%HOMEDRIVE%%HOMEPATH%"; IconFilename: "{app}\etc\git.ico"
44 Name: "{group}\Uninstall Git"; Filename: "{uninstallexe}"
45 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
46 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
48 [Messages]
49 BeveledLabel={#emit APP_URL}
50 SetupAppTitle={#emit APP_NAME} Setup
51 SetupWindowTitle={#emit APP_NAME} Setup
53 [UninstallDelete]
54 Type: files; Name: "{app}\bin\git-*.exe"
55 Type: files; Name: "{app}\libexec\git-core\git-*.exe"
56 Type: dirifempty; Name: "{app}\home\{username}"
57 Type: dirifempty; Name: "{app}\home"
59 [Code]
61 Helper methods
64 function GetShellFolder(Param:string):string;
65 begin
66 if IsAdminLoggedOn then begin
67 Param:='{common'+Param+'}';
68 end else begin
69 Param:='{user'+Param+'}';
70 end;
71 Result:=ExpandConstant(Param);
72 end;
74 function CreateHardLink(lpFileName,lpExistingFileName:string;lpSecurityAttributes:Integer):Boolean;
75 external 'CreateHardLinkA@Kernel32.dll';
77 function GetEnvStrings(VarName:string;AllUsers:Boolean):TArrayOfString;
78 var
79 Path:string;
80 i:Longint;
81 p:Integer;
82 begin
83 Path:='';
85 // See http://www.jrsoftware.org/isfaq.php#env
86 if AllUsers then begin
87 // We ignore errors here. The resulting array of strings will be empty.
88 RegQueryStringValue(HKEY_LOCAL_MACHINE,'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',VarName,Path);
89 end else begin
90 // We ignore errors here. The resulting array of strings will be empty.
91 RegQueryStringValue(HKEY_CURRENT_USER,'Environment',VarName,Path);
92 end;
94 // Make sure we have at least one semicolon.
95 Path:=Path+';';
97 // Split the directories in PATH into an array of strings.
98 i:=0;
99 SetArrayLength(Result,0);
101 p:=Pos(';',Path);
102 while p>0 do begin
103 SetArrayLength(Result,i+1);
104 if p>1 then begin
105 Result[i]:=Copy(Path,1,p-1);
106 i:=i+1;
107 end;
108 Path:=Copy(Path,p+1,Length(Path));
109 p:=Pos(';',Path);
110 end;
111 end;
113 function SetEnvStrings(VarName:string;AllUsers,DeleteIfEmpty:Boolean;DirStrings:TArrayOfString):Boolean;
115 Path,KeyName:string;
116 i:Longint;
117 begin
118 // Merge all non-empty directory strings into a PATH variable.
119 Path:='';
120 for i:=0 to GetArrayLength(DirStrings)-1 do begin
121 if Length(DirStrings[i])>0 then begin
122 if Length(Path)>0 then begin
123 Path:=Path+';'+DirStrings[i];
124 end else begin
125 Path:=DirStrings[i];
126 end;
127 end;
128 end;
130 // See http://www.jrsoftware.org/isfaq.php#env
131 if AllUsers then begin
132 KeyName:='SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
133 if DeleteIfEmpty and (Length(Path)=0) then begin
134 Result:=(not RegValueExists(HKEY_LOCAL_MACHINE,KeyName,VarName))
135 or RegDeleteValue(HKEY_LOCAL_MACHINE,KeyName,VarName);
136 end else begin
137 Result:=RegWriteStringValue(HKEY_LOCAL_MACHINE,KeyName,VarName,Path);
138 end;
139 end else begin
140 KeyName:='Environment';
141 if DeleteIfEmpty and (Length(Path)=0) then begin
142 Result:=(not RegValueExists(HKEY_CURRENT_USER,KeyName,VarName))
143 or RegDeleteValue(HKEY_CURRENT_USER,KeyName,VarName);
144 end else begin
145 Result:=RegWriteStringValue(HKEY_CURRENT_USER,KeyName,VarName,Path);
146 end;
147 end;
148 end;
150 const
151 TortoiseSVNInstallKey='SOFTWARE\TortoiseSVN';
152 TortoiseCVSUninstallKey='SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\TortoiseCVS_is1';
153 PuTTYUninstallKey='SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\PuTTY_is1';
154 PuTTYPrivateKeyAssoc='PuTTYPrivateKey\shell\open\command';
156 function GetPuTTYLocation:string;
157 begin
158 // Prefer TortoisePlink over vanilla Plink for its GUI dialog to accept host keys.
159 if (IsWin64 and RegQueryStringValue(HKEY_LOCAL_MACHINE_64,TortoiseSVNInstallKey,'Directory',Result))
160 or RegQueryStringValue(HKEY_LOCAL_MACHINE_32,TortoiseSVNInstallKey,'Directory',Result) then begin
161 // C:\Program Files\TortoiseSVN\
162 Result:=Result+'bin\';
163 // C:\Program Files\TortoiseSVN\bin\
164 end else begin
165 if not (IsWin64 and RegQueryStringValue(HKEY_LOCAL_MACHINE_64,TortoiseCVSUninstallKey,'InstallLocation',Result)) then begin
166 RegQueryStringValue(HKEY_LOCAL_MACHINE_32,TortoiseCVSUninstallKey,'InstallLocation',Result);
167 end;
168 // C:\Program Files\TortoiseCVS\
169 end;
171 if DirExists(Result) then begin
172 Result:=Result+'TortoisePlink.exe'
173 Exit;
174 end;
176 if not RegQueryStringValue(HKEY_LOCAL_MACHINE,PuTTYUninstallKey,'InstallLocation',Result) then begin
177 if RegQueryStringValue(HKEY_CLASSES_ROOT,PuTTYPrivateKeyAssoc,'',Result) then begin
178 // "C:\Program Files\PuTTY\pageant.exe" "%1"
179 Result:=RemoveQuotes(Result);
180 // C:\Program Files\PuTTY\pageant.exe" "%1
181 Result:=ExtractFilePath(Result);
182 end;
183 end;
184 // C:\Program Files\PuTTY\
186 if not DirExists(Result) then begin
187 // Guess something.
188 Result:='C:\Program Files\PuTTY\';
189 end;
191 Result:=Result+'plink.exe'
192 end;
194 const
195 // Git Path options.
196 GP_BashOnly = 1;
197 GP_Cmd = 2;
198 GP_CmdTools = 3;
200 // Git SSH options.
201 GS_OpenSSH = 1;
202 GS_Plink = 2;
204 // Git CRLF options.
205 GC_LFOnly = 1;
206 GC_CRLFAlways = 2;
207 GC_CRLFCommitAsIs = 3;
210 // Wizard page and variables for the Path options.
211 PathPage:TWizardPage;
212 RdbPath:array[GP_BashOnly..GP_CmdTools] of TRadioButton;
214 // Wizard page and variables for the SSH options.
215 PuTTYPage:TWizardPage;
216 RdbSSH:array[GS_OpenSSH..GS_Plink] of TRadioButton;
217 EdtPlink:TEdit;
219 // Wizard page and variables for the CR/LF options.
220 CRLFPage:TWizardPage;
221 RdbCRLF:array[GC_LFOnly..GC_CRLFCommitAsIs] of TRadioButton;
223 procedure BrowseForPuTTYFolder(Sender:TObject);
225 Path:string;
226 begin
227 Path:=ExtractFilePath(EdtPlink.Text);
228 BrowseForFolder('Please select the PuTTY folder:',Path,False);
229 if FileExists(Path+'\TortoisePlink.exe') then begin
230 EdtPlink.Text:=Path+'\TortoisePlink.exe';
231 RdbSSH[GS_Plink].Checked:=True;
232 end else if FileExists(Path+'\plink.exe') then begin
233 EdtPlink.Text:=Path+'\plink.exe';
234 RdbSSH[GS_Plink].Checked:=True;
235 end else begin
236 MsgBox('Please enter a valid path to "TortoisePlink.exe" or "plink.exe".',mbError,MB_OK);
237 end;
238 end;
241 Installer code
244 procedure InitializeWizard;
246 LblGitBash,LblGitCmd,LblGitCmdTools,LblGitCmdToolsWarn:TLabel;
247 LblOpenSSH,LblPlink:TLabel;
248 LblLFOnly,LblCRLFAlways,LblCRLFCommitAsIs:TLabel;
249 BtnPlink:TButton;
250 Data:String;
251 begin
252 // Create a custom page for modifying the environment.
253 PathPage:=CreateCustomPage(
254 wpSelectTasks,
255 'Adjusting your PATH environment',
256 'How would you like to use Git from the command line?'
259 // 1st choice
260 RdbPath[GP_BashOnly]:=TRadioButton.Create(PathPage);
261 with RdbPath[GP_BashOnly] do begin
262 Parent:=PathPage.Surface;
263 Caption:='Use Git Bash only';
264 Left:=ScaleX(4);
265 Top:=ScaleY(8);
266 Width:=ScaleX(129);
267 Height:=ScaleY(17);
268 Font.Style:=[fsBold];
269 TabOrder:=0;
270 Checked:=True;
271 end;
272 LblGitBash:=TLabel.Create(PathPage);
273 with LblGitBash do begin
274 Parent:=PathPage.Surface;
275 Caption:=
276 'This is the most conservative choice if you are concerned about the stability' + #13 +
277 'of your system. Your PATH will not be modified.';
278 Left:=ScaleX(28);
279 Top:=ScaleY(32);
280 Width:=ScaleX(405);
281 Height:=ScaleY(26);
282 end;
284 // 2nd choice
285 RdbPath[GP_Cmd]:=TRadioButton.Create(PathPage);
286 with RdbPath[GP_Cmd] do begin
287 Parent:=PathPage.Surface;
288 Caption:='Run Git from the Windows Command Prompt';
289 Left:=ScaleX(4);
290 Top:=ScaleY(76);
291 Width:=ScaleX(281);
292 Height:=ScaleY(17);
293 Font.Style:=[fsBold];
294 TabOrder:=1;
295 end;
296 LblGitCmd:=TLabel.Create(PathPage);
297 with LblGitCmd do begin
298 Parent:=PathPage.Surface;
299 Caption:=
300 'This option is considered safe and no conflicts with other tools are known.' + #13 +
301 'Only Git will be added to your PATH. Use this option if you want to use Git' + #13 +
302 'from a Cygwin Prompt (make sure to not have Cygwin''s Git installed).';
303 Left:=ScaleX(28);
304 Top:=ScaleY(100);
305 Width:=ScaleX(405);
306 Height:=ScaleY(39);
307 end;
309 // 3rd choice
310 RdbPath[GP_CmdTools]:=TRadioButton.Create(PathPage);
311 with RdbPath[GP_CmdTools] do begin
312 Parent:=PathPage.Surface;
313 Caption:='Run Git and included Unix tools from the Windows Command Prompt';
314 Left:=ScaleX(4);
315 Top:=ScaleY(152);
316 Width:=ScaleX(405);
317 Height:=ScaleY(17);
318 Font.Style:=[fsBold];
319 TabOrder:=2;
320 end;
321 LblGitCmdTools:=TLabel.Create(PathPage);
322 with LblGitCmdTools do begin
323 Parent:=PathPage.Surface;
324 Caption:='Both Git and its accompanying Unix tools will be added to your PATH.';
325 Left:=ScaleX(28);
326 Top:=ScaleY(176);
327 Width:=ScaleX(405);
328 Height:=ScaleY(13);
329 end;
330 LblGitCmdToolsWarn:=TLabel.Create(PathPage);
331 with LblGitCmdToolsWarn do begin
332 Parent:=PathPage.Surface;
333 Caption:=
334 'Warning: This will override Windows tools like find.exe and' + #13 +
335 'sort.exe. Select this option only if you understand the implications.';
336 Left:=ScaleX(28);
337 Top:=ScaleY(192);
338 Width:=ScaleX(376);
339 Height:=ScaleY(26);
340 Font.Color:=255;
341 Font.Style:=[fsBold];
342 end;
344 // Restore the setting chosen during a previous install.
345 Data:=GetPreviousData('Path Option','BashOnly');
346 if Data='BashOnly' then begin
347 RdbPath[GP_BashOnly].Checked:=True;
348 end else if Data='Cmd' then begin
349 RdbPath[GP_Cmd].Checked:=True;
350 end else if Data='CmdTools' then begin
351 RdbPath[GP_CmdTools].Checked:=True;
352 end;
354 // Create a custom page for using (Tortoise)Plink instead of OpenSSH.
355 PuTTYPage:=CreateCustomPage(
356 PathPage.ID,
357 'Choosing the SSH executable',
358 'Which Secure Shell client program would you like Git to use?'
361 // 1st choice
362 RdbSSH[GS_OpenSSH]:=TRadioButton.Create(PuTTYPage);
363 with RdbSSH[GS_OpenSSH] do begin
364 Parent:=PuTTYPage.Surface;
365 Caption:='Use OpenSSH';
366 Left:=ScaleX(4);
367 Top:=ScaleY(8);
368 Width:=ScaleX(129);
369 Height:=ScaleY(17);
370 Font.Style:=[fsBold];
371 TabOrder:=0;
372 Checked:=True;
373 end;
374 LblOpenSSH:=TLabel.Create(PuTTYPage);
375 with LblOpenSSH do begin
376 Parent:=PuTTYPage.Surface;
377 Caption:=
378 'This uses ssh.exe that comes with Git. The GIT_SSH environment' + #13 +
379 'variable will not be modified.';
380 Left:=ScaleX(28);
381 Top:=ScaleY(32);
382 Width:=ScaleX(324);
383 Height:=ScaleY(26);
384 end;
386 // 2nd choice
387 RdbSSH[GS_Plink]:=TRadioButton.Create(PuTTYPage);
388 with RdbSSH[GS_Plink] do begin
389 Parent:=PuTTYPage.Surface;
390 Caption:='Use (Tortoise)Plink';
391 Left:=ScaleX(4);
392 Top:=ScaleY(76);
393 Width:=ScaleX(281);
394 Height:=ScaleY(17);
395 Font.Style:=[fsBold];
396 TabOrder:=1;
397 end;
398 LblPlink:=TLabel.Create(PuTTYPage);
399 with LblPlink do begin
400 Parent:=PuTTYPage.Surface;
401 Caption:=
402 'This uses (Tortoise)Plink.exe from the TortoiseSVN/CVS or PuTTY' + #13 +
403 'applications which need to be provided by the user. The GIT_SSH' + #13 +
404 'environment variable will be set to the executable specified below.';
405 Left:=ScaleX(28);
406 Top:=ScaleY(100);
407 Width:=ScaleX(316);
408 Height:=ScaleY(39);
409 end;
410 EdtPlink:=TEdit.Create(PuTTYPage);
411 with EdtPlink do begin
412 Parent:=PuTTYPage.Surface;
413 Text:=GetPuTTYLocation;
414 if not FileExists(Text) then begin
415 Text:='';
416 end;
417 Left:=ScaleX(28);
418 Top:=ScaleY(148);
419 Width:=ScaleX(316);
420 Height:=ScaleY(13);
421 end;
422 BtnPlink:=TButton.Create(PuTTYPage);
423 with BtnPlink do begin
424 Parent:=PuTTYPage.Surface;
425 Caption:='...';
426 OnClick:=@BrowseForPuTTYFolder;
427 Left:=ScaleX(348);
428 Top:=ScaleY(148);
429 Width:=ScaleX(21);
430 Height:=ScaleY(21);
431 end;
433 // Restore the setting chosen during a previous install.
434 Data:=GetPreviousData('SSH Option','OpenSSH');
435 if Data='OpenSSH' then begin
436 RdbSSH[GS_OpenSSH].Checked:=True;
437 end else if Data='Plink' then begin
438 RdbSSH[GS_Plink].Checked:=True;
439 end;
441 // Create a custom page for the autoCRLF setting.
442 CRLFPage:=CreateCustomPage(
443 PuTTYPage.ID,
444 'Choosing CR/LF behavior',
445 'Which CR/LF behavior would you like Git to have?'
448 // 1st choice
449 RdbCRLF[GC_LFOnly]:=TRadioButton.Create(CRLFPage);
450 with RdbCRLF[GC_LFOnly] do begin
451 Parent:=CRLFPage.Surface;
452 Caption:='Use Unix style line endings';
453 Left:=ScaleX(4);
454 Top:=ScaleY(8);
455 Width:=ScaleX(329);
456 Height:=ScaleY(17);
457 Font.Style:=[fsBold];
458 TabOrder:=0;
459 Checked:=False;
460 end;
461 LblLFOnly:=TLabel.Create(CRLFPage);
462 with LblLFOnly do begin
463 Parent:=CRLFPage.Surface;
464 Caption:=
465 'Choose this if a single Line Feed character ends your lines. Most Windows' + #13 +
466 'programs can cope with these line endings. However, some editors, like' + #13 +
467 'Notepad, will show everything in one line with this mode.';
468 Left:=ScaleX(28);
469 Top:=ScaleY(32);
470 Width:=ScaleX(364);
471 Height:=ScaleY(47);
472 end;
474 // 2nd choice
475 RdbCRLF[GC_CRLFAlways]:=TRadioButton.Create(CRLFPage);
476 with RdbCRLF[GC_CRLFAlways] do begin
477 Parent:=CRLFPage.Surface;
478 Caption:='Use Windows style line endings';
479 Left:=ScaleX(4);
480 Top:=ScaleY(76);
481 Width:=ScaleX(329);
482 Height:=ScaleY(17);
483 Font.Style:=[fsBold];
484 TabOrder:=1;
485 Checked:=True;
486 end;
487 LblCRLFAlways:=TLabel.Create(CRLFPage);
488 with LblCRLFAlways do begin
489 Parent:=CRLFPage.Surface;
490 Caption:=
491 'Choose this if your source code uses a Carriage Return and a Line Feed' + #13 +
492 'character to end lines. This is the DOS convention; your checked-out files' + #13 +
493 'might not be handled gracefully by MSYS / Cygwin command line utilities.';
494 Left:=ScaleX(28);
495 Top:=ScaleY(100);
496 Width:=ScaleX(364);
497 Height:=ScaleY(47);
498 end;
500 // 3rd choice
501 RdbCRLF[GC_CRLFCommitAsIs]:=TRadioButton.Create(CRLFPage);
502 with RdbCRLF[GC_CRLFCommitAsIs] do begin
503 Parent:=CRLFPage.Surface;
504 Caption:='Commit line endings as they are';
505 Left:=ScaleX(4);
506 Top:=ScaleY(152);
507 Width:=ScaleX(329);
508 Height:=ScaleY(17);
509 Font.Style:=[fsBold];
510 TabOrder:=2;
511 Checked:=False;
512 end;
513 LblCRLFCommitAsIs:=TLabel.Create(CRLFPage);
514 with LblCRLFCommitAsIs do begin
515 Parent:=CRLFPage.Surface;
516 Caption:=
517 'Choose this if you know what you are doing and want to track the files with' + #13 +
518 'the line endings exactly as they appear in the files. This option might' + #13 +
519 'cause your projects to be hard to use on other platforms.';
520 Left:=ScaleX(28);
521 Top:=ScaleY(176);
522 Width:=ScaleX(364);
523 Height:=ScaleY(47);
524 end;
526 // Restore the setting chosen during a previous install.
527 Data:=GetPreviousData('CRLF Option','CRLFAlways');
528 if Data='LFOnly' then begin
529 RdbCRLF[GC_LFOnly].Checked:=True;
530 end else if Data='CRLFAlways' then begin
531 RdbCRLF[GC_CRLFAlways].Checked:=True;
532 end else if Data='CRLFCommitAsIs' then begin
533 RdbCRLF[GC_CRLFCommitAsIs].Checked:=True;
534 end;
535 end;
537 function NextButtonClick(CurPageID:Integer):Boolean;
538 begin
539 if CurPageID<>PuTTYPage.ID then begin
540 Result:=True;
541 Exit;
542 end;
544 Result:=RdbSSH[GS_OpenSSH].Checked
545 or (RdbSSH[GS_Plink].Checked and FileExists(EdtPlink.Text));
547 if not Result then begin
548 MsgBox('Please enter a valid path to (Tortoise)Plink.exe.',mbError,MB_OK);
549 end;
550 end;
552 procedure CurStepChanged(CurStep:TSetupStep);
554 AppDir,FileName,Cmd,Msg:string;
555 BuiltIns,EnvPath,EnvHome,EnvSSH:TArrayOfString;
556 Count,i:Longint;
557 IsNTFS:Boolean;
558 FindRec:TFindRec;
559 RootKey:Integer;
560 begin
561 if CurStep<>ssPostInstall then begin
562 Exit;
563 end;
565 AppDir:=ExpandConstant('{app}');
568 Create the built-ins
571 // Load the built-ins from a text file.
572 FileName:=ExpandConstant('{app}\'+'{#emit APP_BUILTINS}');
573 if LoadStringsFromFile(FileName,BuiltIns) then begin
574 // Check if we are running on NTFS.
575 IsNTFS:=False;
576 if SetNTFSCompression(AppDir+'\bin\git.exe',true) then begin
577 IsNTFS:=SetNTFSCompression(AppDir+'\bin\git.exe',false);
578 end;
580 Count:=GetArrayLength(BuiltIns)-1;
582 // Delete those scripts from "bin" which have been replaced by built-ins in "libexec\git-core".
583 for i:=0 to Count do begin
584 FileName:=AppDir+'\bin\'+ChangeFileExt(ExtractFileName(BuiltIns[i]),'');
585 if (FileExists(FileName) and (not DeleteFile(FileName))) then begin
586 Log('Line {#emit __LINE__}: Unable to delete script "'+FileName+'", ignoring.');
587 end;
588 end;
590 // Map the built-ins to git.exe.
591 if IsNTFS then begin
592 Log('Line {#emit __LINE__}: Partition seems to be NTFS, trying to create built-in aliases as hard links.');
594 for i:=0 to Count do begin
595 FileName:=AppDir+'\'+BuiltIns[i];
597 // On non-NTFS partitions, create hard links.
598 if (FileExists(FileName) and (not DeleteFile(FileName)))
599 or (not CreateHardLink(FileName,AppDir+'\bin\git.exe',0)) then begin
600 Log('Line {#emit __LINE__}: Unable to create hard link "'+FileName+'", will try to copy files.');
601 IsNTFS:=False;
602 Break;
603 end;
604 end;
606 Log('Line {#emit __LINE__}: Successfully created built-in aliases as hard links.');
607 end;
609 // The fallback is to copy the files.
610 if not IsNTFS then begin
611 Log('Line {#emit __LINE__}: Trying to create built-in aliases as file copies.');
613 for i:=0 to Count do begin
614 FileName:=AppDir+'\'+BuiltIns[i];
616 // On non-NTFS partitions, copy simply the files (overwriting existing ones).
617 if not FileCopy(AppDir+'\bin\git.exe',FileName,false) then begin
618 Msg:='Line {#emit __LINE__}: Unable to create file copy "'+FileName+'".';
619 MsgBox(Msg,mbError,MB_OK);
620 Log(Msg);
621 // This is not a critical error, Git could basically be used without the
622 // aliases for built-ins, so we continue.
623 end;
624 end;
626 Log('Line {#emit __LINE__}: Successfully created built-in aliases as file copies.');
627 end;
629 // Delete any duplicate files in case we are updating from a non-libexec to a libexec directory layout.
630 if FindFirst(AppDir+'\libexec\git-core\*',FindRec) then begin
631 repeat
632 if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY)=0 then begin
633 FileName:=AppDir+'\bin\'+FindRec.name;
634 if ((not Pos(FindRec.name,'git.exe')=1) and FileExists(FileName) and (not DeleteFile(FileName))) then begin
635 Log('Line {#emit __LINE__}: Unable to delete dupe "'+FileName+'", ignoring.');
636 end;
637 end;
638 until not FindNext(FindRec);
639 FindClose(FindRec);
640 end;
641 end else begin
642 Msg:='Line {#emit __LINE__}: Unable to read file "{#emit APP_BUILTINS}".';
643 MsgBox(Msg,mbError,MB_OK);
644 Log(Msg);
645 // This is not a critical error, Git could basically be used without the
646 // aliases for built-ins, so we continue.
647 end;
650 Adapt core.autocrlf
653 if RdbCRLF[GC_LFOnly].checked then begin
654 Cmd:='core.autoCRLF input';
655 end else if RdbCRLF[GC_CRLFAlways].checked then begin
656 Cmd:='core.autoCRLF true';
657 end else begin
658 Cmd:='core.autoCRLF false';
659 end;
660 if not Exec(AppDir + '\bin\git.exe', 'config -f gitconfig ' + Cmd,
661 AppDir + '\etc', SW_HIDE, ewWaitUntilTerminated, i) then begin
662 Msg:='Could not set CR/LF behavior: ' + Cmd;
663 MsgBox(Msg,mbError,MB_OK);
664 Log(Msg);
665 end;
668 Modify the environment
670 This must happen no later than ssPostInstall to make
671 "ChangesEnvironment=yes" not happend before the change!
674 FileName:=AppDir+'\setup.ini';
676 // Delete GIT_SSH if a previous installation modified it.
677 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
678 if (GetArrayLength(EnvSSH)=1) and
679 (CompareStr(EnvSSH[0],GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
680 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
681 Msg:='Line {#emit __LINE__}: Unable to reset GIT_SSH prior to install.';
682 MsgBox(Msg,mbError,MB_OK);
683 Log(Msg);
684 // This is not a critical error, the user can probably fix it manually,
685 // so we continue.
686 end;
687 end;
689 if RdbSSH[GS_Plink].Checked then begin
690 SetArrayLength(EnvSSH,1);
691 EnvSSH[0]:=EdtPlink.Text;
692 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
693 Msg:='Line {#emit __LINE__}: Unable to set the GIT_SSH environment variable.';
694 MsgBox(Msg,mbError,MB_OK);
695 Log(Msg);
696 // This is not a critical error, the user can probably fix it manually,
697 // so we continue.
698 end;
700 // Mark that we have changed GIT_SSH.
701 if not SetIniString('Environment','GIT_SSH',EnvSSH[0],FileName) then begin
702 Msg:='Line {#emit __LINE__}: Unable to write to file "'+FileName+'".';
703 MsgBox(Msg,mbError,MB_OK);
704 Log(Msg);
705 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
706 // so we continue.
707 end;
708 end;
710 // Get the current user's directories in PATH.
711 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
713 // First, remove the installation directory from PATH in any case.
714 for i:=0 to GetArrayLength(EnvPath)-1 do begin
715 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
716 EnvPath[i]:='';
717 end;
718 end;
720 // Delete HOME if a previous installation modified it.
721 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
722 if (GetArrayLength(EnvHome)=1) and
723 (CompareStr(EnvHome[0],GetIniString('Environment','HOME','',FileName))=0) then begin
724 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
725 Msg:='Line {#emit __LINE__}: Unable to reset HOME prior to install.';
726 MsgBox(Msg,mbError,MB_OK);
727 Log(Msg);
728 // This is not a critical error, the user can probably fix it manually,
729 // so we continue.
730 end;
731 end;
733 // Modify the PATH variable as requested by the user.
734 if RdbPath[GP_Cmd].Checked or RdbPath[GP_CmdTools].Checked then begin
735 i:=GetArrayLength(EnvPath);
736 SetArrayLength(EnvPath,i+1);
738 // List \cmd before \bin so \cmd has higher priority and programs in
739 // there will be called in favor of those in \bin.
740 EnvPath[i]:=ExpandConstant('{app}\cmd');
742 if RdbPath[GP_CmdTools].Checked then begin
743 SetArrayLength(EnvPath,i+2);
744 EnvPath[i+1]:=ExpandConstant('{app}\bin');
746 // Set HOME for the Windows Command Prompt, but only if it has not been set manually before.
747 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
748 i:=GetArrayLength(EnvHome);
749 if (i=0) or ((i=1) and (Length(EnvHome[0])=0)) then begin
750 SetArrayLength(EnvHome,1);
751 EnvHome[0]:=ExpandConstant('{%HOMEDRIVE}{%HOMEPATH}');
752 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,EnvHome) then begin
753 Msg:='Line {#emit __LINE__}: Unable to set the HOME environment variable.';
754 MsgBox(Msg,mbError,MB_OK);
755 Log(Msg);
756 // This is not a critical error, the user can probably fix it manually,
757 // so we continue.
758 end;
760 // Mark that we have changed HOME.
761 if not SetIniString('Environment','HOME',EnvHome[0],FileName) then begin
762 Msg:='Line {#emit __LINE__}: Unable to write to file "'+FileName+'".';
763 MsgBox(Msg,mbError,MB_OK);
764 Log(Msg);
765 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
766 // so we continue.
767 end;
768 end;
769 end;
770 end;
772 // Set the current user's PATH directories.
773 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
774 Msg:='Line {#emit __LINE__}: Unable to set the PATH environment variable.';
775 MsgBox(Msg,mbError,MB_OK);
776 Log(Msg);
777 // This is not a critical error, the user can probably fix it manually,
778 // so we continue.
779 end;
782 Create the Windows Explorer shell extensions
785 if IsAdminLoggedOn then begin
786 RootKey:=HKEY_LOCAL_MACHINE;
787 end else begin
788 RootKey:=HKEY_CURRENT_USER;
789 end;
791 if IsTaskSelected('shellextension') then begin
792 Cmd:=ExpandConstant('"{syswow64}\cmd.exe"');
793 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell','','Git Ba&sh Here'))
794 or (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','',Cmd+' /c "pushd "%1" && "'+AppDir+'\bin\sh.exe" --login -i"')) then begin
795 Msg:='Line {#emit __LINE__}: Unable to create "Git Bash 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;
803 if IsTaskSelected('guiextension') then begin
804 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui','','Git &GUI Here'))
805 or (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
806 Msg:='Line {#emit __LINE__}: Unable to create "Git GUI Here" shell extension.';
807 MsgBox(Msg,mbError,MB_OK);
808 Log(Msg);
809 // This is not a critical error, the user can probably fix it manually,
810 // so we continue.
811 end;
812 end;
813 end;
815 procedure RegisterPreviousData(PreviousDataKey:Integer);
817 Data:string;
818 begin
819 // Git Path options.
820 Data:='';
821 if RdbPath[GP_BashOnly].Checked then begin
822 Data:='BashOnly';
823 end else if RdbPath[GP_Cmd].Checked then begin
824 Data:='Cmd';
825 end else if RdbPath[GP_CmdTools].Checked then begin
826 Data:='CmdTools';
827 end;
828 SetPreviousData(PreviousDataKey,'Path Option',Data);
830 // Git SSH options.
831 Data:='';
832 if RdbSSH[GS_OpenSSH].Checked then begin
833 Data:='OpenSSH';
834 end else if RdbSSH[GS_Plink].Checked then begin
835 Data:='Plink';
836 end;
837 SetPreviousData(PreviousDataKey,'SSH Option',Data);
839 // Git CR/LF options.
840 Data:='';
841 if RdbCRLF[GC_LFOnly].Checked then begin
842 Data:='LFOnly';
843 end else if RdbCRLF[GC_CRLFAlways].Checked then begin
844 Data:='CRLFAlways';
845 end else if RdbCRLF[GC_CRLFCommitAsIs].Checked then begin
846 Data:='CRLFCommitAsIs';
847 end;
848 SetPreviousData(PreviousDataKey,'CRLF Option',Data);
849 end;
852 Uninstaller code
855 function InitializeUninstall:Boolean;
857 FileName,NewName,Msg:string;
858 begin
859 FileName:=ExpandConstant('{app}')+'\bin\ssh-agent.exe';
860 if FileExists(FileName) then begin
861 // Create a temporary copy of the file we try to delete.
862 NewName:=FileName+'.'+IntToStr(1000+Random(9000));
863 Result:=FileCopy(FileName,NewName,True) and DeleteFile(FileName);
865 if not Result then begin
866 Msg:='Line {#emit __LINE__}: Please stop all ssh-agent processes and run uninstall again.';
867 MsgBox(Msg,mbError,MB_OK);
868 Log(Msg);
870 // Clean-up the temporary copy (ignoring any errors).
871 DeleteFile(NewName);
872 end else begin
873 // Clean-up the temporary copy (ignoring any errors).
874 RenameFile(NewName,FileName);
875 end;
876 end else begin
877 Result:=True;
878 end;
879 end;
881 procedure CurUninstallStepChanged(CurUninstallStep:TUninstallStep);
883 AppDir,Command,Msg:string;
884 EnvPath,EnvHome,EnvSSH:TArrayOfString;
885 i:Longint;
886 RootKey:Integer;
887 begin
888 if CurUninstallStep<>usUninstall then begin
889 Exit;
890 end;
893 Modify the environment
895 This must happen no later than usUninstall to make
896 "ChangesEnvironment=yes" not happend before the change!
899 AppDir:=ExpandConstant('{app}');
900 Command:=AppDir+'\setup.ini';
902 // Reset the current user's GIT_SSH if we modified it.
903 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
904 if (GetArrayLength(EnvSSH)=1) and
905 (CompareStr(EnvSSH[0],GetIniString('Environment','GIT_SSH','',Command))=0) then begin
906 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
907 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to GIT_SSH.';
908 MsgBox(Msg,mbError,MB_OK);
909 Log(Msg);
910 // This is not a critical error, the user can probably fix it manually,
911 // so we continue.
912 end;
913 end;
915 // Get the current user's directories in PATH.
916 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
918 // Remove the installation directory from PATH in any case, even if it
919 // was not added by the installer.
920 for i:=0 to GetArrayLength(EnvPath)-1 do begin
921 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
922 EnvPath[i]:='';
923 end;
924 end;
926 // Reset the current user's directories in PATH.
927 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
928 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to PATH.';
929 MsgBox(Msg,mbError,MB_OK);
930 Log(Msg);
931 // This is not a critical error, the user can probably fix it manually,
932 // so we continue.
933 end;
935 // Reset the current user's HOME if we modified it.
936 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
937 if (GetArrayLength(EnvHome)=1) and
938 (CompareStr(EnvHome[0],GetIniString('Environment','HOME','',Command))=0) then begin
939 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
940 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to HOME.';
941 MsgBox(Msg,mbError,MB_OK);
942 Log(Msg);
943 // This is not a critical error, the user can probably fix it manually,
944 // so we continue.
945 end;
946 end;
948 if (FileExists(Command) and (not DeleteFile(Command))) then begin
949 Msg:='Line {#emit __LINE__}: Unable to delete file "'+Command+'".';
950 MsgBox(Msg,mbError,MB_OK);
951 Log(Msg);
952 // This is not a critical error, the user can probably fix it manually,
953 // so we continue.
954 end;
957 Delete the Windows Explorer shell extensions
960 if IsAdminLoggedOn then begin
961 RootKey:=HKEY_LOCAL_MACHINE;
962 end else begin
963 RootKey:=HKEY_CURRENT_USER;
964 end;
966 Command:='';
967 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','',Command);
968 if Pos(AppDir,Command)>0 then begin
969 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell') then begin
970 Msg:='Line {#emit __LINE__}: Unable to remove "Git Bash Here" shell extension.';
971 MsgBox(Msg,mbError,MB_OK);
972 Log(Msg);
973 // This is not a critical error, the user can probably fix it manually,
974 // so we continue.
975 end;
976 end;
978 Command:='';
979 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui\command','',Command);
980 if Pos(AppDir,Command)>0 then begin
981 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui') then begin
982 Msg:='Line {#emit __LINE__}: Unable to remove "Git GUI Here" shell extension.';
983 MsgBox(Msg,mbError,MB_OK);
984 Log(Msg);
985 // This is not a critical error, the user can probably fix it manually,
986 // so we continue.
987 end;
988 end;
989 end;