Installer: Use the new {userpf} in favor of {localappdata}
[msysgit.git] / share / WinGit / install.iss
blob58b591ae47376ad4dedff551f5c55928ebe461d5
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 DisableDirPage=auto
30 DefaultGroupName={#APP_NAME}
31 DisableProgramGroupPage=auto
32 DisableReadyPage=yes
33 InfoBeforeFile=gpl-2.0.rtf
34 PrivilegesRequired=none
35 UninstallDisplayIcon={app}\etc\git.ico
37 ; Cosmetic
38 SetupIconFile=etc\git.ico
39 WizardImageBackColor=clWhite
40 WizardImageStretch=no
41 WizardImageFile=git.bmp
42 WizardSmallImageFile=gitsmall.bmp
44 [Types]
45 ; Define a dummy type to avoid getting the default ones.
46 Name: custom; Description: Custom installation; Flags: iscustom
48 [Components]
49 Name: icons; Description: Additional icons; Types: custom
50 Name: icons\quicklaunch; Description: In the Quick Launch; Types: custom
51 Name: icons\desktop; Description: On the Desktop; Types: custom
52 Name: ext; Description: Windows Explorer integration; Types: custom
53 Name: ext\reg; Description: Simple context menu (Registry based); Flags: exclusive; Types: custom
54 Name: ext\reg\shellhere; Description: Git Bash Here; Types: custom
55 Name: ext\reg\guihere; Description: Git GUI Here; Types: custom
56 Name: ext\cheetah; Description: Advanced context menu (git-cheetah plugin); Flags: exclusive; Types: custom
57 Name: assoc; Description: Associate .git* configuration files with the default text editor; Types: custom
58 Name: assoc_sh; Description: Associate .sh files to be run with Bash; Types: custom
59 Name: consolefont; Description: {#COMP_CONSOLE_FONT}; Types: custom
61 [Files]
62 ; Install files that might be in use during setup under a different name.
63 Source: git-cheetah\git_shell_ext.dll; DestDir: {app}\git-cheetah; DestName: git_shell_ext.dll.new; Flags: replacesameversion; Components: ext\cheetah
64 Source: git-cheetah\git_shell_ext64.dll; DestDir: {app}\git-cheetah; DestName: git_shell_ext64.dll.new; Flags: replacesameversion; Components: ext\cheetah
66 Source: *; DestDir: {app}; Excludes: \*.bmp, gpl-2.0.rtf, \*.iss, \tmp.*, \bin\*install*, \git-cheetah\git_shell_ext.dll, \git-cheetah\git_shell_ext64.dll; Flags: recursesubdirs replacesameversion sortfilesbyextension
67 Source: ReleaseNotes.rtf; DestDir: {app}; Flags: isreadme replacesameversion
69 [Icons]
70 Name: {group}\Git GUI; Filename: {app}\bin\wish.exe; Parameters: """{app}\libexec\git-core\git-gui"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico
71 Name: {group}\Git Bash; Filename: {syswow64}\cmd.exe; Parameters: "/c """"{app}\bin\sh.exe"" --login -i"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico
73 [Messages]
74 BeveledLabel={#APP_URL}
75 SetupAppTitle={#APP_NAME} Setup
76 SetupWindowTitle={#APP_NAME} Setup
78 [Registry]
79 ; There is no "Console" key in HKLM.
80 Root: HKCU; Subkey: Console; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: uninsclearvalue; Components: consolefont
81 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Components: consolefont
82 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Components: consolefont
83 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Components: consolefont
85 Root: HKCU; Subkey: Console\Git Bash; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
86 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
87 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
88 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
90 ; Note that we write the Registry values below either to HKLM or to HKCU depending on whether the user running the installer
91 ; is a member of the local Administrators group or not (see the "Check" argument).
93 ; File associations for configuration files that may be contained in a repository (so this does not include ".gitconfig").
94 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
95 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
96 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
97 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
98 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
99 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
101 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
102 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
103 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
104 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
105 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
106 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
108 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
109 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
110 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
111 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
112 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
113 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
115 ; Associate .sh extension with sh.exe so those files are double-clickable,
116 ; startable from cmd.exe, and when files are dropped on them they are passed
117 ; as arguments to the script.
119 ; Install under HKEY_LOCAL_MACHINE if an administrator is installing.
120 Root: HKLM; Subkey: Software\Classes\.sh; ValueType: string; ValueData: sh_auto_file; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
121 Root: HKLM; Subkey: Software\Classes\sh_auto_file; ValueType: string; ValueData: "Shell Script"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
122 Root: HKLM; Subkey: Software\Classes\sh_auto_file\shell\open\command; ValueType: string; ValueData: "{syswow64}\cmd.exe /C """"{app}\bin\sh.exe"" ""--login"" ""%1"" %*"""; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
123 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
124 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
126 ; Install under HKEY_CURRENT_USER if a non-administrator is installing.
127 Root: HKCU; Subkey: Software\Classes\.sh; ValueType: string; ValueData: sh_auto_file; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
128 Root: HKCU; Subkey: Software\Classes\sh_auto_file; ValueType: string; ValueData: "Shell Script"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
129 Root: HKCU; Subkey: Software\Classes\sh_auto_file\shell\open\command; ValueType: string; ValueData: "{syswow64}\cmd.exe /C """"{app}\bin\sh.exe"" ""--login"" ""%1"" %*"""; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
130 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
131 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
133 [UninstallDelete]
134 ; Delete the built-ins.
135 Type: files; Name: {app}\bin\git-*.exe
136 Type: files; Name: {app}\libexec\git-core\git-*.exe
137 Type: files; Name: {app}\libexec\git-core\git.exe
139 ; Delete any (temporary) git-cheetah files.
140 Type: files; Name: {app}\git-cheetah\*.*
142 ; Delete any manually created shortcuts.
143 Type: files; Name: {userappdata}\Microsoft\Internet Explorer\Quick Launch\Git Bash.lnk
144 Type: files; Name: {code:GetShellFolder|desktop}\Git Bash.lnk
145 Type: files; Name: {app}\Git Bash.lnk
147 ; Delete a home directory inside the msysGit directory.
148 Type: dirifempty; Name: {app}\home\{username}
149 Type: dirifempty; Name: {app}\home
151 [Code]
152 #include "helpers.inc.iss"
153 #include "environment.inc.iss"
154 #include "putty.inc.iss"
155 #include "modules.inc.iss"
157 function CreateHardLink(lpFileName,lpExistingFileName:String;lpSecurityAttributes:Integer):Boolean;
158 #ifdef UNICODE
159 external 'CreateHardLinkW@Kernel32.dll stdcall delayload setuponly';
160 #else
161 external 'CreateHardLinkA@Kernel32.dll stdcall delayload setuponly';
162 #endif
164 function BindImageEx(Flags:DWORD;ImageName,DllPath,SymbolPath:AnsiString;StatusRoutine:Integer):Boolean;
165 external 'BindImageEx@Imagehlp.dll stdcall delayload setuponly';
167 const
168 // Git Path options.
169 GP_BashOnly = 1;
170 GP_Cmd = 2;
171 GP_CmdTools = 3;
173 // Git SSH options.
174 GS_OpenSSH = 1;
175 GS_Plink = 2;
177 // Git line ending conversion options.
178 GC_LFOnly = 1;
179 GC_CRLFAlways = 2;
180 GC_CRLFCommitAsIs = 3;
182 // BindImageEx API constants.
183 BIND_NO_BOUND_IMPORTS = $00000001;
184 BIND_NO_UPDATE = $00000002;
185 BIND_ALL_IMAGES = $00000004;
186 BIND_CACHE_IMPORT_DLLS = $00000008;
189 // Wizard page and variables for the Path options.
190 PathPage:TWizardPage;
191 RdbPath:array[GP_BashOnly..GP_CmdTools] of TRadioButton;
193 // Wizard page and variables for the SSH options.
194 PuTTYPage:TWizardPage;
195 RdbSSH:array[GS_OpenSSH..GS_Plink] of TRadioButton;
196 EdtPlink:TEdit;
198 // Wizard page and variables for the line ending conversion options.
199 CRLFPage:TWizardPage;
200 RdbCRLF:array[GC_LFOnly..GC_CRLFCommitAsIs] of TRadioButton;
202 // Wizard page and variables for the processes page.
203 SessionHandle:DWORD;
204 Processes:ProcessList;
205 ProcessesPage:TWizardPage;
206 ProcessesListBox:TListBox;
207 ProcessesRefresh,ContinueButton:TButton;
209 procedure BrowseForPuTTYFolder(Sender:TObject);
211 Path:String;
212 begin
213 Path:=ExtractFilePath(EdtPlink.Text);
214 BrowseForFolder('Please select the PuTTY folder:',Path,False);
215 if FileExists(Path+'\TortoisePlink.exe') then begin
216 EdtPlink.Text:=Path+'\TortoisePlink.exe';
217 RdbSSH[GS_Plink].Checked:=True;
218 end else if FileExists(Path+'\plink.exe') then begin
219 EdtPlink.Text:=Path+'\plink.exe';
220 RdbSSH[GS_Plink].Checked:=True;
221 end else begin
222 MsgBox('Please enter a valid path to "TortoisePlink.exe" or "plink.exe".',mbError,MB_OK);
223 end;
224 end;
226 procedure DeleteContextMenuEntries;
228 AppDir,Command,Msg:String;
229 RootKey:Integer;
230 begin
231 AppDir:=ExpandConstant('{app}');
233 if IsAdminLoggedOn then begin
234 RootKey:=HKEY_LOCAL_MACHINE;
235 end else begin
236 RootKey:=HKEY_CURRENT_USER;
237 end;
239 Command:='';
240 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','',Command);
241 if Pos(AppDir,Command)>0 then begin
242 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell') then begin
243 Msg:='Line {#__LINE__}: Unable to remove "Git Bash Here" shell extension.';
244 MsgBox(Msg,mbError,MB_OK);
245 Log(Msg);
246 // This is not a critical error, the user can probably fix it manually,
247 // so we continue.
248 end;
249 end;
251 Command:='';
252 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui\command','',Command);
253 if Pos(AppDir,Command)>0 then begin
254 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui') then begin
255 Msg:='Line {#__LINE__}: Unable to remove "Git GUI Here" shell extension.';
256 MsgBox(Msg,mbError,MB_OK);
257 Log(Msg);
258 // This is not a critical error, the user can probably fix it manually,
259 // so we continue.
260 end;
261 end;
262 end;
264 procedure RefreshProcessList(Sender:TObject);
266 Version:TWindowsVersion;
267 Modules:TArrayOfString;
268 ProcsCloseRequired,ProcsCloseOptional:ProcessList;
269 i:Longint;
270 Caption:String;
271 ManualClosingRequired:Boolean;
272 begin
273 GetWindowsVersionEx(Version);
275 // Use the Restart Manager API when installing the shell extension on Windows Vista and above.
276 if Version.Major>=6 then begin
277 SetArrayLength(Modules,5);
278 Modules[0]:=ExpandConstant('{app}\bin\msys-1.0.dll');
279 Modules[1]:=ExpandConstant('{app}\bin\tcl85.dll');
280 Modules[2]:=ExpandConstant('{app}\bin\tk85.dll');
281 Modules[3]:=ExpandConstant('{app}\git-cheetah\git_shell_ext.dll');
282 Modules[4]:=ExpandConstant('{app}\git-cheetah\git_shell_ext64.dll');
283 SessionHandle:=FindProcessesUsingModules(Modules,Processes);
284 end else begin
285 SetArrayLength(Modules,3);
286 Modules[0]:=ExpandConstant('{app}\bin\msys-1.0.dll');
287 Modules[1]:=ExpandConstant('{app}\bin\tcl85.dll');
288 Modules[2]:=ExpandConstant('{app}\bin\tk85.dll');
289 SessionHandle:=FindProcessesUsingModules(Modules,ProcsCloseRequired);
291 SetArrayLength(Modules,2);
292 Modules[0]:=ExpandConstant('{app}\git-cheetah\git_shell_ext.dll');
293 Modules[1]:=ExpandConstant('{app}\git-cheetah\git_shell_ext64.dll');
294 SessionHandle:=FindProcessesUsingModules(Modules,ProcsCloseOptional) or SessionHandle;
296 // Misuse the "Restartable" flag to indicate which processes are required
297 // to be closed before setup can continue, and which just should be closed
298 // in order to make changes take effect immediately.
299 SetArrayLength(Processes,GetArrayLength(ProcsCloseRequired)+GetArrayLength(ProcsCloseOptional));
300 for i:=0 to GetArrayLength(ProcsCloseRequired)-1 do begin
301 Processes[i]:=ProcsCloseRequired[i];
302 Processes[i].Restartable:=False;
303 end;
304 for i:=0 to GetArrayLength(ProcsCloseOptional)-1 do begin
305 Processes[GetArrayLength(ProcsCloseRequired)+i]:=ProcsCloseOptional[i];
306 Processes[GetArrayLength(ProcsCloseRequired)+i].Restartable:=True;
307 end;
308 end;
310 ManualClosingRequired:=False;
312 ProcessesListBox.Items.Clear;
313 if (Sender=NIL) or (SessionHandle>0) then begin
314 for i:=0 to GetArrayLength(Processes)-1 do begin
315 Caption:=Processes[i].Name+' (PID '+IntToStr(Processes[i].ID);
316 if Processes[i].Restartable then begin
317 Caption:=Caption+', closing is optional';
318 end else begin
319 Caption:=Caption+', closing is required';
320 ManualClosingRequired:=True;
321 end;
322 Caption:=Caption+')';
323 ProcessesListBox.Items.Append(Caption);
324 end;
325 end;
327 if ContinueButton<>NIL then begin
328 ContinueButton.Enabled:=not ManualClosingRequired;
329 end;
330 end;
333 Installer code
336 procedure InitializeWizard;
338 i,PrevPageID:Integer;
339 LblGitBash,LblGitCmd,LblGitCmdTools,LblGitCmdToolsWarn:TLabel;
340 LblOpenSSH,LblPlink:TLabel;
341 PuTTYSessions:TArrayOfString;
342 LblLFOnly,LblCRLFAlways,LblCRLFCommitAsIs:TLabel;
343 BtnPlink:TButton;
344 Data:String;
345 begin
347 PrevPageID:=wpSelectProgramGroup;
350 * Create a custom page for modifying the environment.
353 PathPage:=CreateCustomPage(
354 PrevPageID,
355 'Adjusting your PATH environment',
356 'How would you like to use Git from the command line?'
358 PrevPageID:=PathPage.ID;
360 // 1st choice
361 RdbPath[GP_BashOnly]:=TRadioButton.Create(PathPage);
362 with RdbPath[GP_BashOnly] do begin
363 Parent:=PathPage.Surface;
364 Caption:='Use Git Bash only';
365 Left:=ScaleX(4);
366 Top:=ScaleY(8);
367 Width:=ScaleX(129);
368 Height:=ScaleY(17);
369 Font.Style:=[fsBold];
370 TabOrder:=0;
371 Checked:=True;
372 end;
373 LblGitBash:=TLabel.Create(PathPage);
374 with LblGitBash do begin
375 Parent:=PathPage.Surface;
376 Caption:=
377 'This is the most conservative choice if you are concerned about the stability' + #13 +
378 'of your system. Your PATH will not be modified.';
379 Left:=ScaleX(28);
380 Top:=ScaleY(32);
381 Width:=ScaleX(405);
382 Height:=ScaleY(26);
383 end;
385 // 2nd choice
386 RdbPath[GP_Cmd]:=TRadioButton.Create(PathPage);
387 with RdbPath[GP_Cmd] do begin
388 Parent:=PathPage.Surface;
389 Caption:='Run Git from the Windows Command Prompt';
390 Left:=ScaleX(4);
391 Top:=ScaleY(76);
392 Width:=ScaleX(281);
393 Height:=ScaleY(17);
394 Font.Style:=[fsBold];
395 TabOrder:=1;
396 end;
397 LblGitCmd:=TLabel.Create(PathPage);
398 with LblGitCmd do begin
399 Parent:=PathPage.Surface;
400 Caption:=
401 'This option is considered safe and no conflicts with other tools are known.' + #13 +
402 'Only Git will be added to your PATH. Use this option if you want to use Git' + #13 +
403 'from a Cygwin Prompt (make sure to not have Cygwin''s Git installed).';
404 Left:=ScaleX(28);
405 Top:=ScaleY(100);
406 Width:=ScaleX(405);
407 Height:=ScaleY(39);
408 end;
410 // 3rd choice
411 RdbPath[GP_CmdTools]:=TRadioButton.Create(PathPage);
412 with RdbPath[GP_CmdTools] do begin
413 Parent:=PathPage.Surface;
414 Caption:='Run Git and included Unix tools from the Windows Command Prompt';
415 Left:=ScaleX(4);
416 Top:=ScaleY(152);
417 Width:=ScaleX(405);
418 Height:=ScaleY(17);
419 Font.Style:=[fsBold];
420 TabOrder:=2;
421 end;
422 LblGitCmdTools:=TLabel.Create(PathPage);
423 with LblGitCmdTools do begin
424 Parent:=PathPage.Surface;
425 Caption:='Both Git and its accompanying Unix tools will be added to your PATH.';
426 Left:=ScaleX(28);
427 Top:=ScaleY(176);
428 Width:=ScaleX(405);
429 Height:=ScaleY(13);
430 end;
431 LblGitCmdToolsWarn:=TLabel.Create(PathPage);
432 with LblGitCmdToolsWarn do begin
433 Parent:=PathPage.Surface;
434 Caption:=
435 'Warning: This will override Windows tools like find.exe and' + #13 +
436 'sort.exe. Select this option only if you understand the implications.';
437 Left:=ScaleX(28);
438 Top:=ScaleY(192);
439 Width:=ScaleX(376);
440 Height:=ScaleY(26);
441 Font.Color:=255;
442 Font.Style:=[fsBold];
443 end;
445 // Restore the setting chosen during a previous install.
446 Data:=GetPreviousData('Path Option','BashOnly');
447 if Data='BashOnly' then begin
448 RdbPath[GP_BashOnly].Checked:=True;
449 end else if Data='Cmd' then begin
450 RdbPath[GP_Cmd].Checked:=True;
451 end else if Data='CmdTools' then begin
452 RdbPath[GP_CmdTools].Checked:=True;
453 end;
456 * Create a custom page for using (Tortoise)Plink instead of OpenSSH
457 * if at least one PuTTY session is found in the Registry.
460 if RegGetSubkeyNames(HKEY_CURRENT_USER,'Software\SimonTatham\PuTTY\Sessions',PuTTYSessions) and (GetArrayLength(PuTTYSessions)>0) then begin
461 PuTTYPage:=CreateCustomPage(
462 PrevPageID,
463 'Choosing the SSH executable',
464 'Which Secure Shell client program would you like Git to use?'
466 PrevPageID:=PuTTYPage.ID;
468 // 1st choice
469 RdbSSH[GS_OpenSSH]:=TRadioButton.Create(PuTTYPage);
470 with RdbSSH[GS_OpenSSH] do begin
471 Parent:=PuTTYPage.Surface;
472 Caption:='Use OpenSSH';
473 Left:=ScaleX(4);
474 Top:=ScaleY(8);
475 Width:=ScaleX(129);
476 Height:=ScaleY(17);
477 Font.Style:=[fsBold];
478 TabOrder:=0;
479 Checked:=True;
480 end;
481 LblOpenSSH:=TLabel.Create(PuTTYPage);
482 with LblOpenSSH do begin
483 Parent:=PuTTYPage.Surface;
484 Caption:=
485 'This uses ssh.exe that comes with Git. The GIT_SSH and SVN_SSH' + #13 +
486 'environment variables will not be modified.';
487 Left:=ScaleX(28);
488 Top:=ScaleY(32);
489 Width:=ScaleX(324);
490 Height:=ScaleY(26);
491 end;
493 // 2nd choice
494 RdbSSH[GS_Plink]:=TRadioButton.Create(PuTTYPage);
495 with RdbSSH[GS_Plink] do begin
496 Parent:=PuTTYPage.Surface;
497 Caption:='Use (Tortoise)Plink';
498 Left:=ScaleX(4);
499 Top:=ScaleY(76);
500 Width:=ScaleX(281);
501 Height:=ScaleY(17);
502 Font.Style:=[fsBold];
503 TabOrder:=1;
504 end;
505 LblPlink:=TLabel.Create(PuTTYPage);
506 with LblPlink do begin
507 Parent:=PuTTYPage.Surface;
508 Caption:=
509 'PuTTY sessions were found in your Registry. You may specify the path' + #13 +
510 'to an existing copy of (Tortoise)Plink.exe from the TortoiseGit/SVN/CVS' + #13 +
511 'or PuTTY applications. The GIT_SSH and SVN_SSH environment' + #13 +
512 'variables will be adjusted to point to the following executable:';
513 Left:=ScaleX(28);
514 Top:=ScaleY(100);
515 Width:=ScaleX(340);
516 Height:=ScaleY(52);
517 end;
518 EdtPlink:=TEdit.Create(PuTTYPage);
519 with EdtPlink do begin
520 Parent:=PuTTYPage.Surface;
521 Text:=GetPuTTYLocation;
522 if not FileExists(Text) then begin
523 Text:='';
524 end;
525 Left:=ScaleX(28);
526 Top:=ScaleY(161);
527 Width:=ScaleX(316);
528 Height:=ScaleY(13);
529 end;
530 BtnPlink:=TButton.Create(PuTTYPage);
531 with BtnPlink do begin
532 Parent:=PuTTYPage.Surface;
533 Caption:='...';
534 OnClick:=@BrowseForPuTTYFolder;
535 Left:=ScaleX(348);
536 Top:=ScaleY(161);
537 Width:=ScaleX(21);
538 Height:=ScaleY(21);
539 end;
541 // Restore the setting chosen during a previous install.
542 Data:=GetPreviousData('SSH Option','OpenSSH');
543 if Data='OpenSSH' then begin
544 RdbSSH[GS_OpenSSH].Checked:=True;
545 end else if Data='Plink' then begin
546 RdbSSH[GS_Plink].Checked:=True;
547 end;
548 end else begin
549 PuTTYPage:=NIL;
550 end;
553 * Create a custom page for the core.autocrlf setting.
556 CRLFPage:=CreateCustomPage(
557 PrevPageID,
558 'Configuring the line ending conversions',
559 'How should Git treat line endings in text files?'
561 PrevPageID:=CRLFPage.ID;
563 // 1st choice
564 RdbCRLF[GC_CRLFAlways]:=TRadioButton.Create(CRLFPage);
565 with RdbCRLF[GC_CRLFAlways] do begin
566 Parent:=CRLFPage.Surface;
567 Caption:='Checkout Windows-style, commit Unix-style line endings';
568 Left:=ScaleX(4);
569 Top:=ScaleY(8);
570 Width:=ScaleX(340);
571 Height:=ScaleY(17);
572 Font.Style:=[fsBold];
573 TabOrder:=0;
574 Checked:=True;
575 end;
576 LblCRLFAlways:=TLabel.Create(CRLFPage);
577 with LblCRLFAlways do begin
578 Parent:=CRLFPage.Surface;
579 Caption:=
580 'Git will convert LF to CRLF when checking out text files. When committing' + #13 +
581 'text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
582 'this is the recommended setting on Windows ("core.autocrlf" is set to "true").';
583 Left:=ScaleX(28);
584 Top:=ScaleY(32);
585 Width:=ScaleX(380);
586 Height:=ScaleY(47);
587 end;
589 // 2nd choice
590 RdbCRLF[GC_LFOnly]:=TRadioButton.Create(CRLFPage);
591 with RdbCRLF[GC_LFOnly] do begin
592 Parent:=CRLFPage.Surface;
593 Caption:='Checkout as-is, commit Unix-style line endings';
594 Left:=ScaleX(4);
595 Top:=ScaleY(80);
596 Width:=ScaleX(340);
597 Height:=ScaleY(17);
598 Font.Style:=[fsBold];
599 TabOrder:=1;
600 Checked:=False;
601 end;
602 LblLFOnly:=TLabel.Create(CRLFPage);
603 with LblLFOnly do begin
604 Parent:=CRLFPage.Surface;
605 Caption:=
606 'Git will not perform any conversion when checking out text files. When' + #13 +
607 'committing text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
608 'this is the recommended setting on Unix ("core.autocrlf" is set to "input").';
609 Left:=ScaleX(28);
610 Top:=ScaleY(104);
611 Width:=ScaleX(380);
612 Height:=ScaleY(47);
613 end;
615 // 3rd choice
616 RdbCRLF[GC_CRLFCommitAsIs]:=TRadioButton.Create(CRLFPage);
617 with RdbCRLF[GC_CRLFCommitAsIs] do begin
618 Parent:=CRLFPage.Surface;
619 Caption:='Checkout as-is, commit as-is';
620 Left:=ScaleX(4);
621 Top:=ScaleY(152);
622 Width:=ScaleX(340);
623 Height:=ScaleY(17);
624 Font.Style:=[fsBold];
625 TabOrder:=2;
626 Checked:=False;
627 end;
628 LblCRLFCommitAsIs:=TLabel.Create(CRLFPage);
629 with LblCRLFCommitAsIs do begin
630 Parent:=CRLFPage.Surface;
631 Caption:=
632 'Git will not perform any conversions when checking out or committing' + #13 +
633 'text files. Choosing this option is not recommended for cross-platform' + #13 +
634 'projects ("core.autocrlf" is set to "false").';
635 Left:=ScaleX(28);
636 Top:=ScaleY(176);
637 Width:=ScaleX(380);
638 Height:=ScaleY(47);
639 end;
641 // Restore the setting chosen during a previous install.
642 Data:=GetPreviousData('CRLF Option','CRLFAlways');
643 if Data='LFOnly' then begin
644 RdbCRLF[GC_LFOnly].Checked:=True;
645 end else if Data='CRLFAlways' then begin
646 RdbCRLF[GC_CRLFAlways].Checked:=True;
647 end else if Data='CRLFCommitAsIs' then begin
648 RdbCRLF[GC_CRLFCommitAsIs].Checked:=True;
649 end;
652 * Create a custom page for finding the processes that lock a module.
655 ProcessesPage:=CreateCustomPage(
656 wpPreparing,
657 'Replacing in-use files',
658 'The following applications use files that need to be replaced, please close them.'
661 ProcessesListBox:=TListBox.Create(ProcessesPage);
662 with ProcessesListBox do begin
663 Parent:=ProcessesPage.Surface;
664 Width:=ProcessesPage.SurfaceWidth;
665 Height:=ProcessesPage.SurfaceHeight-ScaleY(8);
666 end;
668 ProcessesRefresh:=TNewButton.Create(WizardForm);
669 with ProcessesRefresh do begin
670 Parent:=WizardForm;
671 Width:=WizardForm.CancelButton.Width;
672 Height:=WizardForm.CancelButton.Height;
673 Top:=WizardForm.CancelButton.Top;
674 Left:=WizardForm.ClientWidth-(WizardForm.CancelButton.Left+WizardForm.CancelButton.Width);
675 Caption:='&Refresh';
676 OnClick:=@RefreshProcessList;
677 end;
679 // This button is only used by the uninstaller.
680 ContinueButton:=NIL;
682 // Initially hide the Refresh button, show it when the process page becomes current.
683 ProcessesRefresh.Hide;
684 end;
686 function ShouldSkipPage(PageID:Integer):Boolean;
687 begin
688 if (ProcessesPage<>NIL) and (PageID=ProcessesPage.ID) then begin
689 // This page is only reached forward (by pressing "Next", never by pressing "Back").
690 RefreshProcessList(NIL);
691 Result:=(GetArrayLength(Processes)=0);
692 end else begin
693 Result:=False;
694 end;
695 end;
697 procedure CurPageChanged(CurPageID:Integer);
699 i:Integer;
700 begin
701 if CurPageID=wpSelectDir then begin
702 if not IsDirWritable(WizardDirValue) then begin
703 // If the default directory is not writable, choose another default that most likely is.
704 // This will be checked later again when the user clicks "Next".
705 WizardForm.DirEdit.Text:=ExpandConstant('{userpf}\{#APP_NAME}');
706 end;
707 end;
709 // Uncheck the console font option by default.
710 if CurPageID=wpSelectComponents then begin
711 for i:=0 to WizardForm.ComponentsList.Items.Count-1 do begin
712 if WizardForm.ComponentsList.ItemCaption[i]='{#COMP_CONSOLE_FONT}' then begin
713 WizardForm.ComponentsList.Checked[i]:=False;
714 Break;
715 end;
716 end;
717 end;
719 // Show the "Refresh" button only on the processes page.
720 if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin
721 ProcessesRefresh.Show;
722 end else begin
723 ProcessesRefresh.Hide;
724 end;
725 end;
727 function NextButtonClick(CurPageID:Integer):Boolean;
729 i:Integer;
730 Version:TWindowsVersion;
731 begin
732 Result:=True;
734 if CurPageID=wpSelectDir then begin
735 if not IsDirWritable(WizardDirValue) then begin
736 MsgBox(
737 'The specified installation directory does not seem to be writable. ' +
738 + 'Please choose another directory or restart setup as a user with sufficient permissions.'
739 , mbCriticalError
740 , MB_OK
742 Result:=False;
743 Exit;
744 end;
745 end;
747 if (PuTTYPage<>NIL) and (CurPageID=PuTTYPage.ID) then begin
748 Result:=RdbSSH[GS_OpenSSH].Checked or
749 (RdbSSH[GS_Plink].Checked and FileExists(EdtPlink.Text));
750 if not Result then begin
751 MsgBox('Please enter a valid path to (Tortoise)Plink.exe.',mbError,MB_OK);
752 end;
753 end else if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin
754 // It would have been nicer to just disable the "Next" button, but the
755 // WizardForm exports the button just read-only.
756 for i:=0 to GetArrayLength(Processes)-1 do begin
757 if not Processes[i].Restartable then begin
758 MsgBox('Setup cannot continue until you close at least those applications in the list that are marked as "closing is required".',mbCriticalError,MB_OK);
759 Result:=False;
760 Exit;
761 end;
762 end;
764 Result:=(GetArrayLength(Processes)=0);
766 if not Result then begin
767 GetWindowsVersionEx(Version);
768 if Version.Major>=6 then begin
769 Result:=(MsgBox(
770 'If you continue without closing the listed applications they will be closed and restarted automatically.' + #13 + #13 +
771 'Are you sure you want to continue?',
772 mbConfirmation,
773 MB_YESNO
774 )=IDYES);
775 end else begin
776 Result:=(MsgBox(
777 'If you continue without closing the listed applications you will need to log off and on again before changes take effect.' + #13 + #13 +
778 'Are you sure you want to continue anyway?',
779 mbConfirmation,
780 MB_YESNO
781 )=IDYES);
782 end;
783 end;
784 end;
785 end;
787 procedure CurStepChanged(CurStep:TSetupStep);
789 AppDir,DllPath,FileName,TempName,Cmd,Msg:String;
790 BuiltIns,ImageNames,EnvPath,EnvHome,EnvSSH:TArrayOfString;
791 Count,i:Longint;
792 LinkCreated:Boolean;
793 FindRec:TFindRec;
794 RootKey:Integer;
795 begin
796 if CurStep=ssInstall then begin
797 // Shutdown locking processes just before the actual installation starts.
798 if SessionHandle>0 then try
799 RmShutdown(SessionHandle,RmShutdownOnlyRegistered,0);
800 except
801 Log('Line {#__LINE__}: RmShutdown not supported.');
802 end;
804 Exit;
805 end;
807 // Make sure the code below is only executed just after the actual installation finishes.
808 if CurStep<>ssPostInstall then begin
809 Exit;
810 end;
812 AppDir:=ExpandConstant('{app}');
815 Bind the imported function addresses
819 DllPath:=ExpandConstant('{app}\bin;{sys}');
821 // Load the list of images from a text file.
822 FileName:=AppDir+'\{#APP_BINDIMAGE}';
823 if LoadStringsFromFile(FileName,ImageNames) then begin
824 Count:=GetArrayLength(ImageNames)-1;
825 for i:=0 to Count do begin
826 FileName:=AppDir+'\'+ImageNames[i];
827 if not BindImageEx(BIND_NO_BOUND_IMPORTS or BIND_CACHE_IMPORT_DLLS,FileName,DllPath,'',0) then begin
828 Log('Line {#__LINE__}: Error calling BindImageEx for "'+FileName+'".');
829 end;
830 end;
831 end;
832 except
833 Log('Line {#__LINE__}: An exception occurred while calling BindImageEx.');
834 end;
837 Create the built-ins
840 // Load the built-ins from a text file.
841 FileName:=AppDir+'\{#APP_BUILTINS}';
842 if LoadStringsFromFile(FileName,BuiltIns) then begin
843 Count:=GetArrayLength(BuiltIns)-1;
845 // Delete those scripts from "bin" which have been replaced by built-ins in "libexec\git-core".
846 for i:=0 to Count do begin
847 FileName:=AppDir+'\bin\'+ChangeFileExt(ExtractFileName(BuiltIns[i]),'');
848 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
849 Log('Line {#__LINE__}: Unable to delete script "'+FileName+'", ignoring.');
850 end;
851 end;
853 // Create built-ins as aliases for git.exe.
854 for i:=0 to Count do begin
855 FileName:=AppDir+'\'+BuiltIns[i];
857 // Delete any existing built-in.
858 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
859 Log('Line {#__LINE__}: Unable to delete existing built-in "'+FileName+'", skipping.');
860 continue;
861 end;
864 // This will throw an exception on pre-Win2k systems.
865 LinkCreated:=CreateHardLink(FileName,AppDir+'\bin\git.exe',0);
866 except
867 LinkCreated:=False;
868 Log('Line {#__LINE__}: Creating hardlink "'+FileName+'" failed, will try a copy.');
869 end;
871 if not LinkCreated then begin
872 if not FileCopy(AppDir+'\bin\git.exe',FileName,False) then begin
873 Log('Line {#__LINE__}: Creating copy "'+FileName+'" failed, giving up.');
874 // This is not a critical error, Git could basically be used without the
875 // aliases for built-ins, so we continue.
876 end;
877 end;
878 end;
880 // Delete any duplicate files in case we are updating from a non-libexec to a libexec directory layout.
881 if FindFirst(AppDir+'\libexec\git-core\*',FindRec) then begin
882 repeat
883 if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY)=0 then begin
884 FileName:=AppDir+'\bin\'+FindRec.name;
885 if (Pos(FindRec.name,'git.exe')<>1) and FileExists(FileName) and (not DeleteFile(FileName)) then begin
886 Log('Line {#__LINE__}: Unable to delete dupe "'+FileName+'", ignoring.');
887 end;
888 end;
889 until not FindNext(FindRec);
890 FindClose(FindRec);
891 end;
892 end else begin
893 Msg:='Line {#__LINE__}: Unable to read file "{#APP_BUILTINS}".';
894 MsgBox(Msg,mbError,MB_OK);
895 Log(Msg);
896 // This is not a critical error, Git could basically be used without the
897 // aliases for built-ins, so we continue.
898 end;
901 Adapt core.autocrlf
904 if RdbCRLF[GC_LFOnly].checked then begin
905 Cmd:='core.autocrlf input';
906 end else if RdbCRLF[GC_CRLFAlways].checked then begin
907 Cmd:='core.autocrlf true';
908 end else begin
909 Cmd:='core.autocrlf false';
910 end;
911 if not Exec(AppDir + '\bin\git.exe', 'config -f gitconfig ' + Cmd,
912 AppDir + '\etc', SW_HIDE, ewWaitUntilTerminated, i) then begin
913 Msg:='Unable to configure the line ending conversion: ' + Cmd;
914 MsgBox(Msg,mbError,MB_OK);
915 Log(Msg);
916 // This is not a critical error, the user can probably fix it manually,
917 // so we continue.
918 end;
921 Modify the environment
923 This must happen no later than ssPostInstall to make
924 "ChangesEnvironment=yes" not happend before the change!
927 FileName:=AppDir+'\setup.ini';
929 // Delete GIT_SSH and SVN_SSH if a previous installation set them (this is required for the GS_OpenSSH case).
930 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
931 if (GetArrayLength(EnvSSH)=1) and
932 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
933 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
934 Msg:='Line {#__LINE__}: Unable to reset GIT_SSH prior to install.';
935 MsgBox(Msg,mbError,MB_OK);
936 Log(Msg);
937 // This is not a critical error, the user can probably fix it manually,
938 // so we continue.
939 end;
940 end;
942 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
943 if (GetArrayLength(EnvSSH)=1) and
944 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','SVN_SSH','',FileName))=0) then begin
945 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
946 Msg:='Line {#__LINE__}: Unable to reset SVN_SSH prior to install.';
947 MsgBox(Msg,mbError,MB_OK);
948 Log(Msg);
949 // This is not a critical error, the user can probably fix it manually,
950 // so we continue.
951 end;
952 end;
954 if (PuTTYPage<>NIL) and RdbSSH[GS_Plink].Checked then begin
955 SetArrayLength(EnvSSH,1);
956 EnvSSH[0]:=EdtPlink.Text;
958 // Set GIT_SSH as specified by the user.
959 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
960 Msg:='Line {#__LINE__}: Unable to set the GIT_SSH environment variable.';
961 MsgBox(Msg,mbError,MB_OK);
962 Log(Msg);
963 // This is not a critical error, the user can probably fix it manually,
964 // so we continue.
965 end;
967 // Mark that we have changed GIT_SSH by writing its value to a file.
968 if not SetIniString('Environment','GIT_SSH',EnvSSH[0],FileName) then begin
969 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
970 MsgBox(Msg,mbError,MB_OK);
971 Log(Msg);
972 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
973 // so we continue.
974 end;
976 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
977 Msg:='Line {#__LINE__}: Unable to set the SVN_SSH environment variable.';
978 MsgBox(Msg,mbError,MB_OK);
979 Log(Msg);
980 // This is not a critical error, the user can probably fix it manually,
981 // so we continue.
982 end;
984 // Mark that we have changed SVN_SSH by writing its value to a file.
985 if not SetIniString('Environment','SVN_SSH',EnvSSH[0],FileName) then begin
986 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
987 MsgBox(Msg,mbError,MB_OK);
988 Log(Msg);
989 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
990 // so we continue.
991 end;
992 end;
994 // Get the current user's directories in PATH.
995 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
997 // First, remove the installation directory from PATH in any case.
998 for i:=0 to GetArrayLength(EnvPath)-1 do begin
999 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
1000 EnvPath[i]:='';
1001 end;
1002 end;
1004 // Delete HOME if a previous installation modified it.
1005 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
1006 if (GetArrayLength(EnvHome)=1) and
1007 (CompareStr(RemoveQuotes(EnvHome[0]),GetIniString('Environment','HOME','',FileName))=0) then begin
1008 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
1009 Msg:='Line {#__LINE__}: Unable to reset HOME prior to install.';
1010 MsgBox(Msg,mbError,MB_OK);
1011 Log(Msg);
1012 // This is not a critical error, the user can probably fix it manually,
1013 // so we continue.
1014 end;
1015 end;
1017 // Modify the PATH variable as requested by the user.
1018 if RdbPath[GP_Cmd].Checked or RdbPath[GP_CmdTools].Checked then begin
1019 i:=GetArrayLength(EnvPath);
1020 SetArrayLength(EnvPath,i+1);
1022 // List \cmd before \bin so \cmd has higher priority and programs in
1023 // there will be called in favor of those in \bin.
1024 EnvPath[i]:=AppDir+'\cmd';
1026 if RdbPath[GP_CmdTools].Checked then begin
1027 SetArrayLength(EnvPath,i+2);
1028 EnvPath[i+1]:=AppDir+'\bin';
1030 // Set HOME for the Windows Command Prompt, but only if it has not been set manually before.
1031 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
1032 i:=GetArrayLength(EnvHome);
1033 if (i=0) or ((i=1) and (Length(EnvHome[0])=0)) then begin
1034 SetArrayLength(EnvHome,1);
1035 EnvHome[0]:=ExpandConstant('{%HOMEDRIVE}{%HOMEPATH}');
1036 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,EnvHome) then begin
1037 Msg:='Line {#__LINE__}: Unable to set the HOME environment variable.';
1038 MsgBox(Msg,mbError,MB_OK);
1039 Log(Msg);
1040 // This is not a critical error, the user can probably fix it manually,
1041 // so we continue.
1042 end;
1044 // Mark that we have changed HOME.
1045 if not SetIniString('Environment','HOME',EnvHome[0],FileName) then begin
1046 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
1047 MsgBox(Msg,mbError,MB_OK);
1048 Log(Msg);
1049 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
1050 // so we continue.
1051 end;
1052 end;
1053 end;
1054 end;
1056 // Set the current user's PATH directories.
1057 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
1058 Msg:='Line {#__LINE__}: Unable to set the PATH environment variable.';
1059 MsgBox(Msg,mbError,MB_OK);
1060 Log(Msg);
1061 // This is not a critical error, the user can probably fix it manually,
1062 // so we continue.
1063 end;
1066 Create shortcuts that need to be created regardless of the "Don't create a Start Menu folder" toggle
1069 Cmd:=ExpandConstant('{syswow64}\cmd.exe');
1070 TempName:='/c ""'+AppDir+'\bin\sh.exe" --login -i"';
1071 FileName:=AppDir+'\etc\git.ico';
1073 if IsComponentSelected('icons\quicklaunch') then begin
1074 CreateShellLink(
1075 ExpandConstant('{userappdata}\Microsoft\Internet Explorer\Quick Launch\Git Bash.lnk')
1076 , 'Git Bash'
1077 , Cmd
1078 , TempName
1079 , '%HOMEDRIVE%%HOMEPATH%'
1080 , FileName
1082 , SW_SHOWNORMAL
1084 end;
1085 if IsComponentSelected('icons\desktop') then begin
1086 CreateShellLink(
1087 GetShellFolder('desktop')+'\Git Bash.lnk'
1088 , 'Git Bash'
1089 , Cmd
1090 , TempName
1091 , '%HOMEDRIVE%%HOMEPATH%'
1092 , FileName
1094 , SW_SHOWNORMAL
1096 end;
1098 // Create a special shortcut that does not set a working directory (Note: Since Inno Setup 5.3.11,
1099 // passing an empty WorkingDir gets replaced with {sys}, so use '.' instead).
1100 // This shortcut is used by "Git Bash.vbs", which in turn is run by the "Git Bash Here" shell extension.
1101 CreateShellLink(
1102 AppDir+'\Git Bash.lnk'
1103 , 'Git Bash'
1104 , Cmd
1105 , TempName
1106 , '.'
1107 , FileName
1109 , SW_SHOWNORMAL
1113 Create the Windows Explorer integrations
1116 if IsAdminLoggedOn then begin
1117 RootKey:=HKEY_LOCAL_MACHINE;
1118 end else begin
1119 RootKey:=HKEY_CURRENT_USER;
1120 end;
1122 if IsComponentSelected('ext\reg\shellhere') then begin
1123 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell','','Git Ba&sh Here')) or
1124 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','','"'+ExpandConstant('{syswow64}')+'\wscript" "'+AppDir+'\Git Bash.vbs" "%1"')) then begin
1125 Msg:='Line {#__LINE__}: Unable to create "Git Bash Here" shell extension.';
1126 MsgBox(Msg,mbError,MB_OK);
1127 Log(Msg);
1128 // This is not a critical error, the user can probably fix it manually,
1129 // so we continue.
1130 end;
1131 end;
1133 if IsComponentSelected('ext\reg\guihere') then begin
1134 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui','','Git &GUI Here')) or
1135 (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
1136 Msg:='Line {#__LINE__}: Unable to create "Git GUI Here" shell extension.';
1137 MsgBox(Msg,mbError,MB_OK);
1138 Log(Msg);
1139 // This is not a critical error, the user can probably fix it manually,
1140 // so we continue.
1141 end;
1142 end;
1144 // It is either the Registry-based context menu entries, or the shell extension.
1145 if IsComponentSelected('ext\cheetah') then begin
1146 DeleteContextMenuEntries;
1148 if isWin64 then begin
1149 FileName:=AppDir+'\git-cheetah\git_shell_ext64.dll';
1150 end else begin
1151 FileName:=AppDir+'\git-cheetah\git_shell_ext.dll';
1152 end;
1153 if not ReplaceInUseFile(FileName,FileName+'.new',True) then begin
1154 Log('Line {#__LINE__}: Replacing file "'+FileName+'" failed.');
1155 end;
1156 end;
1159 Restart any processes that were shut down via the Restart Manager
1162 if SessionHandle>0 then try
1163 RmRestart(SessionHandle,0,0);
1164 RmEndSession(SessionHandle);
1165 except
1166 Log('Line {#__LINE__}: RmRestart not supported.');
1167 end;
1168 end;
1170 procedure RegisterPreviousData(PreviousDataKey:Integer);
1172 Data:String;
1173 begin
1174 // Git Path options.
1175 Data:='';
1176 if RdbPath[GP_BashOnly].Checked then begin
1177 Data:='BashOnly';
1178 end else if RdbPath[GP_Cmd].Checked then begin
1179 Data:='Cmd';
1180 end else if RdbPath[GP_CmdTools].Checked then begin
1181 Data:='CmdTools';
1182 end;
1183 SetPreviousData(PreviousDataKey,'Path Option',Data);
1185 // Git SSH options.
1186 Data:='';
1187 if (PuTTYPage=NIL) or RdbSSH[GS_OpenSSH].Checked then begin
1188 Data:='OpenSSH';
1189 end else if RdbSSH[GS_Plink].Checked then begin
1190 Data:='Plink';
1191 end;
1192 SetPreviousData(PreviousDataKey,'SSH Option',Data);
1194 // Line ending conversion options.
1195 Data:='';
1196 if RdbCRLF[GC_LFOnly].Checked then begin
1197 Data:='LFOnly';
1198 end else if RdbCRLF[GC_CRLFAlways].Checked then begin
1199 Data:='CRLFAlways';
1200 end else if RdbCRLF[GC_CRLFCommitAsIs].Checked then begin
1201 Data:='CRLFCommitAsIs';
1202 end;
1203 SetPreviousData(PreviousDataKey,'CRLF Option',Data);
1204 end;
1207 Uninstaller code
1210 function InitializeUninstall:Boolean;
1212 Form:TSetupForm;
1213 Info:TLabel;
1214 ExitButton,RefreshButton:TButton;
1215 begin
1216 Result:=True;
1218 Form:=CreateCustomForm;
1220 Form.Caption:='Git Uninstall: Removing in-use files';
1221 Form.ClientWidth:=ScaleX(500);
1222 Form.ClientHeight:=ScaleY(256);
1223 Form.Center;
1225 Info:=TLabel.Create(Form);
1226 with Info do begin
1227 Parent:=Form;
1228 Left:=ScaleX(11);
1229 Top:=ScaleY(11);
1230 AutoSize:=True;
1231 Caption:='The following applications use files that need to be removed, please close them.';
1232 end;
1234 ContinueButton:=TButton.Create(Form);
1235 with ContinueButton do begin
1236 Parent:=Form;
1237 Left:=Form.ClientWidth-ScaleX(75+10);
1238 Top:=Form.ClientHeight-ScaleY(23+10);
1239 Width:=ScaleX(75);
1240 Height:=ScaleY(23);
1241 Caption:='Continue';
1242 ModalResult:=mrOk;
1243 end;
1245 ExitButton:=TButton.Create(Form);
1246 with ExitButton do begin
1247 Parent:=Form;
1248 Left:=ContinueButton.Left-ScaleX(75+6);
1249 Top:=ContinueButton.Top;
1250 Width:=ScaleX(75);
1251 Height:=ScaleY(23);
1252 Caption:='Exit';
1253 ModalResult:=mrCancel;
1254 Cancel:=True;
1255 end;
1257 RefreshButton:=TButton.Create(Form);
1258 with RefreshButton do begin
1259 Parent:=Form;
1260 Left:=ScaleX(10);
1261 Top:=ExitButton.Top;
1262 Width:=ScaleX(75);
1263 Height:=ScaleY(23);
1264 Caption:='Refresh';
1265 OnClick:=@RefreshProcessList;
1266 end;
1268 ProcessesListBox:=TListBox.Create(Form);
1269 with ProcessesListBox do begin
1270 Parent:=Form;
1271 Left:=ScaleX(11);
1272 Top:=Info.Top+Info.Height+11;
1273 Width:=Form.ClientWidth-ScaleX(11*2);
1274 Height:=ContinueButton.Top-ScaleY(11*4);
1275 end;
1277 Form.ActiveControl:=ContinueButton;
1279 RefreshProcessList(NIL);
1280 if GetArrayLength(Processes)>0 then begin
1281 // Now that these dialogs are going to be shown, we should probably
1282 // disable the "Are you sure to remove Git?" confirmation dialog, but
1283 // unfortunately that is not possible with Inno Setup currently.
1284 Result:=(Form.ShowModal()=mrOk);
1286 // Note: The number of processes might have changed during a refresh.
1287 if Result and (GetArrayLength(Processes)>0) then begin
1288 Result:=(MsgBox(
1289 'If you continue without closing the listed applications, you will need to log off and on again to remove some files manually.' + #13 + #13 +
1290 'Are you sure you want to continue anyway?',
1291 mbConfirmation,
1292 MB_YESNO
1293 )=IDYES);
1294 end;
1295 end;
1296 finally
1297 Form.free;
1298 end;
1299 end;
1301 // PreUninstall
1303 // Even though the name of this function suggests otherwise most of the
1304 // code below is only executed right before the actual uninstallation.
1305 // This happens because of the if-guard right in the beginning of this
1306 // function.
1307 procedure CurUninstallStepChanged(CurUninstallStep:TUninstallStep);
1309 AppDir,FileName,Msg:String;
1310 EnvPath,EnvHome,EnvSSH:TArrayOfString;
1311 i:Longint;
1312 begin
1313 if CurUninstallStep<>usUninstall then begin
1314 Exit;
1315 end;
1317 // Reset the console font (the FontType is reset in the Registry section).
1318 if IsComponentInstalled('consolefont') then begin
1319 if MsgBox('Do you want to revert the TrueType font setting for all console windows?',mbConfirmation,MB_YESNO)=IDYES then begin
1320 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontFamily',0);
1321 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontSize',0);
1322 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontWeight',0);
1323 end;
1324 end;
1327 Modify the environment
1329 This must happen no later than usUninstall to make
1330 "ChangesEnvironment=yes" not happend before the change!
1333 AppDir:=ExpandConstant('{app}');
1334 FileName:=AppDir+'\setup.ini';
1336 // Delete the current user's GIT_SSH and SVN_SSH if we set it.
1337 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
1338 if (GetArrayLength(EnvSSH)=1) and
1339 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
1340 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
1341 Msg:='Line {#__LINE__}: Unable to revert any possible changes to GIT_SSH.';
1342 MsgBox(Msg,mbError,MB_OK);
1343 Log(Msg);
1344 // This is not a critical error, the user can probably fix it manually,
1345 // so we continue.
1346 end;
1347 end;
1349 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
1350 if (GetArrayLength(EnvSSH)=1) and
1351 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','SVN_SSH','',FileName))=0) then begin
1352 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
1353 Msg:='Line {#__LINE__}: Unable to revert any possible changes to SVN_SSH.';
1354 MsgBox(Msg,mbError,MB_OK);
1355 Log(Msg);
1356 // This is not a critical error, the user can probably fix it manually,
1357 // so we continue.
1358 end;
1359 end;
1361 // Get the current user's directories in PATH.
1362 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
1364 // Remove the installation directory from PATH in any case, even if it
1365 // was not added by the installer.
1366 for i:=0 to GetArrayLength(EnvPath)-1 do begin
1367 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
1368 EnvPath[i]:='';
1369 end;
1370 end;
1372 // Reset the current user's directories in PATH.
1373 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
1374 Msg:='Line {#__LINE__}: Unable to revert any possible changes to PATH.';
1375 MsgBox(Msg,mbError,MB_OK);
1376 Log(Msg);
1377 // This is not a critical error, the user can probably fix it manually,
1378 // so we continue.
1379 end;
1381 // Reset the current user's HOME if we modified it.
1382 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
1383 if (GetArrayLength(EnvHome)=1) and
1384 (CompareStr(RemoveQuotes(EnvHome[0]),GetIniString('Environment','HOME','',FileName))=0) then begin
1385 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
1386 Msg:='Line {#__LINE__}: Unable to revert any possible changes to HOME.';
1387 MsgBox(Msg,mbError,MB_OK);
1388 Log(Msg);
1389 // This is not a critical error, the user can probably fix it manually,
1390 // so we continue.
1391 end;
1392 end;
1394 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
1395 Msg:='Line {#__LINE__}: Unable to delete file "'+FileName+'".';
1396 MsgBox(Msg,mbError,MB_OK);
1397 Log(Msg);
1398 // This is not a critical error, the user can probably fix it manually,
1399 // so we continue.
1400 end;
1403 Delete the Windows Explorer integrations
1406 DeleteContextMenuEntries;
1408 if isWin64 then begin
1409 FileName:=AppDir+'\git-cheetah\git_shell_ext64.dll';
1410 end else begin
1411 FileName:=AppDir+'\git-cheetah\git_shell_ext.dll';
1412 end;
1413 if FileExists(FileName) then begin
1414 if not UnregisterServer(Is64BitInstallMode,FileName,False) then begin
1415 Msg:='Line {#__LINE__}: Unable to unregister file "'+FileName+'". Please do it manually by running "regsvr32 /u '+ExtractFileName(FileName)+'".';
1416 MsgBox(Msg,mbError,MB_OK);
1417 Log(Msg);
1418 end;
1420 if not DeleteFile(FileName) then begin
1421 Msg:='Line {#__LINE__}: Unable to delete file "'+FileName+'". Please do it manually after logging off and on again.';
1422 MsgBox(Msg,mbError,MB_OK);
1423 Log(Msg);
1424 end;
1425 end;
1426 end;