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