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