Installer: Move code to delete context menu entries to a separate function
[msysgit.git] / share / WinGit / install.iss
blob44026b7208d3aad04e5e8d21f4665baf2bc6a146
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 ; Uncomment the line below to be able to compile the script from within the IDE.
14 ;SourceDir={#GetEnv('TEMP')}\WinGit
16 ; Installer-related
17 AllowNoIcons=yes
18 AppName={#emit APP_NAME}
19 AppPublisherURL={#emit APP_URL}
20 AppVersion={#emit APP_VERSION}
21 AppVerName={#emit APP_NAME+' '+APP_VERSION}
22 ChangesEnvironment=yes
23 DefaultDirName={pf}\{#emit APP_NAME}
24 DefaultGroupName={#emit APP_NAME}
25 DisableReadyPage=yes
26 InfoBeforeFile=gpl-2.0.rtf
27 PrivilegesRequired=none
28 UninstallDisplayIcon=etc\git.ico
30 ; Cosmetic
31 SetupIconFile=etc\git.ico
32 WizardImageBackColor=clWhite
33 WizardImageStretch=no
34 WizardImageFile=git.bmp
35 WizardSmallImageFile=gitsmall.bmp
37 [Types]
38 ; Define a dummy type to avoid getting the default ones.
39 Name: custom; Description: Custom installation; Flags: iscustom
41 [Components]
42 Name: icons; Description: Additional icons; Types: custom
43 Name: icons\quicklaunch; Description: In the Quick Launch; Types: custom
44 Name: icons\desktop; Description: On the Desktop; Types: custom
45 Name: ext; Description: Windows Explorer integration; Types: custom
46 Name: ext\shellhere; Description: """Git Bash Here"" context menu entry"; Types: custom
47 Name: ext\guihere; Description: """Git GUI Here"" context menu entry"; Types: custom
48 Name: assoc; Description: Associate .git* configuration files with the default text editor; Types: custom
49 Name: consolefont; Description: Use a TrueType font in the console (required for proper character encoding); Types: custom
51 [Files]
52 Source: *; DestDir: {app}; Excludes: \*.bmp, gpl-2.0.rtf, \*.iss, \tmp.*, \bin\*install*; Flags: recursesubdirs replacesameversion
53 Source: ReleaseNotes.rtf; DestDir: {app}; Flags: isreadme replacesameversion
55 [Icons]
56 Name: {group}\Git GUI; Filename: {app}\bin\wish.exe; Parameters: """{app}\libexec\git-core\git-gui"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico
57 Name: {group}\Git Bash; Filename: {syswow64}\cmd.exe; Parameters: "/c """"{app}\bin\sh.exe"" --login -i"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico
58 Name: {group}\Uninstall Git; Filename: {uninstallexe}
59 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; Components: icons\quicklaunch
60 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; Components: icons\desktop
62 ; 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.
63 Name: {app}\Git Bash; Filename: {syswow64}\cmd.exe; Parameters: "/c """"{app}\bin\sh.exe"" --login -i"""; IconFilename: {app}\etc\git.ico
65 [Messages]
66 BeveledLabel={#emit APP_URL}
67 SetupAppTitle={#emit APP_NAME} Setup
68 SetupWindowTitle={#emit APP_NAME} Setup
70 [Registry]
71 ; There is no "Console" key in HKLM.
72 Root: HKCU; Subkey: Console; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Components: consolefont
73 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Components: consolefont
74 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Components: consolefont
75 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Components: consolefont
77 Root: HKCU; Subkey: Console\Git Bash; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: createvalueifdoesntexist
78 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Flags: createvalueifdoesntexist
79 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Flags: createvalueifdoesntexist
80 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Flags: createvalueifdoesntexist
82 ; File associations for configuration files that may be contained in a repository (so this does not include ".gitconfig").
83 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist; Check: IsAdminLoggedOn; Components: assoc
84 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist; Check: IsAdminLoggedOn; Components: assoc
85 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist; Check: IsAdminLoggedOn; Components: assoc
86 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist; Check: not IsAdminLoggedOn; Components: assoc
87 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist; Check: not IsAdminLoggedOn; Components: assoc
88 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist; Check: not IsAdminLoggedOn; Components: assoc
90 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist; Check: IsAdminLoggedOn; Components: assoc
91 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist; Check: IsAdminLoggedOn; Components: assoc
92 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist; Check: IsAdminLoggedOn; Components: assoc
93 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist; Check: not IsAdminLoggedOn; Components: assoc
94 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist; Check: not IsAdminLoggedOn; Components: assoc
95 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist; Check: not IsAdminLoggedOn; Components: assoc
97 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist; Check: IsAdminLoggedOn; Components: assoc
98 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist; Check: IsAdminLoggedOn; Components: assoc
99 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist; Check: IsAdminLoggedOn; Components: assoc
100 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist; Check: not IsAdminLoggedOn; Components: assoc
101 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist; Check: not IsAdminLoggedOn; Components: assoc
102 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist; Check: not IsAdminLoggedOn; Components: assoc
104 [UninstallDelete]
105 ; Delete the built-ins.
106 Type: files; Name: {app}\bin\git-*.exe
107 Type: files; Name: {app}\libexec\git-core\git-*.exe
108 Type: files; Name: {app}\libexec\git-core\git.exe
110 ; Delete a home directory inside the msysGit directory.
111 Type: dirifempty; Name: {app}\home\{username}
112 Type: dirifempty; Name: {app}\home
114 [Code]
115 #include "helpers.inc.iss"
116 #include "putty.inc.iss"
118 function CreateHardLink(lpFileName,lpExistingFileName:String;lpSecurityAttributes:Integer):Boolean;
119 #ifdef UNICODE
120 external 'CreateHardLinkW@Kernel32.dll stdcall delayload setuponly';
121 #else
122 external 'CreateHardLinkA@Kernel32.dll stdcall delayload setuponly';
123 #endif
125 function CreateSymbolicLink(lpSymlinkFileName,lpTargetFileName:String;dwFlags:DWORD):Boolean;
126 #ifdef UNICODE
127 external 'CreateSymbolicLinkW@Kernel32.dll stdcall delayload setuponly';
128 #else
129 external 'CreateSymbolicLinkA@Kernel32.dll stdcall delayload setuponly';
130 #endif
132 const
133 // Git Path options.
134 GP_BashOnly = 1;
135 GP_Cmd = 2;
136 GP_CmdTools = 3;
138 // Git SSH options.
139 GS_OpenSSH = 1;
140 GS_Plink = 2;
142 // Git line ending conversion options.
143 GC_LFOnly = 1;
144 GC_CRLFAlways = 2;
145 GC_CRLFCommitAsIs = 3;
148 // Wizard page and variables for the Path options.
149 PathPage:TWizardPage;
150 RdbPath:array[GP_BashOnly..GP_CmdTools] of TRadioButton;
152 // Wizard page and variables for the SSH options.
153 PuTTYPage:TWizardPage;
154 RdbSSH:array[GS_OpenSSH..GS_Plink] of TRadioButton;
155 EdtPlink:TEdit;
157 // Wizard page and variables for the line ending conversion options.
158 CRLFPage:TWizardPage;
159 RdbCRLF:array[GC_LFOnly..GC_CRLFCommitAsIs] of TRadioButton;
161 procedure BrowseForPuTTYFolder(Sender:TObject);
163 Path:String;
164 begin
165 Path:=ExtractFilePath(EdtPlink.Text);
166 BrowseForFolder('Please select the PuTTY folder:',Path,False);
167 if FileExists(Path+'\TortoisePlink.exe') then begin
168 EdtPlink.Text:=Path+'\TortoisePlink.exe';
169 RdbSSH[GS_Plink].Checked:=True;
170 end else if FileExists(Path+'\plink.exe') then begin
171 EdtPlink.Text:=Path+'\plink.exe';
172 RdbSSH[GS_Plink].Checked:=True;
173 end else begin
174 MsgBox('Please enter a valid path to "TortoisePlink.exe" or "plink.exe".',mbError,MB_OK);
175 end;
176 end;
178 procedure DeleteContextMenuEntries;
180 AppDir,Command,Msg:String;
181 RootKey:Integer;
182 begin
183 AppDir:=ExpandConstant('{app}');
185 if IsAdminLoggedOn then begin
186 RootKey:=HKEY_LOCAL_MACHINE;
187 end else begin
188 RootKey:=HKEY_CURRENT_USER;
189 end;
191 Command:='';
192 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','',Command);
193 if Pos(AppDir,Command)>0 then begin
194 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell') then begin
195 Msg:='Line {#emit __LINE__}: Unable to remove "Git Bash Here" shell extension.';
196 MsgBox(Msg,mbError,MB_OK);
197 Log(Msg);
198 // This is not a critical error, the user can probably fix it manually,
199 // so we continue.
200 end;
201 end;
203 Command:='';
204 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui\command','',Command);
205 if Pos(AppDir,Command)>0 then begin
206 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui') then begin
207 Msg:='Line {#emit __LINE__}: Unable to remove "Git GUI Here" shell extension.';
208 MsgBox(Msg,mbError,MB_OK);
209 Log(Msg);
210 // This is not a critical error, the user can probably fix it manually,
211 // so we continue.
212 end;
213 end;
214 end;
217 Installer code
220 procedure InitializeWizard;
222 PrevPageID:Integer;
223 LblGitBash,LblGitCmd,LblGitCmdTools,LblGitCmdToolsWarn:TLabel;
224 LblOpenSSH,LblPlink:TLabel;
225 PuTTYSessions:TArrayOfString;
226 LblLFOnly,LblCRLFAlways,LblCRLFCommitAsIs:TLabel;
227 BtnPlink:TButton;
228 Data:String;
229 begin
230 PrevPageID:=wpSelectProgramGroup;
233 * Create a custom page for modifying the environment.
236 PathPage:=CreateCustomPage(
237 PrevPageID,
238 'Adjusting your PATH environment',
239 'How would you like to use Git from the command line?'
241 PrevPageID:=PathPage.ID;
243 // 1st choice
244 RdbPath[GP_BashOnly]:=TRadioButton.Create(PathPage);
245 with RdbPath[GP_BashOnly] do begin
246 Parent:=PathPage.Surface;
247 Caption:='Use Git Bash only';
248 Left:=ScaleX(4);
249 Top:=ScaleY(8);
250 Width:=ScaleX(129);
251 Height:=ScaleY(17);
252 Font.Style:=[fsBold];
253 TabOrder:=0;
254 Checked:=True;
255 end;
256 LblGitBash:=TLabel.Create(PathPage);
257 with LblGitBash do begin
258 Parent:=PathPage.Surface;
259 Caption:=
260 'This is the most conservative choice if you are concerned about the stability' + #13 +
261 'of your system. Your PATH will not be modified.';
262 Left:=ScaleX(28);
263 Top:=ScaleY(32);
264 Width:=ScaleX(405);
265 Height:=ScaleY(26);
266 end;
268 // 2nd choice
269 RdbPath[GP_Cmd]:=TRadioButton.Create(PathPage);
270 with RdbPath[GP_Cmd] do begin
271 Parent:=PathPage.Surface;
272 Caption:='Run Git from the Windows Command Prompt';
273 Left:=ScaleX(4);
274 Top:=ScaleY(76);
275 Width:=ScaleX(281);
276 Height:=ScaleY(17);
277 Font.Style:=[fsBold];
278 TabOrder:=1;
279 end;
280 LblGitCmd:=TLabel.Create(PathPage);
281 with LblGitCmd do begin
282 Parent:=PathPage.Surface;
283 Caption:=
284 'This option is considered safe and no conflicts with other tools are known.' + #13 +
285 'Only Git will be added to your PATH. Use this option if you want to use Git' + #13 +
286 'from a Cygwin Prompt (make sure to not have Cygwin''s Git installed).';
287 Left:=ScaleX(28);
288 Top:=ScaleY(100);
289 Width:=ScaleX(405);
290 Height:=ScaleY(39);
291 end;
293 // 3rd choice
294 RdbPath[GP_CmdTools]:=TRadioButton.Create(PathPage);
295 with RdbPath[GP_CmdTools] do begin
296 Parent:=PathPage.Surface;
297 Caption:='Run Git and included Unix tools from the Windows Command Prompt';
298 Left:=ScaleX(4);
299 Top:=ScaleY(152);
300 Width:=ScaleX(405);
301 Height:=ScaleY(17);
302 Font.Style:=[fsBold];
303 TabOrder:=2;
304 end;
305 LblGitCmdTools:=TLabel.Create(PathPage);
306 with LblGitCmdTools do begin
307 Parent:=PathPage.Surface;
308 Caption:='Both Git and its accompanying Unix tools will be added to your PATH.';
309 Left:=ScaleX(28);
310 Top:=ScaleY(176);
311 Width:=ScaleX(405);
312 Height:=ScaleY(13);
313 end;
314 LblGitCmdToolsWarn:=TLabel.Create(PathPage);
315 with LblGitCmdToolsWarn do begin
316 Parent:=PathPage.Surface;
317 Caption:=
318 'Warning: This will override Windows tools like find.exe and' + #13 +
319 'sort.exe. Select this option only if you understand the implications.';
320 Left:=ScaleX(28);
321 Top:=ScaleY(192);
322 Width:=ScaleX(376);
323 Height:=ScaleY(26);
324 Font.Color:=255;
325 Font.Style:=[fsBold];
326 end;
328 // Restore the setting chosen during a previous install.
329 Data:=GetPreviousData('Path Option','BashOnly');
330 if Data='BashOnly' then begin
331 RdbPath[GP_BashOnly].Checked:=True;
332 end else if Data='Cmd' then begin
333 RdbPath[GP_Cmd].Checked:=True;
334 end else if Data='CmdTools' then begin
335 RdbPath[GP_CmdTools].Checked:=True;
336 end;
339 * Create a custom page for using (Tortoise)Plink instead of OpenSSH
340 * if at least one PuTTY session is found in the Registry.
343 if RegGetSubkeyNames(HKEY_CURRENT_USER,'Software\SimonTatham\PuTTY\Sessions',PuTTYSessions) and (GetArrayLength(PuTTYSessions)>0) then begin
344 PuTTYPage:=CreateCustomPage(
345 PrevPageID,
346 'Choosing the SSH executable',
347 'Which Secure Shell client program would you like Git to use?'
349 PrevPageID:=PuTTYPage.ID;
351 // 1st choice
352 RdbSSH[GS_OpenSSH]:=TRadioButton.Create(PuTTYPage);
353 with RdbSSH[GS_OpenSSH] do begin
354 Parent:=PuTTYPage.Surface;
355 Caption:='Use OpenSSH';
356 Left:=ScaleX(4);
357 Top:=ScaleY(8);
358 Width:=ScaleX(129);
359 Height:=ScaleY(17);
360 Font.Style:=[fsBold];
361 TabOrder:=0;
362 Checked:=True;
363 end;
364 LblOpenSSH:=TLabel.Create(PuTTYPage);
365 with LblOpenSSH do begin
366 Parent:=PuTTYPage.Surface;
367 Caption:=
368 'This uses ssh.exe that comes with Git. The GIT_SSH and SVN_SSH' + #13 +
369 'environment variables will not be modified.';
370 Left:=ScaleX(28);
371 Top:=ScaleY(32);
372 Width:=ScaleX(324);
373 Height:=ScaleY(26);
374 end;
376 // 2nd choice
377 RdbSSH[GS_Plink]:=TRadioButton.Create(PuTTYPage);
378 with RdbSSH[GS_Plink] do begin
379 Parent:=PuTTYPage.Surface;
380 Caption:='Use (Tortoise)Plink';
381 Left:=ScaleX(4);
382 Top:=ScaleY(76);
383 Width:=ScaleX(281);
384 Height:=ScaleY(17);
385 Font.Style:=[fsBold];
386 TabOrder:=1;
387 end;
388 LblPlink:=TLabel.Create(PuTTYPage);
389 with LblPlink do begin
390 Parent:=PuTTYPage.Surface;
391 Caption:=
392 'PuTTY sessions were found in your Registry. You may specify the path' + #13 +
393 'to an existing copy of (Tortoise)Plink.exe from the TortoiseSVN/CVS' + #13 +
394 'or PuTTY applications. The GIT_SSH and SVN_SSH environment' + #13 +
395 'variables will be adjusted to point to the following executable:';
396 Left:=ScaleX(28);
397 Top:=ScaleY(100);
398 Width:=ScaleX(340);
399 Height:=ScaleY(52);
400 end;
401 EdtPlink:=TEdit.Create(PuTTYPage);
402 with EdtPlink do begin
403 Parent:=PuTTYPage.Surface;
404 Text:=GetPuTTYLocation;
405 if not FileExists(Text) then begin
406 Text:='';
407 end;
408 Left:=ScaleX(28);
409 Top:=ScaleY(161);
410 Width:=ScaleX(316);
411 Height:=ScaleY(13);
412 end;
413 BtnPlink:=TButton.Create(PuTTYPage);
414 with BtnPlink do begin
415 Parent:=PuTTYPage.Surface;
416 Caption:='...';
417 OnClick:=@BrowseForPuTTYFolder;
418 Left:=ScaleX(348);
419 Top:=ScaleY(161);
420 Width:=ScaleX(21);
421 Height:=ScaleY(21);
422 end;
424 // Restore the setting chosen during a previous install.
425 Data:=GetPreviousData('SSH Option','OpenSSH');
426 if Data='OpenSSH' then begin
427 RdbSSH[GS_OpenSSH].Checked:=True;
428 end else if Data='Plink' then begin
429 RdbSSH[GS_Plink].Checked:=True;
430 end;
431 end else begin
432 PuTTYPage:=NIL;
433 end;
436 * Create a custom page for the core.autocrlf setting.
439 CRLFPage:=CreateCustomPage(
440 PrevPageID,
441 'Configuring the line ending conversions',
442 'How should Git treat line endings in text files?'
444 PrevPageID:=CRLFPage.ID;
446 // 1st choice
447 RdbCRLF[GC_CRLFAlways]:=TRadioButton.Create(CRLFPage);
448 with RdbCRLF[GC_CRLFAlways] do begin
449 Parent:=CRLFPage.Surface;
450 Caption:='Checkout Windows-style, commit Unix-style line endings';
451 Left:=ScaleX(4);
452 Top:=ScaleY(8);
453 Width:=ScaleX(340);
454 Height:=ScaleY(17);
455 Font.Style:=[fsBold];
456 TabOrder:=0;
457 Checked:=True;
458 end;
459 LblCRLFAlways:=TLabel.Create(CRLFPage);
460 with LblCRLFAlways do begin
461 Parent:=CRLFPage.Surface;
462 Caption:=
463 'Git will convert LF to CRLF when checking out text files. When committing' + #13 +
464 'text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
465 'this is the recommended setting on Windows ("core.autocrlf" is set to "true").';
466 Left:=ScaleX(28);
467 Top:=ScaleY(32);
468 Width:=ScaleX(372);
469 Height:=ScaleY(47);
470 end;
472 // 2nd choice
473 RdbCRLF[GC_LFOnly]:=TRadioButton.Create(CRLFPage);
474 with RdbCRLF[GC_LFOnly] do begin
475 Parent:=CRLFPage.Surface;
476 Caption:='Checkout as-is, commit Unix-style line endings';
477 Left:=ScaleX(4);
478 Top:=ScaleY(80);
479 Width:=ScaleX(340);
480 Height:=ScaleY(17);
481 Font.Style:=[fsBold];
482 TabOrder:=1;
483 Checked:=False;
484 end;
485 LblLFOnly:=TLabel.Create(CRLFPage);
486 with LblLFOnly do begin
487 Parent:=CRLFPage.Surface;
488 Caption:=
489 'Git will not perform any conversion when checking out text files. When' + #13 +
490 'committing text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
491 'this is the recommended setting on Unix ("core.autocrlf" is set to "input").';
492 Left:=ScaleX(28);
493 Top:=ScaleY(104);
494 Width:=ScaleX(372);
495 Height:=ScaleY(47);
496 end;
498 // 3rd choice
499 RdbCRLF[GC_CRLFCommitAsIs]:=TRadioButton.Create(CRLFPage);
500 with RdbCRLF[GC_CRLFCommitAsIs] do begin
501 Parent:=CRLFPage.Surface;
502 Caption:='Checkout as-is, commit as-is';
503 Left:=ScaleX(4);
504 Top:=ScaleY(152);
505 Width:=ScaleX(340);
506 Height:=ScaleY(17);
507 Font.Style:=[fsBold];
508 TabOrder:=2;
509 Checked:=False;
510 end;
511 LblCRLFCommitAsIs:=TLabel.Create(CRLFPage);
512 with LblCRLFCommitAsIs do begin
513 Parent:=CRLFPage.Surface;
514 Caption:=
515 'Git will not perform any conversions when checking out or committing' + #13 +
516 'text files. Choosing this option is not recommended for cross-platform' + #13 +
517 'projects ("core.autocrlf" is set to "false").';
518 Left:=ScaleX(28);
519 Top:=ScaleY(176);
520 Width:=ScaleX(372);
521 Height:=ScaleY(47);
522 end;
524 // Restore the setting chosen during a previous install.
525 Data:=GetPreviousData('CRLF Option','CRLFAlways');
526 if Data='LFOnly' then begin
527 RdbCRLF[GC_LFOnly].Checked:=True;
528 end else if Data='CRLFAlways' then begin
529 RdbCRLF[GC_CRLFAlways].Checked:=True;
530 end else if Data='CRLFCommitAsIs' then begin
531 RdbCRLF[GC_CRLFCommitAsIs].Checked:=True;
532 end;
533 end;
535 function NextButtonClick(CurPageID:Integer):Boolean;
536 begin
537 if (PuTTYPage=NIL) or (CurPageID<>PuTTYPage.ID) then begin
538 Result:=True;
539 Exit;
540 end;
542 Result:=RdbSSH[GS_OpenSSH].Checked or
543 (RdbSSH[GS_Plink].Checked and FileExists(EdtPlink.Text));
545 if not Result then begin
546 MsgBox('Please enter a valid path to (Tortoise)Plink.exe.',mbError,MB_OK);
547 end;
548 end;
550 // AfterInstall
552 // Even though the name of this procedure suggests otherwise most of the
553 // code below is only executed once after the regular installation code
554 // is finished. This happens because of the if-guard right in the
555 // beginning of this procedure.
556 procedure CurStepChanged(CurStep:TSetupStep);
558 AppDir,FileName,Cmd,Msg:String;
559 BuiltIns,EnvPath,EnvHome,EnvSSH:TArrayOfString;
560 Count,i:Longint;
561 LinkCreated:Boolean;
562 FindRec:TFindRec;
563 RootKey:Integer;
564 begin
565 if CurStep<>ssPostInstall then begin
566 Exit;
567 end;
569 AppDir:=ExpandConstant('{app}');
572 Create the built-ins
575 // Load the built-ins from a text file.
576 FileName:=ExpandConstant('{app}\{#emit APP_BUILTINS}');
577 if LoadStringsFromFile(FileName,BuiltIns) then begin
578 Count:=GetArrayLength(BuiltIns)-1;
580 // Delete those scripts from "bin" which have been replaced by built-ins in "libexec\git-core".
581 for i:=0 to Count do begin
582 FileName:=AppDir+'\bin\'+ChangeFileExt(ExtractFileName(BuiltIns[i]),'');
583 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
584 Log('Line {#emit __LINE__}: Unable to delete script "'+FileName+'", ignoring.');
585 end;
586 end;
588 // Create built-ins as aliases for git.exe.
589 for i:=0 to Count do begin
590 FileName:=AppDir+'\'+BuiltIns[i];
592 // Delete any existing built-in.
593 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
594 Log('Line {#emit __LINE__}: Unable to delete existing built-in "'+FileName+'", skipping.');
595 continue;
596 end;
599 // This will throw an exception on pre-WinVista systems.
600 LinkCreated:=CreateSymbolicLink(FileName,AppDir+'\bin\git.exe',0);
601 except
602 LinkCreated:=False;
603 Log('Line {#emit __LINE__}: Creating symbolic link "'+FileName+'" failed, will try a hard link.');
604 end;
606 if not LinkCreated then begin
608 // This will throw an exception on pre-Win2k systems.
609 LinkCreated:=CreateHardLink(FileName,AppDir+'\bin\git.exe',0);
610 except
611 LinkCreated:=False;
612 Log('Line {#emit __LINE__}: Creating hardlink "'+FileName+'" failed, will try a copy.');
613 end;
614 end;
616 if not LinkCreated then begin
617 if not FileCopy(AppDir+'\bin\git.exe',FileName,False) then begin
618 Log('Line {#emit __LINE__}: Creating copy "'+FileName+'" failed, giving up.');
619 // This is not a critical error, Git could basically be used without the
620 // aliases for built-ins, so we continue.
621 end;
622 end;
623 end;
625 // Delete any duplicate files in case we are updating from a non-libexec to a libexec directory layout.
626 if FindFirst(AppDir+'\libexec\git-core\*',FindRec) then begin
627 repeat
628 if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY)=0 then begin
629 FileName:=AppDir+'\bin\'+FindRec.name;
630 if (Pos(FindRec.name,'git.exe')<>1) and FileExists(FileName) and (not DeleteFile(FileName)) then begin
631 Log('Line {#emit __LINE__}: Unable to delete dupe "'+FileName+'", ignoring.');
632 end;
633 end;
634 until not FindNext(FindRec);
635 FindClose(FindRec);
636 end;
637 end else begin
638 Msg:='Line {#emit __LINE__}: Unable to read file "{#emit APP_BUILTINS}".';
639 MsgBox(Msg,mbError,MB_OK);
640 Log(Msg);
641 // This is not a critical error, Git could basically be used without the
642 // aliases for built-ins, so we continue.
643 end;
646 Adapt core.autocrlf
649 if RdbCRLF[GC_LFOnly].checked then begin
650 Cmd:='core.autocrlf input';
651 end else if RdbCRLF[GC_CRLFAlways].checked then begin
652 Cmd:='core.autocrlf true';
653 end else begin
654 Cmd:='core.autocrlf false';
655 end;
656 if not Exec(AppDir + '\bin\git.exe', 'config -f gitconfig ' + Cmd,
657 AppDir + '\etc', SW_HIDE, ewWaitUntilTerminated, i) then begin
658 Msg:='Unable to configure the line ending conversion: ' + Cmd;
659 MsgBox(Msg,mbError,MB_OK);
660 Log(Msg);
661 end;
664 Modify the environment
666 This must happen no later than ssPostInstall to make
667 "ChangesEnvironment=yes" not happend before the change!
670 FileName:=AppDir+'\setup.ini';
672 // Delete GIT_SSH and SVN_SSH if a previous installation set them (this is required for the GS_OpenSSH case).
673 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
674 if (GetArrayLength(EnvSSH)=1) and
675 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
676 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
677 Msg:='Line {#emit __LINE__}: Unable to reset GIT_SSH prior to install.';
678 MsgBox(Msg,mbError,MB_OK);
679 Log(Msg);
680 // This is not a critical error, the user can probably fix it manually,
681 // so we continue.
682 end;
683 end;
685 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
686 if (GetArrayLength(EnvSSH)=1) and
687 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','SVN_SSH','',FileName))=0) then begin
688 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
689 Msg:='Line {#emit __LINE__}: Unable to reset SVN_SSH prior to install.';
690 MsgBox(Msg,mbError,MB_OK);
691 Log(Msg);
692 // This is not a critical error, the user can probably fix it manually,
693 // so we continue.
694 end;
695 end;
697 if (PuTTYPage<>NIL) and RdbSSH[GS_Plink].Checked then begin
698 SetArrayLength(EnvSSH,1);
699 EnvSSH[0]:=EdtPlink.Text;
701 // Set GIT_SSH as specified by the user.
702 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
703 Msg:='Line {#emit __LINE__}: Unable to set the GIT_SSH environment variable.';
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;
710 // Mark that we have changed GIT_SSH by writing its value to a file.
711 if not SetIniString('Environment','GIT_SSH',EnvSSH[0],FileName) then begin
712 Msg:='Line {#emit __LINE__}: Unable to write to file "'+FileName+'".';
713 MsgBox(Msg,mbError,MB_OK);
714 Log(Msg);
715 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
716 // so we continue.
717 end;
719 // Set SVN_SSH as specified by the user, but with escaped backslashes and quotes.
720 StringChangeEx(EnvSSH[0],'\','\\',True);
721 EnvSSH[0]:=AddQuotes(EnvSSH[0]);
723 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
724 Msg:='Line {#emit __LINE__}: Unable to set the SVN_SSH environment variable.';
725 MsgBox(Msg,mbError,MB_OK);
726 Log(Msg);
727 // This is not a critical error, the user can probably fix it manually,
728 // so we continue.
729 end;
731 // Mark that we have changed SVN_SSH by writing its value to a file.
732 if not SetIniString('Environment','SVN_SSH',EnvSSH[0],FileName) then begin
733 Msg:='Line {#emit __LINE__}: Unable to write to file "'+FileName+'".';
734 MsgBox(Msg,mbError,MB_OK);
735 Log(Msg);
736 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
737 // so we continue.
738 end;
739 end;
741 // Get the current user's directories in PATH.
742 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
744 // First, remove the installation directory from PATH in any case.
745 for i:=0 to GetArrayLength(EnvPath)-1 do begin
746 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
747 EnvPath[i]:='';
748 end;
749 end;
751 // Delete HOME if a previous installation modified it.
752 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
753 if (GetArrayLength(EnvHome)=1) and
754 (CompareStr(RemoveQuotes(EnvHome[0]),GetIniString('Environment','HOME','',FileName))=0) then begin
755 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
756 Msg:='Line {#emit __LINE__}: Unable to reset HOME prior to install.';
757 MsgBox(Msg,mbError,MB_OK);
758 Log(Msg);
759 // This is not a critical error, the user can probably fix it manually,
760 // so we continue.
761 end;
762 end;
764 // Modify the PATH variable as requested by the user.
765 if RdbPath[GP_Cmd].Checked or RdbPath[GP_CmdTools].Checked then begin
766 i:=GetArrayLength(EnvPath);
767 SetArrayLength(EnvPath,i+1);
769 // List \cmd before \bin so \cmd has higher priority and programs in
770 // there will be called in favor of those in \bin.
771 EnvPath[i]:=ExpandConstant('{app}\cmd');
773 if RdbPath[GP_CmdTools].Checked then begin
774 SetArrayLength(EnvPath,i+2);
775 EnvPath[i+1]:=ExpandConstant('{app}\bin');
777 // Set HOME for the Windows Command Prompt, but only if it has not been set manually before.
778 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
779 i:=GetArrayLength(EnvHome);
780 if (i=0) or ((i=1) and (Length(EnvHome[0])=0)) then begin
781 SetArrayLength(EnvHome,1);
782 EnvHome[0]:=ExpandConstant('{%HOMEDRIVE}{%HOMEPATH}');
783 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,EnvHome) then begin
784 Msg:='Line {#emit __LINE__}: Unable to set the HOME environment variable.';
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;
791 // Mark that we have changed HOME.
792 if not SetIniString('Environment','HOME',EnvHome[0],FileName) then begin
793 Msg:='Line {#emit __LINE__}: Unable to write to file "'+FileName+'".';
794 MsgBox(Msg,mbError,MB_OK);
795 Log(Msg);
796 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
797 // so we continue.
798 end;
799 end;
800 end;
801 end;
803 // Set the current user's PATH directories.
804 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
805 Msg:='Line {#emit __LINE__}: Unable to set the PATH environment variable.';
806 MsgBox(Msg,mbError,MB_OK);
807 Log(Msg);
808 // This is not a critical error, the user can probably fix it manually,
809 // so we continue.
810 end;
813 Create the Windows Explorer shell extensions
816 if IsAdminLoggedOn then begin
817 RootKey:=HKEY_LOCAL_MACHINE;
818 end else begin
819 RootKey:=HKEY_CURRENT_USER;
820 end;
822 if IsComponentSelected('ext\shellhere') then begin
823 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell','','Git Ba&sh Here')) or
824 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','','wscript "'+AppDir+'\Git Bash.vbs" "%1"')) then begin
825 Msg:='Line {#emit __LINE__}: Unable to create "Git Bash Here" shell extension.';
826 MsgBox(Msg,mbError,MB_OK);
827 Log(Msg);
828 // This is not a critical error, the user can probably fix it manually,
829 // so we continue.
830 end;
831 end;
833 if IsComponentSelected('ext\guihere') then begin
834 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui','','Git &GUI Here')) or
835 (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
836 Msg:='Line {#emit __LINE__}: Unable to create "Git GUI Here" shell extension.';
837 MsgBox(Msg,mbError,MB_OK);
838 Log(Msg);
839 // This is not a critical error, the user can probably fix it manually,
840 // so we continue.
841 end;
842 end;
843 end;
845 procedure RegisterPreviousData(PreviousDataKey:Integer);
847 Data:String;
848 begin
849 // Git Path options.
850 Data:='';
851 if RdbPath[GP_BashOnly].Checked then begin
852 Data:='BashOnly';
853 end else if RdbPath[GP_Cmd].Checked then begin
854 Data:='Cmd';
855 end else if RdbPath[GP_CmdTools].Checked then begin
856 Data:='CmdTools';
857 end;
858 SetPreviousData(PreviousDataKey,'Path Option',Data);
860 // Git SSH options.
861 Data:='';
862 if (PuTTYPage=NIL) or RdbSSH[GS_OpenSSH].Checked then begin
863 Data:='OpenSSH';
864 end else if RdbSSH[GS_Plink].Checked then begin
865 Data:='Plink';
866 end;
867 SetPreviousData(PreviousDataKey,'SSH Option',Data);
869 // Line ending conversion options.
870 Data:='';
871 if RdbCRLF[GC_LFOnly].Checked then begin
872 Data:='LFOnly';
873 end else if RdbCRLF[GC_CRLFAlways].Checked then begin
874 Data:='CRLFAlways';
875 end else if RdbCRLF[GC_CRLFCommitAsIs].Checked then begin
876 Data:='CRLFCommitAsIs';
877 end;
878 SetPreviousData(PreviousDataKey,'CRLF Option',Data);
879 end;
882 Uninstaller code
885 function InitializeUninstall:Boolean;
887 FileName,NewName,Msg:String;
888 begin
889 FileName:=ExpandConstant('{app}\bin\ssh-agent.exe');
890 if FileExists(FileName) then begin
891 // Create a temporary copy of the file we try to delete.
892 NewName:=FileName+'.'+IntToStr(1000+Random(9000));
893 Result:=FileCopy(FileName,NewName,True) and DeleteFile(FileName);
895 if not Result then begin
896 Msg:='Line {#emit __LINE__}: Please stop all ssh-agent processes and run uninstall again.';
897 MsgBox(Msg,mbError,MB_OK);
898 Log(Msg);
900 // Clean-up the temporary copy (ignoring any errors).
901 DeleteFile(NewName);
902 end else begin
903 // Clean-up the temporary copy (ignoring any errors).
904 RenameFile(NewName,FileName);
905 end;
906 end else begin
907 Result:=True;
908 end;
909 end;
911 // PreUninstall
913 // Even though the name of this function suggests otherwise most of the
914 // code below is only executed right before the actual uninstallation.
915 // This happens because of the if-guard right in the beginning of this
916 // function.
917 procedure CurUninstallStepChanged(CurUninstallStep:TUninstallStep);
919 AppDir,Command,Msg:String;
920 EnvPath,EnvHome,EnvSSH:TArrayOfString;
921 i:Longint;
922 begin
923 if CurUninstallStep<>usUninstall then begin
924 Exit;
925 end;
928 Modify the environment
930 This must happen no later than usUninstall to make
931 "ChangesEnvironment=yes" not happend before the change!
934 AppDir:=ExpandConstant('{app}');
935 Command:=AppDir+'\setup.ini';
937 // Delete the current user's GIT_SSH and SVN_SSH if we set it.
938 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
939 if (GetArrayLength(EnvSSH)=1) and
940 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','GIT_SSH','',Command))=0) then begin
941 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
942 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to GIT_SSH.';
943 MsgBox(Msg,mbError,MB_OK);
944 Log(Msg);
945 // This is not a critical error, the user can probably fix it manually,
946 // so we continue.
947 end;
948 end;
950 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
951 if (GetArrayLength(EnvSSH)=1) and
952 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','SVN_SSH','',Command))=0) then begin
953 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
954 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to SVN_SSH.';
955 MsgBox(Msg,mbError,MB_OK);
956 Log(Msg);
957 // This is not a critical error, the user can probably fix it manually,
958 // so we continue.
959 end;
960 end;
962 // Get the current user's directories in PATH.
963 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
965 // Remove the installation directory from PATH in any case, even if it
966 // was not added by the installer.
967 for i:=0 to GetArrayLength(EnvPath)-1 do begin
968 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
969 EnvPath[i]:='';
970 end;
971 end;
973 // Reset the current user's directories in PATH.
974 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
975 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to PATH.';
976 MsgBox(Msg,mbError,MB_OK);
977 Log(Msg);
978 // This is not a critical error, the user can probably fix it manually,
979 // so we continue.
980 end;
982 // Reset the current user's HOME if we modified it.
983 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
984 if (GetArrayLength(EnvHome)=1) and
985 (CompareStr(RemoveQuotes(EnvHome[0]),GetIniString('Environment','HOME','',Command))=0) then begin
986 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
987 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to HOME.';
988 MsgBox(Msg,mbError,MB_OK);
989 Log(Msg);
990 // This is not a critical error, the user can probably fix it manually,
991 // so we continue.
992 end;
993 end;
995 if FileExists(Command) and (not DeleteFile(Command)) then begin
996 Msg:='Line {#emit __LINE__}: Unable to delete file "'+Command+'".';
997 MsgBox(Msg,mbError,MB_OK);
998 Log(Msg);
999 // This is not a critical error, the user can probably fix it manually,
1000 // so we continue.
1001 end;
1004 Delete the Windows Explorer shell extensions
1007 DeleteContextMenuEntries;
1008 end;