WinGit installer: make autoCRLF behavior configurable
[msysgit.git] / share / WinGit / install.iss
blobb6ab0b833afc591633ecd775f0fa39efa37a30a3
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
39 Source: ReleaseNotes.rtf; DestDir: "{app}"; Flags: isreadme
41 [Icons]
42 Name: "{group}\Git GUI"; Filename: "{app}\bin\wish.exe"; Parameters: """{app}\libexec\git-core\git-gui"""; WorkingDir: "%USERPROFILE%"; IconFilename: "{app}\etc\git.ico"
43 Name: "{group}\Git Bash"; Filename: "{syswow64}\cmd.exe"; Parameters: "/c """"{app}\bin\sh.exe"" --login -i"""; WorkingDir: "%USERPROFILE%"; 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: "%USERPROFILE%"; 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: "%USERPROFILE%"; 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 PuTTYUninstallKey='SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\PuTTY_is1';
152 PuTTYPrivateKeyAssoc='PuTTYPrivateKey\shell\open\command';
154 function GetPuTTYLocation:string;
155 begin
156 if RegQueryStringValue(HKEY_LOCAL_MACHINE,PuTTYUninstallKey,'InstallLocation',Result) and DirExists(Result) then begin
157 // C:\Program Files\PuTTY\
158 Exit;
159 end;
160 if RegQueryStringValue(HKEY_CLASSES_ROOT,PuTTYPrivateKeyAssoc,'',Result) then begin
161 // "C:\Program Files\PuTTY\pageant.exe" "%1"
162 Result:=RemoveQuotes(Result);
163 // C:\Program Files\PuTTY\pageant.exe" "%1
164 Result:=ExtractFilePath(Result);
165 // C:\Program Files\PuTTY\
166 if DirExists(Result) then begin
167 Exit;
168 end;
169 end;
170 // Guess something.
171 Result:='C:\Program Files\PuTTY\';
172 end;
174 const
175 // Git Path options.
176 GP_BashOnly = 1;
177 GP_Cmd = 2;
178 GP_CmdTools = 3;
180 // Git SSH options.
181 GS_OpenSSH = 1;
182 GS_PLink = 2;
184 // Git CRLF options.
185 GC_LFOnly = 1;
186 GC_CRLFAlways = 2;
187 GC_CRLFCommitAsIs = 3;
190 // Wizard page and variables for the Path options.
191 PathPage:TWizardPage;
192 RdbPath:array[GP_BashOnly..GP_CmdTools] of TRadioButton;
194 // Wizard page and variables for the SSH options.
195 PuTTYPage:TWizardPage;
196 RdbSSH:array[GS_OpenSSH..GS_PLink] of TRadioButton;
197 EdtPLink:TEdit;
199 // Wizard page and variables for the CR/LF options.
200 CRLFPage:TWizardPage;
201 RdbCRLF:array[GC_LFOnly..GC_CRLFCommitAsIs] of TRadioButton;
203 procedure BrowseForPuTTYFolder(Sender:TObject);
205 Path:string;
206 begin
207 Path:=ExtractFilePath(EdtPLink.Text);
208 BrowseForFolder('Please select the PuTTY folder:',Path,False);
209 Path:=Path+'\plink.exe';
210 if FileExists(Path) then begin
211 EdtPLink.Text:=Path;
212 RdbSSH[GS_PLink].Checked:=True;
213 end else begin
214 MsgBox('Please enter a valid path to plink.exe.',mbError,MB_OK);
215 end;
216 end;
219 Installer code
222 procedure InitializeWizard;
224 LblGitBash,LblGitCmd,LblGitCmdTools,LblGitCmdToolsWarn:TLabel;
225 LblOpenSSH,LblPLink:TLabel;
226 LblLFOnly,LblCRLFAlways,LblCRLFCommitAsIs:TLabel;
227 BtnPLink:TButton;
228 Data:String;
229 begin
230 // Create a custom page for modifying the environment.
231 PathPage:=CreateCustomPage(
232 wpSelectTasks,
233 'Adjusting your PATH environment',
234 'How would you like to use Git from the command line?'
237 // 1st choice
238 RdbPath[GP_BashOnly]:=TRadioButton.Create(PathPage);
239 with RdbPath[GP_BashOnly] do begin
240 Parent:=PathPage.Surface;
241 Caption:='Use Git Bash only';
242 Left:=ScaleX(4);
243 Top:=ScaleY(8);
244 Width:=ScaleX(129);
245 Height:=ScaleY(17);
246 Font.Style:=[fsBold];
247 TabOrder:=0;
248 Checked:=True;
249 end;
250 LblGitBash:=TLabel.Create(PathPage);
251 with LblGitBash do begin
252 Parent:=PathPage.Surface;
253 Caption:=
254 'This is the most conservative choice if you are concerned about the stability' + #13 +
255 'of your system. Your PATH will not be modified.';
256 Left:=ScaleX(28);
257 Top:=ScaleY(32);
258 Width:=ScaleX(405);
259 Height:=ScaleY(26);
260 end;
262 // 2nd choice
263 RdbPath[GP_Cmd]:=TRadioButton.Create(PathPage);
264 with RdbPath[GP_Cmd] do begin
265 Parent:=PathPage.Surface;
266 Caption:='Run Git from the Windows Command Prompt';
267 Left:=ScaleX(4);
268 Top:=ScaleY(76);
269 Width:=ScaleX(281);
270 Height:=ScaleY(17);
271 Font.Style:=[fsBold];
272 TabOrder:=1;
273 end;
274 LblGitCmd:=TLabel.Create(PathPage);
275 with LblGitCmd do begin
276 Parent:=PathPage.Surface;
277 Caption:=
278 'This option is considered safe and no conflicts with other tools are known.' + #13 +
279 'Only Git will be added to your PATH. Use this option if you want to use Git' + #13 +
280 'from a Cygwin Prompt (make sure to not have Cygwin''s Git installed).';
281 Left:=ScaleX(28);
282 Top:=ScaleY(100);
283 Width:=ScaleX(405);
284 Height:=ScaleY(39);
285 end;
287 // 3rd choice
288 RdbPath[GP_CmdTools]:=TRadioButton.Create(PathPage);
289 with RdbPath[GP_CmdTools] do begin
290 Parent:=PathPage.Surface;
291 Caption:='Run Git and included Unix tools from the Windows Command Prompt';
292 Left:=ScaleX(4);
293 Top:=ScaleY(152);
294 Width:=ScaleX(405);
295 Height:=ScaleY(17);
296 Font.Style:=[fsBold];
297 TabOrder:=2;
298 end;
299 LblGitCmdTools:=TLabel.Create(PathPage);
300 with LblGitCmdTools do begin
301 Parent:=PathPage.Surface;
302 Caption:='Both Git and its accompanying Unix tools will be added to your PATH.';
303 Left:=ScaleX(28);
304 Top:=ScaleY(176);
305 Width:=ScaleX(405);
306 Height:=ScaleY(13);
307 end;
308 LblGitCmdToolsWarn:=TLabel.Create(PathPage);
309 with LblGitCmdToolsWarn do begin
310 Parent:=PathPage.Surface;
311 Caption:=
312 'Warning: This will override Windows tools like find.exe and' + #13 +
313 'sort.exe. Select this option only if you understand the implications.';
314 Left:=ScaleX(28);
315 Top:=ScaleY(192);
316 Width:=ScaleX(376);
317 Height:=ScaleY(26);
318 Font.Color:=255;
319 Font.Style:=[fsBold];
320 end;
322 // Restore the setting chosen during a previous install.
323 Data:=GetPreviousData('Path Option','BashOnly');
324 if Data='BashOnly' then begin
325 RdbPath[GP_BashOnly].Checked:=True;
326 end else if Data='Cmd' then begin
327 RdbPath[GP_Cmd].Checked:=True;
328 end else if Data='CmdTools' then begin
329 RdbPath[GP_CmdTools].Checked:=True;
330 end;
332 // Create a custom page for using PuTTY's plink instead of ssh.
333 PuTTYPage:=CreateCustomPage(
334 PathPage.ID,
335 'Choosing the SSH executable',
336 'Which Secure Shell client program would you like Git to use?'
339 // 1st choice
340 RdbSSH[GS_OpenSSH]:=TRadioButton.Create(PuTTYPage);
341 with RdbSSH[GS_OpenSSH] do begin
342 Parent:=PuTTYPage.Surface;
343 Caption:='Use OpenSSH';
344 Left:=ScaleX(4);
345 Top:=ScaleY(8);
346 Width:=ScaleX(129);
347 Height:=ScaleY(17);
348 Font.Style:=[fsBold];
349 TabOrder:=0;
350 Checked:=True;
351 end;
352 LblOpenSSH:=TLabel.Create(PuTTYPage);
353 with LblOpenSSH do begin
354 Parent:=PuTTYPage.Surface;
355 Caption:=
356 'This uses ssh.exe that comes with Git. The GIT_SSH environment' + #13 +
357 'variable will not be modified.';
358 Left:=ScaleX(28);
359 Top:=ScaleY(32);
360 Width:=ScaleX(324);
361 Height:=ScaleY(26);
362 end;
364 // 2nd choice
365 RdbSSH[GS_PLink]:=TRadioButton.Create(PuTTYPage);
366 with RdbSSH[GS_PLink] do begin
367 Parent:=PuTTYPage.Surface;
368 Caption:='Use PLink';
369 Left:=ScaleX(4);
370 Top:=ScaleY(76);
371 Width:=ScaleX(281);
372 Height:=ScaleY(17);
373 Font.Style:=[fsBold];
374 TabOrder:=1;
375 end;
376 LblPLink:=TLabel.Create(PuTTYPage);
377 with LblPLink do begin
378 Parent:=PuTTYPage.Surface;
379 Caption:=
380 'This uses plink.exe from the PuTTY package which needs to be' + #13 +
381 'provided by the user. The GIT_SSH environment variable will be' + #13 +
382 'set to the path to plink.exe as specified below.';
383 Left:=ScaleX(28);
384 Top:=ScaleY(100);
385 Width:=ScaleX(316);
386 Height:=ScaleY(39);
387 end;
388 EdtPLink:=TEdit.Create(PuTTYPage);
389 with EdtPLink do begin
390 Parent:=PuTTYPage.Surface;
391 Text:=GetPuTTYLocation+'plink.exe';
392 if not FileExists(Text) then begin
393 Text:='';
394 end;
395 Left:=ScaleX(28);
396 Top:=ScaleY(148);
397 Width:=ScaleX(316);
398 Height:=ScaleY(13);
399 end;
400 BtnPLink:=TButton.Create(PuTTYPage);
401 with BtnPLink do begin
402 Parent:=PuTTYPage.Surface;
403 Caption:='...';
404 OnClick:=@BrowseForPuTTYFolder;
405 Left:=ScaleX(348);
406 Top:=ScaleY(148);
407 Width:=ScaleX(21);
408 Height:=ScaleY(21);
409 end;
411 // Restore the setting chosen during a previous install.
412 Data:=GetPreviousData('SSH Option','OpenSSH');
413 if Data='OpenSSH' then begin
414 RdbSSH[GS_OpenSSH].Checked:=True;
415 end else if Data='PLink' then begin
416 RdbSSH[GS_PLink].Checked:=True;
417 end;
419 // Create a custom page for the autoCRLF setting.
420 CRLFPage:=CreateCustomPage(
421 PuTTYPage.ID,
422 'Choosing CR/LF behavior',
423 'Which CR/LF behavior would you like Git to have?'
426 // 1st choice
427 RdbCRLF[GC_LFOnly]:=TRadioButton.Create(CRLFPage);
428 with RdbCRLF[GC_LFOnly] do begin
429 Parent:=CRLFPage.Surface;
430 Caption:='Use Unix style line endings';
431 Left:=ScaleX(4);
432 Top:=ScaleY(8);
433 Width:=ScaleX(329);
434 Height:=ScaleY(17);
435 Font.Style:=[fsBold];
436 TabOrder:=0;
437 Checked:=False;
438 end;
439 LblLFOnly:=TLabel.Create(CRLFPage);
440 with LblLFOnly do begin
441 Parent:=CRLFPage.Surface;
442 Caption:=
443 'Choose this if a single Line Feed character ends your lines. Most Windows' + #13 +
444 'programs can cope with these line endings. However, some editors, like' + #13 +
445 'Notepad, will show everything in one line with this mode.';
446 Left:=ScaleX(28);
447 Top:=ScaleY(32);
448 Width:=ScaleX(364);
449 Height:=ScaleY(47);
450 end;
452 // 2nd choice
453 RdbCRLF[GC_CRLFAlways]:=TRadioButton.Create(CRLFPage);
454 with RdbCRLF[GC_CRLFAlways] do begin
455 Parent:=CRLFPage.Surface;
456 Caption:='Use Windows style line endings';
457 Left:=ScaleX(4);
458 Top:=ScaleY(76);
459 Width:=ScaleX(329);
460 Height:=ScaleY(17);
461 Font.Style:=[fsBold];
462 TabOrder:=1;
463 Checked:=True;
464 end;
465 LblCRLFAlways:=TLabel.Create(CRLFPage);
466 with LblCRLFAlways do begin
467 Parent:=CRLFPage.Surface;
468 Caption:=
469 'Choose this if your source code uses a Carriage Return and a Line Feed' + #13 +
470 'character to end lines. This is the DOS convention; your checked-out files' + #13 +
471 'might not be handled gracefully by MSYS / Cygwin command line utilities.';
472 Left:=ScaleX(28);
473 Top:=ScaleY(100);
474 Width:=ScaleX(364);
475 Height:=ScaleY(47);
476 end;
478 // 3rd choice
479 RdbCRLF[GC_CRLFCommitAsIs]:=TRadioButton.Create(CRLFPage);
480 with RdbCRLF[GC_CRLFCommitAsIs] do begin
481 Parent:=CRLFPage.Surface;
482 Caption:='Commit line endings as they are';
483 Left:=ScaleX(4);
484 Top:=ScaleY(152);
485 Width:=ScaleX(329);
486 Height:=ScaleY(17);
487 Font.Style:=[fsBold];
488 TabOrder:=2;
489 Checked:=False;
490 end;
491 LblCRLFCommitAsIs:=TLabel.Create(CRLFPage);
492 with LblCRLFCommitAsIs do begin
493 Parent:=CRLFPage.Surface;
494 Caption:=
495 'Choose this if you know what you are doing and want to track the files with' + #13 +
496 'the line endings exactly as they appear in the files. This option might' + #13 +
497 'cause your projects to be hard to use on other platforms.';
498 Left:=ScaleX(28);
499 Top:=ScaleY(176);
500 Width:=ScaleX(364);
501 Height:=ScaleY(47);
502 end;
504 // Restore the setting chosen during a previous install.
505 Data:=GetPreviousData('CRLF Option','CRLFAlways');
506 if Data='LFOnly' then begin
507 RdbCRLF[GC_LFOnly].Checked:=True;
508 end else if Data='CRLFAlways' then begin
509 RdbCRLF[GC_CRLFAlways].Checked:=True;
510 end else if Data='CRLFCommitAsIs' then begin
511 RdbCRLF[GC_CRLFCommitAsIs].Checked:=True;
512 end;
513 end;
515 function NextButtonClick(CurPageID:Integer):Boolean;
516 begin
517 if CurPageID<>PuTTYPage.ID then begin
518 Result:=True;
519 Exit;
520 end;
522 Result:=RdbSSH[GS_OpenSSH].Checked
523 or (RdbSSH[GS_PLink].Checked and FileExists(EdtPLink.Text));
525 if not Result then begin
526 MsgBox('Please enter a valid path to plink.exe.',mbError,MB_OK);
527 end;
528 end;
530 procedure CurStepChanged(CurStep:TSetupStep);
532 AppDir,FileName,Cmd,Msg:string;
533 BuiltIns,EnvPath,EnvHome,EnvSSH:TArrayOfString;
534 Count,i:Longint;
535 IsNTFS:Boolean;
536 FindRec:TFindRec;
537 RootKey:Integer;
538 begin
539 if CurStep<>ssPostInstall then begin
540 Exit;
541 end;
543 AppDir:=ExpandConstant('{app}');
546 Create the built-ins
549 // Load the built-ins from a text file.
550 FileName:=ExpandConstant('{app}\'+'{#emit APP_BUILTINS}');
551 if LoadStringsFromFile(FileName,BuiltIns) then begin
552 // Check if we are running on NTFS.
553 IsNTFS:=False;
554 if SetNTFSCompression(AppDir+'\bin\git.exe',true) then begin
555 IsNTFS:=SetNTFSCompression(AppDir+'\bin\git.exe',false);
556 end;
558 Count:=GetArrayLength(BuiltIns)-1;
560 // Delete all scripts as they might have been replaced by built-ins by now.
561 for i:=0 to Count do begin
562 FileName:=ChangeFileExt(AppDir+'\'+BuiltIns[i],'');
563 if (FileExists(FileName) and (not DeleteFile(FileName))) then begin
564 Log('Line {#emit __LINE__}: Unable to delete script "'+FileName+'", ignoring.');
565 end;
566 end;
568 // Map the built-ins to git.exe.
569 if IsNTFS then begin
570 Log('Line {#emit __LINE__}: Partition seems to be NTFS, trying to create built-in aliases as hard links.');
572 for i:=0 to Count do begin
573 FileName:=AppDir+'\'+BuiltIns[i];
575 // On non-NTFS partitions, create hard links.
576 if (FileExists(FileName) and (not DeleteFile(FileName)))
577 or (not CreateHardLink(FileName,AppDir+'\bin\git.exe',0)) then begin
578 Log('Line {#emit __LINE__}: Unable to create hard link "'+FileName+'", will try to copy files.');
579 IsNTFS:=False;
580 Break;
581 end;
582 end;
584 Log('Line {#emit __LINE__}: Successfully created built-in aliases as hard links.');
585 end;
587 // The fallback is to copy the files.
588 if not IsNTFS then begin
589 Log('Line {#emit __LINE__}: Trying to create built-in aliases as file copies.');
591 for i:=0 to Count do begin
592 FileName:=AppDir+'\'+BuiltIns[i];
594 // On non-NTFS partitions, copy simply the files (overwriting existing ones).
595 if not FileCopy(AppDir+'\bin\git.exe',FileName,false) then begin
596 Msg:='Line {#emit __LINE__}: Unable to create file copy "'+FileName+'".';
597 MsgBox(Msg,mbError,MB_OK);
598 Log(Msg);
599 // This is not a critical error, Git could basically be used without the
600 // aliases for built-ins, so we continue.
601 end;
602 end;
604 Log('Line {#emit __LINE__}: Successfully created built-in aliases as file copies.');
605 end;
607 // Delete any duplicate files in case we are updating from a non-libexec to a libexec directory layout.
608 if FindFirst(AppDir+'\libexec\git-core\*',FindRec) then begin
609 repeat
610 if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY)=0 then begin
611 FileName:=AppDir+'\bin\'+FindRec.name;
612 if (FileExists(FileName) and (not DeleteFile(FileName))) then begin
613 Log('Line {#emit __LINE__}: Unable to delete dupe "'+FileName+'", ignoring.');
614 end;
615 end;
616 until not FindNext(FindRec);
617 FindClose(FindRec);
618 end;
619 end else begin
620 Msg:='Line {#emit __LINE__}: Unable to read file "{#emit APP_BUILTINS}".';
621 MsgBox(Msg,mbError,MB_OK);
622 Log(Msg);
623 // This is not a critical error, Git could basically be used without the
624 // aliases for built-ins, so we continue.
625 end;
628 Adapt core.autocrlf
631 if RdbCRLF[GC_LFOnly].checked then begin
632 Cmd:='core.autoCRLF input';
633 end else if RdbCRLF[GC_CRLFAlways].checked then begin
634 Cmd:='core.autoCRLF true';
635 end else begin
636 Cmd:='core.autoCRLF false';
637 end;
638 if not Exec(AppDir + '\bin\git-config.exe', '-f gitconfig ' + Cmd,
639 AppDir + '\etc', SW_HIDE, ewWaitUntilTerminated, i) then begin
640 Msg:='Could not set CR/LF behavior: ' + Cmd;
641 MsgBox(Msg,mbError,MB_OK);
642 Log(Msg);
643 end;
646 Modify the environment
648 This must happen no later than ssPostInstall to make
649 "ChangesEnvironment=yes" not happend before the change!
652 FileName:=AppDir+'\setup.ini';
654 // Delete GIT_SSH if a previous installation modified it.
655 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
656 if (GetArrayLength(EnvSSH)=1) and
657 (CompareStr(EnvSSH[0],GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
658 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
659 Msg:='Line {#emit __LINE__}: Unable to reset GIT_SSH prior to install.';
660 MsgBox(Msg,mbError,MB_OK);
661 Log(Msg);
662 // This is not a critical error, the user can probably fix it manually,
663 // so we continue.
664 end;
665 end;
667 if RdbSSH[GS_PLink].Checked then begin
668 SetArrayLength(EnvSSH,1);
669 EnvSSH[0]:=EdtPLink.Text;
670 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
671 Msg:='Line {#emit __LINE__}: Unable to set the GIT_SSH environment variable.';
672 MsgBox(Msg,mbError,MB_OK);
673 Log(Msg);
674 // This is not a critical error, the user can probably fix it manually,
675 // so we continue.
676 end;
678 // Mark that we have changed GIT_SSH.
679 if not SetIniString('Environment','GIT_SSH',EnvSSH[0],FileName) then begin
680 Msg:='Line {#emit __LINE__}: Unable to write to file "'+FileName+'".';
681 MsgBox(Msg,mbError,MB_OK);
682 Log(Msg);
683 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
684 // so we continue.
685 end;
686 end;
688 // Get the current user's directories in PATH.
689 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
691 // First, remove the installation directory from PATH in any case.
692 for i:=0 to GetArrayLength(EnvPath)-1 do begin
693 if Pos(AppDir,EnvPath[i])=1 then begin
694 EnvPath[i]:='';
695 end;
696 end;
698 // Delete HOME if a previous installation modified it.
699 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
700 if (GetArrayLength(EnvHome)=1) and
701 (CompareStr(EnvHome[0],GetIniString('Environment','HOME','',FileName))=0) then begin
702 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
703 Msg:='Line {#emit __LINE__}: Unable to reset HOME prior to install.';
704 MsgBox(Msg,mbError,MB_OK);
705 Log(Msg);
706 // This is not a critical error, the user can probably fix it manually,
707 // so we continue.
708 end;
709 end;
711 // Modify the PATH variable as requested by the user.
712 if RdbPath[GP_Cmd].Checked or RdbPath[GP_CmdTools].Checked then begin
713 i:=GetArrayLength(EnvPath);
714 SetArrayLength(EnvPath,i+1);
716 // List \cmd before \bin so \cmd has higher priority and programs in
717 // there will be called in favor of those in \bin.
718 EnvPath[i]:=ExpandConstant('{app}\cmd');
720 if RdbPath[GP_CmdTools].Checked then begin
721 SetArrayLength(EnvPath,i+3);
722 EnvPath[i+1]:=ExpandConstant('{app}\bin');
723 EnvPath[i+2]:=ExpandConstant('{app}\libexec\git-core');
725 // Set HOME for the Windows Command Prompt, but only if it has not been set manually before.
726 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
727 i:=GetArrayLength(EnvHome);
728 if (i=0) or ((i=1) and (Length(EnvHome[0])=0)) then begin
729 SetArrayLength(EnvHome,1);
730 EnvHome[0]:=ExpandConstant('{%USERPROFILE}');
731 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,EnvHome) then begin
732 Msg:='Line {#emit __LINE__}: Unable to set the HOME environment variable.';
733 MsgBox(Msg,mbError,MB_OK);
734 Log(Msg);
735 // This is not a critical error, the user can probably fix it manually,
736 // so we continue.
737 end;
739 // Mark that we have changed HOME.
740 if not SetIniString('Environment','HOME',EnvHome[0],FileName) then begin
741 Msg:='Line {#emit __LINE__}: Unable to write to file "'+FileName+'".';
742 MsgBox(Msg,mbError,MB_OK);
743 Log(Msg);
744 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
745 // so we continue.
746 end;
747 end;
748 end;
749 end;
751 // Set the current user's PATH directories.
752 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
753 Msg:='Line {#emit __LINE__}: Unable to set the PATH 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;
761 Create the Windows Explorer shell extensions
764 if IsAdminLoggedOn then begin
765 RootKey:=HKEY_LOCAL_MACHINE;
766 end else begin
767 RootKey:=HKEY_CURRENT_USER;
768 end;
770 if IsTaskSelected('shellextension') then begin
771 Cmd:=ExpandConstant('"{syswow64}\cmd.exe"');
772 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell','','Git Ba&sh Here'))
773 or (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','',Cmd+' /c "pushd "%1" && "'+AppDir+'\bin\sh.exe" --login -i"')) then begin
774 Msg:='Line {#emit __LINE__}: Unable to create "Git Bash Here" shell extension.';
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;
780 end;
782 if IsTaskSelected('guiextension') then begin
783 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui','','Git &GUI Here'))
784 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
785 Msg:='Line {#emit __LINE__}: Unable to create "Git GUI Here" shell extension.';
786 MsgBox(Msg,mbError,MB_OK);
787 Log(Msg);
788 // This is not a critical error, the user can probably fix it manually,
789 // so we continue.
790 end;
791 end;
792 end;
794 procedure RegisterPreviousData(PreviousDataKey:Integer);
796 Data:string;
797 begin
798 // Git Path options.
799 Data:='';
800 if RdbPath[GP_BashOnly].Checked then begin
801 Data:='BashOnly';
802 end else if RdbPath[GP_Cmd].Checked then begin
803 Data:='Cmd';
804 end else if RdbPath[GP_CmdTools].Checked then begin
805 Data:='CmdTools';
806 end;
807 SetPreviousData(PreviousDataKey,'Path Option',Data);
809 // Git SSH options.
810 Data:='';
811 if RdbSSH[GS_OpenSSH].Checked then begin
812 Data:='OpenSSH';
813 end else if RdbSSH[GS_PLink].Checked then begin
814 Data:='PLink';
815 end;
816 SetPreviousData(PreviousDataKey,'SSH Option',Data);
818 // Git CR/LF options.
819 Data:='';
820 if RdbCRLF[GC_LFOnly].Checked then begin
821 Data:='LFOnly';
822 end else if RdbCRLF[GC_CRLFAlways].Checked then begin
823 Data:='CRLFAlways';
824 end else if RdbCRLF[GC_CRLFCommitAsIs].Checked then begin
825 Data:='CRLFCommitAsIs';
826 end;
827 SetPreviousData(PreviousDataKey,'CRLF Option',Data);
828 end;
831 Uninstaller code
834 function InitializeUninstall:Boolean;
836 FileName,NewName,Msg:string;
837 begin
838 FileName:=ExpandConstant('{app}')+'\bin\ssh-agent.exe';
839 if FileExists(FileName) then begin
840 // Create a temporary copy of the file we try to delete.
841 NewName:=FileName+'.'+IntToStr(1000+Random(9000));
842 Result:=FileCopy(FileName,NewName,True) and DeleteFile(FileName);
844 if not Result then begin
845 Msg:='Line {#emit __LINE__}: Please stop all ssh-agent processes and run uninstall again.';
846 MsgBox(Msg,mbError,MB_OK);
847 Log(Msg);
849 // Clean-up the temporary copy (ignoring any errors).
850 DeleteFile(NewName);
851 end else begin
852 // Clean-up the temporary copy (ignoring any errors).
853 RenameFile(NewName,FileName);
854 end;
855 end else begin
856 Result:=True;
857 end;
858 end;
860 procedure CurUninstallStepChanged(CurUninstallStep:TUninstallStep);
862 AppDir,Command,Msg:string;
863 EnvPath,EnvHome,EnvSSH:TArrayOfString;
864 i:Longint;
865 RootKey:Integer;
866 begin
867 if CurUninstallStep<>usUninstall then begin
868 Exit;
869 end;
872 Modify the environment
874 This must happen no later than usUninstall to make
875 "ChangesEnvironment=yes" not happend before the change!
878 AppDir:=ExpandConstant('{app}');
879 Command:=AppDir+'\setup.ini';
881 // Reset the current user's GIT_SSH if we modified it.
882 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
883 if (GetArrayLength(EnvSSH)=1) and
884 (CompareStr(EnvSSH[0],GetIniString('Environment','GIT_SSH','',Command))=0) then begin
885 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
886 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to GIT_SSH.';
887 MsgBox(Msg,mbError,MB_OK);
888 Log(Msg);
889 // This is not a critical error, the user can probably fix it manually,
890 // so we continue.
891 end;
892 end;
894 // Get the current user's directories in PATH.
895 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
897 // Remove the installation directory from PATH in any case, even if it
898 // was not added by the installer.
899 for i:=0 to GetArrayLength(EnvPath)-1 do begin
900 if Pos(AppDir,EnvPath[i])=1 then begin
901 EnvPath[i]:='';
902 end;
903 end;
905 // Reset the current user's directories in PATH.
906 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
907 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to PATH.';
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;
914 // Reset the current user's HOME if we modified it.
915 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
916 if (GetArrayLength(EnvHome)=1) and
917 (CompareStr(EnvHome[0],GetIniString('Environment','HOME','',Command))=0) then begin
918 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
919 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to HOME.';
920 MsgBox(Msg,mbError,MB_OK);
921 Log(Msg);
922 // This is not a critical error, the user can probably fix it manually,
923 // so we continue.
924 end;
925 end;
927 if (FileExists(Command) and (not DeleteFile(Command))) then begin
928 Msg:='Line {#emit __LINE__}: Unable to delete file "'+Command+'".';
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;
936 Delete the Windows Explorer shell extensions
939 if IsAdminLoggedOn then begin
940 RootKey:=HKEY_LOCAL_MACHINE;
941 end else begin
942 RootKey:=HKEY_CURRENT_USER;
943 end;
945 Command:='';
946 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','',Command);
947 if Pos(AppDir,Command)>0 then begin
948 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell') then begin
949 Msg:='Line {#emit __LINE__}: Unable to remove "Git Bash Here" shell extension.';
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;
955 end;
957 Command:='';
958 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui\command','',Command);
959 if Pos(AppDir,Command)>0 then begin
960 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui') then begin
961 Msg:='Line {#emit __LINE__}: Unable to remove "Git GUI Here" shell extension.';
962 MsgBox(Msg,mbError,MB_OK);
963 Log(Msg);
964 // This is not a critical error, the user can probably fix it manually,
965 // so we continue.
966 end;
967 end;
968 end;