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