Check daily for git for windows updates

Установка Git в Windows в деталях, с объяснением каждого пункта установки. Базовая настройка после установки. Статья дополнена видео с наглядным рассказом и демонастрацией.

Table of Contents

Всем привет! Сегодня мы будем устанавливать Git в Windows. И любая работа с программой
начинается с ее установки, иногда это не составляет сложности и достаточно только нажимать
кнопку Далее. С git тоже можно использовать эту стратегию, хотя лучше сразу выбрать и
настроить нужные параметры еще на этапе установки.

  • 💡 Git — это консольная утилита для контроля истории изменения файлов в вашем проекте. Создана в апреле 2005 года Линусом Торвальдсом для отслеживания и управления изменениями в кодовой базы ядра Linux. В настоящее время подавляющее большинство проектов использует именно git для командной разработке и хранения истории кода.

Скачивание дистрибутива

Установка начинается со скачивания дистрибутива.

Открывайте ваш любимый браузер и переходите на официальный сайт проекта git:

https://git-scm.com

Нажимайте Download, и у вас начнется скачиваться инсталлятор. Долго ждать не придется — размер не более 50мб.

Страница скачивания

Установка

Рассмотрим все пункты установки.

Лицензионное соглашение

Первым делом нам предлагают принять лицензионное соглашение — принимаем нажимая Next

Лицензионное соглашение

Выбор места для файлов git

Далее будет предложено выбрать место куда будет установлены файлы git, менять не требуется, Program Files нас устроит.

Выбор места для файлов git

Выбор компонентов

На данном этапе нам будет предложено выбрать настройки и компоненты git:

Выбор компонентов

  • Additional icons — Выберите этот пункт, если нужна иконка запуска git bash на рабочем столе

  • Windows Explore integration — Выбранные пункты появятся в контекстном меню Проводника:

пункты в контекстном меню Проводник

  • Associate .git configuration files with the default text editor —* если выбрать, то файлы .git* будут открываться в текстовом редакторе установленном в гите по умолчанию

  • Associate .sh files to be run with Bash — если выбрать, то файлы .sh* будут автоматически открываться в Git Bash

  • Use a TrueType font in all console windows — если выбрать, то в консоли Git Bash будет использоваться шрифт TrueType.

  • Check daily for Git for Windows updates — git будет проверять обновления ежедневно.

💡Предлагается выбрать набор компонентов, как указано на скриншоте.

Папка в меню Пуск

Папка в меню Пуск

1 — Выберите как будет называться папка с ярлыками Git программ в меню Пуск

2 — Если выбрать пункт, то в меню Пуск папка не будет создана.

Редактор по умолчанию

Выбор редактора по умолчанию:

Редактор по умолчанию

Рекомендуется поменять редактор Vim на редактора из списка который у вас уже установлен в системе. В любом случае вы можете выбрать стандартный блокнот Windows (Notepad) как редактор по умолчанию для написания комментариев, например к коммитам.

Если у вас уже установлен Sublime/Notepad++/Atom/VSCode — можете выбрать из списка и продолжайте установку.

Выбор ветки по умолчанию

С 2020 сервисы Github и Gitlab начали двигаться в сторону изменения ветки по умолчанию с master на main. Так и появился этот пункт установки.

Выбор ветки по умолчанию

Выбранный первый пункт будет создавать новый репозиторий с основной веткой master, также выбрав второй пункт можно задать свое имя, например main. Название ветки не имеет влияния на функциональность репозитория, поэтому можно пойти по пути крупных сервисов и выбрать main для основной ветки.

Переменная окружения PATH

Настройка переменной окружения PATH:

Переменная окружения PATH

Установка предлагает выбрать, добавлять путь до гит в переменную окружения PATH, если выберем первый пункт, то переменная окружения PATH не будет изменена, а значит среды разработки не увидят установленный git в системе. Поэтому, как и рекомендует установка — выбираем второй пункт и путь до исполняемого файла будет добавлено в переменную PATH.

Библиотека для HTTPS

Выбор программы для работы с HTTPS соединений.

Библиотека для HTTPS

Выбираем первый пункт, а второй требуется только тогда, планируется использовать Active Directory — проверка сертификатов сервера библиотеками Windows.

Стратегия работы с переносами строк

Выбор переноса строк

Выбор переноса строк

Так как в Windows и Linux/macOs используется разные переносы строк. В Windows rn (CRLF), а в Linux/macOS это Unix стиль — n (LF).

Необходимо выбрать, какой подход использовать:

  • Checkout Windows-style, commit Unix-style line ending — при коммите все windows переносы будут преобразованы в Unix (CRLF→LF), и наоборот при получении новых коммитов или клонировании репозитория — обратное преобразование (LF→CRLF). Рекомендуется в большинстве случаев.

💡 При этом вы можете получать предупреждения, что переносы строки были преобразованы, это как раз информирование о замене переносов и нормально.

  • Checkout as-is, commit Unix-style line ending — при коммите CRLF будет конвертироваться в LF. При работе с файлами, переносы не будут изменены.

  • Checkout as-is, commit as-is — никакое работы с переносами гит проводить не будет, будет коммитить файлы без изменений. (Не рекомендуется, так как часть файлом может быть CRLF, а часть с LF переносами.)

Выбор стратегии git pull

Нам предлагают выбрать стратегию работы команды git pull, получение обновления с удаленного сервера. Первый пункт предлагаемый по умолчанию будет всегда пробовать обновить локальную историю коммитов без создания коммитов слияния в отсутствии конфликтов. Его и выбирайте.

Настройка хранилища учетных записей

Настройка хранилища учетных записей

  • Git Credential Manager Core — если мы планируем использовать https доступ к удаленным репозиториям, то есть вводить свои логин и пароль от своего аккаунта на гитлаб или гитхаб — выбираем первый пункт и тогда ввести логин и пароль потребуется только один раз, после учетные данные будут записаны в хранилище и использоваться при подключении автоматически. Используется хранилище учетных записей Windows.

  • Git Credential Manager — помечен как deprecated, то есть устаревший. Он оставлен для совместимости. Не выбирайте его.

  • None — не использует хранилище учетных записей вовсе, и придется вводить логин и пароль каждый раз когда будете соединяться с удаленным репозиторием. Выбирать следует только в том случае если планируете использовать только SSH ключ для аутентификации.

Рекомендуется выбрать первый пункт, он не ограничивает использование ssh ключей, и при этом будет сохранять ваши учетные записи при использовании https доступа.

Extra options

  • Enable file system caching — кэширование файлов

  • Enable symbolic links — использование символьных ссылок, когда потребуется — включите.

Экспериментальные возможности

Экспериментальные возможности

При первой установки не рекомендуется включать экспериментальные возможности, при необходимости можно их активировать позже.

Копирование файлов

Нажимаем Install и запустится процесс установки.

Копирование файлов

После установки снимем галочку с просмотра замечаний к версии git, и установим галочку Запустить Git Bash после установки, нажимаем Finish.

Последний пункт установки

Обязательная настройка после установки

Каждый коммит в git обязательно подписывается именем автора коммита и его электронным адресом. Для установки выполните две команды, в каждой из которых замените строки в кавычках на свои электронный адрес и имя.

git config --global user.email "you@example.com"
git config --global user.name "Your Name"

Пример использования команды:

git config --global user.email "konstantin@shibkov.ru"
git config --global user.name "Konstatin Shibkov"

Чтобы проверить какой пользователь или email уже записаны в git, выполните команду без параметра в кавычках, например:

git config --global user.email
git config --global user.name

Ввод данных пользователя git

Если вы ошиблись при вводе, вы можете заново ввести команду и значения будут заменены

Базовая настройка git на этом завершена.

Пользуйтесь git с удовольствием!

