Installer: Use PrivilegesRequired=lowest
[msysgit.git] / share / WinGit / install.iss
blobef9f54703405b4bc42f3e35f37246f16b678fc6c
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'
5 #define APP_BINDIMAGE 'etc\fileList-bindimage.txt'
7 #define COMP_CONSOLE_FONT 'Use a TrueType font in all console windows (not only for Git Bash)'
9 #define DROP_HANDLER_GUID '{{86C86720-42A0-1069-A2E8-08002B30309D}}'
11 [Setup]
12 ; Compiler-related
13 Compression=lzma2/ultra
14 LZMAUseSeparateProcess=yes
15 OutputBaseFilename={#APP_NAME+'-'+APP_VERSION}
16 OutputDir={#GetEnv('USERPROFILE')}
17 SolidCompression=yes
19 ; Uncomment the line below to be able to compile the script from within the IDE.
20 ;SourceDir={#GetEnv('TEMP')}\WinGit
22 ; Installer-related
23 AllowNoIcons=yes
24 AppName={#APP_NAME}
25 AppPublisherURL={#APP_URL}
26 AppVersion={#APP_VERSION}
27 ChangesEnvironment=yes
28 DefaultDirName={pf}\{#APP_NAME}
29 DefaultGroupName={#APP_NAME}
30 DisableReadyPage=yes
31 InfoBeforeFile=gpl-2.0.rtf
32 PrivilegesRequired=lowest
33 UninstallDisplayIcon=etc\git.ico
35 ; Cosmetic
36 SetupIconFile=etc\git.ico
37 WizardImageBackColor=clWhite
38 WizardImageStretch=no
39 WizardImageFile=git.bmp
40 WizardSmallImageFile=gitsmall.bmp
42 [Types]
43 ; Define a dummy type to avoid getting the default ones.
44 Name: custom; Description: Custom installation; Flags: iscustom
46 [Components]
47 Name: icons; Description: Additional icons; Types: custom
48 Name: icons\quicklaunch; Description: In the Quick Launch; Types: custom
49 Name: icons\desktop; Description: On the Desktop; Types: custom
50 Name: ext; Description: Windows Explorer integration; Types: custom
51 Name: ext\reg; Description: Context menu entries; Flags: exclusive; Types: custom
52 Name: ext\reg\shellhere; Description: Git Bash Here; Types: custom
53 Name: ext\reg\guihere; Description: Git GUI Here; Types: custom
54 Name: ext\cheetah; Description: git-cheetah shell extension (32-bit only); Flags: exclusive; Types: custom
55 Name: assoc; Description: Associate .git* configuration files with the default text editor; Types: custom
56 Name: assoc_sh; Description: Associate .sh files to be run with Bash; Types: custom
57 Name: consolefont; Description: {#COMP_CONSOLE_FONT}; Types: custom
59 [Files]
60 ; Install files that might be in use during setup under a different name.
61 Source: git-cheetah\git_shell_ext.dll; DestDir: {app}\git-cheetah; DestName: git_shell_ext.dll.new; Flags: replacesameversion; Components: ext\cheetah
63 Source: *; DestDir: {app}; Excludes: \*.bmp, gpl-2.0.rtf, \*.iss, \tmp.*, \bin\*install*, \git-cheetah\git_shell_ext.dll; Flags: recursesubdirs replacesameversion
64 Source: ReleaseNotes.rtf; DestDir: {app}; Flags: isreadme replacesameversion
66 [Icons]
67 Name: {group}\Git GUI; Filename: {app}\bin\wish.exe; Parameters: """{app}\libexec\git-core\git-gui"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico
68 Name: {group}\Git Bash; Filename: {syswow64}\cmd.exe; Parameters: "/c """"{app}\bin\sh.exe"" --login -i"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico
70 [Messages]
71 BeveledLabel={#APP_URL}
72 SetupAppTitle={#APP_NAME} Setup
73 SetupWindowTitle={#APP_NAME} Setup
75 [Registry]
76 ; There is no "Console" key in HKLM.
77 Root: HKCU; Subkey: Console; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: uninsclearvalue; Components: consolefont
78 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Components: consolefont
79 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Components: consolefont
80 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Components: consolefont
82 Root: HKCU; Subkey: Console\Git Bash; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
83 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
84 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
85 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
87 ; Note that we write the Registry values below either to HKLM or to HKCU depending on whether the user running the installer
88 ; is a member of the local Administrators group or not (see the "Check" argument).
90 ; File associations for configuration files that may be contained in a repository (so this does not include ".gitconfig").
91 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
92 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
93 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
94 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
95 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
96 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
98 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
99 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
100 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
101 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
102 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
103 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
105 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
106 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
107 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
108 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
109 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
110 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
112 ; Associate .sh extension with sh.exe so those files are double-clickable,
113 ; startable from cmd.exe, and when files are dropped on them they are passed
114 ; as arguments to the script.
116 ; Install under HKEY_LOCAL_MACHINE if an administrator is installing.
117 Root: HKLM; Subkey: Software\Classes\.sh; ValueType: string; ValueData: sh_auto_file; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
118 Root: HKLM; Subkey: Software\Classes\sh_auto_file; ValueType: string; ValueData: "Shell Script"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
119 Root: HKLM; Subkey: Software\Classes\sh_auto_file\shell\open\command; ValueType: string; ValueData: """{app}\bin\sh.exe"" ""--login"" ""%1"" %*"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
120 Root: HKLM; Subkey: Software\Classes\sh_auto_file\DefaultIcon; ValueType: string; ValueData: "%SystemRoot%\System32\shell32.dll,-153"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
121 Root: HKLM; Subkey: Software\Classes\sh_auto_file\ShellEx\DropHandler; ValueType: string; ValueData: {#DROP_HANDLER_GUID}; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
123 ; Install under HKEY_CURRENT_USER if a non-administrator is installing.
124 Root: HKCU; Subkey: Software\Classes\.sh; ValueType: string; ValueData: sh_auto_file; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
125 Root: HKCU; Subkey: Software\Classes\sh_auto_file; ValueType: string; ValueData: "Shell Script"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
126 Root: HKCU; Subkey: Software\Classes\sh_auto_file\shell\open\command; ValueType: string; ValueData: """{app}\bin\sh.exe"" ""--login"" ""%1"" %*"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
127 Root: HKCU; Subkey: Software\Classes\sh_auto_file\DefaultIcon; ValueType: string; ValueData: "%SystemRoot%\System32\shell32.dll,-153"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
128 Root: HKCU; Subkey: Software\Classes\sh_auto_file\ShellEx\DropHandler; ValueType: string; ValueData: {#DROP_HANDLER_GUID}; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
130 [UninstallDelete]
131 ; Delete the built-ins.
132 Type: files; Name: {app}\bin\git-*.exe
133 Type: files; Name: {app}\libexec\git-core\git-*.exe
134 Type: files; Name: {app}\libexec\git-core\git.exe
136 ; Delete any (temporary) git-cheetah files.
137 Type: files; Name: {app}\git-cheetah\*.*
139 ; Delete any manually created shortcuts.
140 Type: files; Name: {userappdata}\Microsoft\Internet Explorer\Quick Launch\Git Bash.lnk
141 Type: files; Name: {code:GetShellFolder|desktop}\Git Bash.lnk
142 Type: files; Name: {app}\Git Bash.lnk
144 ; Delete a home directory inside the msysGit directory.
145 Type: dirifempty; Name: {app}\home\{username}
146 Type: dirifempty; Name: {app}\home
148 [Code]
149 #include "helpers.inc.iss"
150 #include "environment.inc.iss"
151 #include "putty.inc.iss"
152 #include "modules.inc.iss"
154 function CreateHardLink(lpFileName,lpExistingFileName:String;lpSecurityAttributes:Integer):Boolean;
155 #ifdef UNICODE
156 external 'CreateHardLinkW@Kernel32.dll stdcall delayload setuponly';
157 #else
158 external 'CreateHardLinkA@Kernel32.dll stdcall delayload setuponly';
159 #endif
161 function CreateSymbolicLink(lpSymlinkFileName,lpTargetFileName:String;dwFlags:DWORD):Boolean;
162 #ifdef UNICODE
163 external 'CreateSymbolicLinkW@Kernel32.dll stdcall delayload setuponly';
164 #else
165 external 'CreateSymbolicLinkA@Kernel32.dll stdcall delayload setuponly';
166 #endif
168 function BindImageEx(Flags:DWORD;ImageName,DllPath,SymbolPath:AnsiString;StatusRoutine:Integer):Boolean;
169 external 'BindImageEx@Imagehlp.dll stdcall delayload setuponly';
171 const
172 // Git Path options.
173 GP_BashOnly = 1;
174 GP_Cmd = 2;
175 GP_CmdTools = 3;
177 // Git SSH options.
178 GS_OpenSSH = 1;
179 GS_Plink = 2;
181 // Git line ending conversion options.
182 GC_LFOnly = 1;
183 GC_CRLFAlways = 2;
184 GC_CRLFCommitAsIs = 3;
186 // BindImageEx API constants.
187 BIND_NO_BOUND_IMPORTS = $00000001;
188 BIND_NO_UPDATE = $00000002;
189 BIND_ALL_IMAGES = $00000004;
190 BIND_CACHE_IMPORT_DLLS = $00000008;
193 // Wizard page and variables for the Path options.
194 PathPage:TWizardPage;
195 RdbPath:array[GP_BashOnly..GP_CmdTools] of TRadioButton;
197 // Wizard page and variables for the SSH options.
198 PuTTYPage:TWizardPage;
199 RdbSSH:array[GS_OpenSSH..GS_Plink] of TRadioButton;
200 EdtPlink:TEdit;
202 // Wizard page and variables for the line ending conversion options.
203 CRLFPage:TWizardPage;
204 RdbCRLF:array[GC_LFOnly..GC_CRLFCommitAsIs] of TRadioButton;
206 // Wizard page and variables for the processes page.
207 Processes:ProcessList;
208 ProcessesPage:TWizardPage;
209 ProcessesListBox:TListBox;
210 ProcessesRefresh,ContinueButton:TButton;
212 procedure BrowseForPuTTYFolder(Sender:TObject);
214 Path:String;
215 begin
216 Path:=ExtractFilePath(EdtPlink.Text);
217 BrowseForFolder('Please select the PuTTY folder:',Path,False);
218 if FileExists(Path+'\TortoisePlink.exe') then begin
219 EdtPlink.Text:=Path+'\TortoisePlink.exe';
220 RdbSSH[GS_Plink].Checked:=True;
221 end else if FileExists(Path+'\plink.exe') then begin
222 EdtPlink.Text:=Path+'\plink.exe';
223 RdbSSH[GS_Plink].Checked:=True;
224 end else begin
225 MsgBox('Please enter a valid path to "TortoisePlink.exe" or "plink.exe".',mbError,MB_OK);
226 end;
227 end;
229 procedure DeleteContextMenuEntries;
231 AppDir,Command,Msg:String;
232 RootKey:Integer;
233 begin
234 AppDir:=ExpandConstant('{app}');
236 if IsAdminLoggedOn then begin
237 RootKey:=HKEY_LOCAL_MACHINE;
238 end else begin
239 RootKey:=HKEY_CURRENT_USER;
240 end;
242 Command:='';
243 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','',Command);
244 if Pos(AppDir,Command)>0 then begin
245 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell') then begin
246 Msg:='Line {#__LINE__}: Unable to remove "Git Bash Here" shell extension.';
247 MsgBox(Msg,mbError,MB_OK);
248 Log(Msg);
249 // This is not a critical error, the user can probably fix it manually,
250 // so we continue.
251 end;
252 end;
254 Command:='';
255 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui\command','',Command);
256 if Pos(AppDir,Command)>0 then begin
257 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui') then begin
258 Msg:='Line {#__LINE__}: Unable to remove "Git GUI Here" shell extension.';
259 MsgBox(Msg,mbError,MB_OK);
260 Log(Msg);
261 // This is not a critical error, the user can probably fix it manually,
262 // so we continue.
263 end;
264 end;
265 end;
267 procedure RefreshProcessList(Sender:TObject);
269 Modules:TArrayOfString;
270 ProcsCloseRequired,ProcsCloseOptional:ProcessList;
271 Found:Boolean;
272 i:Longint;
273 Caption:String;
274 begin
275 SetArrayLength(Modules,2);
276 Modules[0]:=ExpandConstant('{app}\bin\msys-1.0.dll');
277 Modules[1]:=ExpandConstant('{app}\bin\tcl85.dll');
278 Found:=FindProcessesUsingModules(Modules,ProcsCloseRequired);
280 Found:=FindProcessesUsingModule(ExpandConstant('{app}\git-cheetah\git_shell_ext.dll'),ProcsCloseOptional) or Found;
282 // Misuse the "Restartable" flag to indicate which processes are required
283 // to be closed before setup can continue, and which just should be closed
284 // in order to make changes take effect immediately.
285 SetArrayLength(Processes,GetArrayLength(ProcsCloseRequired)+GetArrayLength(ProcsCloseOptional));
286 for i:=0 to GetArrayLength(ProcsCloseRequired)-1 do begin
287 Processes[i]:=ProcsCloseRequired[i];
288 Processes[i].Restartable:=False;
289 end;
290 for i:=0 to GetArrayLength(ProcsCloseOptional)-1 do begin
291 Processes[GetArrayLength(ProcsCloseRequired)+i]:=ProcsCloseOptional[i];
292 Processes[GetArrayLength(ProcsCloseRequired)+i].Restartable:=True;
293 end;
295 ProcessesListBox.Items.Clear;
296 if (Sender=NIL) or Found then begin
297 for i:=0 to GetArrayLength(Processes)-1 do begin
298 Caption:=Processes[i].Name+' (PID '+IntToStr(Processes[i].ID);
299 if not Processes[i].Restartable then begin
300 Caption:=Caption+', closing is required';
301 end;
302 Caption:=Caption+')';
303 ProcessesListBox.Items.Append(Caption);
304 end;
305 end;
307 if ContinueButton<>NIL then begin
308 ContinueButton.Enabled:=(GetArrayLength(ProcsCloseRequired)=0);
309 end;
310 end;
313 Installer code
316 procedure InitializeWizard;
318 i,PrevPageID:Integer;
319 LblGitBash,LblGitCmd,LblGitCmdTools,LblGitCmdToolsWarn:TLabel;
320 LblOpenSSH,LblPlink:TLabel;
321 PuTTYSessions:TArrayOfString;
322 LblLFOnly,LblCRLFAlways,LblCRLFCommitAsIs:TLabel;
323 BtnPlink:TButton;
324 Data:String;
325 begin
326 // Until we have a 64-bit version of git-cheetah, disable it on 64-bit Windows.
327 if isWin64 then begin
328 for i:=0 to WizardForm.ComponentsList.Items.Count-1 do begin
329 Data:=LowerCase(WizardForm.ComponentsList.ItemCaption[i]);
330 if Pos('context',Data)>0 then begin
331 // Select the Registry-based context menu entries.
332 WizardForm.ComponentsList.Checked[i]:=True;
333 end else if Pos('cheetah',Data)>0 then begin
334 // Disable the git-cheetah shell extension.
335 WizardForm.ComponentsList.ItemEnabled[i]:=False;
336 end;
337 end;
338 end;
340 PrevPageID:=wpSelectProgramGroup;
343 * Create a custom page for modifying the environment.
346 PathPage:=CreateCustomPage(
347 PrevPageID,
348 'Adjusting your PATH environment',
349 'How would you like to use Git from the command line?'
351 PrevPageID:=PathPage.ID;
353 // 1st choice
354 RdbPath[GP_BashOnly]:=TRadioButton.Create(PathPage);
355 with RdbPath[GP_BashOnly] do begin
356 Parent:=PathPage.Surface;
357 Caption:='Use Git Bash only';
358 Left:=ScaleX(4);
359 Top:=ScaleY(8);
360 Width:=ScaleX(129);
361 Height:=ScaleY(17);
362 Font.Style:=[fsBold];
363 TabOrder:=0;
364 Checked:=True;
365 end;
366 LblGitBash:=TLabel.Create(PathPage);
367 with LblGitBash do begin
368 Parent:=PathPage.Surface;
369 Caption:=
370 'This is the most conservative choice if you are concerned about the stability' + #13 +
371 'of your system. Your PATH will not be modified.';
372 Left:=ScaleX(28);
373 Top:=ScaleY(32);
374 Width:=ScaleX(405);
375 Height:=ScaleY(26);
376 end;
378 // 2nd choice
379 RdbPath[GP_Cmd]:=TRadioButton.Create(PathPage);
380 with RdbPath[GP_Cmd] do begin
381 Parent:=PathPage.Surface;
382 Caption:='Run Git from the Windows Command Prompt';
383 Left:=ScaleX(4);
384 Top:=ScaleY(76);
385 Width:=ScaleX(281);
386 Height:=ScaleY(17);
387 Font.Style:=[fsBold];
388 TabOrder:=1;
389 end;
390 LblGitCmd:=TLabel.Create(PathPage);
391 with LblGitCmd do begin
392 Parent:=PathPage.Surface;
393 Caption:=
394 'This option is considered safe and no conflicts with other tools are known.' + #13 +
395 'Only Git will be added to your PATH. Use this option if you want to use Git' + #13 +
396 'from a Cygwin Prompt (make sure to not have Cygwin''s Git installed).';
397 Left:=ScaleX(28);
398 Top:=ScaleY(100);
399 Width:=ScaleX(405);
400 Height:=ScaleY(39);
401 end;
403 // 3rd choice
404 RdbPath[GP_CmdTools]:=TRadioButton.Create(PathPage);
405 with RdbPath[GP_CmdTools] do begin
406 Parent:=PathPage.Surface;
407 Caption:='Run Git and included Unix tools from the Windows Command Prompt';
408 Left:=ScaleX(4);
409 Top:=ScaleY(152);
410 Width:=ScaleX(405);
411 Height:=ScaleY(17);
412 Font.Style:=[fsBold];
413 TabOrder:=2;
414 end;
415 LblGitCmdTools:=TLabel.Create(PathPage);
416 with LblGitCmdTools do begin
417 Parent:=PathPage.Surface;
418 Caption:='Both Git and its accompanying Unix tools will be added to your PATH.';
419 Left:=ScaleX(28);
420 Top:=ScaleY(176);
421 Width:=ScaleX(405);
422 Height:=ScaleY(13);
423 end;
424 LblGitCmdToolsWarn:=TLabel.Create(PathPage);
425 with LblGitCmdToolsWarn do begin
426 Parent:=PathPage.Surface;
427 Caption:=
428 'Warning: This will override Windows tools like find.exe and' + #13 +
429 'sort.exe. Select this option only if you understand the implications.';
430 Left:=ScaleX(28);
431 Top:=ScaleY(192);
432 Width:=ScaleX(376);
433 Height:=ScaleY(26);
434 Font.Color:=255;
435 Font.Style:=[fsBold];
436 end;
438 // Restore the setting chosen during a previous install.
439 Data:=GetPreviousData('Path Option','BashOnly');
440 if Data='BashOnly' then begin
441 RdbPath[GP_BashOnly].Checked:=True;
442 end else if Data='Cmd' then begin
443 RdbPath[GP_Cmd].Checked:=True;
444 end else if Data='CmdTools' then begin
445 RdbPath[GP_CmdTools].Checked:=True;
446 end;
449 * Create a custom page for using (Tortoise)Plink instead of OpenSSH
450 * if at least one PuTTY session is found in the Registry.
453 if RegGetSubkeyNames(HKEY_CURRENT_USER,'Software\SimonTatham\PuTTY\Sessions',PuTTYSessions) and (GetArrayLength(PuTTYSessions)>0) then begin
454 PuTTYPage:=CreateCustomPage(
455 PrevPageID,
456 'Choosing the SSH executable',
457 'Which Secure Shell client program would you like Git to use?'
459 PrevPageID:=PuTTYPage.ID;
461 // 1st choice
462 RdbSSH[GS_OpenSSH]:=TRadioButton.Create(PuTTYPage);
463 with RdbSSH[GS_OpenSSH] do begin
464 Parent:=PuTTYPage.Surface;
465 Caption:='Use OpenSSH';
466 Left:=ScaleX(4);
467 Top:=ScaleY(8);
468 Width:=ScaleX(129);
469 Height:=ScaleY(17);
470 Font.Style:=[fsBold];
471 TabOrder:=0;
472 Checked:=True;
473 end;
474 LblOpenSSH:=TLabel.Create(PuTTYPage);
475 with LblOpenSSH do begin
476 Parent:=PuTTYPage.Surface;
477 Caption:=
478 'This uses ssh.exe that comes with Git. The GIT_SSH and SVN_SSH' + #13 +
479 'environment variables will not be modified.';
480 Left:=ScaleX(28);
481 Top:=ScaleY(32);
482 Width:=ScaleX(324);
483 Height:=ScaleY(26);
484 end;
486 // 2nd choice
487 RdbSSH[GS_Plink]:=TRadioButton.Create(PuTTYPage);
488 with RdbSSH[GS_Plink] do begin
489 Parent:=PuTTYPage.Surface;
490 Caption:='Use (Tortoise)Plink';
491 Left:=ScaleX(4);
492 Top:=ScaleY(76);
493 Width:=ScaleX(281);
494 Height:=ScaleY(17);
495 Font.Style:=[fsBold];
496 TabOrder:=1;
497 end;
498 LblPlink:=TLabel.Create(PuTTYPage);
499 with LblPlink do begin
500 Parent:=PuTTYPage.Surface;
501 Caption:=
502 'PuTTY sessions were found in your Registry. You may specify the path' + #13 +
503 'to an existing copy of (Tortoise)Plink.exe from the TortoiseSVN/CVS' + #13 +
504 'or PuTTY applications. The GIT_SSH and SVN_SSH environment' + #13 +
505 'variables will be adjusted to point to the following executable:';
506 Left:=ScaleX(28);
507 Top:=ScaleY(100);
508 Width:=ScaleX(340);
509 Height:=ScaleY(52);
510 end;
511 EdtPlink:=TEdit.Create(PuTTYPage);
512 with EdtPlink do begin
513 Parent:=PuTTYPage.Surface;
514 Text:=GetPuTTYLocation;
515 if not FileExists(Text) then begin
516 Text:='';
517 end;
518 Left:=ScaleX(28);
519 Top:=ScaleY(161);
520 Width:=ScaleX(316);
521 Height:=ScaleY(13);
522 end;
523 BtnPlink:=TButton.Create(PuTTYPage);
524 with BtnPlink do begin
525 Parent:=PuTTYPage.Surface;
526 Caption:='...';
527 OnClick:=@BrowseForPuTTYFolder;
528 Left:=ScaleX(348);
529 Top:=ScaleY(161);
530 Width:=ScaleX(21);
531 Height:=ScaleY(21);
532 end;
534 // Restore the setting chosen during a previous install.
535 Data:=GetPreviousData('SSH Option','OpenSSH');
536 if Data='OpenSSH' then begin
537 RdbSSH[GS_OpenSSH].Checked:=True;
538 end else if Data='Plink' then begin
539 RdbSSH[GS_Plink].Checked:=True;
540 end;
541 end else begin
542 PuTTYPage:=NIL;
543 end;
546 * Create a custom page for the core.autocrlf setting.
549 CRLFPage:=CreateCustomPage(
550 PrevPageID,
551 'Configuring the line ending conversions',
552 'How should Git treat line endings in text files?'
554 PrevPageID:=CRLFPage.ID;
556 // 1st choice
557 RdbCRLF[GC_CRLFAlways]:=TRadioButton.Create(CRLFPage);
558 with RdbCRLF[GC_CRLFAlways] do begin
559 Parent:=CRLFPage.Surface;
560 Caption:='Checkout Windows-style, commit Unix-style line endings';
561 Left:=ScaleX(4);
562 Top:=ScaleY(8);
563 Width:=ScaleX(340);
564 Height:=ScaleY(17);
565 Font.Style:=[fsBold];
566 TabOrder:=0;
567 Checked:=True;
568 end;
569 LblCRLFAlways:=TLabel.Create(CRLFPage);
570 with LblCRLFAlways do begin
571 Parent:=CRLFPage.Surface;
572 Caption:=
573 'Git will convert LF to CRLF when checking out text files. When committing' + #13 +
574 'text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
575 'this is the recommended setting on Windows ("core.autocrlf" is set to "true").';
576 Left:=ScaleX(28);
577 Top:=ScaleY(32);
578 Width:=ScaleX(372);
579 Height:=ScaleY(47);
580 end;
582 // 2nd choice
583 RdbCRLF[GC_LFOnly]:=TRadioButton.Create(CRLFPage);
584 with RdbCRLF[GC_LFOnly] do begin
585 Parent:=CRLFPage.Surface;
586 Caption:='Checkout as-is, commit Unix-style line endings';
587 Left:=ScaleX(4);
588 Top:=ScaleY(80);
589 Width:=ScaleX(340);
590 Height:=ScaleY(17);
591 Font.Style:=[fsBold];
592 TabOrder:=1;
593 Checked:=False;
594 end;
595 LblLFOnly:=TLabel.Create(CRLFPage);
596 with LblLFOnly do begin
597 Parent:=CRLFPage.Surface;
598 Caption:=
599 'Git will not perform any conversion when checking out text files. When' + #13 +
600 'committing text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
601 'this is the recommended setting on Unix ("core.autocrlf" is set to "input").';
602 Left:=ScaleX(28);
603 Top:=ScaleY(104);
604 Width:=ScaleX(372);
605 Height:=ScaleY(47);
606 end;
608 // 3rd choice
609 RdbCRLF[GC_CRLFCommitAsIs]:=TRadioButton.Create(CRLFPage);
610 with RdbCRLF[GC_CRLFCommitAsIs] do begin
611 Parent:=CRLFPage.Surface;
612 Caption:='Checkout as-is, commit as-is';
613 Left:=ScaleX(4);
614 Top:=ScaleY(152);
615 Width:=ScaleX(340);
616 Height:=ScaleY(17);
617 Font.Style:=[fsBold];
618 TabOrder:=2;
619 Checked:=False;
620 end;
621 LblCRLFCommitAsIs:=TLabel.Create(CRLFPage);
622 with LblCRLFCommitAsIs do begin
623 Parent:=CRLFPage.Surface;
624 Caption:=
625 'Git will not perform any conversions when checking out or committing' + #13 +
626 'text files. Choosing this option is not recommended for cross-platform' + #13 +
627 'projects ("core.autocrlf" is set to "false").';
628 Left:=ScaleX(28);
629 Top:=ScaleY(176);
630 Width:=ScaleX(372);
631 Height:=ScaleY(47);
632 end;
634 // Restore the setting chosen during a previous install.
635 Data:=GetPreviousData('CRLF Option','CRLFAlways');
636 if Data='LFOnly' then begin
637 RdbCRLF[GC_LFOnly].Checked:=True;
638 end else if Data='CRLFAlways' then begin
639 RdbCRLF[GC_CRLFAlways].Checked:=True;
640 end else if Data='CRLFCommitAsIs' then begin
641 RdbCRLF[GC_CRLFCommitAsIs].Checked:=True;
642 end;
645 * Create a custom page for finding the processes that lock a module.
648 ProcessesPage:=CreateCustomPage(
649 wpPreparing,
650 'Replacing in-use files',
651 'The following applications use files that need to be replaced, please close them.'
654 ProcessesListBox:=TListBox.Create(ProcessesPage);
655 with ProcessesListBox do begin
656 Parent:=ProcessesPage.Surface;
657 Width:=ProcessesPage.SurfaceWidth;
658 Height:=ProcessesPage.SurfaceHeight-ScaleY(8);
659 end;
661 ProcessesRefresh:=TNewButton.Create(WizardForm);
662 with ProcessesRefresh do begin
663 Parent:=WizardForm;
664 Width:=WizardForm.CancelButton.Width;
665 Height:=WizardForm.CancelButton.Height;
666 Top:=WizardForm.CancelButton.Top;
667 Left:=WizardForm.ClientWidth-(WizardForm.CancelButton.Left+WizardForm.CancelButton.Width);
668 Caption:='&Refresh';
669 OnClick:=@RefreshProcessList;
670 end;
672 // This button is only used by the uninstaller.
673 ContinueButton:=NIL;
675 // Initially hide the Refresh button, show it when the process page becomes current.
676 ProcessesRefresh.Hide;
677 end;
679 function ShouldSkipPage(PageID:Integer):Boolean;
680 begin
681 if (ProcessesPage<>NIL) and (PageID=ProcessesPage.ID) then begin
682 // This page is only reached forward (by pressing "Next", never by pressing "Back").
683 RefreshProcessList(NIL);
684 Result:=(GetArrayLength(Processes)=0);
685 end else begin
686 Result:=False;
687 end;
688 end;
690 procedure CurPageChanged(CurPageID:Integer);
692 i:Integer;
693 begin
694 // Uncheck the console font option by default.
695 if CurPageID=wpSelectComponents then begin
696 for i:=0 to WizardForm.ComponentsList.Items.Count-1 do begin
697 if WizardForm.ComponentsList.ItemCaption[i]='{#COMP_CONSOLE_FONT}' then begin
698 WizardForm.ComponentsList.Checked[i]:=False;
699 Break;
700 end;
701 end;
702 end;
704 // Show the "Refresh" button only on the processes page.
705 if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin
706 ProcessesRefresh.Show;
707 end else begin
708 ProcessesRefresh.Hide;
709 end;
710 end;
712 function NextButtonClick(CurPageID:Integer):Boolean;
714 i:Integer;
715 begin
716 Result:=True;
718 if (PuTTYPage<>NIL) and (CurPageID=PuTTYPage.ID) then begin
719 Result:=RdbSSH[GS_OpenSSH].Checked or
720 (RdbSSH[GS_Plink].Checked and FileExists(EdtPlink.Text));
721 if not Result then begin
722 MsgBox('Please enter a valid path to (Tortoise)Plink.exe.',mbError,MB_OK);
723 end;
724 end else if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin
725 // It would have been nicer to just disable the "Next" button, but the
726 // WizardForm exports the button just read-only.
727 for i:=0 to GetArrayLength(Processes)-1 do begin
728 if not Processes[i].Restartable then begin
729 MsgBox('Setup cannot continue until you close at least those applications in the list that are marked as "closing is required".',mbCriticalError,MB_OK);
730 Result:=False;
731 Exit;
732 end;
733 end;
735 Result:=(GetArrayLength(Processes)=0);
737 if not Result then begin
738 Result:=(MsgBox(
739 'If you continue without closing the listed applications, you will need to log off and on again before changes take effect.' + #13 + #13 +
740 'Are you sure you want to continue anyway?',
741 mbConfirmation,
742 MB_YESNO
743 )=IDYES);
744 end;
745 end;
746 end;
748 // AfterInstall
750 // Even though the name of this procedure suggests otherwise most of the
751 // code below is only executed once after the regular installation code
752 // is finished. This happens because of the if-guard right in the
753 // beginning of this procedure.
754 procedure CurStepChanged(CurStep:TSetupStep);
756 AppDir,DllPath,FileName,TempName,Cmd,Msg:String;
757 BuiltIns,ImageNames,EnvPath,EnvHome,EnvSSH:TArrayOfString;
758 Count,i:Longint;
759 LinkCreated:Boolean;
760 FindRec:TFindRec;
761 RootKey:Integer;
762 begin
763 if CurStep<>ssPostInstall then begin
764 Exit;
765 end;
767 AppDir:=ExpandConstant('{app}');
770 Bind the imported function addresses
774 DllPath:=ExpandConstant('{app}\bin;{sys}');
776 // Load the list of images from a text file.
777 FileName:=AppDir+'\{#APP_BINDIMAGE}';
778 if LoadStringsFromFile(FileName,ImageNames) then begin
779 Count:=GetArrayLength(ImageNames)-1;
780 for i:=0 to Count do begin
781 FileName:=AppDir+'\'+ImageNames[i];
782 if not BindImageEx(BIND_NO_BOUND_IMPORTS or BIND_CACHE_IMPORT_DLLS,FileName,DllPath,'',0) then begin
783 Log('Line {#__LINE__}: Error calling BindImageEx for "'+FileName+'".');
784 end;
785 end;
786 end;
787 except
788 Log('Line {#__LINE__}: An exception occurred while calling BindImageEx.');
789 end;
792 Create the built-ins
795 // Load the built-ins from a text file.
796 FileName:=AppDir+'\{#APP_BUILTINS}';
797 if LoadStringsFromFile(FileName,BuiltIns) then begin
798 Count:=GetArrayLength(BuiltIns)-1;
800 // Delete those scripts from "bin" which have been replaced by built-ins in "libexec\git-core".
801 for i:=0 to Count do begin
802 FileName:=AppDir+'\bin\'+ChangeFileExt(ExtractFileName(BuiltIns[i]),'');
803 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
804 Log('Line {#__LINE__}: Unable to delete script "'+FileName+'", ignoring.');
805 end;
806 end;
808 // Create built-ins as aliases for git.exe.
809 for i:=0 to Count do begin
810 FileName:=AppDir+'\'+BuiltIns[i];
812 // Delete any existing built-in.
813 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
814 Log('Line {#__LINE__}: Unable to delete existing built-in "'+FileName+'", skipping.');
815 continue;
816 end;
819 // This will throw an exception on pre-WinVista systems.
820 LinkCreated:=CreateSymbolicLink(FileName,AppDir+'\bin\git.exe',0);
821 except
822 LinkCreated:=False;
823 Log('Line {#__LINE__}: Creating symbolic link "'+FileName+'" failed, will try a hard link.');
824 end;
826 if not LinkCreated then begin
828 // This will throw an exception on pre-Win2k systems.
829 LinkCreated:=CreateHardLink(FileName,AppDir+'\bin\git.exe',0);
830 except
831 LinkCreated:=False;
832 Log('Line {#__LINE__}: Creating hardlink "'+FileName+'" failed, will try a copy.');
833 end;
834 end;
836 if not LinkCreated then begin
837 if not FileCopy(AppDir+'\bin\git.exe',FileName,False) then begin
838 Log('Line {#__LINE__}: Creating copy "'+FileName+'" failed, giving up.');
839 // This is not a critical error, Git could basically be used without the
840 // aliases for built-ins, so we continue.
841 end;
842 end;
843 end;
845 // Delete any duplicate files in case we are updating from a non-libexec to a libexec directory layout.
846 if FindFirst(AppDir+'\libexec\git-core\*',FindRec) then begin
847 repeat
848 if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY)=0 then begin
849 FileName:=AppDir+'\bin\'+FindRec.name;
850 if (Pos(FindRec.name,'git.exe')<>1) and FileExists(FileName) and (not DeleteFile(FileName)) then begin
851 Log('Line {#__LINE__}: Unable to delete dupe "'+FileName+'", ignoring.');
852 end;
853 end;
854 until not FindNext(FindRec);
855 FindClose(FindRec);
856 end;
857 end else begin
858 Msg:='Line {#__LINE__}: Unable to read file "{#APP_BUILTINS}".';
859 MsgBox(Msg,mbError,MB_OK);
860 Log(Msg);
861 // This is not a critical error, Git could basically be used without the
862 // aliases for built-ins, so we continue.
863 end;
866 Adapt core.autocrlf
869 if RdbCRLF[GC_LFOnly].checked then begin
870 Cmd:='core.autocrlf input';
871 end else if RdbCRLF[GC_CRLFAlways].checked then begin
872 Cmd:='core.autocrlf true';
873 end else begin
874 Cmd:='core.autocrlf false';
875 end;
876 if not Exec(AppDir + '\bin\git.exe', 'config -f gitconfig ' + Cmd,
877 AppDir + '\etc', SW_HIDE, ewWaitUntilTerminated, i) then begin
878 Msg:='Unable to configure the line ending conversion: ' + Cmd;
879 MsgBox(Msg,mbError,MB_OK);
880 Log(Msg);
881 // This is not a critical error, the user can probably fix it manually,
882 // so we continue.
883 end;
886 Modify the environment
888 This must happen no later than ssPostInstall to make
889 "ChangesEnvironment=yes" not happend before the change!
892 FileName:=AppDir+'\setup.ini';
894 // Delete GIT_SSH and SVN_SSH if a previous installation set them (this is required for the GS_OpenSSH case).
895 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
896 if (GetArrayLength(EnvSSH)=1) and
897 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
898 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
899 Msg:='Line {#__LINE__}: Unable to reset GIT_SSH prior to install.';
900 MsgBox(Msg,mbError,MB_OK);
901 Log(Msg);
902 // This is not a critical error, the user can probably fix it manually,
903 // so we continue.
904 end;
905 end;
907 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
908 if (GetArrayLength(EnvSSH)=1) and
909 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','SVN_SSH','',FileName))=0) then begin
910 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
911 Msg:='Line {#__LINE__}: Unable to reset SVN_SSH prior to install.';
912 MsgBox(Msg,mbError,MB_OK);
913 Log(Msg);
914 // This is not a critical error, the user can probably fix it manually,
915 // so we continue.
916 end;
917 end;
919 if (PuTTYPage<>NIL) and RdbSSH[GS_Plink].Checked then begin
920 SetArrayLength(EnvSSH,1);
921 EnvSSH[0]:=EdtPlink.Text;
923 // Set GIT_SSH as specified by the user.
924 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
925 Msg:='Line {#__LINE__}: Unable to set the GIT_SSH environment variable.';
926 MsgBox(Msg,mbError,MB_OK);
927 Log(Msg);
928 // This is not a critical error, the user can probably fix it manually,
929 // so we continue.
930 end;
932 // Mark that we have changed GIT_SSH by writing its value to a file.
933 if not SetIniString('Environment','GIT_SSH',EnvSSH[0],FileName) then begin
934 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
935 MsgBox(Msg,mbError,MB_OK);
936 Log(Msg);
937 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
938 // so we continue.
939 end;
941 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
942 Msg:='Line {#__LINE__}: Unable to set the SVN_SSH environment variable.';
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;
949 // Mark that we have changed SVN_SSH by writing its value to a file.
950 if not SetIniString('Environment','SVN_SSH',EnvSSH[0],FileName) then begin
951 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
952 MsgBox(Msg,mbError,MB_OK);
953 Log(Msg);
954 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
955 // so we continue.
956 end;
957 end;
959 // Get the current user's directories in PATH.
960 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
962 // First, remove the installation directory from PATH in any case.
963 for i:=0 to GetArrayLength(EnvPath)-1 do begin
964 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
965 EnvPath[i]:='';
966 end;
967 end;
969 // Delete HOME if a previous installation modified it.
970 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
971 if (GetArrayLength(EnvHome)=1) and
972 (CompareStr(RemoveQuotes(EnvHome[0]),GetIniString('Environment','HOME','',FileName))=0) then begin
973 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
974 Msg:='Line {#__LINE__}: Unable to reset HOME prior to install.';
975 MsgBox(Msg,mbError,MB_OK);
976 Log(Msg);
977 // This is not a critical error, the user can probably fix it manually,
978 // so we continue.
979 end;
980 end;
982 // Modify the PATH variable as requested by the user.
983 if RdbPath[GP_Cmd].Checked or RdbPath[GP_CmdTools].Checked then begin
984 i:=GetArrayLength(EnvPath);
985 SetArrayLength(EnvPath,i+1);
987 // List \cmd before \bin so \cmd has higher priority and programs in
988 // there will be called in favor of those in \bin.
989 EnvPath[i]:=AppDir+'\cmd';
991 if RdbPath[GP_CmdTools].Checked then begin
992 SetArrayLength(EnvPath,i+2);
993 EnvPath[i+1]:=AppDir+'\bin';
995 // Set HOME for the Windows Command Prompt, but only if it has not been set manually before.
996 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
997 i:=GetArrayLength(EnvHome);
998 if (i=0) or ((i=1) and (Length(EnvHome[0])=0)) then begin
999 SetArrayLength(EnvHome,1);
1000 EnvHome[0]:=ExpandConstant('{%HOMEDRIVE}{%HOMEPATH}');
1001 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,EnvHome) then begin
1002 Msg:='Line {#__LINE__}: Unable to set the HOME environment variable.';
1003 MsgBox(Msg,mbError,MB_OK);
1004 Log(Msg);
1005 // This is not a critical error, the user can probably fix it manually,
1006 // so we continue.
1007 end;
1009 // Mark that we have changed HOME.
1010 if not SetIniString('Environment','HOME',EnvHome[0],FileName) then begin
1011 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
1012 MsgBox(Msg,mbError,MB_OK);
1013 Log(Msg);
1014 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
1015 // so we continue.
1016 end;
1017 end;
1018 end;
1019 end;
1021 // Set the current user's PATH directories.
1022 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
1023 Msg:='Line {#__LINE__}: Unable to set the PATH environment variable.';
1024 MsgBox(Msg,mbError,MB_OK);
1025 Log(Msg);
1026 // This is not a critical error, the user can probably fix it manually,
1027 // so we continue.
1028 end;
1031 Create shortcuts that need to be created regardless of the "Don't create a Start Menu folder" toggle
1034 Cmd:=ExpandConstant('{syswow64}\cmd.exe');
1035 TempName:='/c ""'+AppDir+'\bin\sh.exe" --login -i"';
1036 FileName:=AppDir+'\etc\git.ico';
1038 if IsComponentSelected('icons\quicklaunch') then begin
1039 CreateShellLink(
1040 ExpandConstant('{userappdata}\Microsoft\Internet Explorer\Quick Launch\Git Bash.lnk')
1041 , 'Git Bash'
1042 , Cmd
1043 , TempName
1044 , '%HOMEDRIVE%%HOMEPATH%'
1045 , FileName
1047 , SW_SHOWNORMAL
1049 end;
1050 if IsComponentSelected('icons\desktop') then begin
1051 CreateShellLink(
1052 GetShellFolder('desktop')+'\Git Bash.lnk'
1053 , 'Git Bash'
1054 , Cmd
1055 , TempName
1056 , '%HOMEDRIVE%%HOMEPATH%'
1057 , FileName
1059 , SW_SHOWNORMAL
1061 end;
1063 // Create a special shortcut that does not set a working directory (Note: Since Inno Setup 5.3.11,
1064 // passing an empty WorkingDir gets replaced with {sys}, so use '.' instead).
1065 // This shortcut is used by "Git Bash.vbs", which in turn is run by the "Git Bash Here" shell extension.
1066 CreateShellLink(
1067 AppDir+'\Git Bash.lnk'
1068 , 'Git Bash'
1069 , Cmd
1070 , TempName
1071 , '.'
1072 , FileName
1074 , SW_SHOWNORMAL
1078 Create the Windows Explorer integrations
1081 if IsAdminLoggedOn then begin
1082 RootKey:=HKEY_LOCAL_MACHINE;
1083 end else begin
1084 RootKey:=HKEY_CURRENT_USER;
1085 end;
1087 if IsComponentSelected('ext\reg\shellhere') then begin
1088 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell','','Git Ba&sh Here')) or
1089 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','','wscript "'+AppDir+'\Git Bash.vbs" "%1"')) then begin
1090 Msg:='Line {#__LINE__}: Unable to create "Git Bash Here" shell extension.';
1091 MsgBox(Msg,mbError,MB_OK);
1092 Log(Msg);
1093 // This is not a critical error, the user can probably fix it manually,
1094 // so we continue.
1095 end;
1096 end;
1098 if IsComponentSelected('ext\reg\guihere') then begin
1099 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui','','Git &GUI Here')) or
1100 (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
1101 Msg:='Line {#__LINE__}: Unable to create "Git GUI Here" shell extension.';
1102 MsgBox(Msg,mbError,MB_OK);
1103 Log(Msg);
1104 // This is not a critical error, the user can probably fix it manually,
1105 // so we continue.
1106 end;
1107 end;
1109 // It is either the Registry-based context menu entries, or the shell extension.
1110 if IsComponentSelected('ext\cheetah') then begin
1111 DeleteContextMenuEntries;
1113 FileName:=AppDir+'\git-cheetah\git_shell_ext.dll';
1114 if not ReplaceInUseFile(FileName,FileName+'.new',True) then begin
1115 Log('Line {#__LINE__}: Replacing file "'+FileName+'" failed.');
1116 end;
1117 end;
1118 end;
1120 procedure RegisterPreviousData(PreviousDataKey:Integer);
1122 Data:String;
1123 begin
1124 // Git Path options.
1125 Data:='';
1126 if RdbPath[GP_BashOnly].Checked then begin
1127 Data:='BashOnly';
1128 end else if RdbPath[GP_Cmd].Checked then begin
1129 Data:='Cmd';
1130 end else if RdbPath[GP_CmdTools].Checked then begin
1131 Data:='CmdTools';
1132 end;
1133 SetPreviousData(PreviousDataKey,'Path Option',Data);
1135 // Git SSH options.
1136 Data:='';
1137 if (PuTTYPage=NIL) or RdbSSH[GS_OpenSSH].Checked then begin
1138 Data:='OpenSSH';
1139 end else if RdbSSH[GS_Plink].Checked then begin
1140 Data:='Plink';
1141 end;
1142 SetPreviousData(PreviousDataKey,'SSH Option',Data);
1144 // Line ending conversion options.
1145 Data:='';
1146 if RdbCRLF[GC_LFOnly].Checked then begin
1147 Data:='LFOnly';
1148 end else if RdbCRLF[GC_CRLFAlways].Checked then begin
1149 Data:='CRLFAlways';
1150 end else if RdbCRLF[GC_CRLFCommitAsIs].Checked then begin
1151 Data:='CRLFCommitAsIs';
1152 end;
1153 SetPreviousData(PreviousDataKey,'CRLF Option',Data);
1154 end;
1157 Uninstaller code
1160 function InitializeUninstall:Boolean;
1162 Form:TSetupForm;
1163 Info:TLabel;
1164 ExitButton,RefreshButton:TButton;
1165 begin
1166 Result:=True;
1168 Form:=CreateCustomForm;
1170 Form.Caption:='Git Uninstall: Removing in-use files';
1171 Form.ClientWidth:=ScaleX(500);
1172 Form.ClientHeight:=ScaleY(256);
1173 Form.Center;
1175 Info:=TLabel.Create(Form);
1176 with Info do begin
1177 Parent:=Form;
1178 Left:=ScaleX(11);
1179 Top:=ScaleY(11);
1180 AutoSize:=True;
1181 Caption:='The following applications use files that need to be removed, please close them.';
1182 end;
1184 ContinueButton:=TButton.Create(Form);
1185 with ContinueButton do begin
1186 Parent:=Form;
1187 Left:=Form.ClientWidth-ScaleX(75+10);
1188 Top:=Form.ClientHeight-ScaleY(23+10);
1189 Width:=ScaleX(75);
1190 Height:=ScaleY(23);
1191 Caption:='Continue';
1192 ModalResult:=mrOk;
1193 end;
1195 ExitButton:=TButton.Create(Form);
1196 with ExitButton do begin
1197 Parent:=Form;
1198 Left:=ContinueButton.Left-ScaleX(75+6);
1199 Top:=ContinueButton.Top;
1200 Width:=ScaleX(75);
1201 Height:=ScaleY(23);
1202 Caption:='Exit';
1203 ModalResult:=mrCancel;
1204 Cancel:=True;
1205 end;
1207 RefreshButton:=TButton.Create(Form);
1208 with RefreshButton do begin
1209 Parent:=Form;
1210 Left:=ScaleX(10);
1211 Top:=ExitButton.Top;
1212 Width:=ScaleX(75);
1213 Height:=ScaleY(23);
1214 Caption:='Refresh';
1215 OnClick:=@RefreshProcessList;
1216 end;
1218 ProcessesListBox:=TListBox.Create(Form);
1219 with ProcessesListBox do begin
1220 Parent:=Form;
1221 Left:=ScaleX(11);
1222 Top:=Info.Top+Info.Height+11;
1223 Width:=Form.ClientWidth-ScaleX(11*2);
1224 Height:=ContinueButton.Top-ScaleY(11*4);
1225 end;
1227 Form.ActiveControl:=ContinueButton;
1229 RefreshProcessList(NIL);
1230 if GetArrayLength(Processes)>0 then begin
1231 // Now that these dialogs are going to be shown, we should probably
1232 // disable the "Are you sure to remove Git?" confirmation dialog, but
1233 // unfortunately that is not possible with Inno Setup currently.
1234 Result:=(Form.ShowModal()=mrOk);
1236 // Note: The number of processes might have changed during a refresh.
1237 if Result and (GetArrayLength(Processes)>0) then begin
1238 Result:=(MsgBox(
1239 'If you continue without closing the listed applications, you will need to log off and on again to remove some files manually.' + #13 + #13 +
1240 'Are you sure you want to continue anyway?',
1241 mbConfirmation,
1242 MB_YESNO
1243 )=IDYES);
1244 end;
1245 end;
1246 finally
1247 Form.free;
1248 end;
1249 end;
1251 // PreUninstall
1253 // Even though the name of this function suggests otherwise most of the
1254 // code below is only executed right before the actual uninstallation.
1255 // This happens because of the if-guard right in the beginning of this
1256 // function.
1257 procedure CurUninstallStepChanged(CurUninstallStep:TUninstallStep);
1259 AppDir,FileName,Msg:String;
1260 EnvPath,EnvHome,EnvSSH:TArrayOfString;
1261 i:Longint;
1262 begin
1263 if CurUninstallStep<>usUninstall then begin
1264 Exit;
1265 end;
1267 // Reset the console font (the FontType is reset in the Registry section).
1268 if IsComponentInstalled('consolefont') then begin
1269 if MsgBox('Do you want to revert the TrueType font setting for all console windows?',mbConfirmation,MB_YESNO)=IDYES then begin
1270 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontFamily',0);
1271 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontSize',0);
1272 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontWeight',0);
1273 end;
1274 end;
1277 Modify the environment
1279 This must happen no later than usUninstall to make
1280 "ChangesEnvironment=yes" not happend before the change!
1283 AppDir:=ExpandConstant('{app}');
1284 FileName:=AppDir+'\setup.ini';
1286 // Delete the current user's GIT_SSH and SVN_SSH if we set it.
1287 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
1288 if (GetArrayLength(EnvSSH)=1) and
1289 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
1290 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
1291 Msg:='Line {#__LINE__}: Unable to revert any possible changes to GIT_SSH.';
1292 MsgBox(Msg,mbError,MB_OK);
1293 Log(Msg);
1294 // This is not a critical error, the user can probably fix it manually,
1295 // so we continue.
1296 end;
1297 end;
1299 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
1300 if (GetArrayLength(EnvSSH)=1) and
1301 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','SVN_SSH','',FileName))=0) then begin
1302 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
1303 Msg:='Line {#__LINE__}: Unable to revert any possible changes to SVN_SSH.';
1304 MsgBox(Msg,mbError,MB_OK);
1305 Log(Msg);
1306 // This is not a critical error, the user can probably fix it manually,
1307 // so we continue.
1308 end;
1309 end;
1311 // Get the current user's directories in PATH.
1312 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
1314 // Remove the installation directory from PATH in any case, even if it
1315 // was not added by the installer.
1316 for i:=0 to GetArrayLength(EnvPath)-1 do begin
1317 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
1318 EnvPath[i]:='';
1319 end;
1320 end;
1322 // Reset the current user's directories in PATH.
1323 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
1324 Msg:='Line {#__LINE__}: Unable to revert any possible changes to PATH.';
1325 MsgBox(Msg,mbError,MB_OK);
1326 Log(Msg);
1327 // This is not a critical error, the user can probably fix it manually,
1328 // so we continue.
1329 end;
1331 // Reset the current user's HOME if we modified it.
1332 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
1333 if (GetArrayLength(EnvHome)=1) and
1334 (CompareStr(RemoveQuotes(EnvHome[0]),GetIniString('Environment','HOME','',FileName))=0) then begin
1335 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
1336 Msg:='Line {#__LINE__}: Unable to revert any possible changes to HOME.';
1337 MsgBox(Msg,mbError,MB_OK);
1338 Log(Msg);
1339 // This is not a critical error, the user can probably fix it manually,
1340 // so we continue.
1341 end;
1342 end;
1344 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
1345 Msg:='Line {#__LINE__}: Unable to delete file "'+FileName+'".';
1346 MsgBox(Msg,mbError,MB_OK);
1347 Log(Msg);
1348 // This is not a critical error, the user can probably fix it manually,
1349 // so we continue.
1350 end;
1353 Delete the Windows Explorer integrations
1356 DeleteContextMenuEntries;
1358 FileName:=AppDir+'\git-cheetah\git_shell_ext.dll';
1359 if FileExists(FileName) then begin
1360 if not UnregisterServer(Is64BitInstallMode,FileName,False) then begin
1361 Msg:='Line {#__LINE__}: Unable to unregister file "'+FileName+'". Please do it manually by running "regsvr32 /u '+ExtractFileName(FileName)+'".';
1362 MsgBox(Msg,mbError,MB_OK);
1363 Log(Msg);
1364 end;
1366 if not DeleteFile(FileName) then begin
1367 Msg:='Line {#__LINE__}: Unable to delete file "'+FileName+'". Please do it manually after logging off and on again.';
1368 MsgBox(Msg,mbError,MB_OK);
1369 Log(Msg);
1370 end;
1371 end;
1372 end;