Mark msysGit as obsolete
[msysgit.git] / share / WinGit / install.iss
blob06b870775d4e5eb891bf9dadc8cd83afdb85ecf6
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.github.io/'
11 #define APP_BUILTINS 'etc\fileList-builtins.txt'
12 #define APP_BINDIMAGE 'etc\fileList-bindimage.txt'
14 #define PLINK_PATH_ERROR_MSG 'Please enter a valid path to a Plink executable.'
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 CloseApplications=no
37 DefaultDirName={pf}\{#APP_NAME}
38 DisableDirPage=auto
39 DefaultGroupName={#APP_NAME}
40 DisableProgramGroupPage=auto
41 DisableReadyPage=yes
42 InfoBeforeFile=gpl-2.0.rtf
43 PrivilegesRequired=none
44 UninstallDisplayIcon={app}\etc\git.ico
45 #ifndef COMPILE_FROM_IDE
46 #if Pos('-',APP_VERSION)>0
47 VersionInfoVersion={#Copy(APP_VERSION,1,Pos('-',APP_VERSION)-1)}
48 #else
49 VersionInfoVersion={#APP_VERSION}
50 #endif
51 #endif
53 ; Cosmetic
54 SetupIconFile=etc\git.ico
55 WizardImageBackColor=clWhite
56 WizardImageStretch=no
57 WizardImageFile=git.bmp
58 WizardSmallImageFile=gitsmall.bmp
60 [Types]
61 ; Define a custom type to avoid getting the three default types.
62 Name: default; Description: Default installation; Flags: iscustom
64 [Components]
65 Name: icons; Description: Additional icons
66 Name: icons\quicklaunch; Description: In the Quick Launch
67 Name: icons\desktop; Description: On the Desktop
68 Name: ext; Description: Windows Explorer integration; Types: default
69 Name: ext\reg; Description: Simple context menu (Registry based); Flags: exclusive; Types: default
70 Name: ext\reg\shellhere; Description: Git Bash Here; Types: default
71 Name: ext\reg\guihere; Description: Git GUI Here; Types: default
72 Name: ext\cheetah; Description: Advanced context menu (git-cheetah plugin); Flags: exclusive; Types: default
73 Name: assoc; Description: Associate .git* configuration files with the default text editor; Types: default
74 Name: assoc_sh; Description: Associate .sh files to be run with Bash; Types: default
75 Name: consolefont; Description: Use a TrueType font in all console windows (not only for Git Bash)
77 [Files]
78 ; Install files that might be in use during setup under a different name.
79 Source: git-cheetah\git_shell_ext.dll; DestDir: {app}\git-cheetah; DestName: git_shell_ext.dll.new; Flags: replacesameversion; Components: ext\cheetah; AfterInstall: DeleteFromVirtualStore
80 Source: git-cheetah\git_shell_ext64.dll; DestDir: {app}\git-cheetah; DestName: git_shell_ext64.dll.new; Flags: replacesameversion; Components: ext\cheetah; AfterInstall: DeleteFromVirtualStore
82 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
83 Source: ReleaseNotes.rtf; DestDir: {app}; Flags: isreadme replacesameversion; AfterInstall: DeleteFromVirtualStore
85 [Icons]
86 Name: {group}\Git GUI; Filename: {app}\bin\wish.exe; Parameters: """{app}\libexec\git-core\git-gui"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico
87 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
88 Name: {group}\Git Bash; Filename: {app}\bin\sh.exe; Parameters: "--login -i"; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico; MinVersion: 6.0
90 [Messages]
91 BeveledLabel={#APP_URL}
92 SetupAppTitle={#APP_NAME} Setup
93 SetupWindowTitle={#APP_NAME} Setup
95 [Registry]
96 ; There is no "Console" key in HKLM.
97 Root: HKCU; Subkey: Console; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: uninsclearvalue; Components: consolefont
98 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Components: consolefont
99 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Components: consolefont
100 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Components: consolefont
102 Root: HKCU; Subkey: Console\Git Bash; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
103 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
104 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
105 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
107 ; Note that we write the Registry values below either to HKLM or to HKCU depending on whether the user running the installer
108 ; is a member of the local Administrators group or not (see the "Check" argument).
110 ; File associations for configuration files that may be contained in a repository (so this does not include ".gitconfig").
111 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
112 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
113 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
114 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
115 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
116 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
118 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
119 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
120 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
121 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
122 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
123 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
125 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
126 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
127 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
128 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
129 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
130 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
132 ; Associate .sh extension with sh.exe so those files are double-clickable,
133 ; startable from cmd.exe, and when files are dropped on them they are passed
134 ; as arguments to the script.
136 ; Install under HKEY_LOCAL_MACHINE if an administrator is installing.
137 Root: HKLM; Subkey: Software\Classes\.sh; ValueType: string; ValueData: sh_auto_file; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
138 Root: HKLM; Subkey: Software\Classes\sh_auto_file; ValueType: string; ValueData: "Shell Script"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
139 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
140 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
141 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
142 Root: HKLM; Subkey: Software\Classes\sh_auto_file\ShellEx\DropHandler; ValueType: string; ValueData: {#DROP_HANDLER_GUID}; Flags: uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
144 ; Install under HKEY_CURRENT_USER if a non-administrator is installing.
145 Root: HKCU; Subkey: Software\Classes\.sh; ValueType: string; ValueData: sh_auto_file; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
146 Root: HKCU; Subkey: Software\Classes\sh_auto_file; ValueType: string; ValueData: "Shell Script"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
147 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
148 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
149 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
150 Root: HKCU; Subkey: Software\Classes\sh_auto_file\ShellEx\DropHandler; ValueType: string; ValueData: {#DROP_HANDLER_GUID}; Flags: uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
152 [UninstallDelete]
153 ; Delete the built-ins.
154 Type: files; Name: {app}\bin\git-*.exe
155 Type: files; Name: {app}\libexec\git-core\git-*.exe
156 Type: files; Name: {app}\libexec\git-core\git.exe
158 ; Delete any (temporary) git-cheetah files.
159 Type: files; Name: {app}\git-cheetah\*.*
161 ; Delete any manually created shortcuts.
162 Type: files; Name: {userappdata}\Microsoft\Internet Explorer\Quick Launch\Git Bash.lnk
163 Type: files; Name: {code:GetShellFolder|desktop}\Git Bash.lnk
164 Type: files; Name: {app}\Git Bash.lnk
166 ; Delete a home directory inside the msysGit directory.
167 Type: dirifempty; Name: {app}\home\{username}
168 Type: dirifempty; Name: {app}\home
170 [Code]
171 #include "helpers.inc.iss"
172 #include "environment.inc.iss"
173 #include "putty.inc.iss"
174 #include "modules.inc.iss"
176 function CreateHardLink(lpFileName,lpExistingFileName:String;lpSecurityAttributes:Integer):Boolean;
177 #ifdef UNICODE
178 external 'CreateHardLinkW@Kernel32.dll stdcall delayload setuponly';
179 #else
180 external 'CreateHardLinkA@Kernel32.dll stdcall delayload setuponly';
181 #endif
183 function BindImageEx(Flags:DWORD;ImageName,DllPath,SymbolPath:AnsiString;StatusRoutine:Integer):Boolean;
184 external 'BindImageEx@Imagehlp.dll stdcall delayload setuponly';
186 const
187 // Git Path options.
188 GP_BashOnly = 1;
189 GP_Cmd = 2;
190 GP_CmdTools = 3;
192 // Git SSH options.
193 GS_OpenSSH = 1;
194 GS_Plink = 2;
196 // Git line ending conversion options.
197 GC_LFOnly = 1;
198 GC_CRLFAlways = 2;
199 GC_CRLFCommitAsIs = 3;
201 // BindImageEx API constants.
202 BIND_NO_BOUND_IMPORTS = $00000001;
203 BIND_NO_UPDATE = $00000002;
204 BIND_ALL_IMAGES = $00000004;
205 BIND_CACHE_IMPORT_DLLS = $00000008;
208 // Wizard page and variables for the Path options.
209 PathPage:TWizardPage;
210 RdbPath:array[GP_BashOnly..GP_CmdTools] of TRadioButton;
212 // Wizard page and variables for the SSH options.
213 PuTTYPage:TWizardPage;
214 RdbSSH:array[GS_OpenSSH..GS_Plink] of TRadioButton;
215 EdtPlink:TEdit;
217 // Wizard page and variables for the line ending conversion options.
218 CRLFPage:TWizardPage;
219 RdbCRLF:array[GC_LFOnly..GC_CRLFCommitAsIs] of TRadioButton;
221 // Wizard page and variables for the processes page.
222 SessionHandle:DWORD;
223 Processes:ProcessList;
224 ProcessesPage:TWizardPage;
225 ProcessesListBox:TListBox;
226 ProcessesRefresh,ContinueButton:TButton;
229 Specific helper functions
232 procedure BrowseForPuTTYFolder(Sender:TObject);
234 Name:String;
235 begin
236 if GetOpenFileName(
237 'Please select a Plink executable'
238 , Name
239 , ExtractFilePath(EdtPlink.Text)
240 , 'Executable Files|*.exe'
241 , 'exe'
243 then begin
244 if IsPlinkExecutable(Name) then begin
245 EdtPlink.Text:=Name;
246 RdbSSH[GS_Plink].Checked:=True;
247 end else begin
248 // This message box only gets triggered on interactive use, so it
249 // does not need to be suppressible for silent installations.
250 MsgBox('{#PLINK_PATH_ERROR_MSG}',mbError,MB_OK);
251 end;
252 end;
253 end;
255 procedure DeleteContextMenuEntries;
257 AppDir,Command,Msg:String;
258 RootKey,i:Integer;
259 Keys:TArrayOfString;
260 begin
261 AppDir:=ExpandConstant('{app}');
263 if IsAdminLoggedOn then begin
264 RootKey:=HKEY_LOCAL_MACHINE;
265 end else begin
266 RootKey:=HKEY_CURRENT_USER;
267 end;
269 SetArrayLength(Keys,4);
270 Keys[0]:='SOFTWARE\Classes\Directory\shell\git_shell';
271 Keys[1]:='SOFTWARE\Classes\Directory\Background\shell\git_shell';
272 Keys[2]:='SOFTWARE\Classes\Directory\shell\git_gui';
273 Keys[3]:='SOFTWARE\Classes\Directory\Background\shell\git_gui';
275 for i:=0 to Length(Keys)-1 do begin
276 Command:='';
277 RegQueryStringValue(RootKey,Keys[i]+'\command','',Command);
278 if Pos(AppDir,Command)>0 then begin
279 if not RegDeleteKeyIncludingSubkeys(RootKey,Keys[i]) then begin
280 Msg:='Line {#__LINE__}: Unable to remove "Git Bash / GUI Here" shell extension.';
282 // This is not a critical error, so just notify the user and continue.
283 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
284 Log(Msg);
285 end;
286 end;
287 end;
288 end;
290 procedure RefreshProcessList(Sender:TObject);
292 Version:TWindowsVersion;
293 Modules:TArrayOfString;
294 ProcsCloseRequired,ProcsCloseOptional:ProcessList;
295 i:Longint;
296 Caption:String;
297 ManualClosingRequired:Boolean;
298 begin
299 GetWindowsVersionEx(Version);
301 // Use the Restart Manager API when installing the shell extension on Windows Vista and above.
302 if Version.Major>=6 then begin
303 SetArrayLength(Modules,5);
304 Modules[0]:=ExpandConstant('{app}\bin\msys-1.0.dll');
305 Modules[1]:=ExpandConstant('{app}\bin\tcl85.dll');
306 Modules[2]:=ExpandConstant('{app}\bin\tk85.dll');
307 Modules[3]:=ExpandConstant('{app}\git-cheetah\git_shell_ext.dll');
308 Modules[4]:=ExpandConstant('{app}\git-cheetah\git_shell_ext64.dll');
309 SessionHandle:=FindProcessesUsingModules(Modules,Processes);
310 end else begin
311 SetArrayLength(Modules,3);
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 SessionHandle:=FindProcessesUsingModules(Modules,ProcsCloseRequired);
317 SetArrayLength(Modules,2);
318 Modules[0]:=ExpandConstant('{app}\git-cheetah\git_shell_ext.dll');
319 Modules[1]:=ExpandConstant('{app}\git-cheetah\git_shell_ext64.dll');
320 SessionHandle:=FindProcessesUsingModules(Modules,ProcsCloseOptional) or SessionHandle;
322 // Misuse the "Restartable" flag to indicate which processes are required
323 // to be closed before setup can continue, and which just should be closed
324 // in order to make changes take effect immediately.
325 SetArrayLength(Processes,GetArrayLength(ProcsCloseRequired)+GetArrayLength(ProcsCloseOptional));
326 for i:=0 to GetArrayLength(ProcsCloseRequired)-1 do begin
327 Processes[i]:=ProcsCloseRequired[i];
328 Processes[i].Restartable:=False;
329 end;
330 for i:=0 to GetArrayLength(ProcsCloseOptional)-1 do begin
331 Processes[GetArrayLength(ProcsCloseRequired)+i]:=ProcsCloseOptional[i];
332 Processes[GetArrayLength(ProcsCloseRequired)+i].Restartable:=True;
333 end;
334 end;
336 ManualClosingRequired:=False;
338 ProcessesListBox.Items.Clear;
339 if (Sender=NIL) or (SessionHandle>0) then begin
340 for i:=0 to GetArrayLength(Processes)-1 do begin
341 Caption:=Processes[i].Name+' (PID '+IntToStr(Processes[i].ID);
342 if Processes[i].Restartable then begin
343 Caption:=Caption+', closing is optional';
344 end else begin
345 Caption:=Caption+', closing is required';
346 ManualClosingRequired:=True;
347 end;
348 Caption:=Caption+')';
349 ProcessesListBox.Items.Append(Caption);
350 end;
351 end;
353 if ContinueButton<>NIL then begin
354 ContinueButton.Enabled:=not ManualClosingRequired;
355 end;
356 end;
358 procedure SetAndMarkEnvString(Name,Value:String;Expandable:Boolean);
360 Env:TArrayOfString;
361 FileName,Msg:String;
362 begin
363 SetArrayLength(Env,1);
364 Env[0]:=Value;
366 // Try to set the variable as specified by the user.
367 if not SetEnvStrings(Name,Env,Expandable,IsAdminLoggedOn,True) then begin
368 Msg:='Line {#__LINE__}: Unable to set the '+Name+' environment variable.';
370 // This is not a critical error, so just notify the user and continue.
371 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
372 Log(Msg);
373 end else begin
374 // Mark that we have changed the variable by writing its value to a file.
375 FileName:=ExpandConstant('{app}')+'\setup.ini';
376 if not SetIniString('Environment',Name,Value,FileName) then begin
377 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
379 // This is not a critical error, so just notify the user and continue.
380 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
381 Log(Msg);
382 end;
383 end;
384 end;
386 procedure DeleteMarkedEnvString(Name:String);
388 Env:TArrayOfString;
389 FileName,Msg:String;
390 begin
391 Env:=GetEnvStrings(Name,IsAdminLoggedOn);
392 FileName:=ExpandConstant('{app}')+'\setup.ini';
394 if (GetArrayLength(Env)=1) and
395 (CompareStr(RemoveQuotes(Env[0]),GetIniString('Environment',Name,'',FileName))=0) then begin
396 if not SetEnvStrings(Name,[],False,IsAdminLoggedOn,True) then begin
397 Msg:='Line {#__LINE__}: Unable to delete the '+Name+' environment variable.';
399 // This is not a critical error, so just notify the user and continue.
400 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
401 Log(Msg);
402 end;
403 end;
404 end;
407 Setup event functions
410 function InitializeSetup:Boolean;
411 begin
412 UpdateInfFilenames;
413 Result:=True;
414 end;
416 procedure InitializeWizard;
418 PrevPageID:Integer;
419 LblGitBash,LblGitCmd,LblGitCmdTools,LblGitCmdToolsWarn:TLabel;
420 LblOpenSSH,LblPlink:TLabel;
421 PuTTYSessions,EnvSSH:TArrayOfString;
422 LblLFOnly,LblCRLFAlways,LblCRLFCommitAsIs:TLabel;
423 BtnPlink:TButton;
424 Data:String;
425 begin
427 PrevPageID:=wpSelectProgramGroup;
430 * Create a custom page for modifying the environment.
433 PathPage:=CreateCustomPage(
434 PrevPageID
435 , 'Adjusting your PATH environment'
436 , 'How would you like to use Git from the command line?'
438 PrevPageID:=PathPage.ID;
440 // 1st choice
441 RdbPath[GP_BashOnly]:=TRadioButton.Create(PathPage);
442 with RdbPath[GP_BashOnly] do begin
443 Parent:=PathPage.Surface;
444 Caption:='Use Git from Git Bash only';
445 Left:=ScaleX(4);
446 Top:=ScaleY(8);
447 Width:=ScaleX(405);
448 Height:=ScaleY(17);
449 Font.Style:=[fsBold];
450 TabOrder:=0;
451 Checked:=True;
452 end;
453 LblGitBash:=TLabel.Create(PathPage);
454 with LblGitBash do begin
455 Parent:=PathPage.Surface;
456 Caption:=
457 'This is the safest choice as your PATH will not be modified at all. You will only be' + #13 +
458 'able to use the Git command line tools from Git Bash.';
459 Left:=ScaleX(28);
460 Top:=ScaleY(32);
461 Width:=ScaleX(405);
462 Height:=ScaleY(26);
463 end;
465 // 2nd choice
466 RdbPath[GP_Cmd]:=TRadioButton.Create(PathPage);
467 with RdbPath[GP_Cmd] do begin
468 Parent:=PathPage.Surface;
469 Caption:='Use Git from the Windows Command Prompt';
470 Left:=ScaleX(4);
471 Top:=ScaleY(76);
472 Width:=ScaleX(405);
473 Height:=ScaleY(17);
474 Font.Style:=[fsBold];
475 TabOrder:=1;
476 end;
477 LblGitCmd:=TLabel.Create(PathPage);
478 with LblGitCmd do begin
479 Parent:=PathPage.Surface;
480 Caption:=
481 'This option is considered safe as it only adds some minimal Git wrappers to your' + #13 +
482 'PATH to avoid cluttering your environment with optional Unix tools. You will be' + #13 +
483 'able to use Git from both Git Bash and the Windows Command Prompt.';
484 Left:=ScaleX(28);
485 Top:=ScaleY(100);
486 Width:=ScaleX(405);
487 Height:=ScaleY(39);
488 end;
490 // 3rd choice
491 RdbPath[GP_CmdTools]:=TRadioButton.Create(PathPage);
492 with RdbPath[GP_CmdTools] do begin
493 Parent:=PathPage.Surface;
494 Caption:='Use Git and optional Unix tools from the Windows Command Prompt';
495 Left:=ScaleX(4);
496 Top:=ScaleY(152);
497 Width:=ScaleX(405);
498 Height:=ScaleY(17);
499 Font.Style:=[fsBold];
500 TabOrder:=2;
501 end;
502 LblGitCmdTools:=TLabel.Create(PathPage);
503 with LblGitCmdTools do begin
504 Parent:=PathPage.Surface;
505 Caption:='Both Git and the optional Unix tools will be added to your PATH.';
506 Left:=ScaleX(28);
507 Top:=ScaleY(176);
508 Width:=ScaleX(405);
509 Height:=ScaleY(13);
510 end;
511 LblGitCmdToolsWarn:=TLabel.Create(PathPage);
512 with LblGitCmdToolsWarn do begin
513 Parent:=PathPage.Surface;
514 Caption:=
515 'Warning: This will override Windows tools like "find" and "sort". Only' + #13 +
516 'use this option if you understand the implications.';
517 Left:=ScaleX(28);
518 Top:=ScaleY(192);
519 Width:=ScaleX(405);
520 Height:=ScaleY(26);
521 Font.Color:=255;
522 Font.Style:=[fsBold];
523 end;
525 // Restore the setting chosen during a previous install.
526 Data:=GetPreviousData('Path Option','BashOnly');
528 // Use settings from the user provided INF.
529 if ShouldLoadInf then begin
530 Data:=LoadInfString('Setup','PathOption','BashOnly');
531 end;
533 if Data='BashOnly' then begin
534 RdbPath[GP_BashOnly].Checked:=True;
535 end else if Data='Cmd' then begin
536 RdbPath[GP_Cmd].Checked:=True;
537 end else if Data='CmdTools' then begin
538 RdbPath[GP_CmdTools].Checked:=True;
539 end;
542 * Create a custom page for using (Tortoise)Plink instead of OpenSSH
543 * if at least one PuTTY session is found in the Registry.
546 if RegGetSubkeyNames(HKEY_CURRENT_USER,'Software\SimonTatham\PuTTY\Sessions',PuTTYSessions) and (GetArrayLength(PuTTYSessions)>0) then begin
547 PuTTYPage:=CreateCustomPage(
548 PrevPageID
549 , 'Choosing the SSH executable'
550 , 'Which Secure Shell client program would you like Git to use?'
552 PrevPageID:=PuTTYPage.ID;
554 // 1st choice
555 RdbSSH[GS_OpenSSH]:=TRadioButton.Create(PuTTYPage);
556 with RdbSSH[GS_OpenSSH] do begin
557 Parent:=PuTTYPage.Surface;
558 Caption:='Use OpenSSH';
559 Left:=ScaleX(4);
560 Top:=ScaleY(8);
561 Width:=ScaleX(405);
562 Height:=ScaleY(17);
563 Font.Style:=[fsBold];
564 TabOrder:=0;
565 Checked:=True;
566 end;
567 LblOpenSSH:=TLabel.Create(PuTTYPage);
568 with LblOpenSSH do begin
569 Parent:=PuTTYPage.Surface;
570 Caption:=
571 'This uses ssh.exe that comes with Git. The GIT_SSH and SVN_SSH' + #13 +
572 'environment variables will not be modified.';
573 Left:=ScaleX(28);
574 Top:=ScaleY(32);
575 Width:=ScaleX(405);
576 Height:=ScaleY(26);
577 end;
579 // 2nd choice
580 RdbSSH[GS_Plink]:=TRadioButton.Create(PuTTYPage);
581 with RdbSSH[GS_Plink] do begin
582 Parent:=PuTTYPage.Surface;
583 Caption:='Use (Tortoise)Plink';
584 Left:=ScaleX(4);
585 Top:=ScaleY(76);
586 Width:=ScaleX(405);
587 Height:=ScaleY(17);
588 Font.Style:=[fsBold];
589 TabOrder:=1;
590 end;
591 LblPlink:=TLabel.Create(PuTTYPage);
592 with LblPlink do begin
593 Parent:=PuTTYPage.Surface;
594 Caption:=
595 'PuTTY sessions were found in your Registry. You may specify the path' + #13 +
596 'to an existing copy of (Tortoise)Plink.exe from the TortoiseGit/SVN/CVS' + #13 +
597 'or PuTTY applications. The GIT_SSH and SVN_SSH environment' + #13 +
598 'variables will be adjusted to point to the following executable:';
599 Left:=ScaleX(28);
600 Top:=ScaleY(100);
601 Width:=ScaleX(405);
602 Height:=ScaleY(52);
603 end;
604 EdtPlink:=TEdit.Create(PuTTYPage);
605 with EdtPlink do begin
606 Parent:=PuTTYPage.Surface;
608 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
609 if (GetArrayLength(EnvSSH)=1) and IsPlinkExecutable(EnvSSH[0]) then begin
610 Text:=EnvSSH[0];
611 end;
612 if not FileExists(Text) then begin
613 Text:=GetPreviousData('Plink Path','');
614 end;
615 if not FileExists(Text) then begin
616 Text:=GuessPlinkExecutable;
617 end;
618 if not FileExists(Text) then begin
619 Text:='';
620 end;
622 Left:=ScaleX(28);
623 Top:=ScaleY(161);
624 Width:=ScaleX(316);
625 Height:=ScaleY(13);
626 end;
627 BtnPlink:=TButton.Create(PuTTYPage);
628 with BtnPlink do begin
629 Parent:=PuTTYPage.Surface;
630 Caption:='...';
631 OnClick:=@BrowseForPuTTYFolder;
632 Left:=ScaleX(348);
633 Top:=ScaleY(161);
634 Width:=ScaleX(21);
635 Height:=ScaleY(21);
636 end;
638 // Restore the setting chosen during a previous install.
639 Data:=GetPreviousData('SSH Option','OpenSSH');
641 // Use settings from the user provided INF.
642 if ShouldLoadInf then begin
643 Data:=LoadInfString('Setup','SSHOption','OpenSSH');
644 end;
646 if Data='OpenSSH' then begin
647 RdbSSH[GS_OpenSSH].Checked:=True;
648 end else if Data='Plink' then begin
649 RdbSSH[GS_Plink].Checked:=True;
650 end;
651 end else begin
652 PuTTYPage:=NIL;
653 end;
656 * Create a custom page for the core.autocrlf setting.
659 CRLFPage:=CreateCustomPage(
660 PrevPageID
661 , 'Configuring the line ending conversions'
662 , 'How should Git treat line endings in text files?'
664 PrevPageID:=CRLFPage.ID;
666 // 1st choice
667 RdbCRLF[GC_CRLFAlways]:=TRadioButton.Create(CRLFPage);
668 with RdbCRLF[GC_CRLFAlways] do begin
669 Parent:=CRLFPage.Surface;
670 Caption:='Checkout Windows-style, commit Unix-style line endings';
671 Left:=ScaleX(4);
672 Top:=ScaleY(8);
673 Width:=ScaleX(405);
674 Height:=ScaleY(17);
675 Font.Style:=[fsBold];
676 TabOrder:=0;
677 Checked:=True;
678 end;
679 LblCRLFAlways:=TLabel.Create(CRLFPage);
680 with LblCRLFAlways do begin
681 Parent:=CRLFPage.Surface;
682 Caption:=
683 'Git will convert LF to CRLF when checking out text files. When committing' + #13 +
684 'text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
685 'this is the recommended setting on Windows ("core.autocrlf" is set to "true").';
686 Left:=ScaleX(28);
687 Top:=ScaleY(32);
688 Width:=ScaleX(405);
689 Height:=ScaleY(47);
690 end;
692 // 2nd choice
693 RdbCRLF[GC_LFOnly]:=TRadioButton.Create(CRLFPage);
694 with RdbCRLF[GC_LFOnly] do begin
695 Parent:=CRLFPage.Surface;
696 Caption:='Checkout as-is, commit Unix-style line endings';
697 Left:=ScaleX(4);
698 Top:=ScaleY(80);
699 Width:=ScaleX(405);
700 Height:=ScaleY(17);
701 Font.Style:=[fsBold];
702 TabOrder:=1;
703 Checked:=False;
704 end;
705 LblLFOnly:=TLabel.Create(CRLFPage);
706 with LblLFOnly do begin
707 Parent:=CRLFPage.Surface;
708 Caption:=
709 'Git will not perform any conversion when checking out text files. When' + #13 +
710 'committing text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
711 'this is the recommended setting on Unix ("core.autocrlf" is set to "input").';
712 Left:=ScaleX(28);
713 Top:=ScaleY(104);
714 Width:=ScaleX(405);
715 Height:=ScaleY(47);
716 end;
718 // 3rd choice
719 RdbCRLF[GC_CRLFCommitAsIs]:=TRadioButton.Create(CRLFPage);
720 with RdbCRLF[GC_CRLFCommitAsIs] do begin
721 Parent:=CRLFPage.Surface;
722 Caption:='Checkout as-is, commit as-is';
723 Left:=ScaleX(4);
724 Top:=ScaleY(152);
725 Width:=ScaleX(405);
726 Height:=ScaleY(17);
727 Font.Style:=[fsBold];
728 TabOrder:=2;
729 Checked:=False;
730 end;
731 LblCRLFCommitAsIs:=TLabel.Create(CRLFPage);
732 with LblCRLFCommitAsIs do begin
733 Parent:=CRLFPage.Surface;
734 Caption:=
735 'Git will not perform any conversions when checking out or committing' + #13 +
736 'text files. Choosing this option is not recommended for cross-platform' + #13 +
737 'projects ("core.autocrlf" is set to "false").';
738 Left:=ScaleX(28);
739 Top:=ScaleY(176);
740 Width:=ScaleX(405);
741 Height:=ScaleY(47);
742 end;
744 // Restore the setting chosen during a previous install.
745 Data:=GetPreviousData('CRLF Option','CRLFAlways');
747 // Use settings from the user provided INF.
748 if ShouldLoadInf then begin
749 Data:=LoadInfString('Setup','CRLFOption','CRLFAlways');
750 end;
752 if Data='LFOnly' then begin
753 RdbCRLF[GC_LFOnly].Checked:=True;
754 end else if Data='CRLFAlways' then begin
755 RdbCRLF[GC_CRLFAlways].Checked:=True;
756 end else if Data='CRLFCommitAsIs' then begin
757 RdbCRLF[GC_CRLFCommitAsIs].Checked:=True;
758 end;
761 * Create a custom page for finding the processes that lock a module.
764 ProcessesPage:=CreateCustomPage(
765 wpPreparing
766 , 'Replacing in-use files'
767 , 'The following applications use files that need to be replaced, please close them.'
770 ProcessesListBox:=TListBox.Create(ProcessesPage);
771 with ProcessesListBox do begin
772 Parent:=ProcessesPage.Surface;
773 Width:=ProcessesPage.SurfaceWidth;
774 Height:=ProcessesPage.SurfaceHeight-ScaleY(8);
775 end;
777 ProcessesRefresh:=TNewButton.Create(WizardForm);
778 with ProcessesRefresh do begin
779 Parent:=WizardForm;
780 Width:=WizardForm.CancelButton.Width;
781 Height:=WizardForm.CancelButton.Height;
782 Top:=WizardForm.CancelButton.Top;
783 Left:=WizardForm.ClientWidth-(WizardForm.CancelButton.Left+WizardForm.CancelButton.Width);
784 Caption:='&Refresh';
785 OnClick:=@RefreshProcessList;
786 end;
788 // This button is only used by the uninstaller.
789 ContinueButton:=NIL;
791 // Initially hide the Refresh button, show it when the process page becomes current.
792 ProcessesRefresh.Hide;
793 end;
795 function ShouldSkipPage(PageID:Integer):Boolean;
796 begin
797 if (ProcessesPage<>NIL) and (PageID=ProcessesPage.ID) then begin
798 // This page is only reached forward (by pressing "Next", never by pressing "Back").
799 RefreshProcessList(NIL);
800 Result:=(GetArrayLength(Processes)=0);
801 end else begin
802 Result:=False;
803 end;
804 end;
806 procedure CurPageChanged(CurPageID:Integer);
807 begin
808 if CurPageID=wpInfoBefore then begin
809 if WizardForm.NextButton.Enabled then begin
810 // By default, do not show a blinking cursor for InfoBeforeFile.
811 WizardForm.ActiveControl:=WizardForm.NextButton;
812 end;
813 end else if CurPageID=wpSelectDir then begin
814 if not IsDirWritable(WizardDirValue) then begin
815 // If the default directory is not writable, choose another default that most likely is.
816 // This will be checked later again when the user clicks "Next".
817 WizardForm.DirEdit.Text:=ExpandConstant('{userpf}\{#APP_NAME}');
818 end;
819 end else if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin
820 // Show the "Refresh" button only on the processes page.
821 ProcessesRefresh.Show;
822 end else begin
823 ProcessesRefresh.Hide;
824 end;
825 end;
827 function NextButtonClick(CurPageID:Integer):Boolean;
829 i:Integer;
830 Version:TWindowsVersion;
831 begin
832 // On a silent install, if your NextButtonClick function returns False
833 // prior to installation starting, Setup will exit automatically.
834 Result:=True;
836 if CurPageID=wpSelectDir then begin
837 if not IsDirWritable(WizardDirValue) then begin
838 SuppressibleMsgBox(
839 'The specified installation directory does not seem to be writable. ' +
840 + 'Please choose another directory or restart setup as a user with sufficient permissions.'
841 , mbCriticalError
842 , MB_OK
843 , IDOK
845 Result:=False;
846 Exit;
847 end;
848 end;
850 if (PuTTYPage<>NIL) and (CurPageID=PuTTYPage.ID) then begin
851 Result:=RdbSSH[GS_OpenSSH].Checked or
852 (RdbSSH[GS_Plink].Checked and FileExists(EdtPlink.Text));
853 if not Result then begin
854 SuppressibleMsgBox('{#PLINK_PATH_ERROR_MSG}',mbError,MB_OK,IDOK);
855 end;
856 end else if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin
857 // It would have been nicer to just disable the "Next" button, but the
858 // WizardForm exports the button just read-only.
859 for i:=0 to GetArrayLength(Processes)-1 do begin
860 if not Processes[i].Restartable then begin
861 SuppressibleMsgBox(
862 'Setup cannot continue until you close at least those applications in the list that are marked as "closing is required".'
863 , mbCriticalError
864 , MB_OK
865 , IDOK
867 Result:=False;
868 Exit;
869 end;
870 end;
872 Result:=(GetArrayLength(Processes)=0);
874 if not Result then begin
875 GetWindowsVersionEx(Version);
876 if Version.Major>=6 then begin
877 Result:=(SuppressibleMsgBox(
878 'If you continue without closing the listed applications they will be closed and restarted automatically.' + #13 + #13 +
879 'Are you sure you want to continue?'
880 , mbConfirmation
881 , MB_YESNO
882 , IDYES
883 )=IDYES);
884 end else begin
885 Result:=(SuppressibleMsgBox(
886 'If you continue without closing the listed applications you will need to log off and on again before changes take effect.' + #13 + #13 +
887 'Are you sure you want to continue anyway?'
888 , mbConfirmation
889 , MB_YESNO
890 , IDNO
891 )=IDYES);
892 end;
893 end;
894 end;
895 end;
897 procedure CurStepChanged(CurStep:TSetupStep);
899 AppDir,DllPath,FileName,TempName,Cmd,Msg:String;
900 BuiltIns,ImageNames,EnvPath,EnvHome:TArrayOfString;
901 Count,i:Longint;
902 LinkCreated:Boolean;
903 FindRec:TFindRec;
904 RootKey:Integer;
905 Version:TWindowsVersion;
906 begin
907 if CurStep=ssInstall then begin
908 // Shutdown locking processes just before the actual installation starts.
909 if SessionHandle>0 then try
910 RmShutdown(SessionHandle,RmShutdownOnlyRegistered,0);
911 except
912 Log('Line {#__LINE__}: RmShutdown not supported.');
913 end;
915 Exit;
916 end;
918 // Make sure the code below is only executed just after the actual installation finishes.
919 if CurStep<>ssPostInstall then begin
920 Exit;
921 end;
923 AppDir:=ExpandConstant('{app}');
926 Bind the imported function addresses
930 DllPath:=ExpandConstant('{app}\bin;{sys}');
932 // Load the list of images from a text file.
933 FileName:=AppDir+'\{#APP_BINDIMAGE}';
934 if LoadStringsFromFile(FileName,ImageNames) then begin
935 Count:=GetArrayLength(ImageNames)-1;
936 for i:=0 to Count do begin
937 FileName:=AppDir+'\'+ImageNames[i];
938 if not BindImageEx(BIND_NO_BOUND_IMPORTS or BIND_CACHE_IMPORT_DLLS,FileName,DllPath,'',0) then begin
939 Log('Line {#__LINE__}: Error calling BindImageEx for "'+FileName+'".');
940 end;
941 end;
942 end;
943 except
944 Log('Line {#__LINE__}: An exception occurred while calling BindImageEx.');
945 end;
948 Create the built-ins
951 // Load the built-ins from a text file.
952 FileName:=AppDir+'\{#APP_BUILTINS}';
953 if LoadStringsFromFile(FileName,BuiltIns) then begin
954 Count:=GetArrayLength(BuiltIns)-1;
956 // Delete those scripts from "bin" which have been replaced by built-ins in "libexec\git-core".
957 for i:=0 to Count do begin
958 FileName:=AppDir+'\bin\'+ChangeFileExt(ExtractFileName(BuiltIns[i]),'');
959 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
960 Log('Line {#__LINE__}: Unable to delete script "'+FileName+'", ignoring.');
961 end;
962 end;
964 // Create built-ins as aliases for git.exe.
965 for i:=0 to Count do begin
966 FileName:=AppDir+'\'+BuiltIns[i];
968 // Delete any existing built-in.
969 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
970 Log('Line {#__LINE__}: Unable to delete existing built-in "'+FileName+'", skipping.');
971 continue;
972 end;
975 // This will throw an exception on pre-Win2k systems.
976 LinkCreated:=CreateHardLink(FileName,AppDir+'\bin\git.exe',0);
977 except
978 LinkCreated:=False;
979 Log('Line {#__LINE__}: Creating hardlink "'+FileName+'" failed, will try a copy.');
980 end;
982 if not LinkCreated then begin
983 if not FileCopy(AppDir+'\bin\git.exe',FileName,False) then begin
984 Log('Line {#__LINE__}: Creating copy "'+FileName+'" failed, giving up.');
985 // This is not a critical error, Git could basically be used without the
986 // aliases for built-ins, so we continue.
987 end;
988 end;
989 end;
991 // Delete any duplicate files in case we are updating from a non-libexec to a libexec directory layout.
992 if FindFirst(AppDir+'\libexec\git-core\*',FindRec) then begin
993 repeat
994 if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY)=0 then begin
995 FileName:=AppDir+'\bin\'+FindRec.name;
996 if (Pos(FindRec.name,'git.exe')<>1) and FileExists(FileName) and (not DeleteFile(FileName)) then begin
997 Log('Line {#__LINE__}: Unable to delete dupe "'+FileName+'", ignoring.');
998 end;
999 end;
1000 until not FindNext(FindRec);
1001 FindClose(FindRec);
1002 end;
1003 end else begin
1004 Msg:='Line {#__LINE__}: Unable to read file "{#APP_BUILTINS}".';
1006 // This is in fact a critical error, but "Abort" does not work during ssPostInstall anymore and
1007 // we have no other way of aborting the installation, so just notify the user and continue.
1008 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1009 Log(Msg);
1010 end;
1013 Adapt core.autocrlf
1016 if RdbCRLF[GC_LFOnly].checked then begin
1017 Cmd:='core.autocrlf input';
1018 end else if RdbCRLF[GC_CRLFAlways].checked then begin
1019 Cmd:='core.autocrlf true';
1020 end else begin
1021 Cmd:='core.autocrlf false';
1022 end;
1023 if not Exec(AppDir + '\bin\git.exe', 'config -f gitconfig ' + Cmd,
1024 AppDir + '\etc', SW_HIDE, ewWaitUntilTerminated, i) then begin
1025 Msg:='Unable to configure the line ending conversion: ' + Cmd;
1027 // This is not a critical error, so just notify the user and continue.
1028 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1029 Log(Msg);
1030 end;
1033 Modify the environment
1035 This must happen no later than ssPostInstall to make
1036 "ChangesEnvironment=yes" not happend before the change!
1039 // Delete GIT_SSH and SVN_SSH if a previous installation set them (this is required for the GS_OpenSSH case).
1040 DeleteMarkedEnvString('GIT_SSH');
1041 DeleteMarkedEnvString('SVN_SSH');
1043 if (PuTTYPage<>NIL) and RdbSSH[GS_Plink].Checked then begin
1044 SetAndMarkEnvString('GIT_SSH',EdtPlink.Text,True);
1045 SetAndMarkEnvString('SVN_SSH',EdtPlink.Text,True);
1046 end;
1048 // Get the current user's directories in PATH.
1049 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
1051 // First, remove the installation directory from PATH in any case.
1052 for i:=0 to GetArrayLength(EnvPath)-1 do begin
1053 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
1054 EnvPath[i]:='';
1055 end;
1056 end;
1058 // Modify the PATH variable as requested by the user.
1059 if RdbPath[GP_Cmd].Checked or RdbPath[GP_CmdTools].Checked then begin
1060 i:=GetArrayLength(EnvPath);
1061 SetArrayLength(EnvPath,i+1);
1063 // List \cmd before \bin so \cmd has higher priority and programs in
1064 // there will be called in favor of those in \bin.
1065 EnvPath[i]:=AppDir+'\cmd';
1067 if RdbPath[GP_CmdTools].Checked then begin
1068 SetArrayLength(EnvPath,i+2);
1069 EnvPath[i+1]:=AppDir+'\bin';
1070 end;
1071 end;
1073 // Set the current user's PATH directories.
1074 if not SetEnvStrings('PATH',EnvPath,True,IsAdminLoggedOn,True) then begin
1075 Msg:='Line {#__LINE__}: Unable to set the PATH environment variable.';
1077 // This is not a critical error, so just notify the user and continue.
1078 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1079 Log(Msg);
1080 end;
1083 Create shortcuts that need to be created regardless of the "Don't create a Start Menu folder" toggle
1086 GetWindowsVersionEx(Version);
1087 if Version.Major<6 then begin
1088 Cmd:=ExpandConstant('{syswow64}\cmd.exe');
1089 TempName:='/c ""'+AppDir+'\bin\sh.exe" --login -i"';
1090 end else begin
1091 Cmd:=AppDir+'\bin\sh.exe';
1092 TempName:='--login -i';
1093 end;
1094 FileName:=AppDir+'\etc\git.ico';
1096 if IsComponentSelected('icons\quicklaunch') then begin
1097 CreateShellLink(
1098 ExpandConstant('{userappdata}\Microsoft\Internet Explorer\Quick Launch\Git Bash.lnk')
1099 , 'Git Bash'
1100 , Cmd
1101 , TempName
1102 , '%HOMEDRIVE%%HOMEPATH%'
1103 , FileName
1105 , SW_SHOWNORMAL
1107 end;
1109 if IsComponentSelected('icons\desktop') then begin
1110 CreateShellLink(
1111 GetShellFolder('desktop')+'\Git Bash.lnk'
1112 , 'Git Bash'
1113 , Cmd
1114 , TempName
1115 , '%HOMEDRIVE%%HOMEPATH%'
1116 , FileName
1118 , SW_SHOWNORMAL
1120 end;
1123 Create the Windows Explorer integrations
1126 if IsAdminLoggedOn then begin
1127 RootKey:=HKEY_LOCAL_MACHINE;
1128 end else begin
1129 RootKey:=HKEY_CURRENT_USER;
1130 end;
1132 if IsComponentSelected('ext\reg\shellhere') then begin
1133 Msg:='Git Ba&sh Here';
1134 Cmd:='"'+ExpandConstant('{syswow64}')+'\wscript" "'+AppDir+'\Git Bash.vbs" "%1"';
1135 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell','',Msg)) or
1136 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','',Cmd)) or
1137 (StringChangeEx(Cmd,'%1','%v',false)<>1) or
1138 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\Background\shell\git_shell','',Msg)) or
1139 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\Background\shell\git_shell\command','',Cmd)) then
1140 begin
1141 Msg:='Line {#__LINE__}: Unable to create "Git Bash Here" shell extension.';
1143 // This is not a critical error, so just notify the user and continue.
1144 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1145 Log(Msg);
1146 end;
1147 end;
1149 if IsComponentSelected('ext\reg\guihere') then begin
1150 Msg:='Git &GUI Here';
1151 Cmd:='"'+AppDir+'\bin\wish.exe" "'+AppDir+'\libexec\git-core\git-gui" "--working-dir" "%1"';
1152 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui','',Msg)) or
1153 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui\command','',Cmd)) or
1154 (StringChangeEx(Cmd,'%1','%v',false)<>1) or
1155 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\Background\shell\git_gui','',Msg)) or
1156 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\Background\shell\git_gui\command','',Cmd))
1157 then begin
1158 Msg:='Line {#__LINE__}: Unable to create "Git GUI Here" shell extension.';
1160 // This is not a critical error, so just notify the user and continue.
1161 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1162 Log(Msg);
1163 end;
1164 end;
1166 // It is either the Registry-based context menu entries, or the shell extension.
1167 if IsComponentSelected('ext\cheetah') then begin
1168 DeleteContextMenuEntries;
1170 if isWin64 then begin
1171 FileName:=AppDir+'\git-cheetah\git_shell_ext64.dll';
1172 end else begin
1173 FileName:=AppDir+'\git-cheetah\git_shell_ext.dll';
1174 end;
1175 if not ReplaceInUseFile(FileName,FileName+'.new',True,Msg) then begin
1176 // This is in fact a critical error, but "Abort" does not work during ssPostInstall anymore and
1177 // we have no other way of aborting the installation, so just notify the user and continue.
1178 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1179 Log('Line {#__LINE__}: Replacing file "'+FileName+'" failed.');
1180 end;
1181 end;
1184 Restart any processes that were shut down via the Restart Manager
1187 if SessionHandle>0 then try
1188 RmRestart(SessionHandle,0,0);
1189 RmEndSession(SessionHandle);
1190 except
1191 Log('Line {#__LINE__}: RmRestart not supported.');
1192 end;
1193 end;
1195 procedure RegisterPreviousData(PreviousDataKey:Integer);
1197 Data:String;
1198 begin
1199 // Git Path options.
1200 Data:='';
1201 if RdbPath[GP_BashOnly].Checked then begin
1202 Data:='BashOnly';
1203 end else if RdbPath[GP_Cmd].Checked then begin
1204 Data:='Cmd';
1205 end else if RdbPath[GP_CmdTools].Checked then begin
1206 Data:='CmdTools';
1207 end;
1208 SetPreviousData(PreviousDataKey,'Path Option',Data);
1209 if ShouldSaveInf then begin
1210 SaveInfString('Setup','PathOption',Data);
1211 end;
1213 // Git SSH options.
1214 Data:='';
1215 if (PuTTYPage=NIL) or RdbSSH[GS_OpenSSH].Checked then begin
1216 Data:='OpenSSH';
1217 end else if RdbSSH[GS_Plink].Checked then begin
1218 Data:='Plink';
1219 SetPreviousData(PreviousDataKey,'Plink Path',EdtPlink.Text);
1220 if ShouldSaveInf then begin
1221 SaveInfString('Setup','PlinkPath',EdtPlink.Text);
1222 end;
1223 end;
1224 SetPreviousData(PreviousDataKey,'SSH Option',Data);
1225 if ShouldSaveInf then begin
1226 SaveInfString('Setup','SSHOption',Data);
1227 end;
1229 // Line ending conversion options.
1230 Data:='';
1231 if RdbCRLF[GC_LFOnly].Checked then begin
1232 Data:='LFOnly';
1233 end else if RdbCRLF[GC_CRLFAlways].Checked then begin
1234 Data:='CRLFAlways';
1235 end else if RdbCRLF[GC_CRLFCommitAsIs].Checked then begin
1236 Data:='CRLFCommitAsIs';
1237 end;
1238 SetPreviousData(PreviousDataKey,'CRLF Option',Data);
1239 if ShouldSaveInf then begin
1240 SaveInfString('Setup','CRLFOption',Data);
1241 end;
1242 end;
1245 Uninstall event functions
1248 function InitializeUninstall:Boolean;
1250 Form:TSetupForm;
1251 Info:TLabel;
1252 ExitButton,RefreshButton:TButton;
1253 begin
1254 Result:=True;
1256 Form:=CreateCustomForm;
1258 Form.Caption:='Git Uninstall: Removing in-use files';
1259 Form.ClientWidth:=ScaleX(500);
1260 Form.ClientHeight:=ScaleY(256);
1261 Form.Center;
1263 Info:=TLabel.Create(Form);
1264 with Info do begin
1265 Parent:=Form;
1266 Left:=ScaleX(11);
1267 Top:=ScaleY(11);
1268 AutoSize:=True;
1269 Caption:='The following applications use files that need to be removed, please close them.';
1270 end;
1272 ContinueButton:=TButton.Create(Form);
1273 with ContinueButton do begin
1274 Parent:=Form;
1275 Left:=Form.ClientWidth-ScaleX(75+10);
1276 Top:=Form.ClientHeight-ScaleY(23+10);
1277 Width:=ScaleX(75);
1278 Height:=ScaleY(23);
1279 Caption:='Continue';
1280 ModalResult:=mrOk;
1281 end;
1283 ExitButton:=TButton.Create(Form);
1284 with ExitButton do begin
1285 Parent:=Form;
1286 Left:=ContinueButton.Left-ScaleX(75+6);
1287 Top:=ContinueButton.Top;
1288 Width:=ScaleX(75);
1289 Height:=ScaleY(23);
1290 Caption:='Exit';
1291 ModalResult:=mrCancel;
1292 Cancel:=True;
1293 end;
1295 RefreshButton:=TButton.Create(Form);
1296 with RefreshButton do begin
1297 Parent:=Form;
1298 Left:=ScaleX(10);
1299 Top:=ExitButton.Top;
1300 Width:=ScaleX(75);
1301 Height:=ScaleY(23);
1302 Caption:='Refresh';
1303 OnClick:=@RefreshProcessList;
1304 end;
1306 ProcessesListBox:=TListBox.Create(Form);
1307 with ProcessesListBox do begin
1308 Parent:=Form;
1309 Left:=ScaleX(11);
1310 Top:=Info.Top+Info.Height+11;
1311 Width:=Form.ClientWidth-ScaleX(11*2);
1312 Height:=ContinueButton.Top-ScaleY(11*4);
1313 end;
1315 Form.ActiveControl:=ContinueButton;
1317 RefreshProcessList(NIL);
1318 if GetArrayLength(Processes)>0 then begin
1319 // Now that these dialogs are going to be shown, we should probably
1320 // disable the "Are you sure to remove Git?" confirmation dialog, but
1321 // unfortunately that is not possible with Inno Setup currently.
1322 Result:=(Form.ShowModal()=mrOk);
1324 // Note: The number of processes might have changed during a refresh.
1325 if Result and (GetArrayLength(Processes)>0) then begin
1326 Result:=(SuppressibleMsgBox(
1327 'If you continue without closing the listed applications, you will need to log off and on again to remove some files manually.' + #13 + #13 +
1328 'Are you sure you want to continue anyway?'
1329 , mbConfirmation
1330 , MB_YESNO
1331 , IDNO
1332 )=IDYES);
1333 end;
1334 end;
1335 finally
1336 Form.free;
1337 end;
1338 end;
1340 // PreUninstall
1342 // Even though the name of this function suggests otherwise most of the
1343 // code below is only executed right before the actual uninstallation.
1344 // This happens because of the if-guard right in the beginning of this
1345 // function.
1346 procedure CurUninstallStepChanged(CurUninstallStep:TUninstallStep);
1348 AppDir,FileName,Msg:String;
1349 EnvPath:TArrayOfString;
1350 i:Longint;
1351 begin
1352 if CurUninstallStep<>usUninstall then begin
1353 Exit;
1354 end;
1356 // Reset the console font (the FontType is reset in the Registry section).
1357 if IsComponentInstalled('consolefont') then begin
1358 if SuppressibleMsgBox('Do you want to revert the TrueType font setting for all console windows?',mbConfirmation,MB_YESNO,IDYES)=IDYES then begin
1359 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontFamily',0);
1360 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontSize',0);
1361 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontWeight',0);
1362 end;
1363 end;
1366 Modify the environment
1368 This must happen no later than usUninstall to make
1369 "ChangesEnvironment=yes" not happend before the change!
1372 AppDir:=ExpandConstant('{app}');
1373 FileName:=AppDir+'\setup.ini';
1375 // Delete the current user's GIT_SSH and SVN_SSH if we set it.
1376 DeleteMarkedEnvString('GIT_SSH');
1377 DeleteMarkedEnvString('SVN_SSH');
1379 // Get the current user's directories in PATH.
1380 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
1382 // Remove the installation directory from PATH in any case, even if it
1383 // was not added by the installer.
1384 for i:=0 to GetArrayLength(EnvPath)-1 do begin
1385 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
1386 EnvPath[i]:='';
1387 end;
1388 end;
1390 // Reset the current user's directories in PATH.
1391 if not SetEnvStrings('PATH',EnvPath,True,IsAdminLoggedOn,True) then begin
1392 Msg:='Line {#__LINE__}: Unable to revert any possible changes to PATH.';
1394 // This is not a critical error, so just notify the user and continue.
1395 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1396 Log(Msg);
1397 end;
1399 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
1400 Msg:='Line {#__LINE__}: Unable to delete file "'+FileName+'".';
1402 // This is not a critical error, so just notify the user and continue.
1403 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1404 Log(Msg);
1405 end;
1408 Delete the Windows Explorer integrations
1411 DeleteContextMenuEntries;
1413 if isWin64 then begin
1414 FileName:=AppDir+'\git-cheetah\git_shell_ext64.dll';
1415 end else begin
1416 FileName:=AppDir+'\git-cheetah\git_shell_ext.dll';
1417 end;
1418 if FileExists(FileName) then begin
1419 if not UnregisterServer(Is64BitInstallMode,FileName,False) then begin
1420 Msg:='Line {#__LINE__}: Unable to unregister file "'+FileName+'". Please do it manually by running "regsvr32 /u '+ExtractFileName(FileName)+'".';
1422 // This is not a critical error, so just notify the user and continue.
1423 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1424 Log(Msg);
1425 end;
1427 if not DeleteFile(FileName) then begin
1428 Msg:='Line {#__LINE__}: Unable to delete file "'+FileName+'". Please do it manually after logging off and on again.';
1430 // This is not a critical error, so just notify the user and continue.
1431 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1432 Log(Msg);
1433 end;
1434 end;
1435 end;