; Uncomment the line below to be able to compile the script from within the IDE. ;#define COMPILE_FROM_IDE ; vim: sw=4 expandtab: #include «config.iss« #if !defined(APP_VERSION) || !defined(BITNESS) || !defined(MINGW_BITNESS) #error «config.iss should define APP_VERSION, BITNESS and MINGW_BITNESS« #endif #define APP_NAME Git #ifdef COMPILE_FROM_IDE #undef APP_VERSION #define APP_VERSION Snapshot #endif #define APP_CONTACT_URL https://github.com/git-for-windows/git/wiki/Contact #define APP_URL https://gitforwindows.org/ #define APP_BUILTINS sharegitbuiltins.txt #define PLINK_PATH_ERROR_MSG Please enter a valid path to a Plink executable. #define DROP_HANDLER_GUID {{60254CA5-953B-11CF-8C96-00AA00B8708C} #ifndef DEFAULT_BRANCH_NAME #define DEFAULT_BRANCH_NAME master #endif #ifndef INSTALLER_FILENAME_SUFFIX #define INSTALLER_FILENAME_SUFFIX #endif [Setup] ; Compiler-related Compression=lzma2/ultra64 LZMAUseSeparateProcess=yes #ifdef OUTPUT_TO_TEMP OutputBaseFilename={#FILENAME_VERSION} OutputDir={#GetEnv(TEMP)} #else #if INSTALLER_FILENAME_SUFFIX!= OutputBaseFilename={#APP_NAME++FILENAME_VERSION++INSTALLER_FILENAME_SUFFIX} #else OutputBaseFilename={#APP_NAME++FILENAME_VERSION}-{#BITNESS}-bit #endif #ifdef OUTPUT_DIRECTORY OutputDir={#OUTPUT_DIRECTORY} #else OutputDir={#GetEnv(USERPROFILE)} #endif #endif SolidCompression=yes #ifndef SOURCE_DIR #define SOURCE_DIR SourcePath+…….. #endif SourceDir={#SOURCE_DIR} #if BITNESS==64 || INSTALLER_FILENAME_SUFFIX==arm64 ArchitecturesInstallIn64BitMode=x64 arm64 #endif #ifdef SIGNTOOL SignTool=signtool #endif #define FILE_VERSION GetFileVersion(SOURCE_DIR++MINGW_BITNESS+bingit.exe) ; Installer-related AllowNoIcons=yes AppName={#APP_NAME} AppPublisher=The Git Development Community AppPublisherURL={#APP_URL} AppSupportURL={#APP_CONTACT_URL} AppVersion={#APP_VERSION} ChangesAssociations=yes ChangesEnvironment=yes CloseApplications=no DefaultDirName={pf}{#APP_NAME} DisableDirPage=auto DefaultGroupName={#APP_NAME} DisableProgramGroupPage=auto DisableReadyPage=yes InfoBeforeFile={#SourcePath}..gpl-2.0.rtf #ifdef OUTPUT_TO_TEMP PrivilegesRequired=lowest #else PrivilegesRequired=none #endif UninstallDisplayName={#APP_NAME} UninstallDisplayIcon={app}{#MINGW_BITNESS}sharegitgit-for-windows.ico #ifndef COMPILE_FROM_IDE VersionInfoVersion={#FILE_VERSION} #endif ; Cosmetic SetupIconFile={#SourcePath}..git.ico WizardImageBackColor=clWhite WizardImageStretch=no WizardImageFile={#SourcePath}git.bmp WizardSmallImageFile={#SourcePath}gitsmall.bmp [Types] ; Define a custom type to avoid getting the three default types. Name: default; Description: Default installation; Flags: iscustom [Components] Name: icons; Description: Additional icons Name: iconsquicklaunch; Description: In the Quick Launch; Check: not IsAdminLoggedOn Name: iconsdesktop; Description: On the Desktop Name: ext; Description: Windows Explorer integration; Types: default Name: extshellhere; Description: Git Bash Here; Types: default Name: extguihere; Description: Git GUI Here; Types: default Name: gitlfs; Description: Git LFS (Large File Support); Types: default; Flags: disablenouninstallwarning Name: assoc; Description: Associate .git* configuration files with the default text editor; Types: default Name: assoc_sh; Description: Associate .sh files to be run with Bash; Types: default Name: consolefont; Description: Use a TrueType font in all console windows; OnlyBelowVersion: 10.0 Name: autoupdate; Description: Check daily for Git for Windows updates Name: windowsterminal; Description: «(NEW!) Add a Git Bash Profile to Windows Terminal«; MinVersion: 10.0.18362 #ifdef WITH_SCALAR Name: scalar; Description: «(NEW!) Scalar (Git add-on to manage large-scale repositories)«; Types: default #endif [Run] Filename: {app}git-bash.exe; Parameters: —cd-to-home; Description: Launch Git Bash; Flags: nowait postinstall skipifsilent runasoriginaluser unchecked Filename: {app}ReleaseNotes.html; Description: View Release Notes; Flags: shellexec skipifdoesntexist postinstall skipifsilent [Files] ; Install files that might be in use during setup under a different name. #include «file-list.iss« Source: {#SourcePath}ReleaseNotes.html; DestDir: {app}; Flags: replacesameversion; AfterInstall: DeleteFromVirtualStore Source: {#SourcePath}..LICENSE.txt; DestDir: {app}; Flags: replacesameversion; AfterInstall: DeleteFromVirtualStore Source: {#SourcePath}NOTICE.txt; DestDir: {app}; Flags: replacesameversion; AfterInstall: DeleteFromVirtualStore; Check: ParamIsSet(VSNOTICE) #ifdef INCLUDE_EDIT_GIT_BASH Source: {#SourcePath}..edit-git-bash.exe; Flags: dontcopy #endif [Dirs] Name: «{app}dev« Name: «{app}devmqueue« Name: «{app}devshm« Name: «{app}tmp« Name: «{commonappdata}MicrosoftWindows TerminalFragmentsGit«; Components: windowsterminal; Check: IsAdminLoggedOn Name: «{localappdata}MicrosoftWindows TerminalFragmentsGit«; Components: windowsterminal; Check: not IsAdminLoggedOn [Icons] Name: {group}Git GUI; Filename: {app}cmdgit-gui.exe; Parameters: ««; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}{#MINGW_BITNESS}sharegitgit-for-windows.ico Name: {group}Git Bash; Filename: {app}git-bash.exe; Parameters: «—cd-to-home«; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}{#MINGW_BITNESS}sharegitgit-for-windows.ico Name: {group}Git CMD; Filename: {app}git-cmd.exe; Parameters: «—cd-to-home«; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}{#MINGW_BITNESS}sharegitgit-for-windows.ico Name: {group}Git Release Notes; Filename: {app}ReleaseNotes.html; Parameters: ««; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}{#MINGW_BITNESS}sharegitgit-for-windows.ico Name: {group}Git FAQs (Frequently Asked Questions); Filename: https://github.com/git-for-windows/git/wiki/FAQ; IconFilename: {app}{#MINGW_BITNESS}sharegitgit-for-windows.ico [Messages] BeveledLabel={#APP_URL} #ifdef WINDOW_TITLE_VERSION SetupAppTitle={#APP_NAME} {#WINDOW_TITLE_VERSION} Setup SetupWindowTitle={#APP_NAME} {#WINDOW_TITLE_VERSION} Setup #else SetupAppTitle={#APP_NAME} {#APP_VERSION} Setup SetupWindowTitle={#APP_NAME} {#APP_VERSION} Setup #endif [Registry] ; Aides installing third-party (credential, remote, etc) helpers Root: HKLM; Subkey: SoftwareGitForWindows; ValueType: string; ValueName: CurrentVersion; ValueData: {#APP_VERSION}; Flags: uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn Root: HKLM; Subkey: SoftwareGitForWindows; ValueType: string; ValueName: InstallPath; ValueData: {app}; Flags: uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn Root: HKLM; Subkey: SoftwareGitForWindows; ValueType: string; ValueName: LibexecPath; ValueData: {app}{#MINGW_BITNESS}libexecgit-core; Flags: uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn Root: HKCU; Subkey: SoftwareGitForWindows; ValueType: string; ValueName: CurrentVersion; ValueData: {#APP_VERSION}; Flags: uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn Root: HKCU; Subkey: SoftwareGitForWindows; ValueType: string; ValueName: InstallPath; ValueData: {app}; Flags: uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn Root: HKCU; Subkey: SoftwareGitForWindows; ValueType: string; ValueName: LibexecPath; ValueData: {app}{#MINGW_BITNESS}libexecgit-core; Flags: uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn ; There is no «Console» key in HKLM. Root: HKCU; Subkey: Console; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: uninsclearvalue; Components: consolefont Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Components: consolefont Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Components: consolefont Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Components: consolefont Root: HKCU; Subkey: ConsoleGit Bash; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty Root: HKCU; Subkey: ConsoleGit Bash; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty Root: HKCU; Subkey: ConsoleGit Bash; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty Root: HKCU; Subkey: ConsoleGit Bash; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty Root: HKCU; Subkey: ConsoleGit CMD; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty Root: HKCU; Subkey: ConsoleGit CMD; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty Root: HKCU; Subkey: ConsoleGit CMD; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty Root: HKCU; Subkey: ConsoleGit CMD; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty ; Note that we write the Registry values below either to HKLM or to HKCU depending on whether the user running the installer ; is a member of the local Administrators group or not (see the «Check» argument). ; File associations for configuration files that may be contained in a repository (so this does not include «.gitconfig»). Root: HKLM; Subkey: SoftwareClasses.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc Root: HKLM; Subkey: SoftwareClasses.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc Root: HKLM; Subkey: SoftwareClasses.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc Root: HKCU; Subkey: SoftwareClasses.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc Root: HKCU; Subkey: SoftwareClasses.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc Root: HKCU; Subkey: SoftwareClasses.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc Root: HKLM; Subkey: SoftwareClasses.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc Root: HKLM; Subkey: SoftwareClasses.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc Root: HKLM; Subkey: SoftwareClasses.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc Root: HKCU; Subkey: SoftwareClasses.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc Root: HKCU; Subkey: SoftwareClasses.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc Root: HKCU; Subkey: SoftwareClasses.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc Root: HKLM; Subkey: SoftwareClasses.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc Root: HKLM; Subkey: SoftwareClasses.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc Root: HKLM; Subkey: SoftwareClasses.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc Root: HKCU; Subkey: SoftwareClasses.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc Root: HKCU; Subkey: SoftwareClasses.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc Root: HKCU; Subkey: SoftwareClasses.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc ; Associate .sh extension with sh.exe so those files are double-clickable, ; startable from cmd.exe, and when files are dropped on them they are passed ; as arguments to the script. ; Install under HKEY_LOCAL_MACHINE if an administrator is installing. Root: HKLM; Subkey: SoftwareClasses.sh; ValueType: string; ValueData: sh_auto_file; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh Root: HKLM; Subkey: SoftwareClassessh_auto_file; ValueType: string; ValueData: «Shell Script«; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh Root: HKLM; Subkey: SoftwareClassessh_auto_fileshellopencommand; ValueType: string; ValueData: «««{app}git-bash.exe«« —no-cd ««%L«« %*«; Flags: uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh Root: HKLM; Subkey: SoftwareClassessh_auto_fileDefaultIcon; ValueType: string; ValueData: «%SystemRoot%System32shell32.dll,-153«; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh Root: HKLM; Subkey: SoftwareClassessh_auto_fileShellExDropHandler; ValueType: string; ValueData: {#DROP_HANDLER_GUID}; Flags: uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh ; Install under HKEY_CURRENT_USER if a non-administrator is installing. Root: HKCU; Subkey: SoftwareClasses.sh; ValueType: string; ValueData: sh_auto_file; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh Root: HKCU; Subkey: SoftwareClassessh_auto_file; ValueType: string; ValueData: «Shell Script«; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh Root: HKCU; Subkey: SoftwareClassessh_auto_fileshellopencommand; ValueType: string; ValueData: «««{app}git-bash.exe«« —no-cd ««%L«« %*«; Flags: uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh Root: HKCU; Subkey: SoftwareClassessh_auto_fileDefaultIcon; ValueType: string; ValueData: «%SystemRoot%System32shell32.dll,-153«; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh Root: HKCU; Subkey: SoftwareClassessh_auto_fileShellExDropHandler; ValueType: string; ValueData: {#DROP_HANDLER_GUID}; Flags: uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh [UninstallDelete] ; Delete the built-ins. Type: files; Name: {app}{#MINGW_BITNESS}bingit-*.exe Type: files; Name: {app}{#MINGW_BITNESS}libexecgit-coregit-*.exe Type: files; Name: {app}{#MINGW_BITNESS}libexecgit-coregit.exe ; Delete git-lfs wrapper Type: files; Name: {app}cmdgit-lfs.exe ; Delete copied *.dll files Type: files; Name: {app}{#MINGW_BITNESS}libexecgit-core*.dll ; Delete the dynamical generated MSYS2 files Type: files; Name: {app}etchosts Type: files; Name: {app}etcmtab Type: files; Name: {app}etcnetworks Type: files; Name: {app}etcprotocols Type: files; Name: {app}etcservices Type: files; Name: {app}devfd Type: files; Name: {app}devstderr Type: files; Name: {app}devstdin Type: files; Name: {app}devstdout Type: dirifempty; Name: {app}devmqueue Type: dirifempty; Name: {app}devshm Type: dirifempty; Name: {app}dev ; Delete any manually created shortcuts. Type: files; Name: {userappdata}MicrosoftInternet ExplorerQuick LaunchGit Bash.lnk Type: files; Name: {code:GetShellFolder|desktop}Git Bash.lnk Type: files; Name: {app}Git Bash.lnk ; Delete a home directory inside the Git for Windows directory. Type: dirifempty; Name: {app}home{username} Type: dirifempty; Name: {app}home #if BITNESS==32 ; Delete the files required for rebaseall Type: files; Name: {app}binmsys-2.0.dll Type: files; Name: {app}binrebase.exe Type: dirifempty; Name: {app}bin Type: files; Name: {app}etcrebase.db.i386 Type: dirifempty; Name: {app}etc #endif ; Delete recorded install options Type: files; Name: {app}etcinstall-options.txt Type: dirifempty; Name: {app}etc Type: dirifempty; Name: {app}{#MINGW_BITNESS}libexecgit-core Type: dirifempty; Name: {app}{#MINGW_BITNESS}libexec Type: dirifempty; Name: {app}{#MINGW_BITNESS} Type: dirifempty; Name: {app} ; Delete Git Bash options Type: files; Name: {app}etcgit-bash.config ; Delete Windows Terminal profile fragments Type: files; Name: {commonappdata}MicrosoftWindows TerminalFragmentsGitgit-bash.json Type: files; Name: {localappdata}MicrosoftWindows TerminalFragmentsGitgit-bash.json [Code] #include «helpers.inc.iss« #include «environment.inc.iss« #include «putty.inc.iss« #include «modules.inc.iss« #include «exec-with-capture.inc.iss« function ParamIsSet(Key:String):Boolean; begin Result:=CompareStr(0,ExpandConstant({param:+Key+|0}))<>0; end; function CreateHardLink(lpFileName,lpExistingFileName:String;lpSecurityAttributes:Integer):Boolean; #ifdef UNICODE external CreateHardLinkW@Kernel32.dll stdcall delayload setuponly; #else external CreateHardLinkA@Kernel32.dll stdcall delayload setuponly; #endif function OverrideGitBashCommandLine(GitBashPath:String;CommandLine:String):Integer; var Msg:String; begin #ifdef INCLUDE_EDIT_GIT_BASH if not FileExists(ExpandConstant({tmp}edit-git-bash.exe)) then ExtractTemporaryFile(edit-git-bash.exe); #endif StringChangeEx(GitBashPath,«,«,True); StringChangeEx(CommandLine,«,«,True); CommandLine:=«+GitBashPath+» «+CommandLine+«; #ifdef INCLUDE_EDIT_GIT_BASH Exec(ExpandConstant({tmp}edit-git-bash.exe),CommandLine,,SW_HIDE,ewWaitUntilTerminated,Result); #else Exec(ExpandConstant({app}{#MINGW_BITNESS}sharegitedit-git-bash.exe),CommandLine,,SW_HIDE,ewWaitUntilTerminated,Result); #endif if Result<>0 then begin if Result=1 then begin Msg:=Unable to edit +GitBashPath+ (out of memory).; end else if Result=2 then begin Msg:=Unable to open +GitBashPath+ for editing.; end else if Result=3 then begin Msg:=Unable to edit the command-line of +GitBashPath+.; end else if Result=4 then begin Msg:=Unable to close +GitBashPath+ after editing.; end; LogError(Msg); end; end; const // Git Editor options. GE_Nano = 0; GE_VIM = 1; GE_NotepadPlusPlus = 2; GE_VisualStudioCode = 3; GE_VisualStudioCodeInsiders = 4; GE_SublimeText = 5; GE_Atom = 6; GE_VSCodium = 7; GE_Notepad = 8; GE_Wordpad = 9; GE_CustomEditor = 10; // Git Path options. GP_BashOnly = 1; GP_Cmd = 2; GP_CmdTools = 3; // Default Branch options. DB_Unspecified = 1; DB_Manual = 2; // Git SSH options. GS_OpenSSH = 1; GS_Plink = 2; GS_ExternalOpenSSH = 3; // Git HTTPS (cURL) options. GC_OpenSSL = 1; GC_WinSSL = 2; // Git line ending conversion options. GC_LFOnly = 1; GC_CRLFAlways = 2; GC_CRLFCommitAsIs = 3; // Git Bash terminal settings. GB_MinTTY = 1; GB_ConHost = 2; // `git pull` behavior settings. GP_GitPullMerge = 1; GP_GitPullRebase = 2; GP_GitPullFFOnly = 3; // Git Credential Manager settings. GCM_None = 1; GCM = 2; // Extra options GP_FSCache = 1; GP_Symlinks = 2; #ifdef WITH_EXPERIMENTAL_BUILTIN_DIFFTOOL #define HAVE_EXPERIMENTAL_OPTIONS 1 #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_REBASE #define HAVE_EXPERIMENTAL_OPTIONS 1 #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_STASH #define HAVE_EXPERIMENTAL_OPTIONS 1 #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_ADD_I #define HAVE_EXPERIMENTAL_OPTIONS 1 #endif #ifdef WITH_EXPERIMENTAL_PCON #define HAVE_EXPERIMENTAL_OPTIONS 1 #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_FSMONITOR #define HAVE_EXPERIMENTAL_OPTIONS 1 #endif #ifdef HAVE_EXPERIMENTAL_OPTIONS // Experimental options GP_BuiltinDifftool = 1; GP_BuiltinRebase = 2; GP_BuiltinStash = 3; GP_BuiltinAddI = 4; GP_EnablePCon = 5; GP_EnableFSMonitor = 6; #endif var AppDir,UninstallAppPath,UninstallString:String; InferredDefaultKeys,InferredDefaultValues:TStringList; // The options chosen at install time, to be written to /etc/install-options.txt ChosenOptions:String; // Accumulated set of custom pages that have options, and those that have ‘new’ parameters on them CurrentCustomPageID,FirstCustomPageID:Integer; AllCustomPages,CustomPagesWithUnseenOptions:String; HasUnseenComponents:Boolean; // Previous Git for Windows version (if upgrading) PreviousGitForWindowsVersion:String; // Wizard page and variables for the Editor options. EditorPage:TInputFileWizardPage; CbbEditor:TNewComboBox; LblEditor:array[GE_Nano..GE_CustomEditor] of array of TLabel; EditorAvailable:array[GE_Nano..GE_CustomEditor] of Boolean; SelectedEditor:Integer; VisualStudioCodeUserInstallation:Boolean; VisualStudioCodeInsidersUserInstallation:Boolean; SublimeTextUserInstallation:Boolean; VSCodiumUserInstallation:Boolean; NotepadPlusPlusPath:String; VisualStudioCodePath:String; VisualStudioCodeInsidersPath:String; SublimeTextPath:String; AtomPath:String; VSCodiumPath:String; CustomEditorPath:String; CustomEditorOptions:String; // Wizard page and variables for the Default Branch options. DefaultBranchPage:TWizardPage; RdbDefaultBranch:array[DB_Unspecified..DB_Manual] of TRadioButton; EdtDefaultBranch:TEdit; // Wizard page and variables for the Path options. PathPage:TWizardPage; RdbPath:array[GP_BashOnly..GP_CmdTools] of TRadioButton; // Wizard page and variables for the SSH options. SSHChoicePage:TWizardPage; RdbSSH:array[GS_OpenSSH..GS_ExternalOpenSSH] of TRadioButton; EdtPlink:TEdit; TortoisePlink:TCheckBox; // Wizard page and variables for the HTTPS implementation (cURL) settings. CurlVariantPage:TWizardPage; RdbCurlVariant:array[GC_OpenSSL..GC_WinSSL] of TRadioButton; // Wizard page and variables for the line ending conversion options. CRLFPage:TWizardPage; RdbCRLF:array[GC_LFOnly..GC_CRLFCommitAsIs] of TRadioButton; // Wizard page and variables for the terminal emulator settings. BashTerminalPage:TWizardPage; RdbBashTerminal:array[GB_MinTTY..GB_ConHost] of TRadioButton; // Wizard page and variables for the `git pull` options. GitPullBehaviorPage:TWizardPage; RdbGitPullBehavior:array[GP_GitPullMerge..GP_GitPullFFOnly] of TRadioButton; // Wizard page and variables for the credential manager options. GitCredentialManagerPage:TWizardPage; RdbGitCredentialManager:array[GCM_None..GCM] of TRadioButton; // Wizard page and variables for the extra options. ExtraOptionsPage:TWizardPage; RdbExtraOptions:array[GP_FSCache..GP_Symlinks] of TCheckBox; #ifdef HAVE_EXPERIMENTAL_OPTIONS // Wizard page and variables for the experimental options. ExperimentalOptionsPage:TWizardPage; RdbExperimentalOptions:array[GP_BuiltinDifftool..GP_EnableFSMonitor] of TCheckBox; #endif // Mapping controls to hyperlinks HyperlinkSource:array of TObject; HyperlinkTarget:array of String; HyperlinkCount:Integer; // Wizard page and variables for the processes page. SessionHandle:DWORD; Processes:ProcessList; ProcessesPage:TWizardPage; ProcessesListBox:TListBox; ProcessesRefresh,ContinueButton,TestCustomEditorButton:TButton; OnlyShowNewOptions:TCheckBox; PageIDBeforeInstall:Integer; #ifdef DEBUG_WIZARD_PAGE DebugWizardPage:Integer; #endif { Specific helper functions } procedure BrowseForPuTTYFolder(Sender:TObject); var Name:String; begin if GetOpenFileName( Please select a Plink executable , Name , ExtractFilePath(EdtPlink.Text) , Executable Files|*.exe , exe ) then begin if IsPlinkExecutable(Name) then begin EdtPlink.Text:=Name; RdbSSH[GS_Plink].Checked:=True; end else begin // This message box only gets triggered on interactive use, so it // does not need to be suppressible for silent installations. MsgBox({#PLINK_PATH_ERROR_MSG},mbError,MB_OK); end; end; end; procedure DeleteContextMenuEntries; var Command:String; RootKey,i:Integer; Keys:TArrayOfString; begin if IsAdminLoggedOn then begin RootKey:=HKEY_LOCAL_MACHINE; end else begin RootKey:=HKEY_CURRENT_USER; end; SetArrayLength(Keys,4); Keys[0]:=SOFTWAREClassesDirectoryshellgit_shell; Keys[1]:=SOFTWAREClassesDirectoryBackgroundshellgit_shell; Keys[2]:=SOFTWAREClassesDirectoryshellgit_gui; Keys[3]:=SOFTWAREClassesDirectoryBackgroundshellgit_gui; for i:=0 to Length(Keys)-1 do begin Command:=; RegQueryStringValue(RootKey,Keys[i]+command,,Command); if Pos(AppDir,Command)>0 then begin if not RegDeleteKeyIncludingSubkeys(RootKey,Keys[i]) then begin LogError(Line {#__LINE__}: Unable to remove «Git Bash / GUI Here» shell extension.); end; end; end; end; var BuiltinFSMonitorStopOption,AlreadyHandledFSMonitorPaths:String; // Returns true if at least one FSMonitor daemon was shut down successfully function ShutdownFSMonitorDaemons():Boolean; var FindRec:TFindRec; ExitCode:DWORD; Path,Str:String; Len,i:Integer; begin Result:=False; #ifdef WITH_EXPERIMENTAL_BUILTIN_FSMONITOR if (BuiltinFSMonitorStopOption=(huh?)) then Exit; if not FindFirst(\.pipe*,FindRec) then Exit; if (AlreadyHandledFSMonitorPaths=) then AlreadyHandledFSMonitorPaths:=#0; repeat if WildcardMatch(FindRec.Name,*fsmonitor—daemon.ipc) or WildcardMatch(FindRec.Name,*.gitfsmonitor) then begin if (Pos(#0+FindRec.Name+#0,AlreadyHandledFSMonitorPaths)>0) then Continue; AlreadyHandledFSMonitorPaths:=AlreadyHandledFSMonitorPaths+FindRec.Name+#0; // An earlier `fsmonitor—daemon` iteration called it `—stop`, not `stop`; // Find out which form to use. if (BuiltinFSMonitorStopOption=) then begin BuiltinFSMonitorStopOption:=(huh?); if not ExecWithCapture(«+AppDir+cmdgit.exe» fsmonitor—daemon -h,Str,Str,ExitCode) or (ExitCode<>129) then begin if (i<>1) and (i<>127) then // Suppress message if `git.exe` was not found, or if it does not know about the built-in FSMonitor LogError(Could not get FSMonitor help (exit code +IntToStr(ExitCode)+):+#13+Str); Exit; end else begin i:=Pos(stop+#10,Str); if (i=0) then begin LogError(Could not determine stop option from:+#13+Str); Exit; end; if (i>2) and (Str[i-1]=) and (Str[i-2]=) then BuiltinFSMonitorStopOption:=—stop else BuiltinFSMonitorStopOption:=stop; end; Str:=; end; // The colon was replaced with an underscore by the FSMonitor daemon Len:=Length(FindRec.Name); if WildcardMatch(FindRec.Name,*fsmonitor—daemon.ipc) then Len:=Len-22 else Len:=Len-10; Path:=Copy(FindRec.Name,1,Len); if (Length(Path)>2) and (Path[2]=_) then Path[2]:=:; // Now we have the gitdir, but we need to get to the worktree if FileExists(Path+gitdir) then begin Path:=ReadFileAsString(Path+gitdir); StringChangeEx(Path,/,,True); end; if WildcardMatch(Path,*.git) then Path:=Copy(Path,1,Length(Path)-5); if ExecSilently(«+AppDir+cmdgit.exe» -C «+Path+» fsmonitor—daemon +BuiltinFSMonitorStopOption,fsmonitor-stop,Could not stop FSMonitor daemon in +Path) then Result:=True; end; until not FindNext(FindRec); #endif end; procedure RefreshProcessList(Sender:TObject); var Version:TWindowsVersion; Modules:TArrayOfString; ProcsCloseRequired,ProcsCloseOptional:ProcessList; i:Longint; Caption:String; ManualClosingRequired:Boolean; begin if (AppDir=) then begin SetArrayLength(Processes,0); Exit; end; // Use the Restart Manager API when installing the shell extension. AppendToArray(Modules,AppDir+usrbinmsys-2.0.dll); AppendToArray(Modules,AppDir+{#MINGW_BITNESS}bintcl85.dll); AppendToArray(Modules,AppDir+{#MINGW_BITNESS}bintk85.dll); AppendToArray(Modules,AppDir+{#MINGW_BITNESS}bintcl86.dll); AppendToArray(Modules,AppDir+{#MINGW_BITNESS}bintk86.dll); AppendToArray(Modules,AppDir+{#MINGW_BITNESS}binzlib1.dll); AppendToArray(Modules,AppDir+{#MINGW_BITNESS}libexecgit-corezlib1.dll); SessionHandle:=FindProcessesUsingModules(Modules,Processes); if (GetArrayLength(Processes)>0) and ShutdownFSMonitorDaemons() then begin // We potentially shut down at least one process, refresh again RmEndSession(SessionHandle); SessionHandle:=FindProcessesUsingModules(Modules,Processes); end; ManualClosingRequired:=False; ProcessesListBox.Items.Clear; if (Sender=NIL) or (SessionHandle>0) then begin for i:=0 to GetArrayLength(Processes)-1 do begin Caption:=Processes[i].Name+ (PID +IntToStr(Processes[i].ID); if Processes[i].Restartable then begin Caption:=Caption+, closing is optional; end else if Processes[i].ToTerminate then begin Caption:=Caption+, will be terminated; end else begin Caption:=Caption+, closing is required; ManualClosingRequired:=True; end; Caption:=Caption+); ProcessesListBox.Items.Append(Caption); end; end; if ContinueButton<>NIL then begin ContinueButton.Enabled:=not ManualClosingRequired; end; end; procedure SetAndMarkEnvString(Name,Value:String;Expandable:Boolean); var Env:TArrayOfString; FileName:String; begin SetArrayLength(Env,1); Env[0]:=Value; // Try to set the variable as specified by the user. if not SetEnvStrings(Name,Env,Expandable,IsAdminLoggedOn,True) then LogError(Line {#__LINE__}: Unable to set the +Name+ environment variable.) else begin // Mark that we have changed the variable by writing its value to a file. FileName:=ExpandConstant({app})+setup.ini; if not SetIniString(Environment,Name,Value,FileName) then LogError(Line {#__LINE__}: Unable to write to file «+FileName+«.); end; end; procedure DeleteMarkedEnvString(Name:String); var Env:TArrayOfString; FileName:String; begin Env:=GetEnvStrings(Name,IsAdminLoggedOn); FileName:=ExpandConstant({app})+setup.ini; if (GetArrayLength(Env)=1) and (CompareStr(RemoveQuotes(Env[0]),GetIniString(Environment,Name,,FileName))=0) then begin if not SetEnvStrings(Name,[],False,IsAdminLoggedOn,True) then LogError(Line {#__LINE__}: Unable to delete the +Name+ environment variable.); end; end; function ShellQuote(Value:String):String; begin // Sadly, we cannot use the »’ trick used throughout Git’s // source code, as InnoSetup quotes those in a way that // git.exe does not understand them. // // So we try to imitate quote_arg_msvc() in Git’s // compat/mingw.c instead: => \, followed by » => «, // then surround with double quotes. StringChangeEx(Value,#92,#92+#92,True); StringChangeEx(Value,#34,#92+#34,True); Result:=#34+Value+#34; end; function GitSystemConfigSet(Key,Value:String):Boolean; var ExitCode:DWORD; StdOut,StdErr:String; begin if (Value=#0) then begin if ExecWithCapture(«+AppDir+{#MINGW_BITNESS}bingit.exe» config —system —unset-all +Key,StdOut,StdErr,ExitCode) And ((ExitCode=0) Or (ExitCode=5)) then // exit code 5 means it was already unset, so that’s okay Result:=True else begin LogError(Unable to unset system config «+Key+«: exit code +IntToStr(ExitCode)+#13+#10+StdOut+#13+#10+stderr:+#13+#10+StdErr); Result:=False end end else if ExecWithCapture(«+AppDir+{#MINGW_BITNESS}bingit.exe» config —system —replace-all +ShellQuote(Key)+ +ShellQuote(Value),StdOut,StdErr,ExitCode) And (ExitCode=0) then Result:=True else begin LogError(Unable to set system config «+Key+«:=»+Value+«: exit code +IntToStr(ExitCode)+#13+#10+StdOut+#13+#10+stderr:+#13+#10+StdErr); Result:=False; end; end; procedure RecordInferredDefault(Key,Value:String); var i:Integer; begin i:=InferredDefaultKeys.IndexOf(Key); // cannot use .Find because the list is not sorted if (i>=0) then InferredDefaultValues[i]:=Value else begin i:=InferredDefaultKeys.Add(Key); InferredDefaultValues.Add(Value) end; end; function EndsWith(S:String;T:String):Boolean; begin if (Length(S)>Length(T)) then Delete(S,1,Length(S)-Length(T)); Result:=(CompareText(S,T)=0) end; function GetDefaultsFromGitConfig(WhichOne:String):Boolean; var ExtraOptions,StdOut,StdErr,Key,Value:String; ExitCode:DWORD; Values:TArrayOfString; c,i,j,k:Integer; begin if AppDir= then begin // No previous installation detected, therefore we cannot execute `git config` Result:=True; Exit; end; case WhichOne of ProgramData: ExtraOptions:=-f «+ExpandConstant({commonappdata}Gitconfig)+«; system: ExtraOptions:=—system; else begin LogError(Invalid config type: +WhichOne); Result:=False; Exit end end; if not ExecWithCapture(«+AppDir+{#MINGW_BITNESS}bingit.exe» config -l -z +ExtraOptions,StdOut,StdErr,ExitCode) then begin if FileExists(AppDir+{#MINGW_BITNESS}bingit.exe) then LogError(Unable to get system config (exit code +IntToStr(ExitCode)+):+#13+#10+StdErr); end; // Split NUL-delimited key/value pairs, extract LF that denotes end of key Value:=StdOut; i:=1; j:=i; k:=i; while (j<=Length(StdOut)) do begin c:=Ord(StdOut[j]); if (c=10) then k:=j else if (c=0) then begin if (i<>k) then begin // Ignore keys without values Key:=Copy(StdOut,i,k-i); Value:=Copy(StdOut,k+1,j-k-1); case Key of http.sslbackend: case Value of schannel: RecordInferredDefault(CURL Option,WinSSL); openssl: RecordInferredDefault(CURL Option,OpenSSL); end; core.autocrlf: case Value of true: RecordInferredDefault(CRLF Option,CRLFAlways); false: RecordInferredDefault(CRLF Option,CRLFCommitAsIs); input: RecordInferredDefault(CRLF Option,LFOnly); end; core.fscache: case Value of true: RecordInferredDefault(Performance Tweaks FSCache,Enabled); false: RecordInferredDefault(Performance Tweaks FSCache,Disabled); end; credential.helper: case Value of manager: RecordInferredDefault(Use Credential Manager,Enabled); manager-core: RecordInferredDefault(Use Credential Manager,Enabled); else begin if EndsWith(Value,manager-core) or EndsWith(Value,manager) then RecordInferredDefault(Use Credential Manager,Enabled) else RecordInferredDefault(Use Credential Manager,Disabled); end; end; core.symlinks: case Value of true: RecordInferredDefault(Enable Symlinks,Enabled); false: RecordInferredDefault(Enable Symlinks,Disabled); end; pull.ff: case Value of only: RecordInferredDefault(Git Pull Behavior Option,FFOnly); end; pull.rebase: case Value of true: RecordInferredDefault(Git Pull Behavior Option,Rebase); false: RecordInferredDefault(Git Pull Behavior Option,Merge); end; end; end; i:=j+1; j:=i; k:=i; end; j:=j+1; end; Result:=True; end; { Setup event functions } function NextNumber(Str:String;var Pos:Integer):Integer; var From:Integer; begin From:=Pos; while (Pos<=Length(Str)) and (Str[Pos]>=#48) and (Str[Pos]<=#57) do Pos:=Pos+1; if Pos>From then Result:=StrToInt(Copy(Str,From,Pos-From)) else Result:=-1; end; function VersionCompare(CurrentVersion,PreviousVersion:String):Integer; var i,j,Current,Previous:Integer; begin Result:=0; i:=1; j:=1; while True do begin if j>Length(PreviousVersion) then begin Result:=+1; Exit; end; if i>Length(CurrentVersion) then begin Result:=-1; Exit; end; Previous:=NextNumber(PreviousVersion,j); Current:=NextNumber(CurrentVersion,i); if Previous<0 then begin if Current>=0 then Result:=+1; Result:=Ord(CurrentVersion[i])-Ord(PreviousVersion[j]); if (Result=0) then begin // skip identical non-numerical characters i:=i+1; j:=j+1; Continue; end; Exit; end; if Current<0 then begin Result:=-1; Exit; end; if Current>Previous then begin Result:=+1; Exit; end; if Current<Previous then begin Result:=-1; Exit; end; if j>Length(PreviousVersion) then begin if i<=Length(CurrentVersion) then Result:=+1; Exit; end; if i>Length(CurrentVersion) then begin Result:=-1; Exit; end; if CurrentVersion[i]<>PreviousVersion[j] then begin if PreviousVersion[j]=. then Result:=-1 else Result:=+1; Exit; end; if CurrentVersion[i]<>. then Exit; i:=i+1; j:=j+1; end; end; procedure ExitProcess(uExitCode:Integer); external ExitProcess@kernel32.dll stdcall; procedure ExitEarlyWithSuccess(); begin DelTree(ExpandConstant({tmp}),True,True,True); ExitProcess(0); end; function CountDots(S:String):Integer; var i:Integer; begin Result:=0; for i:=1 to Length(S) do if (S[i]=#46) then Result:=Result+1; end; var PreviousGitVersion:String; PreviousGitVersionInitialized:Boolean; function GetPreviousGitVersion():String; var Path,StdOut,StdErr:String; ExitCode:DWORD; begin if not PreviousGitVersionInitialized then begin PreviousGitVersionInitialized:=True; if (RegQueryStringValue(HKEY_LOCAL_MACHINE,SoftwareGitForWindows,InstallPath,Path)) and (ExecWithCapture(«+Path+cmdgit.exe» version,StdOut,StdErr,ExitCode)) and (ExitCode=0) then begin PreviousGitVersion:=Trim(StdOut); end; end; Result:=PreviousGitVersion; end; function IsDowngrade(CurrentVersion:String):Boolean; begin Result:=(VersionCompare(CurrentVersion,PreviousGitForWindowsVersion)<0); #ifdef GIT_VERSION if Result or (CountDots(CurrentVersion)>3) or (CountDots(PreviousGitForWindowsVersion)>3) then begin // maybe the previous version was a prerelease (prereleases have five numbers: v2.24.0-rc1.windows.1 reduces to ‘2.24.0.1.1’)? if CurrentVersion=ExpandConstant({#APP_VERSION}) then CurrentVersion:={#GIT_VERSION} else CurrentVersion:=git version +CurrentVersion+.windows.1; Result:=(VersionCompare(CurrentVersion,GetPreviousGitVersion())<0); end; #endif end; function IsUpgrade(CurrentVersion:String):Boolean; var PreviousVersion:String; begin PreviousVersion:=PreviousGitForWindowsVersion; // It is not an upgrade: // — if there was no previous version // — or if the previous version is identical to the current one (re-install) // — or if it is actually a downgrade Result:=(PreviousVersion<>) and (CurrentVersion<>PreviousVersion) and not IsDowngrade(CurrentVersion) end; { Represent a set as a string of comma-separated values } function IsInSet(var ASet:String;Value:Integer):Boolean; begin Result:=(Pos(,+IntToStr(Value)+,,ASet)>0); end; procedure AddToSet(var ASet:String;Value:Integer); begin if ASet= then ASet:=,; if not IsInSet(ASet,Value) then ASet:=ASet+IntToStr(Value)+,; end; function IsLastPageBeforeInstall(PageID:Integer):Boolean; begin if (OnlyShowNewOptions.Checked) then begin // The «Select Program Group» page is suppressed when // re-installing/upgrading/downgrading, but not the Components page. if (PageID<wpSelectComponents) and HasUnseenComponents then PageID:=wpSelectComponents else if (PageID<=wpSelectProgramGroup) then PageID:=FirstCustomPageID else PageID:=PageID+1; while (PageID<=PageIDBeforeInstall) and not IsInSet(CustomPagesWithUnseenOptions,PageID) do PageID:=PageID+1; Result:=(PageID>PageIDBeforeInstall); end else Result:=(PageID=PageIDBeforeInstall); end; procedure AdjustNextButtonLabel(Sender:TObject); begin if (CurrentCustomPageID=ProcessesPage.ID) then WizardForm.NextButton.Caption:=SetupMessage(msgButtonInstall) else if (CurrentCustomPageID=wpFinished) then // Always let the final page’s button read «Finish» WizardForm.NextButton.Caption:=SetupMessage(msgButtonFinish) else if IsLastPageBeforeInstall(CurrentCustomPageID) then begin RefreshProcessList(NIL); if GetArrayLength(Processes)=0 then WizardForm.NextButton.Caption:=SetupMessage(msgButtonInstall) else WizardForm.NextButton.Caption:=SetupMessage(msgButtonNext); end else WizardForm.NextButton.Caption:=SetupMessage(msgButtonNext); end; function InitializeSetup:Boolean; var CurrentVersion,Msg:String; ErrorCode:Integer; begin UpdateInfFilenames; #if BITNESS==32 Result:=True; #else if not IsWin64 then begin LogError(The 64-bit version of Git requires a 64-bit Windows. Aborting.); Result:=False; end else begin Result:=True; end; #endif RegQueryStringValue(HKEY_LOCAL_MACHINE,SoftwareGitForWindows,CurrentVersion,PreviousGitForWindowsVersion); // The Windows Terminal profile is new in v2.32.0 HasUnseenComponents:=IsUpgrade(2.32.0); if HasUnseenComponents then AddToSet(CustomPagesWithUnseenOptions,wpSelectComponents); #if APP_VERSION!=0-test if Result and not ParamIsSet(ALLOWDOWNGRADE) then begin CurrentVersion:=ExpandConstant({#APP_VERSION}); if IsDowngrade(CurrentVersion) then begin if WizardSilent() and (ParamIsSet(SKIPDOWNGRADE) or ParamIsSet(VSNOTICE)) then begin Msg:=Skipping downgrade from +PreviousGitForWindowsVersion+ to +CurrentVersion; if ParamIsSet(SKIPDOWNGRADE) or (ExpandConstant({log})=) then LogError(Msg) else Log(Msg); ExitEarlyWithSuccess(); end; if SuppressibleMsgBox(Git for Windows +PreviousGitForWindowsVersion+ is currently installed.+#13+Do you really want to downgrade to Git for Windows +CurrentVersion+?,mbConfirmation,MB_YESNO or MB_DEFBUTTON2,IDNO)=IDNO then Result:=False; end; end; #ifdef GIT_VERSION // Warn about switching away from VFS-enabled Git if Result and (Pos(.vfs.,{#GIT_VERSION})=0) and (Pos(.vfs.,GetPreviousGitVersion())>0) then if SuppressibleMsgBox(The VFS for Git-aware flavor of Git for Windows is currently installed.+#13+Switching away from that flavor might break your Scalar/VFS for Git enlistments.+#13+Do you still want to switch?,mbConfirmation,MB_YESNO or MB_DEFBUTTON2,IDNO)=IDNO then Result:=False; #endif #endif end; procedure RecordChoice(PreviousDataKey:Integer;Key,Data:String); begin ChosenOptions:=ChosenOptions+Key+: +Data+#13+#10; SetPreviousData(PreviousDataKey,Key,Data); if ShouldSaveInf then begin // .inf files do not like keys with spaces. StringChangeEx(Key, ,,True); SaveInfString(Setup,Key,Data); end; end; function ReplayChoice(Key,Default:String):String; var NoSpaces:String; i:Integer; begin NoSpaces:=Key; StringChangeEx(NoSpaces, ,,True); // A side effect of ReplayChoice is to collect a set of pages that have options on them, and the subset // of those pages have _new_ options (options whose values have no previously-set value) AddToSet(AllCustomPages,CurrentCustomPageID); // Interpret /o:PathOption=Cmd and friends Result:=ExpandConstant({param:o:+NoSpaces+| }); if Result<> then Log(Parameter +Key+=+Result+ set via command-line) else if ShouldLoadInf then // Use settings from the user provided INF. // .inf files do not like keys with spaces. Result:=LoadInfString(Setup,NoSpaces,Default) else begin i:=InferredDefaultKeys.IndexOf(Key); // cannot use .Find because the list is not sorted if (i>=0) then Result:=InferredDefaultValues[i] else begin // Restore the settings chosen during a previous install. Result:=GetPreviousData(Key,Default); // Check to see if this result was the default, or was previously set. // If it was the default, the user has not seen this option yet. if GetPreviousData(Key,z+Result)<>Result then AddToSet(CustomPagesWithUnseenOptions,CurrentCustomPageID) end end; end; function DetectNetFxVersion:Cardinal; begin // We are only interested in version v4.7.2 or later, therefore it // is enough to only use the «4.5 and later» method described in // https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed if IsWin64 then begin if ( not RegQueryDWordValue(HKLM64,SOFTWAREMicrosoftNET Framework SetupNDPv4Full,Release,Result) and not RegQueryDWordValue(HKLM32,SOFTWAREMicrosoftNET Framework SetupNDPv4Full,Release,Result) ) then Result:=0; end else begin if not RegQueryDWordValue(HKLM32,SOFTWAREMicrosoftNET Framework SetupNDPv4Full,Release,Result) then Result:=0; end; end; procedure OpenHyperlink(Sender:TObject); var i,ExitStatus:Integer; begin for i:=0 to (HyperlinkCount-1) do begin if (HyperlinkSource[i]=Sender) then begin ShellExec(,HyperlinkTarget[i],,,SW_SHOW,ewNoWait,ExitStatus); exit; end; end; LogError(Missing hyperlink!); end; procedure OpenNanoHomepage(Sender:TObject); var ExitStatus:Integer; begin ShellExec(,https://www.nano-editor.org/dist/v2.8/nano.html,,,SW_SHOW,ewNoWait,ExitStatus); end; procedure OpenVIMHomepage(Sender:TObject); var ExitStatus:Integer; begin ShellExec(,http://www.vim.org/,,,SW_SHOW,ewNoWait,ExitStatus); end; procedure OpenExitVIMPost(Sender:TObject); var ExitStatus:Integer; begin ShellExec(,https://stackoverflow.blog/2017/05/23/stack-overflow-helping-one-million-developers-exit-vim/,,,SW_SHOW,ewNoWait,ExitStatus); end; procedure OpenSymlinksWikiPage(Sender:TObject); var ExitStatus:Integer; begin ShellExec(,https://github.com/git-for-windows/git/wiki/Symbolic-Links,,,SW_SHOW,ewNoWait,ExitStatus); end; function IsOriginalUserAdmin():Boolean; var ResultCode:Integer; begin if not ExecAsOriginalUser(ExpandConstant({cmd}),ExpandConstant(/d /c net session >»{tmp}net-session.txt»),,SW_HIDE,ewWaitUntilTerminated,ResultCode) then ResultCode:=-1; Result:=(ResultCode=0); end; function EnableSymlinksByDefault():Boolean; var SymlinksForRegularUsers:Cardinal; Root:Integer; ResultCode:Integer; Version:TWindowsVersion; begin RegQueryDwordValue(HKEY_LOCAL_MACHINE, SOFTWAREMicrosoftWindowsCurrentVersionAppModelUnlock, AllowDevelopmentWithoutDevLicense, SymlinksForRegularUsers) GetWindowsVersionEx(Version); if ((SymlinksForRegularUsers=1) and (Version.Build>=14972)) then begin // awesome, Developer mode enabled, and we’re in Creators update, we can create symlinks w/o being admin Log(Symbolic links enabled, machine has Developer Mode enabled); Result:=True Exit; end; if IsOriginalUserAdmin then begin // detection only works when we’re not running as admin Log(Symbolic link permission detection failed: running as admin); Result:=False; end else begin // maybe rights assigned through group policy without enabling developer mode? // let’s test by creating a symbolic link ExecAsOriginalUser(ExpandConstant({cmd}),ExpandConstant(/d /c mklink /d «{tmp}symbolic link» «{tmp}» >»{tmp}symlink test.txt»),,SW_HIDE,ewWaitUntilTerminated,ResultCode); Result:=DirExists(ExpandConstant({tmp}symbolic link)); end; end; function GetTextWidth(Text:String;Font:TFont):Integer; var DummyBitmap:TBitmap; begin DummyBitmap:=TBitmap.Create(); DummyBitmap.Canvas.Font.Assign(Font); Result:=DummyBitmap.Canvas.TextWidth(Text); DummyBitmap.Free(); end; function CreatePage(var PrevPageID:Integer;const Caption,Description:String;var TabOrder,Top,Left:Integer):TWizardPage; begin Result:=CreateCustomPage(PrevPageID,Caption,Description); CurrentCustomPageID:=Result.ID; if (FirstCustomPageID=0) then FirstCustomPageID:=Result.ID; PrevPageID:=Result.ID; TabOrder:=0; Top:=8; Left:=4; end; function CreateFilePage(var PrevPageID:Integer;const Caption,Description,SubCaption:String;var TabOrder,Top,Left:Integer):TInputFileWizardPage; begin Result:=CreateInputFilePage(PrevPageID,Caption,Description,SubCaption); CurrentCustomPageID:=Result.ID; if (FirstCustomPageID=0) then FirstCustomPageID:=Result.ID; PrevPageID:=Result.ID; TabOrder:=0; Top:=8; Left:=4; end; function SubString(S:String;Start,Count:Integer):String; begin Result:=S; if (Start>1) then Delete(Result,1,Start-1); if (Count>=0) then SetLength(Result,Count); end; { Find the position of the next of the three specified tokens (if any). Returns 0 if none were found. } function Pos3(S,Token1,Token2,Token3:String;var ResultPos:Integer):String; var i:Integer; begin ResultPos:=Pos(Token1,S); if (ResultPos>0) then Result:=Token1; i:=Pos(Token2,S); if (i>0) and ((ResultPos=0) or (i<ResultPos)) then begin ResultPos:=i; Result:=Token2; end; i:=Pos(Token3,S); if (i>0) and ((ResultPos=0) or (i<ResultPos)) then begin ResultPos:=i; Result:=Token3; end; end; function CountLines(S:String):Integer; begin Result:=1+StringChangeEx(S,#13,,True); end; { Description can contain pseudo tags <RED>…</RED> and <A HREF=…>…</A> (which cannot be mixed). } function CreateItemDescription(Page:TWizardPage;const Description:String;var Top,Left:Integer;var Labels:array of TLabel;Visible:Boolean):TLabel; var SubLabel:TLabel; Untagged,RowPrefix,Link:String; RowStart,RowCount,i,j:Integer; begin Untagged:=; Result:=TLabel.Create(Page); Result.Parent:=Page.Surface; Result.Caption:=Untagged; Result.Top:=ScaleY(Top); Result.Left:=ScaleX(Left+24); Result.Width:=ScaleX(405); Result.Height:=ScaleY(13); Result.Visible:=Visible; SetArrayLength(Labels,GetArrayLength(Labels)+1); Labels[GetArrayLength(Labels)-1]:=Result; RowPrefix:=; RowCount:=1; while True do begin case Pos3(Description,#13,<RED>,<A HREF=,i) of : begin Untagged:=Untagged+Description; Result.Caption:=Untagged; Result.Height:=ScaleY(13*RowCount); Top:=Top+13+18; Exit; end; +#13: begin Untagged:=Untagged+SubString(Description,1,i); Description:=SubString(Description,i+1,-1); RowCount:=RowCount+1; RowPrefix:=; Top:=Top+13; end; <RED>: begin Untagged:=Untagged+SubString(Description,1,i-1); if (Description[1]=#10) then RowPrefix:=RowPrefix+SubString(Description,2,i-2) else RowPrefix:=RowPrefix+SubString(Description,1,i-1); Description:=SubString(Description,i+5,-1); i:=Pos(</RED>,Description); if (i=0) then LogError(Could not find </RED> in +Description); j:=Pos(#13,Description); if (j>0) and (j<i) and (RowPrefix<>) then begin SubLabeL:=TLabel.Create(Page); SubLabel.Parent:=Page.Surface; SubLabel.Caption:=SubString(Description,1,j-1); SubLabel.Top:=ScaleY(Top); SubLabel.Left:=GetTextWidth(RowPrefix,Result.Font)+ScaleX(Left+24); SubLabel.Width:=ScaleX(405); SubLabel.Height:=ScaleY(13); SubLabel.Font.Color:=clRed; SubLabel.Visible:=Visible; Untagged:=Untagged+SubString(Description,1,j); Description:=SubString(Description,j+1,-1); i:=i-j; RowPrefix:=; Top:=Top+13; RowCount:=RowCount+1; SetArrayLength(Labels,GetArrayLength(Labels)+1); Labels[GetArrayLength(Labels)-1]:=SubLabel; end; SubLabeL:=TLabel.Create(Page); SubLabel.Parent:=Page.Surface; SubLabel.Caption:=SubString(Description,1,i-1); SubLabel.Top:=ScaleY(Top); SubLabel.Left:=GetTextWidth(RowPrefix,Result.Font)+ScaleX(Left+24); SubLabel.Width:=ScaleX(405); SubLabel.Height:=ScaleY(13*CountLines(SubLabel.Caption)); SubLabel.Font.Color:=clRed; SubLabel.Visible:=Visible; Untagged:=Untagged+SubString(Description,1,i-1); RowPrefix:=RowPrefix+SubString(Description,1,i-1); Description:=SubString(Description,i+6,-1); SetArrayLength(Labels,GetArrayLength(Labels)+1); Labels[GetArrayLength(Labels)-1]:=SubLabel; end; <A HREF=: begin Untagged:=Untagged+SubString(Description,1,i-1); if Description[1]=#10 then RowPrefix:=RowPrefix+SubString(Description,2,i-2) else RowPrefix:=RowPrefix+SubString(Description,1,i-1); Description:=SubString(Description,i+8,-1); i:=Pos(>,Description); if (i=0) then LogError(Could not find > in +Description); HyperlinkCount:=HyperlinkCount+1; SetArrayLength(HyperlinkSource,HyperlinkCount); SetArrayLength(HyperlinkTarget,HyperlinkCount); HyperlinkTarget[HyperlinkCount-1]:=SubString(Description,1,i-1); Description:=SubString(Description,i+1,-1); i:=Pos(</A>,Description); if (i=0) then LogError(Could not find </A> in +Description); j:=Pos(#13,Description); if (j>0) and (j<i) and (RowPrefix<>) then begin SubLabeL:=TLabel.Create(Page); HyperlinkSource[HyperlinkCount-1]:=SubLabel; HyperlinkCount:=HyperlinkCount+1; SetArrayLength(HyperlinkSource,HyperlinkCount); SetArrayLength(HyperlinkTarget,HyperlinkCount); HyperlinkTarget[HyperlinkCount-1]:=HyperlinkTarget[HyperlinkCount-2]; SubLabel.Parent:=Page.Surface; SubLabel.Caption:=SubString(Description,1,j-1); SubLabel.Top:=ScaleY(Top); SubLabel.Left:=GetTextWidth(RowPrefix,Result.Font)+ScaleX(Left+24); SubLabel.Width:=ScaleX(405); SubLabel.Height:=ScaleY(13); SubLabel.Font.Color:=clBlue; SubLabel.Font.Style:=[fsUnderline]; SubLabel.Cursor:=crHand; SubLabel.OnClick:=@OpenHyperlink; SubLabel.Visible:=Visible; Untagged:=Untagged+SubString(Description,1,j); Description:=SubString(Description,j+1,-1); i:=i-j; RowPrefix:=; Top:=Top+13; RowCount:=RowCount+1; SetArrayLength(Labels,GetArrayLength(Labels)+1); Labels[GetArrayLength(Labels)-1]:=SubLabel; end; SubLabeL:=TLabel.Create(Page); HyperlinkSource[HyperlinkCount-1]:=SubLabel; SubLabel.Parent:=Page.Surface; SubLabel.Caption:=SubString(Description,1,i-1); SubLabel.Top:=ScaleY(Top); SubLabel.Left:=GetTextWidth(RowPrefix,Result.Font)+ScaleX(Left+24); SubLabel.Width:=ScaleX(405); SubLabel.Height:=ScaleY(13*CountLines(SubLabel.Caption)); SubLabel.Font.Color:=clBlue; SubLabel.Font.Style:=[fsUnderline]; SubLabel.Cursor:=crHand; SubLabel.OnClick:=@OpenHyperlink; SubLabel.Visible:=Visible; Untagged:=Untagged+SubString(Description,1,i-1); RowPrefix:=RowPrefix+SubString(Description,1,i-1); Description:=SubString(Description,i+4,-1); SetArrayLength(Labels,GetArrayLength(Labels)+1); Labels[GetArrayLength(Labels)-1]:=SubLabel; end; end; end; end; function CreateRadioButtonOrCheckBox(CreateRadioButton:Boolean;Page:TWizardPage;const Caption,Description:String;var TabOrder,Top,Left:Integer):TButtonControl; var RadioButton:TRadioButton; CheckBox:TCheckBox; Dummy:array of TLabel; begin if (CreateRadioButton) then begin RadioButton:=TRadioButton.Create(Page); RadioButton.Caption:=Caption; RadioButton.Font.Style:=[fsBold]; Result:=RadioButton; end else begin CheckBox:=TCheckBox.Create(Page); CheckBox.Caption:=Caption; CheckBox.Font.Style:=[fsBold]; Result:=CheckBox; end; Result.Parent:=Page.Surface; Result.Left:=ScaleX(Left); Result.Top:=ScaleY(Top); Result.Width:=ScaleX(405); Result.Height:=ScaleY(17); Result.TabOrder:=TabOrder; TabOrder:=TabOrder+1; Top:=Top+24; CreateItemDescription(Page,Description,Top,Left,Dummy,True); end; function CreateRadioButton(Page:TWizardPage;const Caption,Description:String;var TabOrder,Top,Left:Integer):TRadioButton; begin Result:=TRadioButton(CreateRadioButtonOrCheckBox(True,Page,Caption,Description,TabOrder,Top,Left)); end; function CreateCheckBox(Page:TWizardPage;const Caption,Description:String;var TabOrder,Top,Left:Integer):TCheckBox; begin Result:=TCheckBox(CreateRadioButtonOrCheckBox(False,Page,Caption,Description,TabOrder,Top,Left)); end; procedure SetInputFileTop(Page:TInputFileWizardPage;Offset:Integer); begin Page.Edits[0].Top:=Offset+Page.Edits[0].Top; Page.Buttons[0].Top:=Offset+Page.Buttons[0].Top; Page.PromptLabels[0].Top:=Offset+Page.PromptLabels[0].Top; TestCustomEditorButton.Top:=Page.Buttons[0].Top+Page.Buttons[0].Height+ScaleY(3); end; procedure SetInputFileState(Page:TInputFileWizardPage;State:Boolean); begin Page.Edits[0].Enabled:=State; Page.Buttons[0].Enabled:=State; Page.PromptLabels[0].Enabled:=State; TestCustomEditorButton.Enabled:=State; end; procedure SetInputFileVisible(Page:TInputFileWizardPage;Visible:Boolean); begin Page.Edits[0].Visible:=Visible; Page.Buttons[0].Visible:=Visible; Page.PromptLabels[0].Visible:=Visible; TestCustomEditorButton.Visible:=Visible; end; { Find the position of the next of the three specified tokens (if any). Returns 0 if none were found. } function PathIsValidExecutable(var Path: String):Boolean; var Env,Path2,Ext:String; PathExt:String; ExtArray:TArrayOfString; i,Len:Integer; j:Integer; ExtensionFlag:Boolean; begin Result:=False; if Path= then Exit; (* If Path contains only the file name, search through PATH *) if Pos(,Path)=0 then begin Env:=GetEnv(PATH)+;; repeat i:=Pos(;,Env); Path2:=Copy(Env,1,i-1)++Path; Env:=Copy(Env,i+1,Length(Env)); if PathIsValidExecutable(Path2) then begin Path:=Path2; Result:=True; end; until Result or (Env=); Exit; end; (* * If Path lacks a file extension, look through PATHEXT, otherwise * verify that the file extension is in PATHEXT. *) Env:=GetEnv(PATHEXT)+;; Ext:=ExtractFileExt(Path); if Ext<> then Result:=(Pos(;+Uppercase(Ext)+;,;+Uppercase(Env))>0) and FileExists(Path) else begin repeat i:=Pos(;,Env); Path2:=Path+Copy(Env,1,i-1); Env:=Copy(Env,i+1,Length(Env)); if FileExists(Path2) then begin Path:=Path2; Result:=True; end; until Result or (Env=); end end; procedure TestCustomEditor(Sender:TObject); var InputText,OutputText:AnsiString; TmpFile:String; Res:Longint; CustomEditorExt:String; Show:Integer; begin if not PathIsValidExecutable(CustomEditorPath) then begin Wizardform.NextButton.Enabled:=False; SuppressibleMsgBox(Not a valid executable: «+CustomEditorPath+«,mbError,MB_OK,IDOK); Exit; end; TmpFile:=ExpandConstant({tmp}editor-test.txt); InputText:=Please modify this text, e.g. delete it, then save it and exit the editor. SaveStringToFile(TmpFile,InputText,False); CustomEditorExt:=ExtractFileExt(CustomEditorPath); if (CompareText(CustomEditorExt,.bat)=0) or (CompareText(CustomEditorExt,.cmd)=0) then Show:=SW_HIDE else Show:=SW_SHOW; if not ShellExecAsOriginalUser(,CustomEditorPath,CustomEditorOptions+ «+TmpFile+«,,Show,ewWaitUntilTerminated,Res) then begin Wizardform.NextButton.Enabled:=False; SuppressibleMsgBox(Could not launch: «+CustomEditorPath+«,mbError,MB_OK,IDOK); Exit; end; if (Res<>0) then begin Wizardform.NextButton.Enabled:=False; SuppressibleMsgBox(Exited with failure: «+CustomEditorPath+«,mbError,MB_OK,IDOK); Exit; end; if not LoadStringFromFile(TmpFile,OutputText) then begin Wizardform.NextButton.Enabled:=False; SuppressibleMsgBox(Could not read «+TmpFile+«,mbError,MB_OK,IDOK); Exit; end; if not DeleteFile(TmpFile) then begin Wizardform.NextButton.Enabled:=False; SuppressibleMsgBox(Could not delete «+TmpFile+«,mbError,MB_OK,IDOK); Exit; end; if InputText=OutputText then begin Wizardform.NextButton.Enabled:=False; SuppressibleMsgBox(The file was not modified!+#13+#10+Does the editor require an option to wait?,mbError,MB_OK,IDOK); Exit; end; Wizardform.NextButton.Enabled:=True; SuppressibleMsgBox(Success!,mbInformation,MB_OK,IDOK); end; procedure EnableNextButtonOnValidExecutablePath(Path: String); begin if PathIsValidExecutable(Path) then Wizardform.NextButton.Enabled:=True else Wizardform.NextButton.Enabled:=False; end; procedure UpdateCustomEditorPath(Sender: TObject); var PathLength:Integer; Path:String; begin (* * Add space at the end of the string in order to rule out paths like * ‘FOO.exeBAR’, but allow paths like ‘FOO.exe —BAR’. *) Path:=EditorPage.Values[0]+ ; if WildcardMatch(Path, «*» *) then begin Path:=Copy(Path,2,Length(Path)); PathLength:=Pos(#34,Path)-1; CustomEditorPath:=Copy(Path,1,PathLength); CustomEditorOptions:=Copy(Path,PathLength+3,Length(Path)-PathLength-3) end else begin Path:=EditorPage.Values[0]; PathLength:=Pos( ,Path)-1; if PathIsValidExecutable(Path) or (PathLength=0) then begin CustomEditorPath:=Path; CustomEditorOptions:= end else begin CustomEditorPath:=Copy(Path,1,PathLength); CustomEditorOptions:=Copy(Path,PathLength+1,Length(Path)) end end; (* * If the specified path does not contain ‘.exe’ at the end, * CustomEditorPath will be formed with the first three letters of Path, * but that should not be a problem because the next button is enabled * only when PathIsValidExecutable() returns True. *) EnableNextButtonOnValidExecutablePath(CustomEditorPath); end; procedure EditorSelectionChanged(Sender: TObject); var i:Integer; begin for i:=0 to GetArrayLength(LblEditor[SelectedEditor])-1 do LblEditor[SelectedEditor][i].Visible:=False; SelectedEditor:=CbbEditor.ItemIndex; for i:=0 to GetArrayLength(LblEditor[SelectedEditor])-1 do begin LblEditor[SelectedEditor][i].Visible:=True; if (LblEditor[SelectedEditor][i].Cursor<>crHand) then LblEditor[SelectedEditor][i].Enabled:=EditorAvailable[SelectedEditor]; end; if (SelectedEditor=GE_CustomEditor) then begin SetInputFileState(EditorPage,True); SetInputFileVisible(EditorPage,True); EnableNextButtonOnValidExecutablePath(CustomEditorPath); end else begin SetInputFileState(EditorPage,False); SetInputFileVisible(EditorPage,False); Wizardform.NextButton.Enabled:=EditorAvailable[SelectedEditor]; end; end; procedure DefaultBranchOptionChanged(Sender: TObject); begin EdtDefaultBranch.Enabled:=RdbDefaultBranch[DB_Manual].Checked; if EdtDefaultBranch.Enabled and (WizardForm.CurPageID=DefaultBranchPage.ID) then // If the manual option was just checked, move the focus to the text box WizardForm.ActiveControl:=EdtDefaultBranch; end; procedure DefaultBranchNameChanged(Sender: TObject); var IsValidBranchName:Boolean; begin if EdtDefaultBranch.Enabled then begin // Disallow illegal ref names with EdtDefaultBranch do IsValidBranchName:=(Text<>) and (Text<>@) and (Pos(..,Text)=0) and (Pos(@{,Text)=0) and (Pos(//,Text)=0) and (Pos(#8,Text)=0) and (Pos( ,Text)=0) and (Pos(:,Text)=0) and (Pos(?,Text)=0) and (Pos([,Text)=0) and (Pos(,Text)=0) and (Pos(^,Text)=0) and (Pos(~,Text)=0) and (Pos(#127,Text)=0) and (Pos(/.,/+Text)=0) and (Pos(./,Text+/)=0) and (Pos(.lock/,Text+/)=0); if (WizardForm.CurPageID=DefaultBranchPage.ID) then Wizardform.NextButton.Enabled:=IsValidBranchName; if IsValidBranchName then EdtDefaultBranch.Color:=clWhite else EdtDefaultBranch.Color:=clRed; end; end; // Extract <path> from «<path>» «%1» function ExtractCommandPath(Command:String):String; begin Result:=Copy(Command, 2, Length(Command) — 7); end; function ExtractCodeCLIPath(Command:String;Suffix:String):String; begin Result:=ExtractFilePath(ExtractCommandPath(Command)) + Suffix; end; procedure QueryUninstallValues; forward; procedure InitializeWizard; var PrevPageID,TabOrder,TopOfLabels,Top,Left:Integer; PuTTYSessions,EnvSSH:TArrayOfString; BtnPlink:TButton; Data:String; LblInfo:TLabel; begin InferredDefaultKeys:=TStringList.Create; InferredDefaultValues:=TStringList.Create; QueryUninstallValues(); AppDir:=UninstallAppPath; GetDefaultsFromGitConfig(ProgramData); GetDefaultsFromGitConfig(system); ChosenOptions:=; PrevPageID:=wpSelectProgramGroup; (* * Allow skipping pages that contain only previously-seen options. * For upgrades, default to skipping. *) OnlyShowNewOptions:=TCheckBox.Create(WizardForm); with OnlyShowNewOptions do begin Parent:=WizardForm; Caption:=&Only show new options; Width:=GetTextWidth(Caption,Font)+20; // 20 is the estimated width of the checkbox itself Left:=WizardForm.BackButton.Left-Width-(WizardForm.CancelButton.Left-WizardForm.NextButton.Left-WizardForm.NextButton.Width); Checked:=IsUpgrade(ExpandConstant({#APP_VERSION})); OnClick:=@AdjustNextButtonLabel; Height:=WizardForm.CancelButton.Height; Top:=WizardForm.CancelButton.Top; end; if (PreviousGitForWindowsVersion=) then OnlyShowNewOptions.Hide; (* * Create a custom page for configuring the default Git editor. *) EditorPage:=CreateFilePage(PrevPageID,Choosing the default editor used by Git,Which editor would you like Git to use?,,TabOrder,Top,Left); CbbEditor:=TNewComboBox.Create(EditorPage); CbbEditor.Style:=csDropDownList; CbbEditor.OnChange:=@EditorSelectionChanged; CbbEditor.Parent:=EditorPage.Surface; CbbEditor.Left:=ScaleX(Left); CbbEditor.Top:=ScaleY(Top); CbbEditor.Width:=ScaleX(405); CbbEditor.Height:=ScaleY(17); CbbEditor.TabOrder:=TabOrder; TabOrder:=TabOrder+1; Top:=Top+24; TopOfLabels:=Top; EditorAvailable[GE_NotepadPlusPlus]:=RegQueryStringValue(HKEY_LOCAL_MACHINE,SOFTWAREMicrosoftWindowsCurrentVersionApp Pathsnotepad++.exe,,NotepadPlusPlusPath); EditorAvailable[GE_VisualStudioCode]:=RegQueryStringValue(HKEY_LOCAL_MACHINE,SOFTWAREClassesApplicationsCode.exeshellopencommand,,VisualStudioCodePath); if (not EditorAvailable[GE_VisualStudioCode]) then begin EditorAvailable[GE_VisualStudioCode]:=RegQueryStringValue(HKEY_CURRENT_USER,SoftwareClassesApplicationsCode.exeshellopencommand,,VisualStudioCodePath); VisualStudioCodeUserInstallation:=True; end; EditorAvailable[GE_VisualStudioCodeInsiders]:=RegQueryStringValue(HKEY_LOCAL_MACHINE,SOFTWAREClassesApplicationsCode — Insiders.exeshellopencommand,,VisualStudioCodeInsidersPath); if (not EditorAvailable[GE_VisualStudioCodeInsiders]) then begin EditorAvailable[GE_VisualStudioCodeInsiders]:=RegQueryStringValue(HKEY_CURRENT_USER,SoftwareClassesApplicationsCode — Insiders.exeshellopencommand,,VisualStudioCodeInsidersPath); VisualStudioCodeInsidersUserInstallation:=True; end; SublimeTextPath:=ExpandConstant({pf}Sublime Textsubl.exe); EditorAvailable[GE_SublimeText]:=PathIsValidExecutable(SublimeTextPath); if (not EditorAvailable[GE_SublimeText]) then begin SublimeTextPath:=ExpandConstant({pf}Sublime Text 3subl.exe); EditorAvailable[GE_SublimeText]:=PathIsValidExecutable(SublimeTextPath); if (not EditorAvailable[GE_SublimeText]) then begin EditorAvailable[GE_SublimeText]:=RegQueryStringValue(HKEY_CURRENT_USER,SoftwareClassesApplicationssublime_text.exeshellopencommand,,SublimeTextPath); SublimeTextUserInstallation:=True; end; end; EditorAvailable[GE_Atom]:=RegQueryStringValue(HKEY_CURRENT_USER,SoftwareClassesApplicationsatom.exeshellopencommand,,AtomPath); EditorAvailable[GE_CustomEditor]:=True; EditorAvailable[GE_VSCodium]:=RegQueryStringValue(HKEY_LOCAL_MACHINE,SOFTWAREClassesApplicationsVSCodium.exeshellopencommand,,VSCodiumPath); if (not EditorAvailable[GE_VSCodium]) then begin EditorAvailable[GE_VSCodium]:=RegQueryStringValue(HKEY_CURRENT_USER,SoftwareClassesApplicationsVSCodium.exeshellopencommand,,VSCodiumPath); VSCodiumUserInstallation:=True; end; // Remove `» %1″` from end and unqote the string. if (EditorAvailable[GE_VisualStudioCode]) then VisualStudioCodePath:=ExtractCodeCLIPath(VisualStudioCodePath, bincode); if (EditorAvailable[GE_VisualStudioCodeInsiders]) then VisualStudioCodeInsidersPath:=ExtractCodeCLIPath(VisualStudioCodeInsidersPath, bincode-insiders); if (EditorAvailable[GE_SublimeText]) and SublimeTextUserInstallation then SublimeTextPath:=ExtractCommandPath(SublimeTextPath); if (EditorAvailable[GE_Atom]) then AtomPath:=ExtractCommandPath(AtomPath); if (EditorAvailable[GE_VSCodium]) then VSCodiumPath:=ExtractCodeCLIPath(VSCodiumPath, bincodium); // 1st choice Top:=TopOfLabels; CbbEditor.Items.Add(Use the Nano editor by default); Data:=<A HREF=https://www.nano-editor.org/dist/v2.8/nano.html>GNU nano</A> is a small and friendly text editor running in the console+#13+window.; if (not EditorAvailable[GE_NotepadPlusPlus] and not EditorAvailable[GE_VisualStudioCode] and not EditorAvailable[GE_VisualStudioCodeInsiders] and not EditorAvailable[GE_SublimeText] and not EditorAvailable[GE_Atom]) and not EditorAvailable[GE_VSCodium] then Data:=Data+#13+#13+This is the recommended option for end users if no GUI editors are installed.; CreateItemDescription(EditorPage,Data,Top,Left,LblEditor[GE_Nano],False); EditorAvailable[GE_Nano]:=True; // 2nd choice Top:=TopOfLabels; CbbEditor.Items.Add(Use Vim (the ubiquitous text editor) as Git+#39+s default editor); CreateItemDescription(EditorPage,The <A HREF=http://www.vim.org/>Vim editor</A>, while powerful, <A HREF=https://stackoverflow.blog/2017/05/23/stack-overflow-helping-one-million-developers-exit-vim/>can be hard to use</A>. Its user interface is+#13+unintuitive and its key bindings are awkward.+#13+#13+<RED>Note:</RED> Vim is the default editor of Git for Windows only for historical reasons, and+#13+it is highly recommended to switch to a modern GUI editor instead.+#13+#13+<RED>Note:</RED> This will leave the +#39+core.editor+#39+ option unset, which will make Git fall back+#13+to the +#39+EDITOR+#39+ environment variable. The default editor is Vim — but you+#13+may set it to some other editor of your choice.,Top,Left,LblEditor[GE_VIM],False); EditorAvailable[GE_VIM]:=True; // 3rd choice Top:=TopOfLabels; CbbEditor.Items.Add(Use Notepad++ as Git+#39+s default editor); CreateItemDescription(EditorPage,<A HREF=https://notepad-plus-plus.org/>Notepad++</A> is a popular GUI editor that can be used by Git.+#13+#13+This editor is popular in part due to the vast number of available plugins;+#13+However, when configured via this option, Git will call Notepad++ with+#13+plugins disabled (to open the editor as quickly as possible).,Top,Left,LblEditor[GE_NotepadPlusPlus],False); // 4th choice Top:=TopOfLabels; CbbEditor.Items.Add(Use Visual Studio Code as Git+#39+s default editor); if (VisualStudioCodeUserInstallation=False) then CreateItemDescription(EditorPage,<A HREF=https://code.visualstudio.com//>Visual Studio Code</A> is an Open Source, lightweight and powerful editor+#13+running as a desktop application. It comes with built-in support for JavaScript,+#13+TypeScript and Node.js and has a rich ecosystem of extensions for other+#13+languages (such as C++, C#, Java, Python, PHP, Go) and runtimes (such as+#13+.NET and Unity).+#13+#13+Use this option to let Git use Visual Studio Code as its default editor.,Top,Left,LblEditor[GE_VisualStudioCode],False) else CreateItemDescription(EditorPage,<A HREF=https://code.visualstudio.com//>Visual Studio Code</A> is an Open Source, lightweight and powerful editor+#13+running as a desktop application. It comes with built-in support for JavaScript,+#13+TypeScript and Node.js and has a rich ecosystem of extensions for other+#13+languages (such as C++, C#, Java, Python, PHP, Go) and runtimes (such as+#13+.NET and Unity).+#13+<RED>(WARNING!) This will be installed only for this user.</RED>+#13+#13+Use this option to let Git use Visual Studio Code as its default editor.,Top,Left,LblEditor[GE_VisualStudioCode],False); // 5th choice Top:=TopOfLabels; CbbEditor.Items.Add(Use Visual Studio Code Insiders as Git+#39+s default editor); if (VisualStudioCodeInsidersUserInstallation=False) then CreateItemDescription(EditorPage,<A HREF=https://code.visualstudio.com/insiders/>Visual Studio Code</A> is an Open Source, lightweight and powerful editor+#13+running as a desktop application. It comes with built-in support for JavaScript,+#13+TypeScript and Node.js and has a rich ecosystem of extensions for other+#13+languages (such as C++, C#, Java, Python, PHP, Go) and runtimes (such as+#13+.NET and Unity).+#13+#13+Use this option to let Git use Visual Studio Code Insiders as its default editor.,Top,Left,LblEditor[GE_VisualStudioCodeInsiders],False) else CreateItemDescription(EditorPage,<A HREF=https://code.visualstudio.com/insiders/>Visual Studio Code</A> is an Open Source, lightweight and powerful editor+#13+running as a desktop application. It comes with built-in support for JavaScript,+#13+TypeScript and Node.js and has a rich ecosystem of extensions for other+#13+languages (such as C++, C#, Java, Python, PHP, Go) and runtimes (such as+#13+.NET and Unity).+#13+<RED>(WARNING!) This will be installed only for this user.</RED>+#13+#13+Use this option to let Git use Visual Studio Code Insiders as its default editor.,Top,Left,LblEditor[GE_VisualStudioCodeInsiders],False); // 6th choice Top:=TopOfLabels; CbbEditor.Items.Add(Use Sublime Text as Git+#39+s default editor); if (SublimeTextUserInstallation=False) then CreateItemDescription(EditorPage,<A HREF=https://www.sublimetext.com/>Sublime text</A> is a lightweight editor which supports a great number+#13+of plugins.+#13+#13+Use this option to let Git use Sublime Text as its default editor.,Top,Left,LblEditor[GE_SublimeText],False) else CreateItemDescription(EditorPage,<A HREF=https://www.sublimetext.com/>Sublime text</A> is a lightweight editor which supports a great number+#13+of plugins.+#13+<RED>(WARNING!) This will be installed only for this user.</RED>+#13+#13+Use this option to let Git use Sublime Text as its default editor.,Top,Left,LblEditor[GE_SublimeText],False); // 7th choice Top:=TopOfLabels; CbbEditor.Items.Add(Use Atom as Git+#39+s default editor); CreateItemDescription(EditorPage,<A HREF=https://atom.io/>Atom</A> is an open source text editor which comes with builtin support+#13+for Git and Github.+#13+<RED>(WARNING!) This will be installed only for this user.</RED>+#13+#13+Use this option to let Git use Atom as its default editor.,Top,Left,LblEditor[GE_Atom],False); // 8th choice Top:=TopOfLabels; CbbEditor.Items.Add(Use VSCodium as Git+#39+s default editor); if (VSCodiumUserInstallation=False) then CreateItemDescription(EditorPage,<A HREF=https://vscodium.com///>VSCodium</A> provides Free/Libre Open Source Software Binaries of VSCode with the same features, but without telemetry/tracking of Microsoft or any non-floss code parts. It comes with built-in support for JavaScript,+#13+TypeScript and Node.js and has a rich ecosystem of extensions for other+#13+languages (such as C++, C#, Java, Python, PHP, Go) and runtimes (such as+#13+.NET and Unity).+#13+#13+Use this option to let Git use VSCodium as its default editor.,Top,Left,LblEditor[GE_VSCodium],False) else CreateItemDescription(EditorPage,<A HREF=https://vscodium.com///>VSCodium</A> provides Free/Libre Open Source Software Binaries of VSCode with the same features, but without telemetry/tracking of Microsoft or any non-floss code parts. It comes with built-in support for JavaScript,+#13+TypeScript and Node.js and has a rich ecosystem of extensions for other+#13+languages (such as C++, C#, Java, Python, PHP, Go) and runtimes (such as+#13+.NET and Unity).+#13+<RED>(WARNING!) This will be installed only for this user.</RED>+#13+#13+Use this option to let Git use VSCodium as its default editor.,Top,Left,LblEditor[GE_VSCodium],False); // 9th choice Top:=TopOfLabels; CbbEditor.Items.Add(Use Notepad as Git+#39+s default editor); CreateItemDescription(EditorPage,<RED>(NEW!)</RED> Notepad is a simple GUI editor that comes with Windows.,Top,Left,LblEditor[GE_Notepad],False); EditorAvailable[GE_Notepad]:=True; // 10th choice Top:=TopOfLabels; CbbEditor.Items.Add(Use Wordpad as Git+#39+s default editor); CreateItemDescription(EditorPage,<RED>(NEW!)</RED> Wordpad is a basic word processor that comes with Windows.+#13+It can also be used as a text editor.,Top,Left,LblEditor[GE_Wordpad],False); EditorAvailable[GE_Wordpad]:=True; // Custom choice Top:=TopOfLabels; CbbEditor.Items.Add(Select other editor as Git+#39+s default editor); CreateItemDescription(EditorPage,Use this option to select the path to Git+#39+s default editor.,Top,Left,LblEditor[GE_CustomEditor],False); EditorPage.add(Location of editor (plus command-line options, if necessary):,Executable files|*.exe;*.cmd;*.bat|All files|*.*,.exe); TestCustomEditorButton:=TButton.Create(EditorPage); with TestCustomEditorButton do begin Parent:=EditorPage.Surface; Caption:=Test Custom Editor; Left:=ScaleX(Left+24); OnClick:=@TestCustomEditor; Width:=ScaleX(128); Height:=ScaleY(21); end; SetInputFileTop(EditorPage, ScaleY(Top) + ScaleY(CbbEditor.Height)) EditorPage.Edits[0].OnChange:=@UpdateCustomEditorPath; SetInputFileState(EditorPage, False); SetInputFileVisible(EditorPage, False); // Restore the setting chosen during a previous install. case ReplayChoice(Editor Option,VIM) of Nano: CbbEditor.ItemIndex:=GE_Nano; VIM: CbbEditor.ItemIndex:=GE_VIM; Notepad++: begin if EditorAvailable[GE_NotepadPlusPlus] then CbbEditor.ItemIndex:=GE_NotepadPlusPlus else CbbEditor.ItemIndex:=GE_VIM; end; VisualStudioCode: begin if EditorAvailable[GE_VisualStudioCode] then CbbEditor.ItemIndex:=GE_VisualStudioCode else CbbEditor.ItemIndex:=GE_VIM; end; VisualStudioCodeInsiders: begin if EditorAvailable[GE_VisualStudioCodeInsiders] then CbbEditor.ItemIndex:=GE_VisualStudioCodeInsiders else CbbEditor.ItemIndex:=GE_VIM; end; SublimeText: begin if EditorAvailable[GE_SublimeText] then CbbEditor.ItemIndex:=GE_SublimeText else CbbEditor.ItemIndex:=GE_VIM; end; Atom: begin if EditorAvailable[GE_Atom] then CbbEditor.ItemIndex:=GE_Atom else CbbEditor.ItemIndex:=GE_VIM; end; VSCodium: begin if EditorAvailable[GE_VSCodium] then CbbEditor.ItemIndex:=GE_VSCodium else CbbEditor.ItemIndex:=GE_VIM; end; Notepad: CbbEditor.ItemIndex:=GE_Notepad; Wordpad: CbbEditor.ItemIndex:=GE_Wordpad; CustomEditor: begin CbbEditor.ItemIndex:=GE_CustomEditor; EditorPage.Values[0]:=ReplayChoice(Custom Editor Path,); (* * UpdateCustomEditorPath() will also check if the path is still available and * enable / disable the «Next» button. *) UpdateCustomEditorPath(NIL); end; else CbbEditor.ItemIndex:=GE_VIM; end; EditorSelectionChanged(NIL); (* * Create a custom page for modifying the default branch. *) DefaultBranchPage:=CreatePage(PrevPageID,Adjusting the name of the initial branch in new repositories,What would you like Git to name the initial branch after «git init»?,TabOrder,Top,Left); // 1st choice RdbDefaultBranch[DB_Unspecified]:=CreateRadioButton(DefaultBranchPage,Let Git decide,Let Git use its default branch name (currently: «{#DEFAULT_BRANCH_NAME}») for the initial branch+#13+in newly created repositories. The Git project <A HREF=https://sfconservancy.org/news/2020/jun/23/gitbranchname/>intends</A> to change this default to+#13+a more inclusive name in the near future.,TabOrder,Top,Left); RdbDefaultBranch[DB_Unspecified].OnClick:=@DefaultBranchOptionChanged; // 2nd choice RdbDefaultBranch[DB_Manual]:=CreateRadioButton(DefaultBranchPage,Override the default branch name for new repositories,<RED>NEW!</RED> Many teams already renamed their default branches; common choices are+#13+«main», «trunk» and «development». Specify the name «git init» should use for the+#13+initial branch:,TabOrder,Top,Left); RdbDefaultBranch[DB_Manual].OnClick:=@DefaultBranchOptionChanged; // Text field for the overridden branch name Top:=Top-13; EdtDefaultBranch:=TEdit.Create(DefaultBranchPage); EdtDefaultBranch.Parent:=DefaultBranchPage.Surface; EdtDefaultBranch.Left:=ScaleX(Left+24); EdtDefaultBranch.Top:=ScaleY(Top); EdtDefaultBranch.Width:=ScaleX(158); EdtDefaultBranch.Height:=ScaleY(13); EdtDefaultBranch.TabOrder:=TabOrder; TabOrder:=TabOrder+1; EdtDefaultBranch.Text:=main; EdtDefaultBranch.Enabled:=False; EdtDefaultBranch.OnChange:=@DefaultBranchNameChanged; Top:=Top+13+24; LblInfo:=TLabel.Create(DefaultBranchPage); LblInfo.Parent:=DefaultBranchPage.Surface; LblInfo.Caption:=This setting does not affect existing repositories.; LblInfo.Left:=ScaleX(Left); LblInfo.Top:=ScaleY(Top); // Restore the setting chosen during a previous install. Data:=ReplayChoice(Default Branch Option,); case Data of : RdbDefaultBranch[DB_Unspecified].Checked:=True; : RdbDefaultBranch[DB_Unspecified].Checked:=True; else begin RdbDefaultBranch[DB_Manual].Checked:=True; EdtDefaultBranch.Text:=Data; EdtDefaultBranch.Enabled:=True; DefaultBranchNameChanged(NIL); end end; (* * Create a custom page for modifying the environment. *) PathPage:=CreatePage(PrevPageID,Adjusting your PATH environment,How would you like to use Git from the command line?,TabOrder,Top,Left); // 1st choice RdbPath[GP_BashOnly]:=CreateRadioButton(PathPage,Use Git from Git Bash only,This is the most cautious choice as your PATH will not be modified at all. You will+#13+only be able to use the Git command line tools from Git Bash.,TabOrder,Top,Left); // 2nd choice RdbPath[GP_Cmd]:=CreateRadioButton(PathPage,Git from the command line and also from 3rd-party software,<RED>(Recommended)</RED> This option adds only some minimal Git wrappers to your+#13+PATH to avoid cluttering your environment with optional Unix tools.+#13+You will be able to use Git from Git Bash, the Command Prompt and the Windows+#13+PowerShell as well as any third-party software looking for Git in PATH.,TabOrder,Top,Left); // 3rd choice RdbPath[GP_CmdTools]:=CreateRadioButton(PathPage,Use Git and optional Unix tools from the Command Prompt,Both Git and the optional Unix tools will be added to your PATH.+#13+<RED>Warning: This will override Windows tools like «find» and «sort». Only+#13+use this option if you understand the implications.</RED>,TabOrder,Top,Left); // Restore the setting chosen during a previous install. case ReplayChoice(Path Option,Cmd) of BashOnly: RdbPath[GP_BashOnly].Checked:=True; Cmd: RdbPath[GP_Cmd].Checked:=True; CmdTools: RdbPath[GP_CmdTools].Checked:=True; else RdbPath[GP_Cmd].Checked:=True; end; (* * Create a custom page for using (Tortoise)Plink or a self-supplied OpenSSH instead of bundled OpenSSH * if at least one PuTTY session is found in the Registry or an OpenSSH binary is found on the PATH. *) if (RegGetSubkeyNames(HKEY_CURRENT_USER,SoftwareSimonTathamPuTTYSessions,PuTTYSessions) and (GetArrayLength(PuTTYSessions)>0)) or (FileSearch(ssh.exe, GetEnv(PATH)) <> ) then begin SSHChoicePage:=CreatePage(PrevPageID,Choosing the SSH executable,Which Secure Shell client program would you like Git to use?,TabOrder,Top,Left); // 1st choice RdbSSH[GS_OpenSSH]:=CreateRadioButton(SSHChoicePage,Use bundled OpenSSH,This uses ssh.exe that comes with Git.,TabOrder,Top,Left); // 2nd choice if (GetArrayLength(PuTTYSessions)=0) then begin RdbSSH[GS_Plink]:=TRadioButton.Create(SSHChoicePage); end else begin RdbSSH[GS_Plink]:=CreateRadioButton(SSHChoicePage,Use (Tortoise)Plink,To use PuTTY, specify the path to an existing copy of (Tortoise)Plink.exe:,TabOrder,Top,Left); EdtPlink:=TEdit.Create(SSHChoicePage); EdtPlink.Left:=ScaleX(Left+24); EdtPlink.Top:=ScaleY(Top); with EdtPlink do begin Parent:=SSHChoicePage.Surface; EnvSSH:=GetEnvStrings(GIT_SSH,IsAdminLoggedOn); if (GetArrayLength(EnvSSH)=1) and IsPlinkExecutable(EnvSSH[0]) then begin Text:=EnvSSH[0]; end; if not FileExists(Text) then begin Text:=GetPreviousData(Plink Path,); end; if not FileExists(Text) then begin Text:=GuessPlinkExecutable; end; if not FileExists(Text) then begin Text:=; end; Width:=ScaleX(316); Height:=ScaleY(13); TabOrder:=TabOrder; end; TabOrder:=TabOrder+1; BtnPlink:=TButton.Create(SSHChoicePage); BtnPlink.Left:=ScaleX(Left+344); BtnPlink.Top:=ScaleY(Top); with BtnPlink do begin Parent:=SSHChoicePage.Surface; Caption:=; OnClick:=@BrowseForPuTTYFolder; Width:=ScaleX(21); Height:=ScaleY(21); TabOrder:=TabOrder; end; TabOrder:=TabOrder+1; Top:=Top+29; // Add checkbox for tortoise plink TortoisePlink:=TCheckBox.Create(SSHChoicePage); TortoisePlink.Left:=ScaleX(Left+24); TortoisePlink.Top:=ScaleY(Top); with TortoisePlink do begin Caption:=Set ssh.variant for Tortoise Plink; Parent:=SSHChoicePage.Surface; Width:=ScaleX(405); Height:=ScaleY(17); TabOrder:=TabOrder; end; TabOrder:=TabOrder+1; Top:=Top+17; end; // 3rd choice RdbSSH[GS_ExternalOpenSSH]:=CreateRadioButton(SSHChoicePage,Use external OpenSSH, <RED>NEW!</RED> This uses an external ssh.exe. Git will not install its own OpenSSH+#13+ (and related) binaries but use them as found on the PATH., TabOrder,Top,Left); // Restore the setting chosen during a previous install. case ReplayChoice(SSH Option,OpenSSH) of OpenSSH: RdbSSH[GS_OpenSSH].Checked:=True; ExternalOpenSSH: RdbSSH[GS_ExternalOpenSSH].Checked:=True; Plink: RdbSSH[GS_Plink].Checked:=True; else RdbSSH[GS_OpenSSH].Checked:=True; end; // Even if the user saw the Tortoise options before v2.33.0, the external SSH choice was not seen yet if IsUpgrade(2.33.0) then AddToSet(CustomPagesWithUnseenOptions,SSHChoicePage.ID); data:=ReplayChoice(Tortoise Option,); if (data=true) then TortoisePlink.Checked:=True; end else begin SSHChoicePage:=NIL; end; (* * Create a custom page for HTTPS implementation (cURL) setting. *) CurlVariantPage:=CreatePage(PrevPageID,Choosing HTTPS transport backend,Which SSL/TLS library would you like Git to use for HTTPS connections?,TabOrder,Top,Left); // 1st choice RdbCurlVariant[GC_OpenSSL]:=CreateRadioButton(CurlVariantPage,Use the OpenSSL library,Server certificates will be validated using the ca-bundle.crt file.,TabOrder,Top,Left); // 2nd choice RdbCurlVariant[GC_WinSSL]:=CreateRadioButton(CurlVariantPage,Use the native Windows Secure Channel library,Server certificates will be validated using Windows Certificate Stores.+#13+This option also allows you to use your company»s internal Root CA certificates+#13+distributed e.g. via Active Directory Domain Services.,TabOrder,Top,Left); // Restore the setting chosen during a previous install. case ReplayChoice(CURL Option,OpenSSL) of OpenSSL: RdbCurlVariant[GC_OpenSSL].Checked:=True; WinSSL: RdbCurlVariant[GC_WinSSL].Checked:=True; else RdbCurlVariant[GC_OpenSSL].Checked:=True; end; (* * Create a custom page for the core.autocrlf setting. *) CRLFPage:=CreatePage(PrevPageID,Configuring the line ending conversions,How should Git treat line endings in text files?,TabOrder,Top,Left); // 1st choice RdbCRLF[GC_CRLFAlways]:=CreateRadioButton(CRLFPage,Checkout Windows-style, commit Unix-style line endings,Git will convert LF to CRLF when checking out text files. When committing+#13+text files, CRLF will be converted to LF. For cross-platform projects,+#13+this is the recommended setting on Windows («core.autocrlf» is set to «true»).,TabOrder,Top,Left); // 2nd choice RdbCRLF[GC_LFOnly]:=CreateRadioButton(CRLFPage,Checkout as-is, commit Unix-style line endings,Git will not perform any conversion when checking out text files. When+#13+committing text files, CRLF will be converted to LF. For cross-platform projects,+#13+this is the recommended setting on Unix («core.autocrlf» is set to «input»).,TabOrder,Top,Left); // 3rd choice RdbCRLF[GC_CRLFCommitAsIs]:=CreateRadioButton(CRLFPage,Checkout as-is, commit as-is,Git will not perform any conversions when checking out or committing+#13+text files. Choosing this option is not recommended for cross-platform+#13+projects («core.autocrlf» is set to «false»).,TabOrder,Top,Left); // Restore the setting chosen during a previous install. case ReplayChoice(CRLF Option,CRLFAlways) of LFOnly: RdbCRLF[GC_LFOnly].Checked:=True; CRLFAlways: RdbCRLF[GC_CRLFAlways].Checked:=True; CRLFCommitAsIs: RdbCRLF[GC_CRLFCommitAsIs].Checked:=True; else RdbCRLF[GC_CRLFAlways].Checked:=True; end; (* * Create a custom page for Git Bash’s terminal emulator setting. *) BashTerminalPage:=CreatePage(PrevPageID,Configuring the terminal emulator to use with Git Bash,Which terminal emulator do you want to use with your Git Bash?,TabOrder,Top,Left); // 1st choice RdbBashTerminal[GB_MinTTY]:=CreateRadioButton(BashTerminalPage,Use MinTTY (the default terminal of MSYS2),Git Bash will use MinTTY as terminal emulator, which sports a resizable window,+#13+non-rectangular selections and a Unicode font. Windows console programs (such+#13+as interactive Python) must be launched via `winpty` to work in MinTTY.,TabOrder,Top,Left); // 2nd choice RdbBashTerminal[GB_ConHost]:=CreateRadioButton(BashTerminalPage,Use Windows» default console window,Git will use the default console window of Windows («cmd.exe»), which works well+#13+with Win32 console programs such as interactive Python or node.js, but has a+#13+very limited default scroll-back, needs to be configured to use a Unicode font in+#13+order to display non-ASCII characters correctly, and prior to Windows 10 its+#13+window was not freely resizable and it only allowed rectangular text selections.,TabOrder,Top,Left); // Restore the setting chosen during a previous install. case ReplayChoice(Bash Terminal Option,MinTTY) of MinTTY: RdbBashTerminal[GB_MinTTY].Checked:=True; ConHost: RdbBashTerminal[GB_ConHost].Checked:=True; else RdbBashTerminal[GB_MinTTY].Checked:=True; end; (* * Create a custom page for the default behavior of `git pull`. *) GitPullBehaviorPage:=CreatePage(PrevPageID,Choose the default behavior of `git pull`,What should `git pull` do by default?,TabOrder,Top,Left); // 1st choice RdbGitPullBehavior[GP_GitPullMerge]:=CreateRadioButton(GitPullBehaviorPage,Default (fast-forward or merge),This is the standard behavior of `git pull`: fast-forward the current branch to+#13+the fetched branch when possible, otherwise create a merge commit.,TabOrder,Top,Left); // 2nd choice RdbGitPullBehavior[GP_GitPullRebase]:=CreateRadioButton(GitPullBehaviorPage,Rebase,Rebase the current branch onto the fetched branch. If there are no local+#13+commits to rebase, this is equivalent to a fast-forward.,TabOrder,Top,Left); // 3rd choice RdbGitPullBehavior[GP_GitPullFFOnly]:=CreateRadioButton(GitPullBehaviorPage,Only ever fast-forward,Fast-forward to the fetched branch. Fail if that is not possible.,TabOrder,Top,Left); // Restore the setting chosen during a previous install. case ReplayChoice(Git Pull Behavior Option,Merge) of Merge: RdbGitPullBehavior[GP_GitPullMerge].Checked:=True; Rebase: RdbGitPullBehavior[GP_GitPullRebase].Checked:=True; FFOnly: RdbGitPullBehavior[GP_GitPullFFOnly].Checked:=True; else RdbGitPullBehavior[GP_GitPullMerge].Checked:=True; end; (* * Create a custom page for the choice of Git Credential Manager. *) GitCredentialManagerPage:=CreatePage(PrevPageID,Choose a credential helper,Which credential helper should be configured?,TabOrder,Top,Left); // Git Credential Manager RdbGitCredentialManager[GCM]:=CreateRadioButton(GitCredentialManagerPage,Git Credential Manager,Use the <A HREF=https://github.com/GitCredentialManager/git-credential-manager>cross-platform Git Credential Manager</A>.+#13+See more information about the future of Git Credential Manager <A HREF=https://github.com/GitCredentialManager/git-credential-manager/blob/HEAD/docs/faq.md#about-the-project>here</A>.,TabOrder,Top,Left); // No credential helper RdbGitCredentialManager[GCM_None]:=CreateRadioButton(GitCredentialManagerPage,None,Do not use a credential helper.,TabOrder,Top,Left); // Restore the settings chosen during a previous install, if .NET Framework 4.7.2 // or later is available. if DetectNetFxVersion()<461808 then begin RdbGitCredentialManager[GCM].Checked:=False; RdbGitCredentialManager[GCM].Enabled:=False; RdbGitCredentialManager[GCM].Caption:=RdbGitCredentialManager[GCM].Caption+ (requires .NET Framework >= 4.7.2) end else begin case ReplayChoice(Use Credential Manager,Enabled) of Disabled: RdbGitCredentialManager[GCM_None].Checked:=True; Enabled: RdbGitCredentialManager[GCM].Checked:=True; Core: RdbGitCredentialManager[GCM].Checked:=True; else RdbGitCredentialManager[GCM].Checked:=True; end; end; (* * Create a custom page for extra options. *) ExtraOptionsPage:=CreatePage(PrevPageID,Configuring extra options,Which features would you like to enable?,TabOrder,Top,Left); // 1st option RdbExtraOptions[GP_FSCache]:=CreateCheckBox(ExtraOptionsPage,Enable file system caching,File system data will be read in bulk and cached in memory for certain+#13+operations («core.fscache» is set to «true»). This provides a significant+#13+performance boost.,TabOrder,Top,Left); // Restore the settings chosen during a previous install. RdbExtraOptions[GP_FSCache].Checked:=ReplayChoice(Performance Tweaks FSCache,Enabled)<>Disabled; // 2nd option RdbExtraOptions[GP_Symlinks]:=CreateCheckBox(ExtraOptionsPage,Enable symbolic links,Enable <A HREF=https://github.com/git-for-windows/git/wiki/Symbolic-Links>symbolic links</A> (requires the SeCreateSymbolicLink permission).+#13+Please note that existing repositories are unaffected by this setting.,TabOrder,Top,Left); // Restore the settings chosen during a previous install, or auto-detect // by running `mklink` (unless started as administrator, in which case that // test would be meaningless). Data:=ReplayChoice(Enable Symlinks,Auto); if (Data=Auto) Or ((Data=Disabled) And (VersionCompare(PreviousGitForWindowsVersion,2.14.1)<=0)) then begin if EnableSymlinksByDefault() then Data:=Enabled else Data:=Disabled; end; RdbExtraOptions[GP_Symlinks].Checked:=Data<>Disabled; #ifdef HAVE_EXPERIMENTAL_OPTIONS (* * Create a custom page for experimental options. *) ExperimentalOptionsPage:=CreatePage(PrevPageID,Configuring experimental options,These features are developed actively. Would you like to try them?,TabOrder,Top,Left); #ifdef WITH_EXPERIMENTAL_BUILTIN_DIFFTOOL RdbExperimentalOptions[GP_BuiltinDifftool]:=CreateCheckBox(ExperimentalOptionsPage,Enable experimental, builtin difftool,Use the experimental builtin difftool (fast, but only lightly tested).,TabOrder,Top,Left); // Restore the settings chosen during a previous install RdbExperimentalOptions[GP_BuiltinDifftool].Checked:=ReplayChoice(Enable Builtin Difftool,Auto)=Enabled; #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_REBASE RdbExperimentalOptions[GP_BuiltinRebase]:=CreateCheckBox(ExperimentalOptionsPage,Enable experimental, built-in rebase,<RED>(NEW!)</RED> Use the experimental built-in rebase (about 70% faster, but only+#13+lightly tested).,TabOrder,Top,Left); // Restore the settings chosen during a previous install RdbExperimentalOptions[GP_BuiltinRebase].Checked:=ReplayChoice(Enable Builtin Rebase,Auto)=Enabled; #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_STASH RdbExperimentalOptions[GP_BuiltinStash]:=CreateCheckBox(ExperimentalOptionsPage,Enable experimental, built-in stash,<RED>(NEW!)</RED> Use the experimental built-in stash (about 90% faster, but only+#13+lightly tested).,TabOrder,Top,Left); // Restore the settings chosen during a previous install RdbExperimentalOptions[GP_BuiltinStash].Checked:=ReplayChoice(Enable Builtin Stash,Auto)=Enabled; #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_ADD_I RdbExperimentalOptions[GP_BuiltinAddI]:=CreateCheckBox(ExperimentalOptionsPage,Enable experimental, built-in add -i/-p,<RED>(NEW!)</RED> Use the experimental built-in interactive add («git add -i» or «git add -p»).+#13+This makes it faster (especially the startup!), but it is not yet considered robust.,TabOrder,Top,Left); // Restore the settings chosen during a previous install RdbExperimentalOptions[GP_BuiltinAddI].Checked:=ReplayChoice(Enable Builtin Interactive Add,Auto)=Enabled; #endif #ifdef WITH_EXPERIMENTAL_PCON RdbExperimentalOptions[GP_EnablePCon]:=CreateCheckBox(ExperimentalOptionsPage,Enable experimental support for pseudo consoles.,<RED>(NEW!)</RED> This allows running native console programs like Node or Python in a+#13+Git Bash window without using winpty, but it still has known bugs.,TabOrder,Top,Left); // Restore the settings chosen during a previous install RdbExperimentalOptions[GP_EnablePCon].Checked:=ReplayChoice(Enable Pseudo Console Support,Auto)=Enabled; #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_FSMONITOR RdbExperimentalOptions[GP_EnableFSMonitor]:=CreateCheckBox(ExperimentalOptionsPage,Enable experimental built-in file system monitor,<RED>(NEW!)</RED> Automatically run a <A HREF=https://github.com/git-for-windows/git/discussions/3251>built-in file system watcher</A>, to speed up common+#13+operations such as `git status`, `git add`, `git commit`, etc in worktrees+#13+containing many files.,TabOrder,Top,Left); // Restore the settings chosen during a previous install RdbExperimentalOptions[GP_EnableFSMonitor].Checked:=ReplayChoice(Enable FSMonitor,Auto)=Enabled; #endif #endif PageIDBeforeInstall:=CurrentCustomPageID; (* * Create a custom page for finding the processes that lock a module. *) ProcessesPage:=CreateCustomPage( #ifdef DEBUG_WIZARD_PAGE PrevPageID #else wpPreparing #endif , Replacing in-use files , The following applications use files that need to be replaced, please close them. ); ProcessesListBox:=TListBox.Create(ProcessesPage); with ProcessesListBox do begin Parent:=ProcessesPage.Surface; Width:=ProcessesPage.SurfaceWidth; Height:=ProcessesPage.SurfaceHeight-ScaleY(8); end; ProcessesRefresh:=TNewButton.Create(WizardForm); with ProcessesRefresh do begin Parent:=WizardForm; Width:=WizardForm.CancelButton.Width; Height:=WizardForm.CancelButton.Height; Top:=WizardForm.CancelButton.Top; Left:=WizardForm.ClientWidth-(WizardForm.CancelButton.Left+WizardForm.CancelButton.Width); Caption:=&Refresh; OnClick:=@RefreshProcessList; end; // This button is only used by the uninstaller. ContinueButton:=NIL; #ifdef DEBUG_WIZARD_PAGE DebugWizardPage:={#DEBUG_WIZARD_PAGE}; #endif // Initially hide the Refresh button, show it when the process page becomes current. ProcessesRefresh.Hide; end; function ShouldSkipPage(PageID:Integer):Boolean; var Msg,Cmd,StdOut,StdErr:String; Res:DWORD; begin if (ProcessesPage<>NIL) and (PageID=ProcessesPage.ID) then begin // This page is only reached forward (by pressing «Next», never by pressing «Back»). if (ParamIsSet(SKIPIFINUSE) or ParamIsSet(VSNOTICE)) then begin AppDir:=ExpandConstant({app}); if DirExists(AppDir) then begin if not FileExists(ExpandConstant({tmp}blocked-file-util.exe)) then ExtractTemporaryFile(blocked-file-util.exe); Cmd:=«+ExpandConstant({tmp}blocked-file-util.exe)+» blocking-pids «+AppDir+«; if not ExecWithCapture(Cmd,StdOut,StdErr,Res) or (Res<>0) then begin Msg:=Skipping installation because +AppDir+ is still in use:+#13+#10+StdErr; if ParamIsSet(SKIPIFINUSE) or (ExpandConstant({log})=) then LogError(Msg) else Log(Msg); ExitEarlyWithSuccess(); end; end; end; RefreshProcessList(NIL); Result:=(GetArrayLength(Processes)=0); end else if OnlyShowNewOptions.Checked then begin if (IsInSet(AllCustomPages,PageID)) then Result:=not IsInSet(CustomPagesWithUnseenOptions,PageID) else if (PageID=wpSelectComponents) and HasUnseenComponents then Result:=False else Result:=(PageID<>wpInfoBefore) and (PageID<>wpFinished); end else Result:=False; #ifdef DEBUG_WIZARD_PAGE Result:=PageID<>DebugWizardPage Exit; #endif end; procedure CurPageChanged(CurPageID:Integer); begin CurrentCustomPageID:=CurPageID; if CurPageID=wpInfoBefore then begin if WizardForm.NextButton.Enabled then begin // By default, do not show a blinking cursor for InfoBeforeFile. WizardForm.ActiveControl:=WizardForm.NextButton; end; end else if CurPageID=wpSelectDir then begin if not IsDirWritable(WizardDirValue) then begin // If the default directory is not writable, choose another default that most likely is. // This will be checked later again when the user clicks «Next». WizardForm.DirEdit.Text:=ExpandConstant({userpf}{#APP_NAME}); end; end else if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin // Show the «Refresh» button only on the processes page. ProcessesRefresh.Show; WizardForm.NextButton.Caption:=SetupMessage(msgButtonInstall); end else begin ProcessesRefresh.Hide; end; AdjustNextButtonLabel(Nil); end; function NextButtonClick(CurPageID:Integer):Boolean; var i,j:Integer; Version:TWindowsVersion; Msg:String; begin // On a silent install, if your NextButtonClick function returns False // prior to installation starting, Setup will exit automatically. Result:=True; if CurPageID=wpSelectDir then begin if not IsDirWritable(WizardDirValue) then begin SuppressibleMsgBox( The specified installation directory does not seem to be writable. + + Please choose another directory or restart setup as a user with sufficient permissions. , mbCriticalError , MB_OK , IDOK ); Result:=False; Exit; end; end; if (EditorPage<>NIL) and (CurPageID=EditorPage.ID) then begin EditorSelectionChanged(NIL); (* * Before continuing, we need to check one last time if the path * to the custom editor (if selected) is still valid. *) if (CbbEditor.ItemIndex=GE_CustomEditor) and not PathIsValidExecutable(CustomEditorPath) then begin Result:=False; MsgBox(The path you specified is no longer available.,mbError,MB_OK); Wizardform.NextButton.Enabled:=False; Exit; end; end else if (SSHChoicePage<>NIL) and (CurPageID=SSHChoicePage.ID) then begin Result:=RdbSSH[GS_OpenSSH].Checked or (RdbSSH[GS_ExternalOpenSSH].Checked and (FileSearch(ssh.exe, GetEnv(PATH)) <> )) or (RdbSSH[GS_Plink].Checked and FileExists(EdtPlink.Text)); if not Result then begin SuppressibleMsgBox({#PLINK_PATH_ERROR_MSG},mbError,MB_OK,IDOK); end; end else if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin RefreshProcessList(NIL); // It would have been nicer to just disable the «Next» button, but the // WizardForm exports the button just read-only. for i:=0 to GetArrayLength(Processes)-1 do begin if Processes[i].ToTerminate then begin if not TerminateProcessByID(Processes[i].ID) then begin SuppressibleMsgBox(Failed to terminate +Processes[i].Name+ (pid +IntToStr(Processes[i].ID)+)+#13+Please terminate it manually and press the «Refresh» button.,mbCriticalError,MB_OK,IDOK); Result:=False; Exit; end; end else if not Processes[i].Restartable then begin if WizardSilent() and (ParamIsSet(SKIPIFINUSE) or ParamIsSet(VSNOTICE)) then begin Msg:=Skipping installation because the process +Processes[i].Name+ (pid +IntToStr(Processes[i].ID)+) is running, using Git for Windows+#39+ files.; if ParamIsSet(SKIPIFINUSE) or (ExpandConstant({log})=) then LogError(Msg) else Log(Msg); ExitEarlyWithSuccess(); end; if WizardSilent() then while True do begin Msg:=; for j:=i to GetArrayLength(Processes)-1 do if not Processes[j].Restartable then Msg:=Msg+#13+#10+Processes[j].Name+ (PID +IntToStr(Processes[j].ID)+) else if (Processes[j].ToTerminate) and (not TerminateProcessByID(Processes[i].ID)) then Msg:=Msg+#13+#10+Processes[j].Name+ (PID +IntToStr(Processes[j].ID)+); if Msg= then begin Result:=True; Exit; end; Msg:=The following process(es) use Git for Windows:+#13+#10+Msg+#13+#10+#13+#10+Please terminate those processes and retry.+#13+#10+Alternatively, cancel to abandon setup altogether.; if SuppressibleMsgBox(Msg, mbCriticalError, MB_RETRYCANCEL, IDCANCEL) = IDCANCEL then begin Result:=False; Exit; end; RefreshProcessList(NIL); i:=0; end else begin Msg:=Setup cannot continue until you close at least those applications in the list that are marked as «closing is required».; SuppressibleMsgBox(Msg, mbCriticalError, MB_OK, IDOK); end; Result:=False; Exit; end; end; Result:=(GetArrayLength(Processes)=0); if not Result then begin Result:=(SuppressibleMsgBox( If you continue without closing the listed applications they will be terminated/restarted automatically. + #13 + #13 + Are you sure you want to continue? , mbConfirmation , MB_YESNO , IDYES )=IDYES); end; end; end; // Procedure to create hardlinks for builtins. This procedure relies upon that // git-wrapper.exe is already copied to {app}tmp. procedure HardlinkOrCopyGit(FileName:String;Builtin:Boolean); var GitTarget:String; LinkCreated:Boolean; begin if FileExists(FileName) and (not DeleteFile(FileName)) then begin Log(Line {#__LINE__}: Unable to delete existing built-in «+FileName+«, skipping.); Exit; end; if Builtin then GitTarget:=AppDir+{#MINGW_BITNESS}bingit.exe else // For non-builtins, we want to use the Git wrapper in cmd GitTarget:=AppDir+cmdgit.exe; try // This will throw an exception on pre-Win2k systems. LinkCreated:=CreateHardLink(FileName,GitTarget,0); except LinkCreated:=False; Log(Line {#__LINE__}: Creating hardlink «+FileName+» failed, will try a copy.); end; if not LinkCreated then begin if not FileCopy(AppDir+tmpgit-wrapper.exe,FileName,False) then begin Log(Line {#__LINE__}: Creating copy «+FileName+» failed.); // This is not a critical error, Git could basically be used without the // aliases for built-ins, so we continue. end; end; end; procedure QueryUninstallValues; var Domain:Integer; Key,Path:String; begin Key:=MicrosoftWindowsCurrentVersionUninstallGit_is1; if RegKeyExists(HKEY_LOCAL_MACHINE,SoftwareWow6432Node+Key) then begin Domain:=HKEY_LOCAL_MACHINE; Key:=SoftwareWow6432Node+Key; end else if RegKeyExists(HKEY_CURRENT_USER,SoftwareWow6432Node+Key) then begin Domain:=HKEY_CURRENT_USER; Key:=SoftwareWow6432Node+Key; end else if RegKeyExists(HKEY_LOCAL_MACHINE,Software+Key) then begin Domain:=HKEY_LOCAL_MACHINE; Key:=Software+Key; end else if RegKeyExists(HKEY_CURRENT_USER,Software+Key) then begin Domain:=HKEY_CURRENT_USER; Key:=Software+Key; end else Domain:=-1; if Domain<>-1 then begin if not RegQueryStringValue(Domain,Key,Inno Setup: App Path,UninstallAppPath) then UninstallAppPath:=; if not RegQueryStringValue(Domain,Key,UninstallString,UninstallString) then UninstallString:=; end else begin UninstallAppPath:=; UninstallString:=; end; end; procedure CleanupWhenUpgrading; var StdOut,StdErr:String; ErrorCode:DWORD; begin if UninstallAppPath<> then begin // Save a copy of the system config so that we can copy it back later if FileExists(UninstallAppPath+{#MINGW_BITNESS}etcgitconfig) then begin if (not FileCopy(UninstallAppPath+{#MINGW_BITNESS}etcgitconfig,ExpandConstant({tmp}gitconfig.system),True)) then LogError(Could not save system config; continuing anyway); // Save a copy of the system config so that we can copy it back later end else if FileExists(UninstallAppPath+etcgitconfig) and (not FileCopy(UninstallAppPath+etcgitconfig,ExpandConstant({tmp}gitconfig.system),True)) then LogError(Could not save system config; continuing anyway); end; if UninstallString<> then begin WizardForm.StatusLabel.Caption:=Removing previous Git version (+PreviousGitForWindowsVersion+); if not ExecWithCapture(UninstallString+ /VERYSILENT /SILENT /NORESTART /SUPPRESSMSGBOXES,StdOut,StdErr,ErrorCode) then LogError(Could not uninstall previous version (stderr: +StdErr+). Trying to continue anyway.); end; end; procedure HardlinkOrCopy(Target,Source:String); var LinkCreated:Boolean; begin try // This will throw an exception on pre-Win2k systems. LinkCreated:=CreateHardLink(Target,Source,0); except LinkCreated:=False; Log(Line {#__LINE__}: Creating hardlink «+Target+» failed, will try a copy.); end; if not LinkCreated then begin if not FileCopy(Source,Target,False) then begin Log(Line {#__LINE__}: Creating copy «+Target+» failed.); end; end; end; procedure MaybeHardlinkDLLFiles; var FindRec: TFindRec; Bin,LibExec:String; begin Bin:=AppDir+{#MINGW_BITNESS}bin; LibExec:=AppDir+{#MINGW_BITNESS}libexecgit-core; if FindFirst(ExpandConstant(Bin+*.dll), FindRec) then try repeat if ((FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY) = 0) then begin if FileExists(LibExec+FindRec.Name) then DeleteFile(LibExec+FindRec.Name); HardlinkOrCopy(LibExec+FindRec.Name,Bin+FindRec.Name); end; until not FindNext(FindRec); finally FindClose(FindRec); end; end; function ReplaceFile(SourceFile,TargetFile:String):Boolean; begin if not DeleteFile(TargetFile) then begin LogError(Line {#__LINE__}: Unable to delete file «+TargetFile+«.); Result:=False; Exit; end; if not RenameFile(SourceFile,TargetFile) then begin LogError(Line {#__LINE__}: Unable to overwrite file «+TargetFile+» with «+SourceFile+«.); Result:=False; Exit; end; Result:=True; end; procedure InstallAutoUpdater; var AppPath,XMLPath,Start:String; begin Start:=GetDateTimeString(yyyy-mm-dd,,:)+T+GetDateTimeString(hh:nn:ss,,:); XMLPath:=ExpandConstant({tmp}auto-updater.xml); AppPath:=ExpandConstant({app}); SaveStringToFile(XMLPath, <?xml version=»1.0″ encoding=»UTF-16″?>+ <Task version=»1.2″ xmlns=»http://schemas.microsoft.com/windows/2004/02/mit/task»>+ <Settings>+ <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>+ <RunOnlyIfNetworkAvailable>true</RunOnlyIfNetworkAvailable>+ <StartWhenAvailable>true</StartWhenAvailable>+ <IdleSettings>+ <StopOnIdleEnd>false</StopOnIdleEnd>+ <RestartOnIdle>false</RestartOnIdle>+ </IdleSettings>+ </Settings>+ <Triggers>+ <CalendarTrigger>+ <StartBoundary>+Start+</StartBoundary>+ <ExecutionTimeLimit>PT4H</ExecutionTimeLimit>+ <ScheduleByDay>+ <DaysInterval>1</DaysInterval>+ </ScheduleByDay>+ </CalendarTrigger>+ </Triggers>+ <Actions Context=»Author»>+ <Exec>+ <Command>»+AppPath+git-bash.exe»</Command>+ <Arguments>—hide —no-needs-console —command=cmdgit.exe update-git-for-windows —quiet —gui</Arguments>+ </Exec>+ </Actions>+ </Task>,False); ExecSilently(schtasks /Create /F /TN «Git for Windows Updater» /XML «+XMLPath+«,install-autoupdate,ExpandConstant(Line {#__LINE__}: Unable to schedule the Git for Windows updater)); end; procedure UninstallAutoUpdater; begin ExecSilently(schtasks /Delete /F /TN «Git for Windows Updater»,remove-autoupdate,ExpandConstant(Line {#__LINE__}: Unable to remove the Git for Windows updater)); end; procedure InstallWindowsTerminalFragment; var Res:Longint; AppPath,JSONDirectory,JSONPath:String; begin if IsAdminInstallMode() then JSONDirectory:=ExpandConstant({commonappdata}MicrosoftWindows TerminalFragmentsGit) else JSONDirectory:=ExpandConstant({localappdata}MicrosoftWindows TerminalFragmentsGit); if not ForceDirectories(JSONDirectory) then begin LogError(Line {#__LINE__}: Unable to install Windows Terminal Fragment to +JSONDirectory); Exit; end; JSONPath:=JSONDirectory+git-bash.json; AppPath:=ExpandConstant({app}); StringChangeEx(AppPath, , /, True) if not SaveStringToFile(JSONPath, {+ «profiles»: [+ {+ «guid»: «{2ece5bfe-50ed-5f3a-ab87-5cd4baafed2b}»,+ «name»: «Git Bash»,+ «commandline»: «+AppPath+/bin/bash.exe -i -l»,+ «icon»: «+AppPath+/{#MINGW_BITNESS}/share/git/git-for-windows.ico»,+ «startingDirectory»: «%USERPROFILE%»+ }+ ]+ },False) then begin LogError(Line {#__LINE__}: Unable to install Windows Terminal Fragment to +JSONPath) end; end; { Create Cygwin’s idea of a symbolic link: — a system file — starting with the prefix `!<symlink>xffxfe` — followed by the symlink target, in UTF-16 — ending in two NUL bytes (reflecting a UTF-16 NUL) } function GetFileAttributes(Path:PAnsiChar):DWORD; external GetFileAttributesA@kernel32.dll stdcall; function SetFileAttributes(Path:PAnsiChar;dwFileAttributes:DWORD):BOOL; external SetFileAttributesA@kernel32.dll stdcall; function CryptStringToBinary(sz:string;cch:LongWord;flags:LongWord;binary:string;var size:LongWord;skip:LongWord;flagsused:LongWord):Integer; external CryptStringToBinaryW@crypt32.dll stdcall; const CRYPT_STRING_HEX = $04; HEX_CHARS = 0123456789abcdef; function CharToHex(C:Integer):string; begin Result:=HEX_CHARS[((C div 16) and 15)+1]+HEX_CHARS[(C and 15)+1]; end; function CreateCygwinSymlink(SymlinkPath,TargetPath:String):Boolean; var Attribute:DWord; i:Integer; Hex,Buffer:string; Stream:TStream; Size:LongWord; begin Result:=True; // assuming that the target is actually all-ASCII, convert to UTF-16 for i:=Length(TargetPath) downto 1 do TargetPath:=Copy(TargetPath,1,i)+#0+Copy(TargetPath,i+1,Length(TargetPath)-i); Hex:=213c73796d6c696e6b3efffe; // «!<symlink>xffxfe» for i:=1 to Length(TargetPath) do Hex:=Hex+CharToHex(Ord(TargetPath[i])); // append wide characters as hex Hex:=Hex+0000; // append a wide NUL // write the file Stream:=TFileStream.Create(SymlinkPath,fmCreate); try Size:=Length(Hex) div 2; SetLength(Buffer,Size); if (CryptStringToBinary(Hex,Length(Hex),CRYPT_STRING_HEX,Buffer,Size,0,0)=0) or (Size<>Length(Hex) div 2) then RaiseException(could not decode hex +Hex); Stream.WriteBuffer(Buffer,Size); except LogError(Could not write «+SymlinkPath+» +GetExceptionMessage()); Result:=False; finally Stream.Free end; // Set system bit (required for Cygwin to interpret this as a symlink) Attribute:=GetFileAttributes(SymlinkPath); if (Attribute and 4) = 0 then begin Attribute:=Attribute or 4; if not SetFileAttributes(SymlinkPath,Attribute) then begin LogError(Could not mark «+SymlinkPath+» as system file); Result:=False; end; end; end; function UpgradeFromDotNetBasedScalar:Boolean; var RegKey,UninstallScalar,ScalarExe,Cmd,StdOut,StdErr:String; Res:DWORD; Enlistments:TArrayOfString; i:Integer; begin Result:=True; RegKey:=SOFTWAREMicrosoftWindowsCurrentVersionUninstall{82F731CB-1CFC-406D-8D84-8467BF6040C7}_is1; if not RegQueryStringValue(HKEY_LOCAL_MACHINE,RegKey,UninstallString,UninstallScalar) then // No existing Scalar found; ignore this silently Exit; // Check twice that .NET-based Scalar is there ScalarExe:=UninstallScalar; StringChangeEx(ScalarExe,«,,True); // strip the surrounding double-quote characters StringChangeEx(ScalarExe,unins000.exe,scalar.exe,True); if not FileExists(ScalarExe) then Exit; WizardForm.StatusLabel.Caption:=Upgrading from .NET-based Scalar; // First, get .NET-based Scalar’s idea of the registered enlistments if not ExecSilently(«+ScalarExe+» list,scalar-list,ExpandConstant(Line {#__LINE__}: Unable to run `scalar list`)) then Result:=False; LoadStringsFromFile(ExpandConstant({tmp}scalar-list.out),Enlistments); // Now, register them with the C-based Scalar for i:=0 to Length(Enlistments)-1 do begin if DirExists(Enlistments[i]) then begin WizardForm.StatusLabel.Caption:=Registering +Enlistments[i]+ with Scalar; ExecSilentlyAsOriginalUser(«+AppDir+cmdscalar.exe» register «+Enlistments[i]+«,scalar-register-+IntToStr(i),ExpandConstant(Line {#__LINE__}: Could not register «+Enlistments[i]+» with Scalar)); end; end; // Now uninstall the .NET-based Scalar // (leaving C:ProgramDataScalar in place, in case // the user needs to downgrade again to get unblocked) WizardForm.StatusLabel.Caption:=Uninstalling .NET-based Scalar; Cmd:=UninstallScalar+/VERYSILENT /SILENT /NORESTART /SUPPRESSMSGBOXES /LOG; if (not ExecWithCapture(Cmd,StdOut,StdErr,Res)) or (Res<>0) then LogError(Could not uninstall Scalar (stderr: +StdErr+). Trying to continue anyway.); end; procedure CurStepChanged(CurStep:TSetupStep); var DllPath,FileName,Cmd,Msg,Ico:String; BuiltIns,ImageNames,EnvPath:TArrayOfString; Count,i:Longint; RootKey:Integer; begin if CurStep=ssInstall then begin #ifdef DO_NOT_INSTALL ExitEarlyWithSuccess(); #endif // Shutdown locking processes just before the actual installation starts. if SessionHandle>0 then try RmShutdown(SessionHandle,RmShutdownOnlyRegistered,0); except Log(Line {#__LINE__}: RmShutdown not supported.); end; CleanupWhenUpgrading(); Exit; end; // Make sure the code below is only executed just after the actual installation finishes. if CurStep<>ssPostInstall then begin Exit; end; AppDir:=ExpandConstant({app}); { Copy dlls from «/mingw64/bin» to «/mingw64/libexec/git-core» if they are conflicting with system ones. For example, if a dll named «ssleay32.dll» in «/mingw64/bin» is also present in «%SystemRootSystem32», the version in «/mingw64/bin» is copied to «/mingw64/libexec/git-core». This call ensures that the dll in «/mingw64/libexec/git-core» is picked first when Windows load dll dependencies for executables in «/mingw64/libexec/git-core». (See https://github.com/git-for-windows/git/issues/145) } WizardForm.StatusLabel.Caption:=Hard-linking .dll files; MaybeHardlinkDLLFiles(); { Create the symlinks in `/dev/` } WizardForm.StatusLabel.Caption:=Creating some Cygwin symlinks; CreateCygwinSymlink(AppDir+devfd,/proc/self/fd); CreateCygwinSymlink(AppDir+devstdin,/proc/self/fd/0); CreateCygwinSymlink(AppDir+devstdout,/proc/self/fd/1); CreateCygwinSymlink(AppDir+devstderr,/proc/self/fd/2); { Create the built-ins } WizardForm.StatusLabel.Caption:=Hard-linking Git+#39+s built-ins; // Load the built-ins from a text file. FileName:=AppDir+{#MINGW_BITNESS}{#APP_BUILTINS}; if not FileExists(FileName) then Exit; // testing… if LoadStringsFromFile(FileName,BuiltIns) then begin Count:=GetArrayLength(BuiltIns)-1; // Delete those scripts from «bin» which have been replaced by built-ins in «libexecgit-core». for i:=0 to Count do begin FileName:=AppDir+{#MINGW_BITNESS}bin+ChangeFileExt(ExtractFileName(BuiltIns[i]),); if FileExists(FileName) and (not DeleteFile(FileName)) then begin Log(Line {#__LINE__}: Unable to delete script «+FileName+«, ignoring.); end; end; // Copy git-wrapper to the temp directory. if not FileCopy(AppDir+{#MINGW_BITNESS}libexecgit-coregit-log.exe,AppDir+tmpgit-wrapper.exe,False) then begin Log(Line {#__LINE__}: Creating copy «+AppDir+tmpgit-wrapper.exe» failed.); end; // Create built-ins as aliases for git.exe. for i:=0 to Count do begin FileName:=AppDir+{#MINGW_BITNESS}bin+BuiltIns[i]; if FileExists(FileName) then begin HardlinkOrCopyGit(FileName,True); end; FileName:=AppDir+{#MINGW_BITNESS}libexecgit-core+BuiltIns[i]; if FileExists(FileName) then begin HardlinkOrCopyGit(FileName,True); end; end; if IsComponentSelected(gitlfs) then begin HardlinkOrCopyGit(AppDir+cmdgit-lfs.exe,False); end; // Delete git-wrapper from the temp directory. if not DeleteFile(AppDir+tmpgit-wrapper.exe) then begin Log(Line {#__LINE__}: Deleting temporary «+AppDir+tmpgit-wrapper.exe» failed.); end; end else LogError(Line {#__LINE__}: Unable to read file «{#MINGW_BITNESS}{#APP_BUILTINS}».); // Copy previous system wide git config file, if any if FileExists(ExpandConstant({tmp}gitconfig.system)) then begin if (not ForceDirectories(AppDir+{#ETC_GITCONFIG_DIR})) then LogError(Failed to create {#ETC_GITCONFIG_DIR}; continuing anyway) else FileCopy(ExpandConstant({tmp}gitconfig.system),AppDir+{#ETC_GITCONFIG_DIR}gitconfig,True) end; { Configure some defaults in the system config } #ifdef HAVE_SET_SYSTEM_CONFIG_DEFAULTS WizardForm.StatusLabel.Caption:=Initializing the system config; if not SetSystemConfigDefaults() then LogError(Unable to set system config defaults); #endif { Configure http.sslBackend according to the user’s choice. } WizardForm.StatusLabel.Caption:=Configuring the SSL/TLS backend; if RdbCurlVariant[GC_WinSSL].Checked then GitSystemConfigSet(http.sslBackend,schannel) else GitSystemConfigSet(http.sslBackend,openssl); if not RdbCurlVariant[GC_WinSSL].Checked then begin Cmd:=AppDir+/{#MINGW_BITNESS}/etc/ssl/certs/ca-bundle.crt; if not FileExists(Cmd) then Cmd:=AppDir+/{#MINGW_BITNESS}/ssl/certs/ca-bundle.crt; StringChangeEx(Cmd,,/,True); GitSystemConfigSet(http.sslCAInfo,Cmd); end else GitSystemConfigSet(http.sslCAInfo,#0); { Adapt core.autocrlf } WizardForm.StatusLabel.Caption:=Configuring core.autoCRLF; if RdbCRLF[GC_LFOnly].checked then begin Cmd:=input; end else if RdbCRLF[GC_CRLFAlways].checked then begin Cmd:=true; end else begin Cmd:=false; end; GitSystemConfigSet(core.autocrlf,Cmd); { Configure the terminal window for Git Bash } if RdbBashTerminal[GB_ConHost].checked then begin WizardForm.StatusLabel.Caption:=Setting up Git Bash to run in a ConHost window; OverrideGitBashCommandLine(AppDir+git-bash.exe,SHOW_CONSOLE=1 APPEND_QUOTE=1 @@COMSPEC@@ /S /C «»@@EXEPATH@@usrbinbash.exe» —login -i); end; { Configure the default `git pull` behavior } WizardForm.StatusLabel.Caption:=Configuring default `git pull` behavior; if RdbGitPullBehavior[GP_GitPullMerge].Checked then begin GitSystemConfigSet(pull.rebase,false) end else if RdbGitPullBehavior[GP_GitPullRebase].Checked then begin GitSystemConfigSet(pull.rebase,true) end else if RdbGitPullBehavior[GP_GitPullFFOnly].Checked then begin GitSystemConfigSet(pull.ff,only) end; { Configure credential helper } WizardForm.StatusLabel.Caption:=Configuring the credential helper; if RdbGitCredentialManager[GCM_None].checked then begin GitSystemConfigSet(credential.helper,#0); GitSystemConfigSet(credential.https://dev.azure.com.useHttpPath,#0); end else if RdbGitCredentialManager[GCM].checked then begin GitSystemConfigSet(credential.helper,manager); GitSystemConfigSet(credential.https://dev.azure.com.useHttpPath,true); end; { Configure extra options } WizardForm.StatusLabel.Caption:=Configuring extra options; if RdbExtraOptions[GP_FSCache].checked then GitSystemConfigSet(core.fscache,true); if RdbExtraOptions[GP_Symlinks].checked then Cmd:=true else Cmd:=false; GitSystemConfigSet(core.symlinks,Cmd); { Configure experimental options } WizardForm.StatusLabel.Caption:=Configuring experimental options; #ifdef WITH_EXPERIMENTAL_BUILTIN_DIFFTOOL if RdbExperimentalOptions[GP_BuiltinDifftool].checked then GitSystemConfigSet(difftool.useBuiltin,true) else GitSystemConfigSet(difftool.useBuiltin,#0); #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_REBASE if RdbExperimentalOptions[GP_BuiltinRebase].checked then GitSystemConfigSet(rebase.useBuiltin,true) else GitSystemConfigSet(rebase.useBuiltin,#0); #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_STASH if RdbExperimentalOptions[GP_BuiltinStash].checked then GitSystemConfigSet(stash.useBuiltin,true) else GitSystemConfigSet(stash.useBuiltin,#0); #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_ADD_I if RdbExperimentalOptions[GP_BuiltinAddI].checked then GitSystemConfigSet(add.interactive.useBuiltin,true) else GitSystemConfigSet(add.interactive.useBuiltin,#0); #endif #ifdef WITH_EXPERIMENTAL_PCON if RdbExperimentalOptions[GP_EnablePCon].checked and not SaveStringToFile(ExpandConstant({app}etcgit-bash.config),MSYS=enable_pcon,False) then LogError(Could not write to +ExpandConstant({app}etcgit-bash.config)); #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_FSMONITOR if RdbExperimentalOptions[GP_EnableFSMonitor].checked then GitSystemConfigSet(core.fsmonitor,true) else GitSystemConfigSet(core.fsmonitor,#0); #endif { Modify the environment This must happen no later than ssPostInstall to make «ChangesEnvironment=yes» not happend before the change! } WizardForm.StatusLabel.Caption:=Modifying the environment; // Delete GIT_SSH and SVN_SSH if a previous installation set them (this is required for the GS_OpenSSH case). DeleteMarkedEnvString(GIT_SSH); DeleteMarkedEnvString(SVN_SSH); if (SSHChoicePage<>NIL) then begin GitSystemConfigSet(ssh.variant,#0); if RdbSSH[GS_Plink].Checked then begin SetAndMarkEnvString(GIT_SSH,EdtPlink.Text,True); SetAndMarkEnvString(SVN_SSH,EdtPlink.Text,True); if TortoisePlink.Checked then begin Log(Line {#__LINE__}: Setting config option ssh.variant=tortoiseplink); GitSystemConfigSet(ssh.variant,tortoiseplink) end else begin Log(Line {#__LINE__}: Setting config option ssh.variant=putty); GitSystemConfigSet(ssh.variant,putty); end; end; end; if RdbDefaultBranch[DB_Manual].Checked then GitSystemConfigSet(init.defaultBranch,EdtDefaultBranch.Text) else GitSystemConfigSet(init.defaultBranch,{#DEFAULT_BRANCH_NAME}); // Get the current user’s directories in PATH. EnvPath:=GetEnvStrings(PATH,IsAdminLoggedOn); // Modify the PATH variable as requested by the user. if RdbPath[GP_Cmd].Checked or RdbPath[GP_CmdTools].Checked then begin // First, remove the current installation directory from PATH. for i:=0 to GetArrayLength(EnvPath)-1 do begin if Pos(AppDir+,EnvPath[i]+)=1 then begin EnvPath[i]:=; end; end; i:=GetArrayLength(EnvPath); SetArrayLength(EnvPath,i+1); // List cmd before bin so cmd has higher priority and programs in // there will be called in favor of those in bin. EnvPath[i]:=AppDir+cmd; if RdbPath[GP_CmdTools].Checked then begin SetArrayLength(EnvPath,i+3); EnvPath[i+1]:=AppDir+{#MINGW_BITNESS}bin; EnvPath[i+2]:=AppDir+usrbin; end; end; // Set the current user’s PATH directories. if not SetEnvStrings(PATH,EnvPath,True,IsAdminLoggedOn,True) then LogError(Line {#__LINE__}: Unable to set the PATH environment variable.); { Create shortcuts that need to be created regardless of the «Don’t create a Start Menu folder» toggle } WizardForm.StatusLabel.Caption:=Configuring some shortcuts; Cmd:=AppDir+git-bash.exe; FileName:=AppDir+{#MINGW_BITNESS}sharegitgit-for-windows.ico; if IsComponentSelected(iconsquicklaunch) then begin CreateShellLink( ExpandConstant({userappdata}MicrosoftInternet ExplorerQuick LaunchGit Bash.lnk) , Git Bash , Cmd , —cd-to-home , %HOMEDRIVE%%HOMEPATH% , FileName , 0 , SW_SHOWNORMAL ); end; if IsComponentSelected(iconsdesktop) then begin CreateShellLink( GetShellFolder(desktop)+Git Bash.lnk , Git Bash , Cmd , —cd-to-home , %HOMEDRIVE%%HOMEPATH% , FileName , 0 , SW_SHOWNORMAL ); end; { Create the Windows Explorer integrations } WizardForm.StatusLabel.Caption:=Initializing Explorer integration; if IsAdminLoggedOn then begin RootKey:=HKEY_LOCAL_MACHINE; end else begin RootKey:=HKEY_CURRENT_USER; end; if IsComponentSelected(extshellhere) then begin Msg:=Git Ba&sh Here; Cmd:=«+AppDir+git-bash.exe» «—cd=%1»; Ico:=AppDir+git-bash.exe; if (not RegWriteStringValue(RootKey,SOFTWAREClassesDirectoryshellgit_shell,,Msg)) or (not RegWriteStringValue(RootKey,SOFTWAREClassesDirectoryshellgit_shellcommand,,Cmd)) or (not RegWriteStringValue(RootKey,SOFTWAREClassesDirectoryshellgit_shell,Icon,Ico)) or (StringChangeEx(Cmd,%1,%v.,false)<>1) or (not RegWriteStringValue(RootKey,SOFTWAREClassesDirectoryBackgroundshellgit_shell,,Msg)) or (not RegWriteStringValue(RootKey,SOFTWAREClassesDirectoryBackgroundshellgit_shellcommand,,Cmd)) or (not RegWriteStringValue(RootKey,SOFTWAREClassesDirectoryBackgroundshellgit_shell,Icon,Ico)) or (not RegWriteStringValue(RootKey,SOFTWAREClassesLibraryFolderbackgroundshellgit_shell,,Msg)) or (not RegWriteStringValue(RootKey,SOFTWAREClassesLibraryFolderbackgroundshellgit_shellcommand,,Cmd)) or (not RegWriteStringValue(RootKey,SOFTWAREClassesLibraryFolderbackgroundshellgit_shell,Icon,Ico)) then LogError(Line {#__LINE__}: Unable to create «Git Bash Here» shell extension.); end; if IsComponentSelected(extguihere) then begin Msg:=Git &GUI Here; Cmd:=«+AppDir+cmdgit-gui.exe» «—working-dir» «%1»; Ico:=AppDir+cmdgit-gui.exe; if (not RegWriteStringValue(RootKey,SOFTWAREClassesDirectoryshellgit_gui,,Msg)) or (not RegWriteStringValue(RootKey,SOFTWAREClassesDirectoryshellgit_guicommand,,Cmd)) or (not RegWriteStringValue(RootKey,SOFTWAREClassesDirectoryshellgit_gui,Icon,Ico)) or (StringChangeEx(Cmd,%1,%v.,false)<>1) or (not RegWriteStringValue(RootKey,SOFTWAREClassesDirectoryBackgroundshellgit_gui,,Msg)) or (not RegWriteStringValue(RootKey,SOFTWAREClassesDirectoryBackgroundshellgit_guicommand,,Cmd)) or (not RegWriteStringValue(RootKey,SOFTWAREClassesDirectoryBackgroundshellgit_gui,Icon,Ico)) or (not RegWriteStringValue(RootKey,SOFTWAREClassesLibraryFolderBackgroundshellgit_gui,,Msg)) or (not RegWriteStringValue(RootKey,SOFTWAREClassesLibraryFolderBackgroundshellgit_guicommand,,Cmd)) or (not RegWriteStringValue(RootKey,SOFTWAREClassesLibraryFolderBackgroundshellgit_gui,Icon,Ico)) then LogError(Line {#__LINE__}: Unable to create «Git GUI Here» shell extension.); end; { Optionally disable Git LFS completely } if not IsComponentSelected(gitlfs) then begin WizardForm.StatusLabel.Caption:=Removing bundled Git LFS; if not DeleteFile(AppDir+{#MINGW_BITNESS}bingit-lfs.exe) and not DeleteFile(AppDir+{#MINGW_BITNESS}libexecgit-coregit-lfs.exe) then LogError(Line {#__LINE__}: Unable to delete «git-lfs.exe».); end; { Handle Scalar } #ifdef WITH_SCALAR if not IsComponentSelected(scalar) then begin WizardForm.StatusLabel.Caption:=Removing bundled Scalar; // Remove scalar.exe from Git for Windows’ files if not DeleteFile(AppDir+cmdscalar.exe) or not DeleteFile(AppDir+{#MINGW_BITNESS}binscalar.exe) or not DeleteFile(AppDir+{#MINGW_BITNESS}libexecgit-corescalar.exe) or not DeleteFile(AppDir+{#MINGW_BITNESS}sharedocgit-docscalar.html) then begin LogError(Line {#__LINE__}: Unable to delete «scalar.exe».); end; end else begin UpgradeFromDotNetBasedScalar(); ExecSilentlyAsOriginalUser(«+AppDir+cmdscalar.exe» reconfigure —all,scalar-reconfigure,Line {#__LINE__}: Could not reconfigure Scalar enlistments); end; #endif { Create the Windows Terminal integration } if IsComponentSelected(windowsterminal) then begin WizardForm.StatusLabel.Caption:=Writing Windows Terminal Profile; InstallWindowsTerminalFragment(); end; { Optionally «skip» installing bundled SSH binaries conflicting with external OpenSSH: } #ifdef DELETE_OPENSSH_FILES if (SSHChoicePage<>NIL) and (RdbSSH[GS_ExternalOpenSSH].Checked) then begin WizardForm.StatusLabel.Caption:=Removing bundled Git OpenSSH binaries; if not DeleteOpenSSHFiles() then LogError(Failed to remove OpenSSH file(s)); end; #endif { Set the default Git editor } WizardForm.StatusLabel.Caption:=Configuring default editor; if (CbbEditor.ItemIndex=GE_Nano) then GitSystemConfigSet(core.editor,nano.exe) else if ((CbbEditor.ItemIndex=GE_NotepadPlusPlus)) and (NotepadPlusPlusPath<>) then GitSystemConfigSet(core.editor,«+NotepadPlusPlusPath+» -multiInst -notabbar -nosession -noPlugin) else if ((CbbEditor.ItemIndex=GE_VisualStudioCode)) and (VisualStudioCodePath<>) then begin if (VisualStudioCodeUserInstallation=False) then GitSystemConfigSet(core.editor,«+VisualStudioCodePath+» —wait) else if not ExecAsOriginalUser(AppDir + {#MINGW_BITNESS}bingit.exe,config —global core.editor «»+VisualStudioCodePath+» —wait»,,SW_HIDE,ewWaitUntilTerminated, i) then LogError(Could not set Visual Studio Code as core.editor in the gitconfig.) end else if ((CbbEditor.ItemIndex=GE_VisualStudioCodeInsiders)) and (VisualStudioCodeInsidersPath<>) then begin if (VisualStudioCodeInsidersUserInstallation=False) then GitSystemConfigSet(core.editor,«+VisualStudioCodeInsidersPath+» —wait) else if not ExecAsOriginalUser(AppDir + {#MINGW_BITNESS}bingit.exe,config —global core.editor «»+VisualStudioCodeInsidersPath+» —wait»,,SW_HIDE,ewWaitUntilTerminated, i) then LogError(Could not set Visual Studio Code Insiders as core.editor in the gitconfig.) end else if ((CbbEditor.ItemIndex=GE_SublimeText)) and (SublimeTextPath<>) then begin if (SublimeTextUserInstallation=False) then GitSystemConfigSet(core.editor,«+SublimeTextPath+» -w) else if not ExecAsOriginalUser(AppDir + {#MINGW_BITNESS}bingit.exe,config —global core.editor «»+SublimeTextPath+» -w»,,SW_HIDE,ewWaitUntilTerminated, i) then LogError(Could not set Sublime Text as core.editor in the gitconfig.); end else if ((CbbEditor.ItemIndex=GE_Atom)) and (AtomPath<>) then begin if not ExecAsOriginalUser(AppDir + {#MINGW_BITNESS}bingit.exe,config —global core.editor «»+AtomPath+» —wait»,,SW_HIDE,ewWaitUntilTerminated, i) then LogError(Could not set Atom as core.editor in the gitconfig.); end else if ((CbbEditor.ItemIndex=GE_VSCodium)) and (VSCodiumPath<>) then begin if (VSCodiumUserInstallation=False) then GitSystemConfigSet(core.editor,«+VSCodiumPath+» —wait) else if not ExecAsOriginalUser(AppDir + {#MINGW_BITNESS}bingit.exe,config —global core.editor «»+VSCodiumPath+» —wait»,,SW_HIDE,ewWaitUntilTerminated, i) then LogError(Could not set VSCodium as core.editor in the gitconfig.) end else if (CbbEditor.ItemIndex=GE_Notepad) then GitSystemConfigSet(core.editor,notepad) else if (CbbEditor.ItemIndex=GE_Wordpad) then GitSystemConfigSet(core.editor,wordpad) else if ((CbbEditor.ItemIndex=GE_CustomEditor)) and (PathIsValidExecutable(CustomEditorPath)) then GitSystemConfigSet(core.editor,«+CustomEditorPath+» +CustomEditorOptions); { Install a scheduled task to try to auto-update Git for Windows } if IsComponentInstalled(autoupdate) then begin WizardForm.StatusLabel.Caption:=Set up daily up to date check; InstallAutoUpdater(); end; { Run post-install scripts to set up system environment } WizardForm.StatusLabel.Caption:=Running post-install script; Cmd:=AppDir+post-install.bat; Log(Line {#__LINE__}: Executing +Cmd); if (not Exec(Cmd,ExpandConstant(>»{tmp}post-install.log»),AppDir,SW_HIDE,ewWaitUntilTerminated,i) or (i<>0)) and FileExists(Cmd) then begin if FileExists(ExpandConstant({tmp}post-install.log)) then LogError(Line {#__LINE__}: Unable to run post-install scripts:+#13+#10+ReadFileAsString(ExpandConstant({tmp}post-install.log))) else LogError(Line {#__LINE__}: Unable to run post-install scripts; no output?); end else begin if FileExists(ExpandConstant({tmp}post-install.log)) then Log(Line {#__LINE__}: post-install scripts run successfully:+#13+#10+ReadFileAsString(ExpandConstant({tmp}post-install.log))) else LogError(Line {#__LINE__}: Unable to run post-install scripts; no error, no output?); end; { Restart any processes that were shut down via the Restart Manager } if SessionHandle>0 then try WizardForm.StatusLabel.Caption:=Restarting processes; RmRestart(SessionHandle,0,0); RmEndSession(SessionHandle); except Log(Line {#__LINE__}: RmRestart not supported.); end; end; procedure RegisterPreviousData(PreviousDataKey:Integer); var Data,Data2,CustomEditorData,Path:String; begin // Git Editor options. Data:=; if (CbbEditor.ItemIndex=GE_Nano) then begin Data:=Nano; end else if (CbbEditor.ItemIndex=GE_VIM) then begin Data:=VIM; end else if (CbbEditor.ItemIndex=GE_NotepadPlusPlus) then begin Data:=Notepad++; end else if (CbbEditor.ItemIndex=GE_VisualStudioCode) then begin Data:=VisualStudioCode; end else if (CbbEditor.ItemIndex=GE_VisualStudioCodeInsiders) then begin Data:=VisualStudioCodeInsiders; end else if (CbbEditor.ItemIndex=GE_SublimeText) then begin Data:=SublimeText; end else if (CbbEditor.ItemIndex=GE_Atom) then begin Data:=Atom; end else if (CbbEditor.ItemIndex=GE_VSCodium) then begin Data:=VSCodium; end else if (CbbEditor.ItemIndex=GE_Notepad) then begin Data:=Notepad; end else if (CbbEditor.ItemIndex=GE_Wordpad) then begin Data:=Wordpad; end else if (CbbEditor.ItemIndex=GE_CustomEditor) then begin Data:=CustomEditor CustomEditorData:=EditorPage.Values[0]; end; RecordChoice(PreviousDataKey,Editor Option,Data); RecordChoice(PreviousDataKey,Custom Editor Path,CustomEditorData); // Default Branch options. Data:= ; if RdbDefaultBranch[DB_Manual].Checked then Data:=EdtDefaultBranch.Text; RecordChoice(PreviousDataKey,Default Branch Option,Data); // Git Path options. Data:=; if RdbPath[GP_BashOnly].Checked then begin Data:=BashOnly; end else if RdbPath[GP_Cmd].Checked then begin Data:=Cmd; end else if RdbPath[GP_CmdTools].Checked then begin Data:=CmdTools; end; RecordChoice(PreviousDataKey,Path Option,Data); // Git SSH options. Data:=; Data2:=false; if (SSHChoicePage=NIL) or RdbSSH[GS_OpenSSH].Checked then begin Data:=OpenSSH; end else if RdbSSH[GS_ExternalOpenSSH].Checked then begin Data:=ExternalOpenSSH end else if RdbSSH[GS_Plink].Checked then begin Data:=Plink; RecordChoice(PreviousDataKey,Plink Path,EdtPlink.Text); if TortoisePlink.Checked then Data2:=true; end; RecordChoice(PreviousDataKey,SSH Option,Data); RecordChoice(PreviousDataKey,Tortoise Option,Data2); // HTTPS implementation (cURL) options. Data:=OpenSSL; if RdbCurlVariant[GC_WinSSL].Checked then begin Data:=WinSSL; end; RecordChoice(PreviousDataKey,CURL Option,Data); // Line ending conversion options. Data:=; if RdbCRLF[GC_LFOnly].Checked then begin Data:=LFOnly; end else if RdbCRLF[GC_CRLFAlways].Checked then begin Data:=CRLFAlways; end else if RdbCRLF[GC_CRLFCommitAsIs].Checked then begin Data:=CRLFCommitAsIs; end; RecordChoice(PreviousDataKey,CRLF Option,Data); // Bash’s terminal emulator options. Data:=MinTTY; if RdbBashTerminal[GB_ConHost].Checked then begin Data:=ConHost; end; RecordChoice(PreviousDataKey,Bash Terminal Option,Data); // Default behavior of `git pull`. Data:=Merge; if RdbGitPullBehavior[GP_GitPullRebase].Checked then begin Data:=Rebase end else if RdbGitPullBehavior[GP_GitPullFFOnly].Checked then begin Data:=FFOnly end; RecordChoice(PreviousDataKey,Git Pull Behavior Option,Data); // Credential helper. Data:=Disabled; if RdbGitCredentialManager[GCM].Checked then begin; Data:=Enabled; end; RecordChoice(PreviousDataKey,Use Credential Manager,Data); // Extra options. Data:=Disabled; if RdbExtraOptions[GP_FSCache].Checked then begin Data:=Enabled; end; RecordChoice(PreviousDataKey,Performance Tweaks FSCache,Data); Data:=Disabled; if RdbExtraOptions[GP_Symlinks].Checked then begin Data:=Enabled; end; RecordChoice(PreviousDataKey,Enable Symlinks,Data); // Experimental options. #ifdef WITH_EXPERIMENTAL_BUILTIN_DIFFTOOL Data:=Disabled; if RdbExperimentalOptions[GP_BuiltinDifftool].Checked then begin Data:=Enabled; end; RecordChoice(PreviousDataKey,Enable Builtin Difftool,Data); #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_REBASE Data:=Disabled; if RdbExperimentalOptions[GP_BuiltinRebase].Checked then begin Data:=Enabled; end; RecordChoice(PreviousDataKey,Enable Builtin Rebase,Data); #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_STASH Data:=Disabled; if RdbExperimentalOptions[GP_BuiltinStash].Checked then begin Data:=Enabled; end; RecordChoice(PreviousDataKey,Enable Builtin Stash,Data); #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_ADD_I Data:=Disabled; if RdbExperimentalOptions[GP_BuiltinAddI].Checked then begin Data:=Enabled; end; RecordChoice(PreviousDataKey,Enable Builtin Interactive Add,Data); #endif #ifdef WITH_EXPERIMENTAL_PCON Data:=Disabled; if RdbExperimentalOptions[GP_EnablePCon].Checked then begin Data:=Enabled; end; RecordChoice(PreviousDataKey,Enable Pseudo Console Support,Data); #endif #ifdef WITH_EXPERIMENTAL_BUILTIN_FSMONITOR Data:=Disabled; if RdbExperimentalOptions[GP_EnableFSMonitor].Checked then begin Data:=Enabled; end; RecordChoice(PreviousDataKey,Enable FSMonitor,Data); #endif Path:=ExpandConstant({app}etcinstall-options.txt); if not SaveStringToFile(Path,ChosenOptions,False) then LogError(Could not write to +Path); end; { Uninstall event functions } function InitializeUninstall:Boolean; var Form:TSetupForm; Info:TLabel; ExitButton,RefreshButton:TButton; begin Result:=True; Form:=CreateCustomForm; try Form.Caption:=Git Uninstall: Removing in-use files; Form.ClientWidth:=ScaleX(500); Form.ClientHeight:=ScaleY(256); Info:=TLabel.Create(Form); with Info do begin Parent:=Form; Left:=ScaleX(11); Top:=ScaleY(11); AutoSize:=True; Caption:=The following applications use files that need to be removed, please close them.; end; ContinueButton:=TButton.Create(Form); with ContinueButton do begin Parent:=Form; Left:=Form.ClientWidth-ScaleX(75+10); Top:=Form.ClientHeight-ScaleY(23+10); Width:=ScaleX(75); Height:=ScaleY(23); Caption:=Continue; ModalResult:=mrOk; end; ExitButton:=TButton.Create(Form); with ExitButton do begin Parent:=Form; Left:=ContinueButton.Left-ScaleX(75+6); Top:=ContinueButton.Top; Width:=ScaleX(75); Height:=ScaleY(23); Caption:=Exit; ModalResult:=mrCancel; Cancel:=True; end; RefreshButton:=TButton.Create(Form); with RefreshButton do begin Parent:=Form; Left:=ScaleX(10); Top:=ExitButton.Top; Width:=ScaleX(75); Height:=ScaleY(23); Caption:=Refresh; OnClick:=@RefreshProcessList; end; ProcessesListBox:=TListBox.Create(Form); with ProcessesListBox do begin Parent:=Form; Left:=ScaleX(11); Top:=Info.Top+Info.Height+11; Width:=Form.ClientWidth-ScaleX(11*2); Height:=ContinueButton.Top-ScaleY(11*4); end; Form.ActiveControl:=ContinueButton; RefreshProcessList(NIL); if GetArrayLength(Processes)>0 then begin // Now that these dialogs are going to be shown, we should probably // disable the «Are you sure to remove Git?» confirmation dialog, but // unfortunately that is not possible with Inno Setup currently. Result:=(Form.ShowModal()=mrOk); // Note: The number of processes might have changed during a refresh. if Result and (GetArrayLength(Processes)>0) then begin Result:=(SuppressibleMsgBox( If you continue without closing the listed applications, you will need to log off and on again to remove some files manually. + #13 + #13 + Are you sure you want to continue anyway? , mbConfirmation , MB_YESNO , IDNO )=IDYES); end; end; finally Form.free; end; end; // PreUninstall // // Even though the name of this function suggests otherwise most of the // code below is only executed right before the actual uninstallation. // This happens because of the if-guard right in the beginning of this // function. procedure CurUninstallStepChanged(CurUninstallStep:TUninstallStep); var FileName,PathOption:String; EnvPath:TArrayOfString; i:Longint; begin if CurUninstallStep<>usUninstall then begin Exit; end; // Reset the console font (the FontType is reset in the Registry section). if IsComponentInstalled(consolefont) then begin if SuppressibleMsgBox(Do you want to revert the TrueType font setting for all console windows?,mbConfirmation,MB_YESNO,IDYES)=IDYES then begin RegWriteDWordValue(HKEY_CURRENT_USER,Console,FontFamily,0); RegWriteDWordValue(HKEY_CURRENT_USER,Console,FontSize,0); RegWriteDWordValue(HKEY_CURRENT_USER,Console,FontWeight,0); end; end; { Remove the scheduled task to try to auto-update Git for Windows } if IsComponentInstalled(autoupdate) then UninstallAutoUpdater(); { Modify the environment This must happen no later than usUninstall to make «ChangesEnvironment=yes» not happend before the change! } AppDir:=ExpandConstant({app}); FileName:=AppDir+setup.ini; // Delete the current user’s GIT_SSH and SVN_SSH if we set it. DeleteMarkedEnvString(GIT_SSH); DeleteMarkedEnvString(SVN_SSH); // Only remove the installation directory from PATH if the previous // installation ‘Path Option’ modified it. PathOption:=GetPreviousData(Path Option,BashOnly); if (PathOption=Cmd) or (PathOption=CmdTools) then begin // Get the current user’s directories in PATH. EnvPath:=GetEnvStrings(PATH,IsAdminLoggedOn); // Remove the installation directory from PATH. for i:=0 to GetArrayLength(EnvPath)-1 do begin if Pos(AppDir+,EnvPath[i]+)=1 then begin EnvPath[i]:=; end; end; // Reset the current user’s directories in PATH. if not SetEnvStrings(PATH,EnvPath,True,IsAdminLoggedOn,True) then LogError(Line {#__LINE__}: Unable to revert any possible changes to PATH.); end; if FileExists(FileName) and (not DeleteFile(FileName)) then LogError(Line {#__LINE__}: Unable to delete file «+FileName+«.); { Delete the Windows Explorer integrations } DeleteContextMenuEntries; if isWin64 then begin FileName:=AppDir+git-cheetahgit_shell_ext64.dll; end else begin FileName:=AppDir+git-cheetahgit_shell_ext.dll; end; if FileExists(FileName) then begin if not UnregisterServer(Is64BitInstallMode,FileName,False) then LogError(Line {#__LINE__}: Unable to unregister file «+FileName+«. Please do it manually by running «regsvr32 /u +ExtractFileName(FileName)+«.); if not DeleteFile(FileName) then LogError(Line {#__LINE__}: Unable to delete file «+FileName+«. Please do it manually after logging off and on again.); end; end;

