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