eeb5a3d9c3ea1b720b22dd0d0ff7155fbd34e324
[msysgit.git] / share / WinGit / install.iss
blobeeb5a3d9c3ea1b720b22dd0d0ff7155fbd34e324
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: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;
557 Text:=GetPreviousData('Plink Path','');
558 if not FileExists(Text) then begin
559 Text:=GuessPlinkExecutable;
560 end;
561 if not FileExists(Text) then begin
562 Text:='';
563 end;
564 Left:=ScaleX(28);
565 Top:=ScaleY(161);
566 Width:=ScaleX(316);
567 Height:=ScaleY(13);
568 end;
569 BtnPlink:=TButton.Create(PuTTYPage);
570 with BtnPlink do begin
571 Parent:=PuTTYPage.Surface;
572 Caption:='...';
573 OnClick:=@BrowseForPuTTYFolder;
574 Left:=ScaleX(348);
575 Top:=ScaleY(161);
576 Width:=ScaleX(21);
577 Height:=ScaleY(21);
578 end;
580 // Restore the setting chosen during a previous install.
581 Data:=GetPreviousData('SSH Option','OpenSSH');
582 if Data='OpenSSH' then begin
583 RdbSSH[GS_OpenSSH].Checked:=True;
584 end else if Data='Plink' then begin
585 RdbSSH[GS_Plink].Checked:=True;
586 end;
587 end else begin
588 PuTTYPage:=NIL;
589 end;
592 * Create a custom page for the core.autocrlf setting.
595 CRLFPage:=CreateCustomPage(
596 PrevPageID,
597 'Configuring the line ending conversions',
598 'How should Git treat line endings in text files?'
600 PrevPageID:=CRLFPage.ID;
602 // 1st choice
603 RdbCRLF[GC_CRLFAlways]:=TRadioButton.Create(CRLFPage);
604 with RdbCRLF[GC_CRLFAlways] do begin
605 Parent:=CRLFPage.Surface;
606 Caption:='Checkout Windows-style, commit Unix-style line endings';
607 Left:=ScaleX(4);
608 Top:=ScaleY(8);
609 Width:=ScaleX(405);
610 Height:=ScaleY(17);
611 Font.Style:=[fsBold];
612 TabOrder:=0;
613 Checked:=True;
614 end;
615 LblCRLFAlways:=TLabel.Create(CRLFPage);
616 with LblCRLFAlways do begin
617 Parent:=CRLFPage.Surface;
618 Caption:=
619 'Git will convert LF to CRLF when checking out text files. When committing' + #13 +
620 'text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
621 'this is the recommended setting on Windows ("core.autocrlf" is set to "true").';
622 Left:=ScaleX(28);
623 Top:=ScaleY(32);
624 Width:=ScaleX(405);
625 Height:=ScaleY(47);
626 end;
628 // 2nd choice
629 RdbCRLF[GC_LFOnly]:=TRadioButton.Create(CRLFPage);
630 with RdbCRLF[GC_LFOnly] do begin
631 Parent:=CRLFPage.Surface;
632 Caption:='Checkout as-is, commit Unix-style line endings';
633 Left:=ScaleX(4);
634 Top:=ScaleY(80);
635 Width:=ScaleX(405);
636 Height:=ScaleY(17);
637 Font.Style:=[fsBold];
638 TabOrder:=1;
639 Checked:=False;
640 end;
641 LblLFOnly:=TLabel.Create(CRLFPage);
642 with LblLFOnly do begin
643 Parent:=CRLFPage.Surface;
644 Caption:=
645 'Git will not perform any conversion when checking out text files. When' + #13 +
646 'committing text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
647 'this is the recommended setting on Unix ("core.autocrlf" is set to "input").';
648 Left:=ScaleX(28);
649 Top:=ScaleY(104);
650 Width:=ScaleX(405);
651 Height:=ScaleY(47);
652 end;
654 // 3rd choice
655 RdbCRLF[GC_CRLFCommitAsIs]:=TRadioButton.Create(CRLFPage);
656 with RdbCRLF[GC_CRLFCommitAsIs] do begin
657 Parent:=CRLFPage.Surface;
658 Caption:='Checkout as-is, commit as-is';
659 Left:=ScaleX(4);
660 Top:=ScaleY(152);
661 Width:=ScaleX(405);
662 Height:=ScaleY(17);
663 Font.Style:=[fsBold];
664 TabOrder:=2;
665 Checked:=False;
666 end;
667 LblCRLFCommitAsIs:=TLabel.Create(CRLFPage);
668 with LblCRLFCommitAsIs do begin
669 Parent:=CRLFPage.Surface;
670 Caption:=
671 'Git will not perform any conversions when checking out or committing' + #13 +
672 'text files. Choosing this option is not recommended for cross-platform' + #13 +
673 'projects ("core.autocrlf" is set to "false").';
674 Left:=ScaleX(28);
675 Top:=ScaleY(176);
676 Width:=ScaleX(405);
677 Height:=ScaleY(47);
678 end;
680 // Restore the setting chosen during a previous install.
681 Data:=GetPreviousData('CRLF Option','CRLFAlways');
682 if Data='LFOnly' then begin
683 RdbCRLF[GC_LFOnly].Checked:=True;
684 end else if Data='CRLFAlways' then begin
685 RdbCRLF[GC_CRLFAlways].Checked:=True;
686 end else if Data='CRLFCommitAsIs' then begin
687 RdbCRLF[GC_CRLFCommitAsIs].Checked:=True;
688 end;
691 * Create a custom page for finding the processes that lock a module.
694 ProcessesPage:=CreateCustomPage(
695 wpPreparing,
696 'Replacing in-use files',
697 'The following applications use files that need to be replaced, please close them.'
700 ProcessesListBox:=TListBox.Create(ProcessesPage);
701 with ProcessesListBox do begin
702 Parent:=ProcessesPage.Surface;
703 Width:=ProcessesPage.SurfaceWidth;
704 Height:=ProcessesPage.SurfaceHeight-ScaleY(8);
705 end;
707 ProcessesRefresh:=TNewButton.Create(WizardForm);
708 with ProcessesRefresh do begin
709 Parent:=WizardForm;
710 Width:=WizardForm.CancelButton.Width;
711 Height:=WizardForm.CancelButton.Height;
712 Top:=WizardForm.CancelButton.Top;
713 Left:=WizardForm.ClientWidth-(WizardForm.CancelButton.Left+WizardForm.CancelButton.Width);
714 Caption:='&Refresh';
715 OnClick:=@RefreshProcessList;
716 end;
718 // This button is only used by the uninstaller.
719 ContinueButton:=NIL;
721 // Initially hide the Refresh button, show it when the process page becomes current.
722 ProcessesRefresh.Hide;
723 end;
725 function ShouldSkipPage(PageID:Integer):Boolean;
726 begin
727 if (ProcessesPage<>NIL) and (PageID=ProcessesPage.ID) then begin
728 // This page is only reached forward (by pressing "Next", never by pressing "Back").
729 RefreshProcessList(NIL);
730 Result:=(GetArrayLength(Processes)=0);
731 end else begin
732 Result:=False;
733 end;
734 end;
736 procedure CurPageChanged(CurPageID:Integer);
737 begin
738 if CurPageID=wpInfoBefore then begin
739 if WizardForm.NextButton.Enabled then begin
740 // By default, do not show a blinking cursor for InfoBeforeFile.
741 WizardForm.ActiveControl:=WizardForm.NextButton;
742 end;
743 end else if CurPageID=wpSelectDir then begin
744 if not IsDirWritable(WizardDirValue) then begin
745 // If the default directory is not writable, choose another default that most likely is.
746 // This will be checked later again when the user clicks "Next".
747 WizardForm.DirEdit.Text:=ExpandConstant('{userpf}\{#APP_NAME}');
748 end;
749 end else if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin
750 // Show the "Refresh" button only on the processes page.
751 ProcessesRefresh.Show;
752 end else begin
753 ProcessesRefresh.Hide;
754 end;
755 end;
757 function NextButtonClick(CurPageID:Integer):Boolean;
759 i:Integer;
760 Version:TWindowsVersion;
761 begin
762 Result:=True;
764 if CurPageID=wpSelectDir then begin
765 if not IsDirWritable(WizardDirValue) then begin
766 MsgBox(
767 'The specified installation directory does not seem to be writable. ' +
768 + 'Please choose another directory or restart setup as a user with sufficient permissions.'
769 , mbCriticalError
770 , MB_OK
772 Result:=False;
773 Exit;
774 end;
775 end;
777 if (PuTTYPage<>NIL) and (CurPageID=PuTTYPage.ID) then begin
778 Result:=RdbSSH[GS_OpenSSH].Checked or
779 (RdbSSH[GS_Plink].Checked and FileExists(EdtPlink.Text));
780 if not Result then begin
781 MsgBox('{#PLINK_PATH_ERROR_MSG}',mbError,MB_OK);
782 end;
783 end else if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin
784 // It would have been nicer to just disable the "Next" button, but the
785 // WizardForm exports the button just read-only.
786 for i:=0 to GetArrayLength(Processes)-1 do begin
787 if not Processes[i].Restartable then begin
788 MsgBox('Setup cannot continue until you close at least those applications in the list that are marked as "closing is required".',mbCriticalError,MB_OK);
789 Result:=False;
790 Exit;
791 end;
792 end;
794 Result:=(GetArrayLength(Processes)=0);
796 if not Result then begin
797 GetWindowsVersionEx(Version);
798 if Version.Major>=6 then begin
799 Result:=(MsgBox(
800 'If you continue without closing the listed applications they will be closed and restarted automatically.' + #13 + #13 +
801 'Are you sure you want to continue?',
802 mbConfirmation,
803 MB_YESNO
804 )=IDYES);
805 end else begin
806 Result:=(MsgBox(
807 'If you continue without closing the listed applications you will need to log off and on again before changes take effect.' + #13 + #13 +
808 'Are you sure you want to continue anyway?',
809 mbConfirmation,
810 MB_YESNO
811 )=IDYES);
812 end;
813 end;
814 end;
815 end;
817 procedure CurStepChanged(CurStep:TSetupStep);
819 AppDir,DllPath,FileName,TempName,Cmd,Msg:String;
820 BuiltIns,ImageNames,EnvPath,EnvHome,EnvSSH:TArrayOfString;
821 Count,i:Longint;
822 LinkCreated:Boolean;
823 FindRec:TFindRec;
824 RootKey:Integer;
825 Version:TWindowsVersion;
826 begin
827 if CurStep=ssInstall then begin
828 // Shutdown locking processes just before the actual installation starts.
829 if SessionHandle>0 then try
830 RmShutdown(SessionHandle,RmShutdownOnlyRegistered,0);
831 except
832 Log('Line {#__LINE__}: RmShutdown not supported.');
833 end;
835 Exit;
836 end;
838 // Make sure the code below is only executed just after the actual installation finishes.
839 if CurStep<>ssPostInstall then begin
840 Exit;
841 end;
843 AppDir:=ExpandConstant('{app}');
846 Bind the imported function addresses
850 DllPath:=ExpandConstant('{app}\bin;{sys}');
852 // Load the list of images from a text file.
853 FileName:=AppDir+'\{#APP_BINDIMAGE}';
854 if LoadStringsFromFile(FileName,ImageNames) then begin
855 Count:=GetArrayLength(ImageNames)-1;
856 for i:=0 to Count do begin
857 FileName:=AppDir+'\'+ImageNames[i];
858 if not BindImageEx(BIND_NO_BOUND_IMPORTS or BIND_CACHE_IMPORT_DLLS,FileName,DllPath,'',0) then begin
859 Log('Line {#__LINE__}: Error calling BindImageEx for "'+FileName+'".');
860 end;
861 end;
862 end;
863 except
864 Log('Line {#__LINE__}: An exception occurred while calling BindImageEx.');
865 end;
868 Create the built-ins
871 // Load the built-ins from a text file.
872 FileName:=AppDir+'\{#APP_BUILTINS}';
873 if LoadStringsFromFile(FileName,BuiltIns) then begin
874 Count:=GetArrayLength(BuiltIns)-1;
876 // Delete those scripts from "bin" which have been replaced by built-ins in "libexec\git-core".
877 for i:=0 to Count do begin
878 FileName:=AppDir+'\bin\'+ChangeFileExt(ExtractFileName(BuiltIns[i]),'');
879 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
880 Log('Line {#__LINE__}: Unable to delete script "'+FileName+'", ignoring.');
881 end;
882 end;
884 // Create built-ins as aliases for git.exe.
885 for i:=0 to Count do begin
886 FileName:=AppDir+'\'+BuiltIns[i];
888 // Delete any existing built-in.
889 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
890 Log('Line {#__LINE__}: Unable to delete existing built-in "'+FileName+'", skipping.');
891 continue;
892 end;
895 // This will throw an exception on pre-Win2k systems.
896 LinkCreated:=CreateHardLink(FileName,AppDir+'\bin\git.exe',0);
897 except
898 LinkCreated:=False;
899 Log('Line {#__LINE__}: Creating hardlink "'+FileName+'" failed, will try a copy.');
900 end;
902 if not LinkCreated then begin
903 if not FileCopy(AppDir+'\bin\git.exe',FileName,False) then begin
904 Log('Line {#__LINE__}: Creating copy "'+FileName+'" failed, giving up.');
905 // This is not a critical error, Git could basically be used without the
906 // aliases for built-ins, so we continue.
907 end;
908 end;
909 end;
911 // Delete any duplicate files in case we are updating from a non-libexec to a libexec directory layout.
912 if FindFirst(AppDir+'\libexec\git-core\*',FindRec) then begin
913 repeat
914 if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY)=0 then begin
915 FileName:=AppDir+'\bin\'+FindRec.name;
916 if (Pos(FindRec.name,'git.exe')<>1) and FileExists(FileName) and (not DeleteFile(FileName)) then begin
917 Log('Line {#__LINE__}: Unable to delete dupe "'+FileName+'", ignoring.');
918 end;
919 end;
920 until not FindNext(FindRec);
921 FindClose(FindRec);
922 end;
923 end else begin
924 Msg:='Line {#__LINE__}: Unable to read file "{#APP_BUILTINS}".';
925 MsgBox(Msg,mbError,MB_OK);
926 Log(Msg);
927 // This is not a critical error, Git could basically be used without the
928 // aliases for built-ins, so we continue.
929 end;
932 Adapt core.autocrlf
935 if RdbCRLF[GC_LFOnly].checked then begin
936 Cmd:='core.autocrlf input';
937 end else if RdbCRLF[GC_CRLFAlways].checked then begin
938 Cmd:='core.autocrlf true';
939 end else begin
940 Cmd:='core.autocrlf false';
941 end;
942 if not Exec(AppDir + '\bin\git.exe', 'config -f gitconfig ' + Cmd,
943 AppDir + '\etc', SW_HIDE, ewWaitUntilTerminated, i) then begin
944 Msg:='Unable to configure the line ending conversion: ' + Cmd;
945 MsgBox(Msg,mbError,MB_OK);
946 Log(Msg);
947 // This is not a critical error, the user can probably fix it manually,
948 // so we continue.
949 end;
952 Modify the environment
954 This must happen no later than ssPostInstall to make
955 "ChangesEnvironment=yes" not happend before the change!
958 FileName:=AppDir+'\setup.ini';
960 // Delete GIT_SSH and SVN_SSH if a previous installation set them (this is required for the GS_OpenSSH case).
961 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
962 if (GetArrayLength(EnvSSH)=1) and
963 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
964 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
965 Msg:='Line {#__LINE__}: Unable to reset GIT_SSH prior to install.';
966 MsgBox(Msg,mbError,MB_OK);
967 Log(Msg);
968 // This is not a critical error, the user can probably fix it manually,
969 // so we continue.
970 end;
971 end;
973 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
974 if (GetArrayLength(EnvSSH)=1) and
975 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','SVN_SSH','',FileName))=0) then begin
976 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
977 Msg:='Line {#__LINE__}: Unable to reset SVN_SSH prior to install.';
978 MsgBox(Msg,mbError,MB_OK);
979 Log(Msg);
980 // This is not a critical error, the user can probably fix it manually,
981 // so we continue.
982 end;
983 end;
985 if (PuTTYPage<>NIL) and RdbSSH[GS_Plink].Checked then begin
986 SetArrayLength(EnvSSH,1);
987 EnvSSH[0]:=EdtPlink.Text;
989 // Set GIT_SSH as specified by the user.
990 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
991 Msg:='Line {#__LINE__}: Unable to set the GIT_SSH environment variable.';
992 MsgBox(Msg,mbError,MB_OK);
993 Log(Msg);
994 // This is not a critical error, the user can probably fix it manually,
995 // so we continue.
996 end;
998 // Mark that we have changed GIT_SSH by writing its value to a file.
999 if not SetIniString('Environment','GIT_SSH',EnvSSH[0],FileName) then begin
1000 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
1001 MsgBox(Msg,mbError,MB_OK);
1002 Log(Msg);
1003 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
1004 // so we continue.
1005 end;
1007 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
1008 Msg:='Line {#__LINE__}: Unable to set the SVN_SSH environment variable.';
1009 MsgBox(Msg,mbError,MB_OK);
1010 Log(Msg);
1011 // This is not a critical error, the user can probably fix it manually,
1012 // so we continue.
1013 end;
1015 // Mark that we have changed SVN_SSH by writing its value to a file.
1016 if not SetIniString('Environment','SVN_SSH',EnvSSH[0],FileName) then begin
1017 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
1018 MsgBox(Msg,mbError,MB_OK);
1019 Log(Msg);
1020 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
1021 // so we continue.
1022 end;
1023 end;
1025 // Get the current user's directories in PATH.
1026 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
1028 // First, remove the installation directory from PATH in any case.
1029 for i:=0 to GetArrayLength(EnvPath)-1 do begin
1030 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
1031 EnvPath[i]:='';
1032 end;
1033 end;
1035 // Delete HOME if a previous installation modified it.
1036 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
1037 if (GetArrayLength(EnvHome)=1) and
1038 (CompareStr(RemoveQuotes(EnvHome[0]),GetIniString('Environment','HOME','',FileName))=0) then begin
1039 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
1040 Msg:='Line {#__LINE__}: Unable to reset HOME prior to install.';
1041 MsgBox(Msg,mbError,MB_OK);
1042 Log(Msg);
1043 // This is not a critical error, the user can probably fix it manually,
1044 // so we continue.
1045 end;
1046 end;
1048 // Modify the PATH variable as requested by the user.
1049 if RdbPath[GP_Cmd].Checked or RdbPath[GP_CmdTools].Checked then begin
1050 i:=GetArrayLength(EnvPath);
1051 SetArrayLength(EnvPath,i+1);
1053 // List \cmd before \bin so \cmd has higher priority and programs in
1054 // there will be called in favor of those in \bin.
1055 EnvPath[i]:=AppDir+'\cmd';
1057 if RdbPath[GP_CmdTools].Checked then begin
1058 SetArrayLength(EnvPath,i+2);
1059 EnvPath[i+1]:=AppDir+'\bin';
1061 // Set HOME for the Windows Command Prompt, but only if it has not been set manually before.
1062 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
1063 i:=GetArrayLength(EnvHome);
1064 if (i=0) or ((i=1) and (Length(EnvHome[0])=0)) then begin
1065 SetArrayLength(EnvHome,1);
1066 EnvHome[0]:=ExpandConstant('{%HOMEDRIVE}{%HOMEPATH}');
1067 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,EnvHome) then begin
1068 Msg:='Line {#__LINE__}: Unable to set the HOME environment variable.';
1069 MsgBox(Msg,mbError,MB_OK);
1070 Log(Msg);
1071 // This is not a critical error, the user can probably fix it manually,
1072 // so we continue.
1073 end;
1075 // Mark that we have changed HOME.
1076 if not SetIniString('Environment','HOME',EnvHome[0],FileName) then begin
1077 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
1078 MsgBox(Msg,mbError,MB_OK);
1079 Log(Msg);
1080 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
1081 // so we continue.
1082 end;
1083 end;
1084 end;
1085 end;
1087 // Set the current user's PATH directories.
1088 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
1089 Msg:='Line {#__LINE__}: Unable to set the PATH environment variable.';
1090 MsgBox(Msg,mbError,MB_OK);
1091 Log(Msg);
1092 // This is not a critical error, the user can probably fix it manually,
1093 // so we continue.
1094 end;
1097 Create shortcuts that need to be created regardless of the "Don't create a Start Menu folder" toggle
1100 GetWindowsVersionEx(Version);
1101 if Version.Major<6 then begin
1102 Cmd:=ExpandConstant('{syswow64}\cmd.exe');
1103 TempName:='/c ""'+AppDir+'\bin\sh.exe" --login -i"';
1104 end else begin
1105 Cmd:=AppDir+'\bin\sh.exe';
1106 TempName:='--login -i';
1107 end;
1108 FileName:=AppDir+'\etc\git.ico';
1110 if IsComponentSelected('icons\quicklaunch') then begin
1111 CreateShellLink(
1112 ExpandConstant('{userappdata}\Microsoft\Internet Explorer\Quick Launch\Git Bash.lnk')
1113 , 'Git Bash'
1114 , Cmd
1115 , TempName
1116 , '%HOMEDRIVE%%HOMEPATH%'
1117 , FileName
1119 , SW_SHOWNORMAL
1121 end;
1122 if IsComponentSelected('icons\desktop') then begin
1123 CreateShellLink(
1124 GetShellFolder('desktop')+'\Git Bash.lnk'
1125 , 'Git Bash'
1126 , Cmd
1127 , TempName
1128 , '%HOMEDRIVE%%HOMEPATH%'
1129 , FileName
1131 , SW_SHOWNORMAL
1133 end;
1135 // Create a special shortcut that does not set a working directory (Note: Since Inno Setup 5.3.11,
1136 // passing an empty WorkingDir gets replaced with {sys}, so use '.' instead).
1137 // This shortcut is used by "Git Bash.vbs", which in turn is run by the "Git Bash Here" shell extension.
1138 CreateShellLink(
1139 AppDir+'\Git Bash.lnk'
1140 , 'Git Bash'
1141 , Cmd
1142 , TempName
1143 , '.'
1144 , FileName
1146 , SW_SHOWNORMAL
1150 Create the Windows Explorer integrations
1153 if IsAdminLoggedOn then begin
1154 RootKey:=HKEY_LOCAL_MACHINE;
1155 end else begin
1156 RootKey:=HKEY_CURRENT_USER;
1157 end;
1159 if IsComponentSelected('ext\reg\shellhere') then begin
1160 Msg:='Git Ba&sh Here';
1161 Cmd:='"'+ExpandConstant('{syswow64}')+'\wscript" "'+AppDir+'\Git Bash.vbs" "%1"';
1162 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell','',Msg)) or
1163 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','',Cmd)) or
1164 (StringChangeEx(Cmd,'%1','%v',false)<>1) or
1165 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\Background\shell\git_shell','',Msg)) or
1166 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\Background\shell\git_shell\command','',Cmd)) then begin
1167 Msg:='Line {#__LINE__}: Unable to create "Git Bash Here" shell extension.';
1168 MsgBox(Msg,mbError,MB_OK);
1169 Log(Msg);
1170 // This is not a critical error, the user can probably fix it manually,
1171 // so we continue.
1172 end;
1173 end;
1175 if IsComponentSelected('ext\reg\guihere') then begin
1176 Msg:='Git &GUI Here';
1177 Cmd:='"'+AppDir+'\bin\wish.exe" "'+AppDir+'\libexec\git-core\git-gui" "--working-dir" "%1"';
1178 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui','',Msg)) or
1179 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui\command','',Cmd)) or
1180 (StringChangeEx(Cmd,'%1','%v',false)<>1) or
1181 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\Background\shell\git_gui','',Msg)) or
1182 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\Background\shell\git_gui\command','',Cmd)) then begin
1183 Msg:='Line {#__LINE__}: Unable to create "Git GUI Here" shell extension.';
1184 MsgBox(Msg,mbError,MB_OK);
1185 Log(Msg);
1186 // This is not a critical error, the user can probably fix it manually,
1187 // so we continue.
1188 end;
1189 end;
1191 // It is either the Registry-based context menu entries, or the shell extension.
1192 if IsComponentSelected('ext\cheetah') then begin
1193 DeleteContextMenuEntries;
1195 if isWin64 then begin
1196 FileName:=AppDir+'\git-cheetah\git_shell_ext64.dll';
1197 end else begin
1198 FileName:=AppDir+'\git-cheetah\git_shell_ext.dll';
1199 end;
1200 if not ReplaceInUseFile(FileName,FileName+'.new',True) then begin
1201 Log('Line {#__LINE__}: Replacing file "'+FileName+'" failed.');
1202 end;
1203 end;
1206 Restart any processes that were shut down via the Restart Manager
1209 if SessionHandle>0 then try
1210 RmRestart(SessionHandle,0,0);
1211 RmEndSession(SessionHandle);
1212 except
1213 Log('Line {#__LINE__}: RmRestart not supported.');
1214 end;
1215 end;
1217 procedure RegisterPreviousData(PreviousDataKey:Integer);
1219 Data:String;
1220 begin
1221 // Git Path options.
1222 Data:='';
1223 if RdbPath[GP_BashOnly].Checked then begin
1224 Data:='BashOnly';
1225 end else if RdbPath[GP_Cmd].Checked then begin
1226 Data:='Cmd';
1227 end else if RdbPath[GP_CmdTools].Checked then begin
1228 Data:='CmdTools';
1229 end;
1230 SetPreviousData(PreviousDataKey,'Path Option',Data);
1232 // Git SSH options.
1233 Data:='';
1234 if (PuTTYPage=NIL) or RdbSSH[GS_OpenSSH].Checked then begin
1235 Data:='OpenSSH';
1236 end else if RdbSSH[GS_Plink].Checked then begin
1237 Data:='Plink';
1238 SetPreviousData(PreviousDataKey,'Plink Path',EdtPlink.Text);
1239 end;
1240 SetPreviousData(PreviousDataKey,'SSH Option',Data);
1242 // Line ending conversion options.
1243 Data:='';
1244 if RdbCRLF[GC_LFOnly].Checked then begin
1245 Data:='LFOnly';
1246 end else if RdbCRLF[GC_CRLFAlways].Checked then begin
1247 Data:='CRLFAlways';
1248 end else if RdbCRLF[GC_CRLFCommitAsIs].Checked then begin
1249 Data:='CRLFCommitAsIs';
1250 end;
1251 SetPreviousData(PreviousDataKey,'CRLF Option',Data);
1252 end;
1255 Uninstaller code
1258 function InitializeUninstall:Boolean;
1260 Form:TSetupForm;
1261 Info:TLabel;
1262 ExitButton,RefreshButton:TButton;
1263 begin
1264 Result:=True;
1266 Form:=CreateCustomForm;
1268 Form.Caption:='Git Uninstall: Removing in-use files';
1269 Form.ClientWidth:=ScaleX(500);
1270 Form.ClientHeight:=ScaleY(256);
1271 Form.Center;
1273 Info:=TLabel.Create(Form);
1274 with Info do begin
1275 Parent:=Form;
1276 Left:=ScaleX(11);
1277 Top:=ScaleY(11);
1278 AutoSize:=True;
1279 Caption:='The following applications use files that need to be removed, please close them.';
1280 end;
1282 ContinueButton:=TButton.Create(Form);
1283 with ContinueButton do begin
1284 Parent:=Form;
1285 Left:=Form.ClientWidth-ScaleX(75+10);
1286 Top:=Form.ClientHeight-ScaleY(23+10);
1287 Width:=ScaleX(75);
1288 Height:=ScaleY(23);
1289 Caption:='Continue';
1290 ModalResult:=mrOk;
1291 end;
1293 ExitButton:=TButton.Create(Form);
1294 with ExitButton do begin
1295 Parent:=Form;
1296 Left:=ContinueButton.Left-ScaleX(75+6);
1297 Top:=ContinueButton.Top;
1298 Width:=ScaleX(75);
1299 Height:=ScaleY(23);
1300 Caption:='Exit';
1301 ModalResult:=mrCancel;
1302 Cancel:=True;
1303 end;
1305 RefreshButton:=TButton.Create(Form);
1306 with RefreshButton do begin
1307 Parent:=Form;
1308 Left:=ScaleX(10);
1309 Top:=ExitButton.Top;
1310 Width:=ScaleX(75);
1311 Height:=ScaleY(23);
1312 Caption:='Refresh';
1313 OnClick:=@RefreshProcessList;
1314 end;
1316 ProcessesListBox:=TListBox.Create(Form);
1317 with ProcessesListBox do begin
1318 Parent:=Form;
1319 Left:=ScaleX(11);
1320 Top:=Info.Top+Info.Height+11;
1321 Width:=Form.ClientWidth-ScaleX(11*2);
1322 Height:=ContinueButton.Top-ScaleY(11*4);
1323 end;
1325 Form.ActiveControl:=ContinueButton;
1327 RefreshProcessList(NIL);
1328 if GetArrayLength(Processes)>0 then begin
1329 // Now that these dialogs are going to be shown, we should probably
1330 // disable the "Are you sure to remove Git?" confirmation dialog, but
1331 // unfortunately that is not possible with Inno Setup currently.
1332 Result:=(Form.ShowModal()=mrOk);
1334 // Note: The number of processes might have changed during a refresh.
1335 if Result and (GetArrayLength(Processes)>0) then begin
1336 Result:=(MsgBox(
1337 'If you continue without closing the listed applications, you will need to log off and on again to remove some files manually.' + #13 + #13 +
1338 'Are you sure you want to continue anyway?',
1339 mbConfirmation,
1340 MB_YESNO
1341 )=IDYES);
1342 end;
1343 end;
1344 finally
1345 Form.free;
1346 end;
1347 end;
1349 // PreUninstall
1351 // Even though the name of this function suggests otherwise most of the
1352 // code below is only executed right before the actual uninstallation.
1353 // This happens because of the if-guard right in the beginning of this
1354 // function.
1355 procedure CurUninstallStepChanged(CurUninstallStep:TUninstallStep);
1357 AppDir,FileName,Msg:String;
1358 EnvPath,EnvHome,EnvSSH:TArrayOfString;
1359 i:Longint;
1360 begin
1361 if CurUninstallStep<>usUninstall then begin
1362 Exit;
1363 end;
1365 // Reset the console font (the FontType is reset in the Registry section).
1366 if IsComponentInstalled('consolefont') then begin
1367 if MsgBox('Do you want to revert the TrueType font setting for all console windows?',mbConfirmation,MB_YESNO)=IDYES then begin
1368 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontFamily',0);
1369 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontSize',0);
1370 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontWeight',0);
1371 end;
1372 end;
1375 Modify the environment
1377 This must happen no later than usUninstall to make
1378 "ChangesEnvironment=yes" not happend before the change!
1381 AppDir:=ExpandConstant('{app}');
1382 FileName:=AppDir+'\setup.ini';
1384 // Delete the current user's GIT_SSH and SVN_SSH if we set it.
1385 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
1386 if (GetArrayLength(EnvSSH)=1) and
1387 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
1388 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
1389 Msg:='Line {#__LINE__}: Unable to revert any possible changes to GIT_SSH.';
1390 MsgBox(Msg,mbError,MB_OK);
1391 Log(Msg);
1392 // This is not a critical error, the user can probably fix it manually,
1393 // so we continue.
1394 end;
1395 end;
1397 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
1398 if (GetArrayLength(EnvSSH)=1) and
1399 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','SVN_SSH','',FileName))=0) then begin
1400 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
1401 Msg:='Line {#__LINE__}: Unable to revert any possible changes to SVN_SSH.';
1402 MsgBox(Msg,mbError,MB_OK);
1403 Log(Msg);
1404 // This is not a critical error, the user can probably fix it manually,
1405 // so we continue.
1406 end;
1407 end;
1409 // Get the current user's directories in PATH.
1410 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
1412 // Remove the installation directory from PATH in any case, even if it
1413 // was not added by the installer.
1414 for i:=0 to GetArrayLength(EnvPath)-1 do begin
1415 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
1416 EnvPath[i]:='';
1417 end;
1418 end;
1420 // Reset the current user's directories in PATH.
1421 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
1422 Msg:='Line {#__LINE__}: Unable to revert any possible changes to PATH.';
1423 MsgBox(Msg,mbError,MB_OK);
1424 Log(Msg);
1425 // This is not a critical error, the user can probably fix it manually,
1426 // so we continue.
1427 end;
1429 // Reset the current user's HOME if we modified it.
1430 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
1431 if (GetArrayLength(EnvHome)=1) and
1432 (CompareStr(RemoveQuotes(EnvHome[0]),GetIniString('Environment','HOME','',FileName))=0) then begin
1433 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
1434 Msg:='Line {#__LINE__}: Unable to revert any possible changes to HOME.';
1435 MsgBox(Msg,mbError,MB_OK);
1436 Log(Msg);
1437 // This is not a critical error, the user can probably fix it manually,
1438 // so we continue.
1439 end;
1440 end;
1442 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
1443 Msg:='Line {#__LINE__}: Unable to delete file "'+FileName+'".';
1444 MsgBox(Msg,mbError,MB_OK);
1445 Log(Msg);
1446 // This is not a critical error, the user can probably fix it manually,
1447 // so we continue.
1448 end;
1451 Delete the Windows Explorer integrations
1454 DeleteContextMenuEntries;
1456 if isWin64 then begin
1457 FileName:=AppDir+'\git-cheetah\git_shell_ext64.dll';
1458 end else begin
1459 FileName:=AppDir+'\git-cheetah\git_shell_ext.dll';
1460 end;
1461 if FileExists(FileName) then begin
1462 if not UnregisterServer(Is64BitInstallMode,FileName,False) then begin
1463 Msg:='Line {#__LINE__}: Unable to unregister file "'+FileName+'". Please do it manually by running "regsvr32 /u '+ExtractFileName(FileName)+'".';
1464 MsgBox(Msg,mbError,MB_OK);
1465 Log(Msg);
1466 end;
1468 if not DeleteFile(FileName) then begin
1469 Msg:='Line {#__LINE__}: Unable to delete file "'+FileName+'". Please do it manually after logging off and on again.';
1470 MsgBox(Msg,mbError,MB_OK);
1471 Log(Msg);
1472 end;
1473 end;
1474 end;