I’ve seen a lot of posts about how to update git repositories, but I want to know if there’s a way to automatically update Git itself. I frequently instal Git on new computers, and I feel like every time I do there’s a new version, so I have to update all the installations on my other machines. I would like to always have the latest version of Git. Is this possible in Windows? Perhaps with some kind of batch script?

Thanks in advance!

asked Nov 14, 2015 at 8:20

Rabadash8820's user avatar

Rabadash8820Rabadash8820

2,2523 gold badges25 silver badges49 bronze badges

5

Going through some old questions of mine and just saw this one. The Git for Windows installer now includes an option to check for git updates daily. Not sure which version introduced this, or whether the same option is available on Mac/Linux, but yeah, that’s how you check for git updates automatically. :P

answered Nov 13, 2021 at 3:43

Rabadash8820's user avatar

Rabadash8820Rabadash8820

2,2523 gold badges25 silver badges49 bronze badges

Install git on windows platform

tags: Git  java

Intelligent Recommendation

Install Git in Windows environment

Purpose: Install Git on a local computer (windows) Note: As long as there is Chinese in the directory you installed, the following error will occur. And after creating the repository, anything can go …

Windows install git and use

The earliest Git was developed on Linux. For a long time, Git could only run on Linux and Unix systems. However, some people slowly transplanted it to Windows. Now, Git works fine on Linux, Unix, Mac,…

