Installer: Continue the idea of d5f2b0e for all calls of cmd.exe
[msysgit.git] / share / WinGit / install.iss
blobffa06e455d79f82d51c00451abbdcdeec6c8eb0b
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; OnlyBelowVersion: 6.0
87 Name: {group}\Git Bash; Filename: {app}\bin\sh.exe; Parameters: "--login -i"; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico; MinVersion: 6.0
89 [Messages]
90 BeveledLabel={#APP_URL}
91 SetupAppTitle={#APP_NAME} Setup
92 SetupWindowTitle={#APP_NAME} Setup
94 [Registry]
95 ; There is no "Console" key in HKLM.
96 Root: HKCU; Subkey: Console; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: uninsclearvalue; Components: consolefont
97 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Components: consolefont
98 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Components: consolefont
99 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Components: consolefont
101 Root: HKCU; Subkey: Console\Git Bash; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
102 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
103 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
104 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
106 ; Note that we write the Registry values below either to HKLM or to HKCU depending on whether the user running the installer
107 ; is a member of the local Administrators group or not (see the "Check" argument).
109 ; File associations for configuration files that may be contained in a repository (so this does not include ".gitconfig").
110 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
111 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
112 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
113 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
114 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
115 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
117 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
118 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
119 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
120 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
121 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
122 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
124 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
125 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
126 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
127 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
128 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
129 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
131 ; Associate .sh extension with sh.exe so those files are double-clickable,
132 ; startable from cmd.exe, and when files are dropped on them they are passed
133 ; as arguments to the script.
135 ; Install under HKEY_LOCAL_MACHINE if an administrator is installing.
136 Root: HKLM; Subkey: Software\Classes\.sh; ValueType: string; ValueData: sh_auto_file; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
137 Root: HKLM; Subkey: Software\Classes\sh_auto_file; ValueType: string; ValueData: "Shell Script"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
138 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; OnlyBelowVersion: 6.0
139 Root: HKLM; Subkey: Software\Classes\sh_auto_file\shell\open\command; ValueType: string; ValueData: """{app}\bin\sh.exe"" ""--login"" ""%1"" %*"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh; MinVersion: 6.0
140 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
141 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
143 ; Install under HKEY_CURRENT_USER if a non-administrator is installing.
144 Root: HKCU; Subkey: Software\Classes\.sh; ValueType: string; ValueData: sh_auto_file; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
145 Root: HKCU; Subkey: Software\Classes\sh_auto_file; ValueType: string; ValueData: "Shell Script"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
146 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; OnlyBelowVersion: 6.0
147 Root: HKCU; Subkey: Software\Classes\sh_auto_file\shell\open\command; ValueType: string; ValueData: """{app}\bin\sh.exe"" ""--login"" ""%1"" %*"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh; MinVersion: 6.0
148 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
149 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
151 [UninstallDelete]
152 ; Delete the built-ins.
153 Type: files; Name: {app}\bin\git-*.exe
154 Type: files; Name: {app}\libexec\git-core\git-*.exe
155 Type: files; Name: {app}\libexec\git-core\git.exe
157 ; Delete any (temporary) git-cheetah files.
158 Type: files; Name: {app}\git-cheetah\*.*
160 ; Delete any manually created shortcuts.
161 Type: files; Name: {userappdata}\Microsoft\Internet Explorer\Quick Launch\Git Bash.lnk
162 Type: files; Name: {code:GetShellFolder|desktop}\Git Bash.lnk
163 Type: files; Name: {app}\Git Bash.lnk
165 ; Delete a home directory inside the msysGit directory.
166 Type: dirifempty; Name: {app}\home\{username}
167 Type: dirifempty; Name: {app}\home
169 [Code]
170 #include "helpers.inc.iss"
171 #include "environment.inc.iss"
172 #include "putty.inc.iss"
173 #include "modules.inc.iss"
175 procedure DeleteFromVirtualStore;
177 VirtualStore,FileName:String;
178 DriveChars:Integer;
179 begin
180 VirtualStore:=AddBackslash(ExpandConstant('{localappdata}'))+'VirtualStore';
181 FileName:=ExpandConstant(CurrentFileName);
182 DriveChars:=Length(ExtractFileDrive(FileName));
183 if DriveChars>0 then begin
184 Delete(FileName,1,DriveChars);
185 FileName:=VirtualStore+FileName;
186 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
187 Log('Line {#__LINE__}: Unable delete "'+FileName+'".');
188 // This is not a critical error, the user can probably fix it manually,
189 // so we continue.
190 end;
191 end;
192 end;
194 function CreateHardLink(lpFileName,lpExistingFileName:String;lpSecurityAttributes:Integer):Boolean;
195 #ifdef UNICODE
196 external 'CreateHardLinkW@Kernel32.dll stdcall delayload setuponly';
197 #else
198 external 'CreateHardLinkA@Kernel32.dll stdcall delayload setuponly';
199 #endif
201 function BindImageEx(Flags:DWORD;ImageName,DllPath,SymbolPath:AnsiString;StatusRoutine:Integer):Boolean;
202 external 'BindImageEx@Imagehlp.dll stdcall delayload setuponly';
204 const
205 // Git Path options.
206 GP_BashOnly = 1;
207 GP_Cmd = 2;
208 GP_CmdTools = 3;
210 // Git SSH options.
211 GS_OpenSSH = 1;
212 GS_Plink = 2;
214 // Git line ending conversion options.
215 GC_LFOnly = 1;
216 GC_CRLFAlways = 2;
217 GC_CRLFCommitAsIs = 3;
219 // BindImageEx API constants.
220 BIND_NO_BOUND_IMPORTS = $00000001;
221 BIND_NO_UPDATE = $00000002;
222 BIND_ALL_IMAGES = $00000004;
223 BIND_CACHE_IMPORT_DLLS = $00000008;
226 // Wizard page and variables for the Path options.
227 PathPage:TWizardPage;
228 RdbPath:array[GP_BashOnly..GP_CmdTools] of TRadioButton;
230 // Wizard page and variables for the SSH options.
231 PuTTYPage:TWizardPage;
232 RdbSSH:array[GS_OpenSSH..GS_Plink] of TRadioButton;
233 EdtPlink:TEdit;
235 // Wizard page and variables for the line ending conversion options.
236 CRLFPage:TWizardPage;
237 RdbCRLF:array[GC_LFOnly..GC_CRLFCommitAsIs] of TRadioButton;
239 // Wizard page and variables for the processes page.
240 SessionHandle:DWORD;
241 Processes:ProcessList;
242 ProcessesPage:TWizardPage;
243 ProcessesListBox:TListBox;
244 ProcessesRefresh,ContinueButton:TButton;
246 procedure BrowseForPuTTYFolder(Sender:TObject);
248 Path:String;
249 begin
250 Path:=ExtractFilePath(EdtPlink.Text);
251 BrowseForFolder('Please select the PuTTY folder:',Path,False);
252 if FileExists(Path+'\TortoisePlink.exe') then begin
253 EdtPlink.Text:=Path+'\TortoisePlink.exe';
254 RdbSSH[GS_Plink].Checked:=True;
255 end else if FileExists(Path+'\plink.exe') then begin
256 EdtPlink.Text:=Path+'\plink.exe';
257 RdbSSH[GS_Plink].Checked:=True;
258 end else begin
259 MsgBox('Please enter a valid path to "TortoisePlink.exe" or "plink.exe".',mbError,MB_OK);
260 end;
261 end;
263 procedure DeleteContextMenuEntries;
265 AppDir,Command,Msg:String;
266 RootKey:Integer;
267 begin
268 AppDir:=ExpandConstant('{app}');
270 if IsAdminLoggedOn then begin
271 RootKey:=HKEY_LOCAL_MACHINE;
272 end else begin
273 RootKey:=HKEY_CURRENT_USER;
274 end;
276 Command:='';
277 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','',Command);
278 if Pos(AppDir,Command)>0 then begin
279 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell') then begin
280 Msg:='Line {#__LINE__}: Unable to remove "Git Bash Here" shell extension.';
281 MsgBox(Msg,mbError,MB_OK);
282 Log(Msg);
283 // This is not a critical error, the user can probably fix it manually,
284 // so we continue.
285 end;
286 end;
288 Command:='';
289 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui\command','',Command);
290 if Pos(AppDir,Command)>0 then begin
291 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui') then begin
292 Msg:='Line {#__LINE__}: Unable to remove "Git GUI Here" shell extension.';
293 MsgBox(Msg,mbError,MB_OK);
294 Log(Msg);
295 // This is not a critical error, the user can probably fix it manually,
296 // so we continue.
297 end;
298 end;
299 end;
301 procedure RefreshProcessList(Sender:TObject);
303 Version:TWindowsVersion;
304 Modules:TArrayOfString;
305 ProcsCloseRequired,ProcsCloseOptional:ProcessList;
306 i:Longint;
307 Caption:String;
308 ManualClosingRequired:Boolean;
309 begin
310 GetWindowsVersionEx(Version);
312 // Use the Restart Manager API when installing the shell extension on Windows Vista and above.
313 if Version.Major>=6 then begin
314 SetArrayLength(Modules,5);
315 Modules[0]:=ExpandConstant('{app}\bin\msys-1.0.dll');
316 Modules[1]:=ExpandConstant('{app}\bin\tcl85.dll');
317 Modules[2]:=ExpandConstant('{app}\bin\tk85.dll');
318 Modules[3]:=ExpandConstant('{app}\git-cheetah\git_shell_ext.dll');
319 Modules[4]:=ExpandConstant('{app}\git-cheetah\git_shell_ext64.dll');
320 SessionHandle:=FindProcessesUsingModules(Modules,Processes);
321 end else begin
322 SetArrayLength(Modules,3);
323 Modules[0]:=ExpandConstant('{app}\bin\msys-1.0.dll');
324 Modules[1]:=ExpandConstant('{app}\bin\tcl85.dll');
325 Modules[2]:=ExpandConstant('{app}\bin\tk85.dll');
326 SessionHandle:=FindProcessesUsingModules(Modules,ProcsCloseRequired);
328 SetArrayLength(Modules,2);
329 Modules[0]:=ExpandConstant('{app}\git-cheetah\git_shell_ext.dll');
330 Modules[1]:=ExpandConstant('{app}\git-cheetah\git_shell_ext64.dll');
331 SessionHandle:=FindProcessesUsingModules(Modules,ProcsCloseOptional) or SessionHandle;
333 // Misuse the "Restartable" flag to indicate which processes are required
334 // to be closed before setup can continue, and which just should be closed
335 // in order to make changes take effect immediately.
336 SetArrayLength(Processes,GetArrayLength(ProcsCloseRequired)+GetArrayLength(ProcsCloseOptional));
337 for i:=0 to GetArrayLength(ProcsCloseRequired)-1 do begin
338 Processes[i]:=ProcsCloseRequired[i];
339 Processes[i].Restartable:=False;
340 end;
341 for i:=0 to GetArrayLength(ProcsCloseOptional)-1 do begin
342 Processes[GetArrayLength(ProcsCloseRequired)+i]:=ProcsCloseOptional[i];
343 Processes[GetArrayLength(ProcsCloseRequired)+i].Restartable:=True;
344 end;
345 end;
347 ManualClosingRequired:=False;
349 ProcessesListBox.Items.Clear;
350 if (Sender=NIL) or (SessionHandle>0) then begin
351 for i:=0 to GetArrayLength(Processes)-1 do begin
352 Caption:=Processes[i].Name+' (PID '+IntToStr(Processes[i].ID);
353 if Processes[i].Restartable then begin
354 Caption:=Caption+', closing is optional';
355 end else begin
356 Caption:=Caption+', closing is required';
357 ManualClosingRequired:=True;
358 end;
359 Caption:=Caption+')';
360 ProcessesListBox.Items.Append(Caption);
361 end;
362 end;
364 if ContinueButton<>NIL then begin
365 ContinueButton.Enabled:=not ManualClosingRequired;
366 end;
367 end;
370 Installer code
373 procedure InitializeWizard;
375 PrevPageID:Integer;
376 LblGitBash,LblGitCmd,LblGitCmdTools,LblGitCmdToolsWarn:TLabel;
377 LblOpenSSH,LblPlink:TLabel;
378 PuTTYSessions:TArrayOfString;
379 LblLFOnly,LblCRLFAlways,LblCRLFCommitAsIs:TLabel;
380 BtnPlink:TButton;
381 Data:String;
382 begin
384 PrevPageID:=wpSelectProgramGroup;
387 * Create a custom page for modifying the environment.
390 PathPage:=CreateCustomPage(
391 PrevPageID,
392 'Adjusting your PATH environment',
393 'How would you like to use Git from the command line?'
395 PrevPageID:=PathPage.ID;
397 // 1st choice
398 RdbPath[GP_BashOnly]:=TRadioButton.Create(PathPage);
399 with RdbPath[GP_BashOnly] do begin
400 Parent:=PathPage.Surface;
401 Caption:='Use Git Bash only';
402 Left:=ScaleX(4);
403 Top:=ScaleY(8);
404 Width:=ScaleX(129);
405 Height:=ScaleY(17);
406 Font.Style:=[fsBold];
407 TabOrder:=0;
408 Checked:=True;
409 end;
410 LblGitBash:=TLabel.Create(PathPage);
411 with LblGitBash do begin
412 Parent:=PathPage.Surface;
413 Caption:=
414 'This is the most conservative choice if you are concerned about the stability' + #13 +
415 'of your system. Your PATH will not be modified.';
416 Left:=ScaleX(28);
417 Top:=ScaleY(32);
418 Width:=ScaleX(405);
419 Height:=ScaleY(26);
420 end;
422 // 2nd choice
423 RdbPath[GP_Cmd]:=TRadioButton.Create(PathPage);
424 with RdbPath[GP_Cmd] do begin
425 Parent:=PathPage.Surface;
426 Caption:='Run Git from the Windows Command Prompt';
427 Left:=ScaleX(4);
428 Top:=ScaleY(76);
429 Width:=ScaleX(281);
430 Height:=ScaleY(17);
431 Font.Style:=[fsBold];
432 TabOrder:=1;
433 end;
434 LblGitCmd:=TLabel.Create(PathPage);
435 with LblGitCmd do begin
436 Parent:=PathPage.Surface;
437 Caption:=
438 'This option is considered safe and no conflicts with other tools are known.' + #13 +
439 'Only Git will be added to your PATH. Use this option if you want to use Git' + #13 +
440 'from a Cygwin Prompt (make sure to not have Cygwin''s Git installed).';
441 Left:=ScaleX(28);
442 Top:=ScaleY(100);
443 Width:=ScaleX(405);
444 Height:=ScaleY(39);
445 end;
447 // 3rd choice
448 RdbPath[GP_CmdTools]:=TRadioButton.Create(PathPage);
449 with RdbPath[GP_CmdTools] do begin
450 Parent:=PathPage.Surface;
451 Caption:='Run Git and included Unix tools from the Windows Command Prompt';
452 Left:=ScaleX(4);
453 Top:=ScaleY(152);
454 Width:=ScaleX(405);
455 Height:=ScaleY(17);
456 Font.Style:=[fsBold];
457 TabOrder:=2;
458 end;
459 LblGitCmdTools:=TLabel.Create(PathPage);
460 with LblGitCmdTools do begin
461 Parent:=PathPage.Surface;
462 Caption:='Both Git and its accompanying Unix tools will be added to your PATH.';
463 Left:=ScaleX(28);
464 Top:=ScaleY(176);
465 Width:=ScaleX(405);
466 Height:=ScaleY(13);
467 end;
468 LblGitCmdToolsWarn:=TLabel.Create(PathPage);
469 with LblGitCmdToolsWarn do begin
470 Parent:=PathPage.Surface;
471 Caption:=
472 'Warning: This will override Windows tools like find.exe and' + #13 +
473 'sort.exe. Select this option only if you understand the implications.';
474 Left:=ScaleX(28);
475 Top:=ScaleY(192);
476 Width:=ScaleX(376);
477 Height:=ScaleY(26);
478 Font.Color:=255;
479 Font.Style:=[fsBold];
480 end;
482 // Restore the setting chosen during a previous install.
483 Data:=GetPreviousData('Path Option','BashOnly');
484 if Data='BashOnly' then begin
485 RdbPath[GP_BashOnly].Checked:=True;
486 end else if Data='Cmd' then begin
487 RdbPath[GP_Cmd].Checked:=True;
488 end else if Data='CmdTools' then begin
489 RdbPath[GP_CmdTools].Checked:=True;
490 end;
493 * Create a custom page for using (Tortoise)Plink instead of OpenSSH
494 * if at least one PuTTY session is found in the Registry.
497 if RegGetSubkeyNames(HKEY_CURRENT_USER,'Software\SimonTatham\PuTTY\Sessions',PuTTYSessions) and (GetArrayLength(PuTTYSessions)>0) then begin
498 PuTTYPage:=CreateCustomPage(
499 PrevPageID,
500 'Choosing the SSH executable',
501 'Which Secure Shell client program would you like Git to use?'
503 PrevPageID:=PuTTYPage.ID;
505 // 1st choice
506 RdbSSH[GS_OpenSSH]:=TRadioButton.Create(PuTTYPage);
507 with RdbSSH[GS_OpenSSH] do begin
508 Parent:=PuTTYPage.Surface;
509 Caption:='Use OpenSSH';
510 Left:=ScaleX(4);
511 Top:=ScaleY(8);
512 Width:=ScaleX(129);
513 Height:=ScaleY(17);
514 Font.Style:=[fsBold];
515 TabOrder:=0;
516 Checked:=True;
517 end;
518 LblOpenSSH:=TLabel.Create(PuTTYPage);
519 with LblOpenSSH do begin
520 Parent:=PuTTYPage.Surface;
521 Caption:=
522 'This uses ssh.exe that comes with Git. The GIT_SSH and SVN_SSH' + #13 +
523 'environment variables will not be modified.';
524 Left:=ScaleX(28);
525 Top:=ScaleY(32);
526 Width:=ScaleX(324);
527 Height:=ScaleY(26);
528 end;
530 // 2nd choice
531 RdbSSH[GS_Plink]:=TRadioButton.Create(PuTTYPage);
532 with RdbSSH[GS_Plink] do begin
533 Parent:=PuTTYPage.Surface;
534 Caption:='Use (Tortoise)Plink';
535 Left:=ScaleX(4);
536 Top:=ScaleY(76);
537 Width:=ScaleX(281);
538 Height:=ScaleY(17);
539 Font.Style:=[fsBold];
540 TabOrder:=1;
541 end;
542 LblPlink:=TLabel.Create(PuTTYPage);
543 with LblPlink do begin
544 Parent:=PuTTYPage.Surface;
545 Caption:=
546 'PuTTY sessions were found in your Registry. You may specify the path' + #13 +
547 'to an existing copy of (Tortoise)Plink.exe from the TortoiseGit/SVN/CVS' + #13 +
548 'or PuTTY applications. The GIT_SSH and SVN_SSH environment' + #13 +
549 'variables will be adjusted to point to the following executable:';
550 Left:=ScaleX(28);
551 Top:=ScaleY(100);
552 Width:=ScaleX(340);
553 Height:=ScaleY(52);
554 end;
555 EdtPlink:=TEdit.Create(PuTTYPage);
556 with EdtPlink do begin
557 Parent:=PuTTYPage.Surface;
558 Text:=GetPuTTYLocation;
559 if not FileExists(Text) then begin
560 Text:='';
561 end;
562 Left:=ScaleX(28);
563 Top:=ScaleY(161);
564 Width:=ScaleX(316);
565 Height:=ScaleY(13);
566 end;
567 BtnPlink:=TButton.Create(PuTTYPage);
568 with BtnPlink do begin
569 Parent:=PuTTYPage.Surface;
570 Caption:='...';
571 OnClick:=@BrowseForPuTTYFolder;
572 Left:=ScaleX(348);
573 Top:=ScaleY(161);
574 Width:=ScaleX(21);
575 Height:=ScaleY(21);
576 end;
578 // Restore the setting chosen during a previous install.
579 Data:=GetPreviousData('SSH Option','OpenSSH');
580 if Data='OpenSSH' then begin
581 RdbSSH[GS_OpenSSH].Checked:=True;
582 end else if Data='Plink' then begin
583 RdbSSH[GS_Plink].Checked:=True;
584 end;
585 end else begin
586 PuTTYPage:=NIL;
587 end;
590 * Create a custom page for the core.autocrlf setting.
593 CRLFPage:=CreateCustomPage(
594 PrevPageID,
595 'Configuring the line ending conversions',
596 'How should Git treat line endings in text files?'
598 PrevPageID:=CRLFPage.ID;
600 // 1st choice
601 RdbCRLF[GC_CRLFAlways]:=TRadioButton.Create(CRLFPage);
602 with RdbCRLF[GC_CRLFAlways] do begin
603 Parent:=CRLFPage.Surface;
604 Caption:='Checkout Windows-style, commit Unix-style line endings';
605 Left:=ScaleX(4);
606 Top:=ScaleY(8);
607 Width:=ScaleX(340);
608 Height:=ScaleY(17);
609 Font.Style:=[fsBold];
610 TabOrder:=0;
611 Checked:=True;
612 end;
613 LblCRLFAlways:=TLabel.Create(CRLFPage);
614 with LblCRLFAlways do begin
615 Parent:=CRLFPage.Surface;
616 Caption:=
617 'Git will convert LF to CRLF when checking out text files. When committing' + #13 +
618 'text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
619 'this is the recommended setting on Windows ("core.autocrlf" is set to "true").';
620 Left:=ScaleX(28);
621 Top:=ScaleY(32);
622 Width:=ScaleX(380);
623 Height:=ScaleY(47);
624 end;
626 // 2nd choice
627 RdbCRLF[GC_LFOnly]:=TRadioButton.Create(CRLFPage);
628 with RdbCRLF[GC_LFOnly] do begin
629 Parent:=CRLFPage.Surface;
630 Caption:='Checkout as-is, commit Unix-style line endings';
631 Left:=ScaleX(4);
632 Top:=ScaleY(80);
633 Width:=ScaleX(340);
634 Height:=ScaleY(17);
635 Font.Style:=[fsBold];
636 TabOrder:=1;
637 Checked:=False;
638 end;
639 LblLFOnly:=TLabel.Create(CRLFPage);
640 with LblLFOnly do begin
641 Parent:=CRLFPage.Surface;
642 Caption:=
643 'Git will not perform any conversion when checking out text files. When' + #13 +
644 'committing text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
645 'this is the recommended setting on Unix ("core.autocrlf" is set to "input").';
646 Left:=ScaleX(28);
647 Top:=ScaleY(104);
648 Width:=ScaleX(380);
649 Height:=ScaleY(47);
650 end;
652 // 3rd choice
653 RdbCRLF[GC_CRLFCommitAsIs]:=TRadioButton.Create(CRLFPage);
654 with RdbCRLF[GC_CRLFCommitAsIs] do begin
655 Parent:=CRLFPage.Surface;
656 Caption:='Checkout as-is, commit as-is';
657 Left:=ScaleX(4);
658 Top:=ScaleY(152);
659 Width:=ScaleX(340);
660 Height:=ScaleY(17);
661 Font.Style:=[fsBold];
662 TabOrder:=2;
663 Checked:=False;
664 end;
665 LblCRLFCommitAsIs:=TLabel.Create(CRLFPage);
666 with LblCRLFCommitAsIs do begin
667 Parent:=CRLFPage.Surface;
668 Caption:=
669 'Git will not perform any conversions when checking out or committing' + #13 +
670 'text files. Choosing this option is not recommended for cross-platform' + #13 +
671 'projects ("core.autocrlf" is set to "false").';
672 Left:=ScaleX(28);
673 Top:=ScaleY(176);
674 Width:=ScaleX(380);
675 Height:=ScaleY(47);
676 end;
678 // Restore the setting chosen during a previous install.
679 Data:=GetPreviousData('CRLF Option','CRLFAlways');
680 if Data='LFOnly' then begin
681 RdbCRLF[GC_LFOnly].Checked:=True;
682 end else if Data='CRLFAlways' then begin
683 RdbCRLF[GC_CRLFAlways].Checked:=True;
684 end else if Data='CRLFCommitAsIs' then begin
685 RdbCRLF[GC_CRLFCommitAsIs].Checked:=True;
686 end;
689 * Create a custom page for finding the processes that lock a module.
692 ProcessesPage:=CreateCustomPage(
693 wpPreparing,
694 'Replacing in-use files',
695 'The following applications use files that need to be replaced, please close them.'
698 ProcessesListBox:=TListBox.Create(ProcessesPage);
699 with ProcessesListBox do begin
700 Parent:=ProcessesPage.Surface;
701 Width:=ProcessesPage.SurfaceWidth;
702 Height:=ProcessesPage.SurfaceHeight-ScaleY(8);
703 end;
705 ProcessesRefresh:=TNewButton.Create(WizardForm);
706 with ProcessesRefresh do begin
707 Parent:=WizardForm;
708 Width:=WizardForm.CancelButton.Width;
709 Height:=WizardForm.CancelButton.Height;
710 Top:=WizardForm.CancelButton.Top;
711 Left:=WizardForm.ClientWidth-(WizardForm.CancelButton.Left+WizardForm.CancelButton.Width);
712 Caption:='&Refresh';
713 OnClick:=@RefreshProcessList;
714 end;
716 // This button is only used by the uninstaller.
717 ContinueButton:=NIL;
719 // Initially hide the Refresh button, show it when the process page becomes current.
720 ProcessesRefresh.Hide;
721 end;
723 function ShouldSkipPage(PageID:Integer):Boolean;
724 begin
725 if (ProcessesPage<>NIL) and (PageID=ProcessesPage.ID) then begin
726 // This page is only reached forward (by pressing "Next", never by pressing "Back").
727 RefreshProcessList(NIL);
728 Result:=(GetArrayLength(Processes)=0);
729 end else begin
730 Result:=False;
731 end;
732 end;
734 procedure CurPageChanged(CurPageID:Integer);
736 i:Integer;
737 begin
738 if CurPageID=wpSelectDir then begin
739 if not IsDirWritable(WizardDirValue) then begin
740 // If the default directory is not writable, choose another default that most likely is.
741 // This will be checked later again when the user clicks "Next".
742 WizardForm.DirEdit.Text:=ExpandConstant('{userpf}\{#APP_NAME}');
743 end;
744 end;
746 // Uncheck the console font option by default.
747 if CurPageID=wpSelectComponents then begin
748 for i:=0 to WizardForm.ComponentsList.Items.Count-1 do begin
749 if WizardForm.ComponentsList.ItemCaption[i]='{#COMP_CONSOLE_FONT}' then begin
750 WizardForm.ComponentsList.Checked[i]:=False;
751 Break;
752 end;
753 end;
754 end;
756 // Show the "Refresh" button only on the processes page.
757 if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin
758 ProcessesRefresh.Show;
759 end else begin
760 ProcessesRefresh.Hide;
761 end;
762 end;
764 function NextButtonClick(CurPageID:Integer):Boolean;
766 i:Integer;
767 Version:TWindowsVersion;
768 begin
769 Result:=True;
771 if CurPageID=wpSelectDir then begin
772 if not IsDirWritable(WizardDirValue) then begin
773 MsgBox(
774 'The specified installation directory does not seem to be writable. ' +
775 + 'Please choose another directory or restart setup as a user with sufficient permissions.'
776 , mbCriticalError
777 , MB_OK
779 Result:=False;
780 Exit;
781 end;
782 end;
784 if (PuTTYPage<>NIL) and (CurPageID=PuTTYPage.ID) then begin
785 Result:=RdbSSH[GS_OpenSSH].Checked or
786 (RdbSSH[GS_Plink].Checked and FileExists(EdtPlink.Text));
787 if not Result then begin
788 MsgBox('Please enter a valid path to (Tortoise)Plink.exe.',mbError,MB_OK);
789 end;
790 end else if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin
791 // It would have been nicer to just disable the "Next" button, but the
792 // WizardForm exports the button just read-only.
793 for i:=0 to GetArrayLength(Processes)-1 do begin
794 if not Processes[i].Restartable then begin
795 MsgBox('Setup cannot continue until you close at least those applications in the list that are marked as "closing is required".',mbCriticalError,MB_OK);
796 Result:=False;
797 Exit;
798 end;
799 end;
801 Result:=(GetArrayLength(Processes)=0);
803 if not Result then begin
804 GetWindowsVersionEx(Version);
805 if Version.Major>=6 then begin
806 Result:=(MsgBox(
807 'If you continue without closing the listed applications they will be closed and restarted automatically.' + #13 + #13 +
808 'Are you sure you want to continue?',
809 mbConfirmation,
810 MB_YESNO
811 )=IDYES);
812 end else begin
813 Result:=(MsgBox(
814 'If you continue without closing the listed applications you will need to log off and on again before changes take effect.' + #13 + #13 +
815 'Are you sure you want to continue anyway?',
816 mbConfirmation,
817 MB_YESNO
818 )=IDYES);
819 end;
820 end;
821 end;
822 end;
824 procedure CurStepChanged(CurStep:TSetupStep);
826 AppDir,DllPath,FileName,TempName,Cmd,Msg:String;
827 BuiltIns,ImageNames,EnvPath,EnvHome,EnvSSH:TArrayOfString;
828 Count,i:Longint;
829 LinkCreated:Boolean;
830 FindRec:TFindRec;
831 RootKey:Integer;
832 Version:TWindowsVersion;
833 begin
834 if CurStep=ssInstall then begin
835 // Shutdown locking processes just before the actual installation starts.
836 if SessionHandle>0 then try
837 RmShutdown(SessionHandle,RmShutdownOnlyRegistered,0);
838 except
839 Log('Line {#__LINE__}: RmShutdown not supported.');
840 end;
842 Exit;
843 end;
845 // Make sure the code below is only executed just after the actual installation finishes.
846 if CurStep<>ssPostInstall then begin
847 Exit;
848 end;
850 AppDir:=ExpandConstant('{app}');
853 Bind the imported function addresses
857 DllPath:=ExpandConstant('{app}\bin;{sys}');
859 // Load the list of images from a text file.
860 FileName:=AppDir+'\{#APP_BINDIMAGE}';
861 if LoadStringsFromFile(FileName,ImageNames) then begin
862 Count:=GetArrayLength(ImageNames)-1;
863 for i:=0 to Count do begin
864 FileName:=AppDir+'\'+ImageNames[i];
865 if not BindImageEx(BIND_NO_BOUND_IMPORTS or BIND_CACHE_IMPORT_DLLS,FileName,DllPath,'',0) then begin
866 Log('Line {#__LINE__}: Error calling BindImageEx for "'+FileName+'".');
867 end;
868 end;
869 end;
870 except
871 Log('Line {#__LINE__}: An exception occurred while calling BindImageEx.');
872 end;
875 Create the built-ins
878 // Load the built-ins from a text file.
879 FileName:=AppDir+'\{#APP_BUILTINS}';
880 if LoadStringsFromFile(FileName,BuiltIns) then begin
881 Count:=GetArrayLength(BuiltIns)-1;
883 // Delete those scripts from "bin" which have been replaced by built-ins in "libexec\git-core".
884 for i:=0 to Count do begin
885 FileName:=AppDir+'\bin\'+ChangeFileExt(ExtractFileName(BuiltIns[i]),'');
886 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
887 Log('Line {#__LINE__}: Unable to delete script "'+FileName+'", ignoring.');
888 end;
889 end;
891 // Create built-ins as aliases for git.exe.
892 for i:=0 to Count do begin
893 FileName:=AppDir+'\'+BuiltIns[i];
895 // Delete any existing built-in.
896 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
897 Log('Line {#__LINE__}: Unable to delete existing built-in "'+FileName+'", skipping.');
898 continue;
899 end;
902 // This will throw an exception on pre-Win2k systems.
903 LinkCreated:=CreateHardLink(FileName,AppDir+'\bin\git.exe',0);
904 except
905 LinkCreated:=False;
906 Log('Line {#__LINE__}: Creating hardlink "'+FileName+'" failed, will try a copy.');
907 end;
909 if not LinkCreated then begin
910 if not FileCopy(AppDir+'\bin\git.exe',FileName,False) then begin
911 Log('Line {#__LINE__}: Creating copy "'+FileName+'" failed, giving up.');
912 // This is not a critical error, Git could basically be used without the
913 // aliases for built-ins, so we continue.
914 end;
915 end;
916 end;
918 // Delete any duplicate files in case we are updating from a non-libexec to a libexec directory layout.
919 if FindFirst(AppDir+'\libexec\git-core\*',FindRec) then begin
920 repeat
921 if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY)=0 then begin
922 FileName:=AppDir+'\bin\'+FindRec.name;
923 if (Pos(FindRec.name,'git.exe')<>1) and FileExists(FileName) and (not DeleteFile(FileName)) then begin
924 Log('Line {#__LINE__}: Unable to delete dupe "'+FileName+'", ignoring.');
925 end;
926 end;
927 until not FindNext(FindRec);
928 FindClose(FindRec);
929 end;
930 end else begin
931 Msg:='Line {#__LINE__}: Unable to read file "{#APP_BUILTINS}".';
932 MsgBox(Msg,mbError,MB_OK);
933 Log(Msg);
934 // This is not a critical error, Git could basically be used without the
935 // aliases for built-ins, so we continue.
936 end;
939 Adapt core.autocrlf
942 if RdbCRLF[GC_LFOnly].checked then begin
943 Cmd:='core.autocrlf input';
944 end else if RdbCRLF[GC_CRLFAlways].checked then begin
945 Cmd:='core.autocrlf true';
946 end else begin
947 Cmd:='core.autocrlf false';
948 end;
949 if not Exec(AppDir + '\bin\git.exe', 'config -f gitconfig ' + Cmd,
950 AppDir + '\etc', SW_HIDE, ewWaitUntilTerminated, i) then begin
951 Msg:='Unable to configure the line ending conversion: ' + Cmd;
952 MsgBox(Msg,mbError,MB_OK);
953 Log(Msg);
954 // This is not a critical error, the user can probably fix it manually,
955 // so we continue.
956 end;
959 Modify the environment
961 This must happen no later than ssPostInstall to make
962 "ChangesEnvironment=yes" not happend before the change!
965 FileName:=AppDir+'\setup.ini';
967 // Delete GIT_SSH and SVN_SSH if a previous installation set them (this is required for the GS_OpenSSH case).
968 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
969 if (GetArrayLength(EnvSSH)=1) and
970 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
971 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
972 Msg:='Line {#__LINE__}: Unable to reset GIT_SSH prior to install.';
973 MsgBox(Msg,mbError,MB_OK);
974 Log(Msg);
975 // This is not a critical error, the user can probably fix it manually,
976 // so we continue.
977 end;
978 end;
980 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
981 if (GetArrayLength(EnvSSH)=1) and
982 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','SVN_SSH','',FileName))=0) then begin
983 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
984 Msg:='Line {#__LINE__}: Unable to reset SVN_SSH prior to install.';
985 MsgBox(Msg,mbError,MB_OK);
986 Log(Msg);
987 // This is not a critical error, the user can probably fix it manually,
988 // so we continue.
989 end;
990 end;
992 if (PuTTYPage<>NIL) and RdbSSH[GS_Plink].Checked then begin
993 SetArrayLength(EnvSSH,1);
994 EnvSSH[0]:=EdtPlink.Text;
996 // Set GIT_SSH as specified by the user.
997 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
998 Msg:='Line {#__LINE__}: Unable to set the GIT_SSH environment variable.';
999 MsgBox(Msg,mbError,MB_OK);
1000 Log(Msg);
1001 // This is not a critical error, the user can probably fix it manually,
1002 // so we continue.
1003 end;
1005 // Mark that we have changed GIT_SSH by writing its value to a file.
1006 if not SetIniString('Environment','GIT_SSH',EnvSSH[0],FileName) then begin
1007 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
1008 MsgBox(Msg,mbError,MB_OK);
1009 Log(Msg);
1010 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
1011 // so we continue.
1012 end;
1014 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
1015 Msg:='Line {#__LINE__}: Unable to set the SVN_SSH environment variable.';
1016 MsgBox(Msg,mbError,MB_OK);
1017 Log(Msg);
1018 // This is not a critical error, the user can probably fix it manually,
1019 // so we continue.
1020 end;
1022 // Mark that we have changed SVN_SSH by writing its value to a file.
1023 if not SetIniString('Environment','SVN_SSH',EnvSSH[0],FileName) then begin
1024 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
1025 MsgBox(Msg,mbError,MB_OK);
1026 Log(Msg);
1027 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
1028 // so we continue.
1029 end;
1030 end;
1032 // Get the current user's directories in PATH.
1033 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
1035 // First, remove the installation directory from PATH in any case.
1036 for i:=0 to GetArrayLength(EnvPath)-1 do begin
1037 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
1038 EnvPath[i]:='';
1039 end;
1040 end;
1042 // Delete HOME if a previous installation modified it.
1043 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
1044 if (GetArrayLength(EnvHome)=1) and
1045 (CompareStr(RemoveQuotes(EnvHome[0]),GetIniString('Environment','HOME','',FileName))=0) then begin
1046 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
1047 Msg:='Line {#__LINE__}: Unable to reset HOME prior to install.';
1048 MsgBox(Msg,mbError,MB_OK);
1049 Log(Msg);
1050 // This is not a critical error, the user can probably fix it manually,
1051 // so we continue.
1052 end;
1053 end;
1055 // Modify the PATH variable as requested by the user.
1056 if RdbPath[GP_Cmd].Checked or RdbPath[GP_CmdTools].Checked then begin
1057 i:=GetArrayLength(EnvPath);
1058 SetArrayLength(EnvPath,i+1);
1060 // List \cmd before \bin so \cmd has higher priority and programs in
1061 // there will be called in favor of those in \bin.
1062 EnvPath[i]:=AppDir+'\cmd';
1064 if RdbPath[GP_CmdTools].Checked then begin
1065 SetArrayLength(EnvPath,i+2);
1066 EnvPath[i+1]:=AppDir+'\bin';
1068 // Set HOME for the Windows Command Prompt, but only if it has not been set manually before.
1069 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
1070 i:=GetArrayLength(EnvHome);
1071 if (i=0) or ((i=1) and (Length(EnvHome[0])=0)) then begin
1072 SetArrayLength(EnvHome,1);
1073 EnvHome[0]:=ExpandConstant('{%HOMEDRIVE}{%HOMEPATH}');
1074 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,EnvHome) then begin
1075 Msg:='Line {#__LINE__}: Unable to set the HOME environment variable.';
1076 MsgBox(Msg,mbError,MB_OK);
1077 Log(Msg);
1078 // This is not a critical error, the user can probably fix it manually,
1079 // so we continue.
1080 end;
1082 // Mark that we have changed HOME.
1083 if not SetIniString('Environment','HOME',EnvHome[0],FileName) then begin
1084 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
1085 MsgBox(Msg,mbError,MB_OK);
1086 Log(Msg);
1087 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
1088 // so we continue.
1089 end;
1090 end;
1091 end;
1092 end;
1094 // Set the current user's PATH directories.
1095 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
1096 Msg:='Line {#__LINE__}: Unable to set the PATH environment variable.';
1097 MsgBox(Msg,mbError,MB_OK);
1098 Log(Msg);
1099 // This is not a critical error, the user can probably fix it manually,
1100 // so we continue.
1101 end;
1104 Create shortcuts that need to be created regardless of the "Don't create a Start Menu folder" toggle
1107 GetWindowsVersionEx(Version);
1108 if Version.Major<6 then begin
1109 Cmd:=ExpandConstant('{syswow64}\cmd.exe');
1110 TempName:='/c ""'+AppDir+'\bin\sh.exe" --login -i"';
1111 end else begin
1112 Cmd:=AppDir+'\bin\sh.exe';
1113 TempName:='--login -i';
1114 end;
1115 FileName:=AppDir+'\etc\git.ico';
1117 if IsComponentSelected('icons\quicklaunch') then begin
1118 CreateShellLink(
1119 ExpandConstant('{userappdata}\Microsoft\Internet Explorer\Quick Launch\Git Bash.lnk')
1120 , 'Git Bash'
1121 , Cmd
1122 , TempName
1123 , '%HOMEDRIVE%%HOMEPATH%'
1124 , FileName
1126 , SW_SHOWNORMAL
1128 end;
1129 if IsComponentSelected('icons\desktop') then begin
1130 CreateShellLink(
1131 GetShellFolder('desktop')+'\Git Bash.lnk'
1132 , 'Git Bash'
1133 , Cmd
1134 , TempName
1135 , '%HOMEDRIVE%%HOMEPATH%'
1136 , FileName
1138 , SW_SHOWNORMAL
1140 end;
1142 // Create a special shortcut that does not set a working directory (Note: Since Inno Setup 5.3.11,
1143 // passing an empty WorkingDir gets replaced with {sys}, so use '.' instead).
1144 // This shortcut is used by "Git Bash.vbs", which in turn is run by the "Git Bash Here" shell extension.
1145 CreateShellLink(
1146 AppDir+'\Git Bash.lnk'
1147 , 'Git Bash'
1148 , Cmd
1149 , TempName
1150 , '.'
1151 , FileName
1153 , SW_SHOWNORMAL
1157 Create the Windows Explorer integrations
1160 if IsAdminLoggedOn then begin
1161 RootKey:=HKEY_LOCAL_MACHINE;
1162 end else begin
1163 RootKey:=HKEY_CURRENT_USER;
1164 end;
1166 if IsComponentSelected('ext\reg\shellhere') then begin
1167 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell','','Git Ba&sh Here')) or
1168 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','','"'+ExpandConstant('{syswow64}')+'\wscript" "'+AppDir+'\Git Bash.vbs" "%1"')) then begin
1169 Msg:='Line {#__LINE__}: Unable to create "Git Bash Here" shell extension.';
1170 MsgBox(Msg,mbError,MB_OK);
1171 Log(Msg);
1172 // This is not a critical error, the user can probably fix it manually,
1173 // so we continue.
1174 end;
1175 end;
1177 if IsComponentSelected('ext\reg\guihere') then begin
1178 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui','','Git &GUI Here')) or
1179 (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
1180 Msg:='Line {#__LINE__}: Unable to create "Git GUI Here" shell extension.';
1181 MsgBox(Msg,mbError,MB_OK);
1182 Log(Msg);
1183 // This is not a critical error, the user can probably fix it manually,
1184 // so we continue.
1185 end;
1186 end;
1188 // It is either the Registry-based context menu entries, or the shell extension.
1189 if IsComponentSelected('ext\cheetah') then begin
1190 DeleteContextMenuEntries;
1192 if isWin64 then begin
1193 FileName:=AppDir+'\git-cheetah\git_shell_ext64.dll';
1194 end else begin
1195 FileName:=AppDir+'\git-cheetah\git_shell_ext.dll';
1196 end;
1197 if not ReplaceInUseFile(FileName,FileName+'.new',True) then begin
1198 Log('Line {#__LINE__}: Replacing file "'+FileName+'" failed.');
1199 end;
1200 end;
1203 Restart any processes that were shut down via the Restart Manager
1206 if SessionHandle>0 then try
1207 RmRestart(SessionHandle,0,0);
1208 RmEndSession(SessionHandle);
1209 except
1210 Log('Line {#__LINE__}: RmRestart not supported.');
1211 end;
1212 end;
1214 procedure RegisterPreviousData(PreviousDataKey:Integer);
1216 Data:String;
1217 begin
1218 // Git Path options.
1219 Data:='';
1220 if RdbPath[GP_BashOnly].Checked then begin
1221 Data:='BashOnly';
1222 end else if RdbPath[GP_Cmd].Checked then begin
1223 Data:='Cmd';
1224 end else if RdbPath[GP_CmdTools].Checked then begin
1225 Data:='CmdTools';
1226 end;
1227 SetPreviousData(PreviousDataKey,'Path Option',Data);
1229 // Git SSH options.
1230 Data:='';
1231 if (PuTTYPage=NIL) or RdbSSH[GS_OpenSSH].Checked then begin
1232 Data:='OpenSSH';
1233 end else if RdbSSH[GS_Plink].Checked then begin
1234 Data:='Plink';
1235 end;
1236 SetPreviousData(PreviousDataKey,'SSH Option',Data);
1238 // Line ending conversion options.
1239 Data:='';
1240 if RdbCRLF[GC_LFOnly].Checked then begin
1241 Data:='LFOnly';
1242 end else if RdbCRLF[GC_CRLFAlways].Checked then begin
1243 Data:='CRLFAlways';
1244 end else if RdbCRLF[GC_CRLFCommitAsIs].Checked then begin
1245 Data:='CRLFCommitAsIs';
1246 end;
1247 SetPreviousData(PreviousDataKey,'CRLF Option',Data);
1248 end;
1251 Uninstaller code
1254 function InitializeUninstall:Boolean;
1256 Form:TSetupForm;
1257 Info:TLabel;
1258 ExitButton,RefreshButton:TButton;
1259 begin
1260 Result:=True;
1262 Form:=CreateCustomForm;
1264 Form.Caption:='Git Uninstall: Removing in-use files';
1265 Form.ClientWidth:=ScaleX(500);
1266 Form.ClientHeight:=ScaleY(256);
1267 Form.Center;
1269 Info:=TLabel.Create(Form);
1270 with Info do begin
1271 Parent:=Form;
1272 Left:=ScaleX(11);
1273 Top:=ScaleY(11);
1274 AutoSize:=True;
1275 Caption:='The following applications use files that need to be removed, please close them.';
1276 end;
1278 ContinueButton:=TButton.Create(Form);
1279 with ContinueButton do begin
1280 Parent:=Form;
1281 Left:=Form.ClientWidth-ScaleX(75+10);
1282 Top:=Form.ClientHeight-ScaleY(23+10);
1283 Width:=ScaleX(75);
1284 Height:=ScaleY(23);
1285 Caption:='Continue';
1286 ModalResult:=mrOk;
1287 end;
1289 ExitButton:=TButton.Create(Form);
1290 with ExitButton do begin
1291 Parent:=Form;
1292 Left:=ContinueButton.Left-ScaleX(75+6);
1293 Top:=ContinueButton.Top;
1294 Width:=ScaleX(75);
1295 Height:=ScaleY(23);
1296 Caption:='Exit';
1297 ModalResult:=mrCancel;
1298 Cancel:=True;
1299 end;
1301 RefreshButton:=TButton.Create(Form);
1302 with RefreshButton do begin
1303 Parent:=Form;
1304 Left:=ScaleX(10);
1305 Top:=ExitButton.Top;
1306 Width:=ScaleX(75);
1307 Height:=ScaleY(23);
1308 Caption:='Refresh';
1309 OnClick:=@RefreshProcessList;
1310 end;
1312 ProcessesListBox:=TListBox.Create(Form);
1313 with ProcessesListBox do begin
1314 Parent:=Form;
1315 Left:=ScaleX(11);
1316 Top:=Info.Top+Info.Height+11;
1317 Width:=Form.ClientWidth-ScaleX(11*2);
1318 Height:=ContinueButton.Top-ScaleY(11*4);
1319 end;
1321 Form.ActiveControl:=ContinueButton;
1323 RefreshProcessList(NIL);
1324 if GetArrayLength(Processes)>0 then begin
1325 // Now that these dialogs are going to be shown, we should probably
1326 // disable the "Are you sure to remove Git?" confirmation dialog, but
1327 // unfortunately that is not possible with Inno Setup currently.
1328 Result:=(Form.ShowModal()=mrOk);
1330 // Note: The number of processes might have changed during a refresh.
1331 if Result and (GetArrayLength(Processes)>0) then begin
1332 Result:=(MsgBox(
1333 'If you continue without closing the listed applications, you will need to log off and on again to remove some files manually.' + #13 + #13 +
1334 'Are you sure you want to continue anyway?',
1335 mbConfirmation,
1336 MB_YESNO
1337 )=IDYES);
1338 end;
1339 end;
1340 finally
1341 Form.free;
1342 end;
1343 end;
1345 // PreUninstall
1347 // Even though the name of this function suggests otherwise most of the
1348 // code below is only executed right before the actual uninstallation.
1349 // This happens because of the if-guard right in the beginning of this
1350 // function.
1351 procedure CurUninstallStepChanged(CurUninstallStep:TUninstallStep);
1353 AppDir,FileName,Msg:String;
1354 EnvPath,EnvHome,EnvSSH:TArrayOfString;
1355 i:Longint;
1356 begin
1357 if CurUninstallStep<>usUninstall then begin
1358 Exit;
1359 end;
1361 // Reset the console font (the FontType is reset in the Registry section).
1362 if IsComponentInstalled('consolefont') then begin
1363 if MsgBox('Do you want to revert the TrueType font setting for all console windows?',mbConfirmation,MB_YESNO)=IDYES then begin
1364 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontFamily',0);
1365 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontSize',0);
1366 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontWeight',0);
1367 end;
1368 end;
1371 Modify the environment
1373 This must happen no later than usUninstall to make
1374 "ChangesEnvironment=yes" not happend before the change!
1377 AppDir:=ExpandConstant('{app}');
1378 FileName:=AppDir+'\setup.ini';
1380 // Delete the current user's GIT_SSH and SVN_SSH if we set it.
1381 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
1382 if (GetArrayLength(EnvSSH)=1) and
1383 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
1384 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
1385 Msg:='Line {#__LINE__}: Unable to revert any possible changes to GIT_SSH.';
1386 MsgBox(Msg,mbError,MB_OK);
1387 Log(Msg);
1388 // This is not a critical error, the user can probably fix it manually,
1389 // so we continue.
1390 end;
1391 end;
1393 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
1394 if (GetArrayLength(EnvSSH)=1) and
1395 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','SVN_SSH','',FileName))=0) then begin
1396 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
1397 Msg:='Line {#__LINE__}: Unable to revert any possible changes to SVN_SSH.';
1398 MsgBox(Msg,mbError,MB_OK);
1399 Log(Msg);
1400 // This is not a critical error, the user can probably fix it manually,
1401 // so we continue.
1402 end;
1403 end;
1405 // Get the current user's directories in PATH.
1406 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
1408 // Remove the installation directory from PATH in any case, even if it
1409 // was not added by the installer.
1410 for i:=0 to GetArrayLength(EnvPath)-1 do begin
1411 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
1412 EnvPath[i]:='';
1413 end;
1414 end;
1416 // Reset the current user's directories in PATH.
1417 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
1418 Msg:='Line {#__LINE__}: Unable to revert any possible changes to PATH.';
1419 MsgBox(Msg,mbError,MB_OK);
1420 Log(Msg);
1421 // This is not a critical error, the user can probably fix it manually,
1422 // so we continue.
1423 end;
1425 // Reset the current user's HOME if we modified it.
1426 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
1427 if (GetArrayLength(EnvHome)=1) and
1428 (CompareStr(RemoveQuotes(EnvHome[0]),GetIniString('Environment','HOME','',FileName))=0) then begin
1429 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
1430 Msg:='Line {#__LINE__}: Unable to revert any possible changes to HOME.';
1431 MsgBox(Msg,mbError,MB_OK);
1432 Log(Msg);
1433 // This is not a critical error, the user can probably fix it manually,
1434 // so we continue.
1435 end;
1436 end;
1438 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
1439 Msg:='Line {#__LINE__}: Unable to delete file "'+FileName+'".';
1440 MsgBox(Msg,mbError,MB_OK);
1441 Log(Msg);
1442 // This is not a critical error, the user can probably fix it manually,
1443 // so we continue.
1444 end;
1447 Delete the Windows Explorer integrations
1450 DeleteContextMenuEntries;
1452 if isWin64 then begin
1453 FileName:=AppDir+'\git-cheetah\git_shell_ext64.dll';
1454 end else begin
1455 FileName:=AppDir+'\git-cheetah\git_shell_ext.dll';
1456 end;
1457 if FileExists(FileName) then begin
1458 if not UnregisterServer(Is64BitInstallMode,FileName,False) then begin
1459 Msg:='Line {#__LINE__}: Unable to unregister file "'+FileName+'". Please do it manually by running "regsvr32 /u '+ExtractFileName(FileName)+'".';
1460 MsgBox(Msg,mbError,MB_OK);
1461 Log(Msg);
1462 end;
1464 if not DeleteFile(FileName) then begin
1465 Msg:='Line {#__LINE__}: Unable to delete file "'+FileName+'". Please do it manually after logging off and on again.';
1466 MsgBox(Msg,mbError,MB_OK);
1467 Log(Msg);
1468 end;
1469 end;
1470 end;