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