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