Install Git under Windows

Foreword: Under Linux, Git can be installed directly through commands, and Git bash can be used under Windows. 1. Download: The following download addresses are available.https://git-scm.com/downloads…

Windows install Git practice

Git installation The earliest Git on Linux is developed over a long period of time, Git can only run on Linux and Unix systems. But slowly it was ported to Windows. Now, Git can normally run on Linux,…

Windows how to install Git?

The following installation using the Git version: 2.17.0 Please read the following information. Choose an installation location. (Note: I did not use the default installation location of the installat…

More Recommendation

windows install git-town

windows install git-town ## title http://www.git-town.com/install.html first to find the version for your computer system, then download to download the executable file, copy and paste into C: Progr…

Windows install git record

Use UnixODBC project on the problem, Windows install git Windows I’ve only ever win + r of Linux operating statement, mac default shell, and shell scripts to be executed on a win, so now win the syste…

(1) Install git on windows

table of Contents 1 Instructions before git installation 1.1 About git 1.2 Advantages 1.3 git download 2 git windows installation 3 git installation verification 4 git tools 1 Instructions before git …

windows git download and install

Git is generally used when we download files from GitHub. After directly searching for Git downloads, he downloaded it on Amazon’s platform, which is very slow, so here are provided: Git download mirr…

Install Git on Windows

1. Install Git for Windows (aka msysgit) download link: https://git-for-windows.github.io/  After the official download is complete, when installing to Windows Explorer integration, check the &qu…

Статьи Справочник

      • 1. git скачать
      • 2. Подробная установка GIT под Windows
        • Запустить файл установки GIT
        • (1) Шаг 1 Информация (информация)
        • (2)Step 2 Select Destination Location
        • (3)Step 3 Select Components
        • (4)Step 4 Select Strat Menu Folder
        • (5)Step 5 Choosing the default editor used by Git
        • (6)Step 6 Adjusting your PATH environment
        • (7)Step 7 Choosing HTTPS transport backend
        • (8)Step 8 Configuring the line ending conversions
        • (9)Step 9 Configuring the terminal emulator to use with Git Bash
        • (10)Step 10 Configuring extra options
        • (11)Step 11 Installing
        • (12)Step 12 Completing the Git Setup Wizard
      • 3. Убедитесь, что GIT успешно установлен

1. git скачать

Введите официальный адрес, чтобы загрузить клиент GIT:https://git-scm.com/download/win

Домашний клиент GIT Client Mirror Adder:https://npm.taobao.org/mirrors/git-for-windows/

2. Подробная установка GIT под Windows

Запустить файл установки GIT

(1) Шаг 1 Информация (информация)

Первое, что я вижу, это протокол оружия, нажмите Далее.

иллюстрировать:
Please read the following important information before continuing
Прежде чем продолжить, прочитайте следующую важную информацию

(2)Step 2 Select Destination Location

Выберите место установки, не -китайское, без места.

(3)Step 3 Select Components

Выберите компонент установки, просто выберите по умолчанию.

оGit GUI HereСущность Я использую все это сейчасGit BashНу давай же. использоватьGit GUIЭто действительно может получить лучший опыт пользовательского интерфейса, но я лично думаю, что это снизит эффективность. И новички по -прежнему понимают общие инструкции GIT сначала, а затем используют его перед использованиемGit GUIТолько чтобы лучше понять.

иллюстрировать:
(1)Additional icons: Дополнительный значок
On the Desktop:На рабочем столе
(2)Windows Explorer integration Windows:Ресурсы менеджер интегрированного правого меню мыши
Другими словами, добавьте следующие две функции в правое меню. Введите интерфейс GIT и пропуститеGit Bash Here
Git Bash Here
Git GUI Here
(3)Git LFS (Large File Support):Поддержка больших файлов.
(4)Associate .git/* configuration files with the default text editor:
Установите текстовый редактор по умолчанию файла конфигурации .git.
(5)Associate .sh files to be run with Bash:
Запустите файл .sh в Bash, то есть позволить GIT открывать файл .sh. настройки по умолчанию
(6)Use a TrueType font in all console windows:Используйте шрифт Truetype во всех консольных окнах и установите его по умолчанию
(7)Check daily for Git for Windows updates:Проверьте, есть ли GIT обновление Windows каждый день, настройки по умолчанию

(4)Step 4 Select Strat Menu Folder

Создать каталог меню «Пуск»(Настройки имени каталога меню), выбор по умолчанию.

Don't create a Start Menu folder
Не создавайте папку меню «Start», а не проверена.

(5)Step 5 Choosing the default editor used by Git

Выберите редактор по умолчанию, используемый GIT, Vim — это то, к чему мы привыкли ежедневно. Выбор по умолчанию.

иллюстрировать:
Use the Nano editor by default:Используйте Nano Editor по умолчанию
Use Vim (The ubiquitous text editor) as Git's default editor:Используйте Vim в качестве редактора GIT по умолчанию
Use Notepad++ as Git's default editor:Используйте Notepad ++ в качестве редактора по умолчанию GIT
Use Visual Studio Code as Git's default editor:Используйте код Visual Studio в качестве редактора по умолчанию GIT
Use Sublime Text as Git's default editor:Используйте превосходный текст в качестве редактора по умолчанию GIT

Необязательная операция основана на ваших привычках.

(6)Step 6 Adjusting your PATH environment

Согласно выбору, настройте соответствующую переменную среды пути.

иллюстрировать:
(1)Use Git from Git Bash only
описывать:This is the safest choice as your PATH will not be modified at all.You will only be able to use the Git command line tools form Git Bash.
Перевод: это самый безопасный выбор, потому что ваш путь вообще не будет изменен. Вы можете использовать только инструмент командной строки GIT в Git Bash.
(2)Use Git from the Windows Command Prompt
описывать:This option is considered safe as it only adds some minimal Git wrappers to your PATH to avoid cluttering your environment with optional Unix tools . You will be able to use Git from both Git Bash and the Windows Command Prompt.
Перевод: этот вариант считается безопасным, потому что он добавляет только некоторые из самых маленьких пакетов GIT к пути, чтобы избежать путаницы среды с дополнительными инструментами Unix. Ты сможешьGIT можно использовать из подсказок команды Git Bash и Windows.(То естьВы также можете запустить команду GIT в командной строке Windows CMD, что удобно для работы
(3)Use Git and optional Unix tools from the Windows Command Prompt
Используйте GIT и дополнительные инструменты Unix из командной строки Windows
Both Git and the optional Unix tools will be added to you PATH
GIT и дополнительные инструменты Unix будут добавлены на путь вашего компьютера
Warning:This will override Windows tools like "find and sort".Only use this option if you understand the implications.
Предупреждение: это будет охватывать инструменты Windows, такие как «Найти и сортировку». Этот вариант используется только после понимания его значения.
  • Первое краткое описание: Установка напрямую, не настраивает переменные среды команды GIT, и вам необходимо вручную настроить переменную среды.
  • Второй элемент: переменные среды команды GIT будут автоматически настроены. (Опция по умолчанию)
  • Третий пункт: не рекомендуется.

SO: новички рекомендуют второй вариант. Если вы обладаете опытом использования инструмента GIT, рекомендуется первый вариант.

(7)Step 7 Choosing HTTPS transport backend

Выберите, какой метод подключения используется при подключении локальной библиотеки и удаленной библиотеки.

То есть, какая библиотека шифрования используется для шифрования информации, передаваемой HTTP, по умолчанию используется OpenSSL, которая обычно используется по умолчанию.

иллюстрировать:
(1)Use the OpenSSL library
Используйте библиотеку OpenSSL
Server certificates will be validated using the ca-bundle.crt file.
Сертификат сервера будет проверен с помощью файла ca-bundle.crt.
(2)Use the native Windows Secure Channel library
Используйте локальную библиотеку каналов безопасности Windows
Server certificates will be validated using Windows Certificate Stores.This option also allows you to use your company's internal Root CA certificates distributed e.g. via Active Directory Domain Services.
Сертификат сервера будет использовать проверку хранения сертификатов Windows. Эта опция также позволяет использовать внутренний сертификат CA CAP, например, через службы домена Active Directory.

(8)Step 8 Configuring the line ending conversions

Когда вы решите отправить, измените формат линии

иллюстрировать:
(1)Checkout Windows-style,commit Unix-style line endings
Перевод: Проверьте формат строки Windows, чтобы преобразовать его в формат формата строки Unix, а затем отправьте его.
(2)Checkout as-is , commit Unix-style line endings**
Перевод: Независимо от того, что проверяется исходный формат формата изменяющегося формата, он будет преобразован в замену формата формата строки Unix и отправить его.
(3)Checkout as-is,commit as-is
Не изменяйте формат формата строки: без преобразования вы отправите все, что проверяете.

То есть какой стиль используется при отправке кода, вы можете использовать настройки по умолчанию.

(9)Step 9 Configuring the terminal emulator to use with Git Bash

Какое программное обеспечение используется как программа GIT терминала (то есть терминал командной строки, правый кликGit Bash HereСтиль окна OUT), использование Mintty Default, также является относительно простым терминалом -к использованию и непосредственно использует настройки по умолчанию.

иллюстрировать:
(1)Use MinTTY (the default terminal of MSYS2)
описывать:Git Bash will use MinTTY as terminal emulator,which sports a resizable window,non-rectangular selections and a Unicode font. Windows console programs (such as interactive Python) must be launched via 'winpty' to work in MinTTY.
Перевод: Git Bash будет использовать Mintty в качестве симулятора терминала. Симулятор имеет регулируемое окно, не -ректанговые избирательные округа и шрифты Unicode. Программа консоли Windows (например, Interactive Python) должна быть запущена через «Винпти», чтобы запустить в Mintty.
(2)Use Windows' default console window
описывать:Git will use the default console window of Windows ("cmd.exe"),which works well with Win32 console programs such as interactive Python or node.js , but has a very limited default scroll-back,needs to be configured to use aUnicode font in order to display non-ASCII characters correctly,and prior to Windows 10 its windows was not freely resizable and it only allowed rectangular text selections.
Перевод: GIT будет использовать окно консоли по умолчанию Windows («cmd.exe»), которая может использоваться с программой консоли Win32 (например, Interactive Python или Node.js), но откат по умолчанию очень ограничен, и конфигурация должна быть настроенным на использование шрифтов Unicode для правильного отображения не -ASCII символов, и перед Windows 10 его окно не может быть свободно настроено и только позволяет выбирать прямоугольный текст.

Совет: параметры выбора, как правило, смещены в направлении Linux. Не выбирайте направление окон. Это соответствует привычкам.

(10)Step 10 Configuring extra options

Настройте дополнительные параметры, такие как вспомогательные функции, такие как открытие кэша файлов, вы можете выбрать по умолчанию

иллюстрировать:
(1)Enable file system caching
Включить кеш файловой системы
описывать:File system data will be read in bulk and cached in memory for certain operations ("core.fscache" is set to "true"). This provides a significant performance boost.
Перевод: Данные файловой системы будут считываться партиями и медленно существуют в памяти («core.fscache», установленный для «true»). Это обеспечивает значительное улучшение производительности.
(2)Enable Git Credential Manager
Включить менеджер ваучеров GIT
описывать:The Git Credential Manager for Windows provides secure Git credential storage for Windows,most notably multi-factor authentication support for Visual Studio Team Services and GitHub. (requires .NET framework v4.5.1 or or later).
Перевод: Windows Voucher Manager предоставляет безопасное хранилище ваучера GIT для Windows. Самое важное, что нужно поддерживать службы команды Visual Studio и GitHub. (Потребность .NET Framework v4.5.1 или более высокая версия).
(3)Enable symbolic links
Включить ссылку на символ
описывать:Enable symbolic links (requires the SeCreateSymbolicLink permission).Please note that existing repositories are unaffected by this setting.
Перевод: включить символические ссылки (необходимые разрешения SecreateSymboliclink). Обратите внимание, что существующий репозиторий не зависит от этой настройки.

(11)Step 11 Installing

Установить

(12)Step 12 Completing the Git Setup Wizard

Полный мастер установки GIT

3. Убедитесь, что GIT успешно установлен

Введите клиент CMD и GIT, введитеgit --version, Просмотр информации версии, доказанная установка успешна

Право -Нажмите на рабочем столе, чтобы отобразить GIT GUI здесь и GIT Bash здесь.

GIT не нуждается в отдельном сервере для его установки. Вам нужно только установить GIT на свой компьютер, чтобы использовать его.

I’ve installed git bash and ticked the «Check daily for updates» option. I thought that it will update automatically, but it requires me to configure the set up manually each time. I don’t update it but it annoys me every day. When I tried to install a new version of git with that option unchecked it still was left intact. Is there a way to remove the daily checking or should I just reinstall my git bash with that option unselected?

asked Oct 19, 2017 at 16:26

Ivan Yurchenko's user avatar

I too encountered this really annoying «automatic update» behavior. It looks like checking this box during install adds a scheduled task to the Windows task scheduler. You ought to be able to remove it from here.

Task Scheduler with Git Update Scheduled Task

answered Oct 26, 2017 at 3:06

Hayden McAfee's user avatar

2

Skip to content

In case you missed it, this blog post picks up where Part 1 – Upgrading PowerShell left off. In continuing on with the Git Integration with VS Code blog series, I now present Part 2 – Installing PowerCLI and Git!

NOTE: This process assumes a Windows-based installation, and for the Git install, most of the options were left to defaults unless otherwise noted.

Installing PowerCLI

Now that PowerShell has been upgraded, it’s time to install VMware PowerCLI. Doing so is actually a rather simple procedure, assuming the OS has connectivity to the Internet.

Upgrading PowerCLI (optional)

If you already have PowerCLI version 6.5.1 or higher installed, upgrading to the latest version is super simple! Although, if you have and older version of PowerCLI installed, you’ll want to completely uninstall that first, and then jump back up to the Installing PowerCLI section.

  • Launch PowerShell as an Administrator and run the following command:
    Update-Module -name VMware.PowerCLI
  • That’s it! It’s that simple!

Installing Git

Now, before we can take advantage of the Git features in Visual Studio Code, we’ll need to install Git first.

  • The latest version of Git can be downloaded from the official Git web page over at https://git-scm.com/downloads
  • Double-click on the Git exe file. On the Information window, click Next.
    01-Git-Install-Information
  • On the Select Destination Location window, choose the folder location where Git should be installed, then click Next to continue.
    02-Git-Install-Select-Destination-Location
  • On the Select Components window, adjust these options as needed. In this example, I checked Check daily for Git for Windows updates. Click Next to continue.
    03-Git-Install-Select-Components
  • On the Select Start Menu Folder window, click Next.
    04-Git-Install-Select-Start-Menu-Folder
  • On the Choosing the default editor used by Git window, click Next.
    05-Git-Install-Choosing-the-Default-Editor
  • On the Adjusting your PATH environment window, click Next.
    06-Git-Install-Adjusting-your-PATH-environment
  • On the Choosing HTTPS transport backend window, I changed this to Use the native Windows Secure Channel library due to certificate-related issues I had with an internal GitLab site. (See my previous post, VS Code: Fixing Git Certificate Issues). Then click Next.
    07-Git-Install-Choosing-HTTPS-Transport-Backend
  • On the Configuring the line ending conversions window, click Next.
    08-Git-Install-Configuring-the-Line-Ending-Conversions
  • On the Configuring extra options window, click Install.
    10-Git-Install-Configuring-Extra-Options
  • The Git installation with then start and copy a number of files to the system.
    11-Git-Install-Installing
  • When the installation has completed, uncheck the two boxes and click Finish.
    12-Git-Install-Setup-Complete

At this point, PowerCLI has been updated and Git is now installed. The next part in this Git Integration with VS Code series will be Part 3 – Installing Visual Studio Code. As always, thanks for stopping by!

Introduction

Git is a version control system that allows multiple developers to work on the same project while tracking changes and revisions. Keeping Git up to date brings you the latest features and usability improvements.

In this tutorial, you will learn how to update to the latest version of Git on Linux, Windows, and macOS.

How to update Git

Prerequisites

  • A system running Linux, Windows, or macOS
  • An installed version of Git
  • Access to the terminal window (Linux, macOS) or command prompt (Windows)
  • An account with administrator-level privileges

How to Check the Current Git Version?

To check the current version of Git, use the following command:

git --version

This command works on all operating systems. This example uses Windows:

Checking the current version of Git using the Windows command prompt

Below, we list different ways you can update your version of Git, depending on the operating system you are using. Skip to the section applicable for your machine.

Update Git on Linux

Note: To update Git on a Linux machine, use the appropriate package manager. When working with Git on CentOS, use a package manager such as yum or pacman.

This example shows how to update Git on Ubuntu.

Start by updating the system packages with the following command:

sudo apt-get update

Update Git by using:

sudo apt-get install git

When prompted, type Y and press Enter to confirm the installation.

Installing the latest version of Git on Linux

To verify the installation has completed, check the Git version one more time:

git --version
Checking the current Git version after installing

Another way to update Git on Linux is to install it from scratch using the original source code. Check out our guide to installing Git on Ubuntu for details.

Update Git on Windows

The method you use to update Git on Windows depends on the version of Git you are currently running.

For versions prior to 2.14.1, uninstall Git from your system and install a copy of the latest version from scratch. Check out our guide to installing Git on Windows for more details.

For versions from 2.14.2 to 2.16.1, use the following command in your command prompt:

git update

For versions 2.16.1 on, update Git with:

git update-git-for-windows
Updating Git using the Windows command prompt

The output above appears when you are running the latest Git version.

Update Git on Mac

The easiest way to update Git on Mac is to use the official installer. Download the installation file from the Git website. Run the installation and follow the install wizard to update Git to the latest version.

Note: Using the install wizard to update Git overwrites the current installation.

Another method is to update Git using Homebrew. If you don’t have Homebrew already, install it by using:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Update Homebrew to make sure you have the latest installation packages:

brew update

Install the latest version of Git with Homebrew:

brew install git

If you already have Git installed using Homebrew, update to the latest version with:

brew upgrade git

Check the current Git version to confirm the update:

git --version

Note: If checking the Git version after updating results in an output that includes (Apple Git-101), your system is still running the default Apple version of Git instead of the official one. Change your local path to the Homebrew version of Git to fix this issue:
export PATH=/usr/local/bin:$PATH

Conclusion

After following this tutorial, you should have a fully updated version of Git installed on a Linux, Windows, or macOS machine.

Take a look at our Git Commands Cheat Sheet for a comprehensive primer on working with Git. If you come across a Git merge conflict, make sure to read our article How to Resolve Merge Conflicts in Git.

Понравилась статья? Поделить с друзьями:
  • Cheats for minecraft windows 10 edition
  • Cheat o matic для windows 10
  • Chipset inf utility windows 7 x64
  • Chipsbnk flash disk usb device не видит флешку windows 10
  • Chips challenge скачать игру для windows 10