Я работаю в операционной системе «Windows 10». Использую программу «Git» (дистрибутив «Git for Windows») версии 2.35.1 (и программу-оболочку «Git Bash» из ее дистрибутива). С программой «Git», которая является системой управления версиями, обычно работаю из командной строки с помощью программы-оболочки «PowerShell» версии 7, запускаемой из программы-«эмулятора терминала» «Windows Terminal» версии 1.16.
Разные способы организации работы над проектом
Погружаясь в мир совместной разработки программ, я узнал, как выполнять вклад в проект, работая с удалёнными репозиториями (по-английски «remote repository» или просто «remote»), которые могут находиться на других компьютерах в компьютерной сети, в том числе — в интернете. При этом для отправки вклада в проект, файлы которого хранятся в удалённом репозитории (хранилище), в программе «Git» используется команда «git push», для применения которой требуется обладать правом записи в удалённый репозиторий.
На сайте «GitHub» я познакомился с другим способом отправки вклада в проект: с помощью создания «форка» (копии исходного репозитория проекта; это слово является калькой с английского «fork») под своей учетной записью, внесения в форк изменений и последующей отправки запроса на принятие изменений (по-английски «pull request», сокращенно «PR») в исходный репозиторий. При этом не требуется обладать правом записи в исходный репозиторий. Получается, что вы передаете предлагаемые вами изменения в проект (вклад) на рассмотрение людям, которые имеют право записи в исходный репозиторий. Они рассмотрят ваш запрос на принятие изменений и, возможно, примут его (выполнят слияние ваших изменений с исходным проектом), если изменение будет признано ими полезным и не будет содержать ошибок. То есть в данном случае мы действуем через посредника.
Только после этого я узнал про существование «патчей» (файлов с вашими изменениями в проект). Сразу мне было непонятно, зачем нужны эти патчи, если существуют вышеописанные способы внесения вклада в проект. Как оказалось, существует еще множество других способов организации работы над проектом, в том числе способ с помощью патчей. При этом способе вы получаете копию файлов проекта из исходного репозитория, вносите в них свои изменения, формируете файл (патч) с изменениями и передаете этот файл каким-либо способом (загрузив патч на сайт проекта, переслав его по электронной почте или еще как-либо) людям, у которых есть право записи в исходный репозиторий.
Таким образом, способ работы над проектом с помощью патчей немного похож на вышеописанный способ работы с помощью запросов на принятие изменений на сайте «GitHub»: здесь тоже работа идет через посредника, который рассмотрит ваш патч и, возможно, сделает его слияние с файлами проекта в исходном репозитории.
Откуда взялось слово «патч», инструменты «diff» и «patch»
Вообще, способ работы над проектом с помощью патчей возник намного раньше, чем появилась программа «Git» (2005 год). Да, в общем-то, и намного раньше, чем появились системы управления версиями (начало 1960-х). Слово «патч» — это калька с английского слова «patch», которое дословно означает «заплатка» или «наложить заплатку». Уже в 1940-х годах «патчи» реально могли представлять собой физически существующие заплатки в виде кусочков бумаги, которыми заклеивали некоторые места на бумажных перфолентах или перфокартах, таким образом исправляя ошибки в программах, хранящихся на этих перфолентах и перфокартах.
В Unix-подобных операционных системах существуют команды (программы-инструменты) «diff» и «patch». С помощью инструмента «diff» можно найти разницу между файлами, которую после этого можно выгрузить в отдельный файл. Полученный файл с изменениями тоже называют «diff», а еще этот же файл могут называть «patch», так как его можно передать другим разработчикам, которые применят этот файл с изменениями к своей копии файлов проекта с помощью инструмента «patch», таким образом внеся предлагаемые изменения в проект. Название инструмента «diff» получено сокращением от английского слова «difference» (по-русски «разница»).
Следует иметь в виду, что программа-инструмент «diff» может выдавать разницу между файлами в разных форматах, например, в «контекстном формате» или в «унифицированном формате» (по-английски «unified format» или «unidiff»).
В программе «Git» для получения разницы между файлами, коммитами, версиями проекта используется команда «git diff», после чего полученная разница может быть выгружена в отдельный файл (патч). После передачи файла-патча человеку, имеющему право записи в исходный проект, этот человек может применить полученный файл-патч к файлам исходного проекта с помощью команды «git apply». Насколько я понимаю, при этом используется формат вывода разниц «unidiff» (унифицированный формат).
Работа с патчем в программе «Git» на тестовом проекте
Подготовка тестового проекта
Сначала подготовим тестовый проект, он у меня находится в папке «C:\Users\Илья\source\repos\test\». Проект состоит из одного текстового файла «shalandy.txt», в который записан текст в кодировке UTF-8 с окончаниями строк вида CRLF, как принято в операционных системах «Windows» (окончания строк я буду показывать, хотя в редакторах кода они обычно скрыты):
shalandy.txt (185 байтов, кодировка UTF-8)
Шаланды, полные кефали,CRLF
В Одессу Костя приводил.CRLF
И все биндюжники вставали,CRLF
Когда в пивную он входил.
Шаг 1. Создание пустого Git-репозитория для проекта:
PS C:\Users\Илья\source\repos\test> git init
Initialized empty Git repository in C:/Users/Илья/source/repos/test/.git/
Шаг 2. Проверка настройки режима работы с окончаниями строк для проекта:
PS C:\Users\Илья\source\repos\test> git config core.autocrlf
true
Про эту настройку у меня есть отдельная статья, там же показано на примере, как ее можно изменить. У меня для этой настройки прописано значение true
на уровне текущего пользователя (global) операционной системы, поэтому нет необходимости задавать ей значение отдельно на уровне проекта. Напомню, при этой настройке со значением true
предполагается, что в рабочей папке в файлах мы имеем дело с окончаниями строк вида CRLF, а в Git-репозитории версии файлов хранятся с окончаниями строк вида LF.
Шаг 3. Добавление исходной версии файла «shalandy.txt» в индекс (stage):
PS C:\Users\Илья\source\repos\test> git add "shalandy.txt"
Для демонстрации создания файла-патча нам достаточно помещения версии файла «shalandy.txt» в индекс (далее в статье будет показано и помещение версии файла в коммит). Команда «git diff» может быть использована как для получения разницы между версией файла в индексе и версией файла в рабочей папке, так и для получения разницы между версией файла в определенном коммите и версией файла в рабочей папке (а также для многих других разных сравнений).
Шаг 4. Внесение изменений в файл «shalandy.txt» (то есть создание его новой версии) в рабочей папке:
shalandy.txt (217 байтов, кодировка UTF-8)
Шаланды, полные кефали,CRLF
В Одессу Гена приводил.CRLF
И все биндюжники вставали,CRLF
Когда в пивную он входил.CRLF
(поёт Марк Бернес)
Создание файла разниц (файла-патча)
У нас есть две версии файла «shalandy.txt» из тестового проекта. Исходная версия содержится в индексе Git-репозитория, новая версия — в рабочей папке проекта. Разницы между ними можно просмотреть в окне консоли (терминала) с помощью следующей команды:
PS C:\Users\Илья\source\repos\test> git diff
Вот как выглядит результат работы этой команды у меня в программе-оболочке «PowerShell» версии 7, запущенной в программе-«эмуляторе терминала» «Windows Terminal» версии 1.16:
Далее я опишу два сценария получения файла-патча и некоторые тонкости работы программ, на которые при этом стоит обратить внимание. Начнем с более простого сценария, а потом перейдем к более сложному.
Сценарий 1. Получение файла-патча «my.patch» из программы-оболочки «Git Bash», которую я получил в составе дистрибутива «Git for Windows». По умолчанию программа-оболочка «Git Bash» у меня настроена для работы в традиционной для операционных систем «Windows» программе-«эмуляторе терминала» «Windows Console»:
Илья@IlyaComp MINGW64 ~/source/repos/test (master)
$ git diff > my.patch
my.patch (493 байта, кодировка UTF-8)
diff --git a/shalandy.txt b/shalandy.txtLF
index 8591ff8..fd3ff04 100644LF
--- a/shalandy.txtLF
+++ b/shalandy.txtLF
@@ -1,4 +1,5 @@LF
Шаланды, полные кефали,LF
-В Одессу Костя приводил.LF
+В Одессу Гена приводил.LF
И все биндюжники вставали,LF
-Когда в пивную он входил.LF
\ No newline at end of fileLF
+Когда в пивную он входил.LF
+(поёт Марк Бернес)LF
\ No newline at end of fileLF
Следует иметь в виду, что в команде git diff > my.patch
только часть git diff
относится к программе «Git», а часть > my.patch
обрабатывается программой-оболочкой (в данном случае «Git Bash»), из которой запущена эта команда. Символ >
означает перенаправление вывода (в данном случае вывод команды git diff
вместо окна консоли отправляется в указанный файл). В разных программах-оболочках часть > my.patch
команды может быть обработана по-разному, из-за чего файл «my.patch» может быть сформирован с ошибками (это будет показано далее).
В вышеприведенном блоке кода видно, что в полученном файле-патче созданы окончания строк вида LF. Следует иметь в виду, что программа «Git» в данном случае возвращает строки исходной версии файла «shalandy.txt» в том виде, в котором они хранятся в Git-репозитории. Если в Git-репозиторий случайно попадут строки с окончаниями вида, к примеру, CRLF, то в файл «my.patch» они будут выгружены тоже с окончаниями вида CRLF.
Сценарий 2. Получение файла-патча «my.patch» из программы-оболочки «PowerShell» версии 7, запущенной в программе-«эмуляторе терминала» «Windows Terminal» версии 1.16.
Сначала следует проверить значения некоторых настроек:
PS C:\Users\Илья\source\repos\test> (Get-WinSystemLocale).Name
ru-RU
PS C:\Users\Илья\source\repos\test> ([System.Console]::OutputEncoding).CodePage
866
PS C:\Users\Илья\source\repos\test> $OutputEncoding.CodePage
65001
Выше, на иллюстрации 1, было показано, что вывод команды git diff
, запущенной из программы-оболочки «PowerShell», в окно терминала у меня происходит без проблем с настройками по умолчанию. Перенаправление вывода этой команды в файл происходит по-другому, русские буквы трансформируются в кракозябры (по-английски «mojibake»).
Это происходит потому, что при перенаправлении вывода в файл в вышеописанном сценарии в процесс вмешивается объект класса System.Console
, в свойстве которого OutputEncoding
у меня по умолчанию записан объект, представляющий кодировку «CP866» (устаревшая 8-битная кодировка, входящая в группу устаревших кодировок «OEM» операционных систем «Windows»). Значение по умолчанию для свойства OutputEncoding
объекта класса System.Console
зависит от языка системы (по-английски «system locale»; не путать с «языком интерфейса», по-английски «display language» или «interface language»). Для языка системы «ru-RU» («Русский (Россия)») кодировкой по умолчанию в группе кодировок «OEM» является кодировка (кодовая страница) «CP866», так как в нее включен русский алфавит.
Текущее значение языка системы я получил в блоке кода выше с помощью командлета Get-WinSystemLocale
.
Свойство OutputEncoding
объекта класса System.Console
не следует путать с предопределенной переменной $OutputEncoding
, это разные вещи. Как видно из блока кода выше, по умолчанию в программе-оболочке «PowerShell» версии 7 эта переменная содержит объект, представляющий кодировку UTF-8 (кодовая страница 65001).
В результате настроек по умолчанию, показанных в блоке кода выше, при применении команды git diff > my.patch
байты текста в кодировке UTF-8 сначала интерпретируются побайтно по кодовой странице «CP866», а затем полученные символы конвертируются в байты по правилам кодировки UTF-8. Вот как это происходит на примере буквы «Ш» слова «Шаланды» (кому интересно, у меня есть более подробный разбор):
D0 A8 -----------------> D0 A8 --------------> ╨ и
Ш интерпретация ╨ и конвертация E2 95 A8 D0 B8
UTF-8 как CP866 в UTF-8
Как видно из блока кода выше, мало того, что в результате получаются кракозябры, так еще размер полученного текста в байтах может вырасти в 2-3 раза (в примере выше из двух изначальных байт D0 A8
получено пять байт E2 95 A8 D0 B8
). Понятно, что всё это не касается символов из таблицы ASCII (в том числе символов латиницы), так как эти символы в кодировках «CP866» и «UTF-8» отображаются одинаково (одними и теми же кодами, и занимают по одному байту).
Конечно, результат можно конвертировать обратно. Например, в редакторе «VS Code» есть нужные для этого инструменты. Но правильнее будет перед выгрузкой файла-патча просто изменить соответствующую настройку:
PS C:\> [System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8
PS C:\> ([System.Console]::OutputEncoding).CodePage
65001
PS C:\Users\Илья\source\repos\test> git diff > my.patch
Теперь команда git diff > my.patch
создаст файл «my.patch» в корректном, то есть читаемом, виде, в кодировке UTF-8, размером 507 байтов. От файла, полученного при сценарии 1, который имел размер в 493 байта, файл, полученный при сценарии 2, отличается только видом окончаний строк. При первом сценарии окончания строк были вида LF, при втором сценарии получились окончания строк вида CRLF (отсюда разница в 14 байтов, так как в файле-патче содержится 14 строк).
Следует иметь в виду, что в программе-оболочке «PowerShell» перенаправление вывода >
делает то же самое, что и передача вывода по конвейеру |
командлету Out-File
(с указанием пути к файлу-результату, но без дополнительных параметров).
В отличие от команды git diff > my.patch
в первом сценарии, в данном случае строки от команды git diff
передаются на конвейер |
в виде 14 объектов (как уже было сказано выше, в файле-патче получилось 14 строк) класса System.String
. Однако, эти строки-объекты не содержат окончаний строк, это касается и тех строк, которые получены из Git-репозитория (это поведение отличается от описанного в сценарии 1). Программа-оболочка «PowerShell» версии 7 записывает полученные строки в файл с окончаниями вида CRLF.
Что делать, если мы хотим получить в этом сценарии файл-патч с окончаниями строк вида LF? Способ попроще: открыть полученный файл в редакторе кода и преобразовать окончания строк в окончания нужного вида. Инструменты для этого есть, например, в редакторе «Notepad++». Способ посложнее, из командной строки: вместо команды git diff > my.patch
в блоке кода выше можно ввести, к примеру, такую команду:
PS C:\Users\Илья\source\repos\test> git diff |
>> Join-String -Separator "`n" -OutputSuffix "`n" |
>> Out-File -FilePath "my.patch" -NoNewline
Суть тут в том, что мы получаем от команды git diff
14 объектов-строк, склеиваем их в один объект-строку с помощью командлета Join-String
, вставляя между ними (параметр -Separator
) символ LF и в конце (параметр -OutputSuffix
) символ LF. После этого передаем полученный один объект-строку командлету Out-File
, запрещая ему с помощью параметра -NoNewline
вставку умолчательного окончания строки CRLF после нашего одного итогового объекта-строки (у меня есть подробный разбор этой команды).
Применение файла-патча с помощью команды «git apply»
Думаю, понятно, что применять созданный нами файл-патч будут другие люди на их копии нашего проекта (или на оригинале проекта, если наш проект является копией оригинала). Чтобы смоделировать эту ситуацию, создадим копию нашего проекта, на которой будем пытаться применить файл-патч. Вернее, мы будем создавать копию Git-репозитория с помощью команды «git clone».
Ранее, в рамках подготовки тестового проекта, я создал Git-репозиторий, но ничего в него не поместил, поэтому Git-репозиторий пока что остался пустым. Я поместил только исходную версию файла «shalandy.txt» в индекс, а в рабочей папке создал измененную версию исходного файла «shalandy.txt», после чего из разниц между этими версиями с помощью команды «git diff» создал файл-патч «my.patch». Напомню, индекс — это отдельный служебный файл, он не входит в состав базы данных «Git», поэтому Git-репозиторий всё еще считается пустым.
Шаг 5. Поместим в базу данных «Git» первый коммит, сформировав его из содержимого индекса:
PS C:\Users\Илья\source\repos\test> git commit -m "Первый коммит"
[master (root-commit) 3834f65] Первый коммит
1 file changed, 4 insertions(+)
create mode 100644 shalandy.txt
Шаг 6. Создание копии (клона) исходного Git-репозитория:
PS C:\Users\Илья\source\repos\test> cd ..
PS C:\Users\Илья\source\repos> git clone test test2
Cloning into 'test2'...
done.
Команда cd
в программе-оболочке «PowerShell» — это псевдоним (alias) командлета «Set-Location». А вообще это известная команда в разных операционных системах. Параметр ..
означает родительскую папку для текущей папки. То есть команда cd ..
выполняет переход на ближайший верхний уровень от текущего местоположения в дереве папок. В результате мы перешли в местоположение «C:\Users\Илья\source\repos\».
Этот переход не обязателен для применения команды «git clone», но он делает ввод параметров для этой команды более удобным: после перехода нам достаточно набрать название исходной папки (наш проект) и название новой папки (новый проект, копия нашего), без набора полного пути к этим папкам. (Вообще, команду «git clone» можно применить несколькими способами, об этом можно прочитать в документации.)
Итак, первый параметр test
команды «git clone» в данном случае — название исходной папки нашего проекта. Папки test2
до ввода вышеприведенной команды «git clone» в местоположении «C:\Users\Илья\source\repos\» у меня не существовало. Команда «git clone» у меня создала папку с таким названием, после чего создала внутри вложенную папку «.git» (скрытую) и скопировала в нее базу данных «Git» моего оригинального проекта «test».
Если бы в данном случае папка «test2» (папка назначения) существовала бы в указанном местоположении, команда «git clone» выполнила бы клонирование, если б папка «test2» была бы пуста. Иначе, если бы в папке «test2» были бы какие-то файлы, то команда «git clone» не выполнила бы клонирование и выдала бы соответствующее предупреждающее сообщение.
После того, как команда «git clone» у меня успешно отработала и создала новую папку «test2» с вложенной в нее скрытой папкой «.git» (скопированная из оригинального проекта база данных «Git»), в рабочей папке «test2» появился и файл «shalandy.txt». Тут следует понимать, что этот файл не был скопирован из рабочей папки оригинального проекта «test», а был восстановлен из коммита в скопированной базе данных «Git». Таким образом, эта версия файла «shalandy.txt» — это первоначальная версия этого файла из оригинального проекта. То есть на данный момент файлы «shalandy.txt» в папке «test» и в папке «test2» отличаются друг от друга на разницу в файле-патче «my.patch», полученном в предыдущих постах. Всё готово для тестирования применения файла-патча с помощью команды «git apply».
Шаг 7. Применение файла-патча «my.patch» к файлу «shalandy.txt» в папке «test2»:
PS C:\Users\Илья\source\repos> cd test2
PS C:\Users\Илья\source\repos\test2> git apply "..\test\my.patch"
Первая команда из двух в блоке кода выше была уже объяснена ранее. Здесь мы с помощью этой команды cd test2
переходим в только что созданную папку с копией (клоном) нашего исходного проекта (вернее, с копией Git-репозитория нашего исходного проекта). Это нужно потому, что команда «git apply» применяет указанный файл-патч к текущей рабочей папке, так что в эту папку сначала следует перейти.
Далее запускаем команду «git apply», которая и применяет файл-патч к файлам в текущей рабочей папке текущего проекта. В данном случае я указал относительный путь "..\test\my.patch"
, который означает, что файл-патч «my.patch» находится в соседней для папки «test2» папке «test» (в рабочей папке исходного проекта).
В итоге у меня команда «git apply» выполнилась успешно, то есть исходная версия файла «shalandy.txt» в рабочей папке проекта «test2» преобразовалась в измененную версию (217 байтов, кодировка UTF-8, окончания строк вида CRLF).
Важно отметить, что всё работает успешно, если в файле-патче «my.patch» окончания строк вида LF. Если там окончания строк вида CRLF, то у меня выдаются сообщения об ошибке следующего вида:
PS C:\Users\Илья\source\repos\test2> git apply "..\test\my.patch"
error: patch failed: shalandy.txt:1
error: shalandy.txt: patch does not apply
В итоге всё это работает не слишком интуитивно: в файле-патче окончания строк вида LF, а в файле, к которому применяется файл-патч, окончания строк оказываются вида CRLF.
Работа с патчем на примере веб-приложения «WordPress»
Существует такое довольно известное веб-приложение «WordPress», которое представляет собой систему управления содержимым сайта. Это проект с открытым исходным кодом. Вы можете внести вклад в код этого проекта разными способами, в частности, предложив файл-патч. Вообще, исходный код веб-приложения «WordPress» находится под управлением программы (системы управления версиями) «Subversion» или сокращенно «SVN», но эта программа принимает патчи, сформированные и из программы «Git».
В принципе, когда разработчик хочет поработать с исходным кодом какого-либо проекта, находящегося под управлением программы «Git», он клонирует Git-репозиторий этого проекта к себе на компьютер и дальше уже работает с этим клоном (существуют зеркала репозитория с исходным кодом для программы «Git»). Но мне удобнее показать создание файла-патча на уже развернутом у меня локально экземпляре веб-приложения «WordPress».
Я установил это веб-приложение (дистрибутив можно загрузить с сайта проекта) к себе на компьютер в папку «C:\inetpub\wwwroot\wp\» (эта папка будет корневой папкой нашего проекта). Я решил предложить небольшое изменение в код одного из файлов проекта: «wp-includes\class-requests.php» (путь к файлу указан относительно корневой папки проекта).
Шаг 1. Создание пустого Git-репозитория для проекта:
PS C:\inetpub\wwwroot\wp> git init
Initialized empty Git repository in C:/inetpub/wwwroot/wp/.git/
Шаг 2. Настройка режима работы с окончаниями строк для проекта:
PS C:\inetpub\wwwroot\wp> git config core.autocrlf
true
PS C:\inetpub\wwwroot\wp> git config --local core.autocrlf input
PS C:\inetpub\wwwroot\wp> git config core.autocrlf
input
Из блока кода выше видно, что я переключил настройку «core.autocrlf» для проекта со значения «true» на значение «input». Этот шаг делать не обязательно. Дело в том, что файлы веб-приложения «WordPress» из его дистрибутива написаны с окончаниями строк вида LF. Настройкой «core.autocrlf=input» я хочу сохранить окончания строк в файлах проекта (в рабочей папке) в исходном виде при извлечении (checkout) версий файлов из Git-репозитория. При настройке «core.autocrlf=true» исходные окончания строк в файлах проекта (в рабочей папке) будут в такой ситуации затерты окончаниями строк вида CRLF.
Шаг 3. Запишем оригинальную версию файла «wp-includes\class-requests.php» в индекс:
PS C:\inetpub\wwwroot\wp> git add "wp-includes\class-requests.php"
Шаг 4. Внесем изменение в файл «wp-includes\class-requests.php» в рабочей папке проекта. Суть изменения в рамках данной статьи неважна, но кому интересно, можно прочитать подробный разбор по следующей ссылке.
Шаг 5. Создание файла-патча из программы-оболочки «Git Bash» и помещение этого файла на рабочий стол (кавычки, обособляющие путь к файлу-результату, в данном случае обязательны):
Илья@IlyaComp MINGW64 /c/inetpub/wwwroot/wp (master)
$ git diff > "C:\Users\Илья\Desktop\57325.diff"
После этого на моем рабочем столе появился файл-патч «57325.diff» размером 444 байта в кодировке UTF-8 с окончаниями строк вида LF. В проекте «WordPress» принимают файлы-патчи с расширением либо «.diff», либо «.patch».
Предварительно вы должны создать в системе управления проектом (это можно сделать через сайт wordpress.org проекта, для этого требуется регистрация на сайте) сообщение об ошибке (по-английски его называют «ticket») с подробным описанием ошибки. После чего к этому сообщению об ошибке можно приложить файл-патч с предлагаемым исправлением ошибки. Название файла-патча должно совпадать с номером сообщения об ошибке. То есть в моем случае я приложу полученный выше файл-патч «57325.diff» к предварительно созданному сообщению об ошибке с номером 57325.
Вот что у меня получилось в результате в системе управления проектом «WordPress» на сайте wordpress.org:
-
Сообщение об ошибке (ticket) номер 57325;
-
Файл-патч, приложенный к сообщению об ошибке номер 57325.
There are plenty of programs out there that can create a diff patch, but I’m having a heck of a time trying to apply one. I’m trying to distribute a patch, and I got a question from a user about how to apply it. So I tried to figure it out on my own and found out that I have no clue, and most of the tools I can find are command-line. (I can handle a command line, but a lot of people would be lost without a nice, friendly GUI. So those are no good for this purpose.)
I tried using TortoiseSVN. I have the patch I’d like to apply. I right-click on the patch, and there’s an option under the TortoiseSVN submenu that says «Apply patch.» All it does is pull up an empty window.
So I tried hitting Open. It has two options: merge and apply unified diff. (The patch is in unified diff format, luckily.) But the apply option just plain doesn’t work: It asks for the patch and a folder. Somehow it forgot to ask for the file to apply the patch to! So TortoiseSVN just plain doesn’t work. Is there a Windows GUI-based utility that will take a patch and a file and apply it properly?
EDIT: Looking at the replies so far, it seems that Tortoise will only do it right if it’s a file that’s already versioned. That’s not the case here. I need to be able to apply a patch to a file that did not come out of an SVN repository. I just tried using Tortoise, because I happen to know that SVN uses diffs and has to know how to both create them and apply them.
isapir
15.2k7 gold badges86 silver badges98 bronze badges
asked Feb 5 ’09 at 18:33
Mason WheelerMason Wheeler
78.4k42 gold badges249 silver badges456 bronze badges
3
in response to the query earlier about patch and Python 3, I got the error
TypeError: can only concatenate str (not «bytes») to str
The solution for me was to change the source, in the _normalize_filenames function:
- debug(" target = " + p.target)
- debug(" source = " + p.source)
+ debug(" target = ", p.target)
+ debug(" source = ", p.source)
then patch worked fine for me (Python 3, Windows 10)
answered Apr 27 at 16:52
If you have git install on Windows and you want to apply a patch for a git repository, you can simply do from a Windows Power Shell:
git apply ..\0001-your-patch.patch
answered Mar 18 at 11:47
OlivierMOlivierM
2,16216 silver badges33 bronze badges
Using git Diff or linux patch to apply a patch on windows using git diff
Patches created anywhere on linux, MacOS or else, using the GNU patch command or git diff can be all applied on windows using git apply.
Create the patch
For instance to create the patch from 2 directories in which 1 or more files have been changed:
diff -Naru original_dir modified_dir > 0001-path-file.patch
- «-N» treat absent files as empty, necessary to only consider modified content
- «-a» treat all files as text, not compulsory, needs removed if dealing — with binary patches.
- «-r» recursive for directories traversal
- «-u» adds 3 lines of context by default around all diffs
OR using git diff
git diff original_dir modified_dir > 0001-path-file.patch
Then copy the .patch file on the windows environment as well as the original_dir directory that contains the original unchanged files.
Apply with git diff:
- Pre-requisite: Install git, i.e. from https://git-scm.com/downloads
1 Copy the patch file in the parent directory
2 cd into the original directory
3 Apply the patch using git apply
copy 0001-path-file.patch ..\original_dir\
cd original_dir
git apply < 0001-path-file.patch
answered Nov 17 ’20 at 0:06
Just use:
patch -p0 < path-file.patch
remember execute this command only from the folder location where you created the patch.
answered Dec 13 ’17 at 10:26
If you are using Mercurial, this is done via «import». So at the command line, the hg import
command, or (you may find the --no-commit
option useful), or «Repository» => «Import…» in Hg Workbench.
Note that these will commit the changes by default; you can avoid this using hg import --no-commit
option if using the command-line, or if you used Hg Workbench, you might find it useful to issue hg rollback
after the merge.
answered May 8 ’13 at 9:07
Marc Gravell♦Marc Gravell
939k239 gold badges2434 silver badges2795 bronze badges
1
I use MSYS2 from http://www.msys2.org/
It provides many utilities like patch
, which
, git
, tree
, and many more.
After installing MSYS2 simply run the package manager to install patch
:
pacman -S patch
answered Jun 26 ’17 at 23:13
isapirisapir
15.2k7 gold badges86 silver badges98 bronze badges
1
The patch.exe utility from the Git installation works on Windows 10.
Install Git for Windows then use the "C:\Program Files\Git\usr\bin\patch.exe"
command to apply a patch.
If any error message like a Hunk #1 FAILED at 1 (different line endings).
had been got on the output during applying a patch, try to add the -l
(that is a shortcut for the --ignore-whitespace
) or the --binary
switches to the command line.
answered Dec 1 ’16 at 20:10
2
I am already using BeyondCompare (commercial) for diffs and merges, and this tool also has the capability to create, view and apply patches.
answered Jul 26 ’16 at 12:19
FourtyTwoFourtyTwo
1,35710 silver badges35 bronze badges
A BusyBox port for Windows has both a diff and patch command, but they only support unified format.
answered Jul 25 ’15 at 18:37
MeowMeow
3,5851 gold badge16 silver badges16 bronze badges
For Java projects, I have used NetBeans to apply patch files. If the Java code you are patching is not already a NetBeans project, create a project for it. To create a new project:
- Select menu File -> New Project
- In the resulting dialog, make it a Java Application project. Give it a name in the dialog, and click Finish.
- Right-click the name of your project, and select Properties from the context menu
- In the resulting dialog, select Sources, and add a Source Folder. Browse to your Java source.
Now that you have a project, apply the patch:
- Highlight your project to select it
- From the main menu, select menu Tools -> Apply Diff Patch
- In the resulting dialog, browse to your patch file, select it, and press the Patch button.
That’s it. Your patch should be applied, and you should see a diff window showing the changes.
answered Sep 12 ’14 at 15:19
I made pure Python tool just for that. It has predictable cross-platform behavior. Although it doesn’t create new files (at the time of writing this) and lacks a GUI, it can be used as a library to create graphic tool.
UPDATE: It should be more convenient to use it if you have Python installed.
pip install patch
python -m patch
answered Feb 28 ’12 at 15:48
anatoly techtonikanatoly techtonik
17.7k8 gold badges114 silver badges134 bronze badges
7
Apply Patch
With TortoiseMerge:
- Find and open an existing SVN repo directory
- Create a new directory named «merges», if it does not exist already
- Copy the file onto which you want to apply the .patch file
- ADD and COMMIT to the svn repository before you continue to the next step
- Right click on merges and choose Apply patch…
- Double click the file from list
- The patched file with diff is displayed on the right pane
- Click on that pane and hit Save or export with File->Save As…
Alternative screeny if you Open from TortoiseMerge. In the screeny below, directory refers to the «merges» directory mentioned at step 2 above:
Screenshot of WinMerge GUI:
answered Mar 24 ’11 at 7:16
Sheriff MdSheriff Md
6261 gold badge7 silver badges11 bronze badges
5
If you are getting «Not a working copy» error message then try selecting a directory from TortoiseMerge dialog box which is a working directory of SVN.
answered Aug 24 ’13 at 12:51
Vinod DalviVinod Dalvi
3391 gold badge4 silver badges32 bronze badges
Eclipse should be able to do it, go to TeamSynchronize perspective and then into Project->Apply patch
answered Jun 12 ’13 at 10:01
Jose OspinaJose Ospina
1,7993 gold badges24 silver badges36 bronze badges
You can use this Win32 native port of the patch utility.
It comes along with a bigger selection of other utilities and in contrast to Cygwin and similar it does not need any DLLs or similar. Just pick your tiny executable of choice and store it wherever you want.
Simple usage:
patch.exe -i <patchfile>
Get more help:
patch.exe --help
RivieraKid
5,8734 gold badges36 silver badges47 bronze badges
answered Jan 8 ’13 at 14:23
logischlogisch
791 silver badge1 bronze badge
3
Do you have two monitors? I was having the same issue with TortoiseMerge and I realized that when I disabled one of the monitors the little window with the file list showed up.
Hope this helps you.
mercator
27.2k8 gold badges59 silver badges71 bronze badges
answered Jul 18 ’11 at 21:16
TortoiseMerge is a separate utility that comes bundled with TortoiseSVN.
It can also be can be downloaded separately in the TortoiseDiff.zip archive. This will allow you to apply unified diffs to non-versioned files.
answered Sep 28 ’10 at 17:17
4
It appears that TortoiseSVN (TortoiseMerge) requires the line Index: foobar.py
in the diff/patch file. This is what I needed to do to make a non-TortoiseSVN patch file work with TortoiseSVN’s right-click Apply Patch command.
Before:
--- foobar.py.org Sat May 08 16:00:56 2010
+++ foobar.py Sat May 08 15:47:48 2010
After:
Index: foobar.py
===================================================================
--- foobar.py
+++ foobar.py (working copy)
Or if you know the specific revision your contributor was working from:
Index: foobar.py
===================================================================
--- foobar.py (revision 1157)
+++ foobar.py (working copy)
answered May 10 ’10 at 6:26
matt wilkiematt wilkie
14.5k21 gold badges70 silver badges100 bronze badges
In TortoiseSVN, patch applying does work. You need to apply the patch to the same directory as it was created from. It is always important to keep this in mind. So here’s how you do it in TortoiseSVN:
Right click on the folder you want to apply the patch to. It will present a dialog asking for the location of the patch file. Select the file and this should open up a little file list window that lists the changed files, and clicking each item should open a diff window that shows what the patch is about to do to that file.
Good luck.
answered Feb 5 ’09 at 18:42
DanDan
3,52728 silver badges38 bronze badges
1
The patch tells it what file to apply to. The header should be something like (view it in Notepad or your fav text editor):
--- Folder/old_file
+++ Folder/new_file
In the case of a Subversion patch, you’d have revision numbers as well (since the file names are the same).
GNU patch will let you override those names, but I don’t know of any GUI tools to do the same. I’d check with the various diff programs — though, it does not appear WinMerge supports applying patches.
Jeromy Anglim
31.1k24 gold badges109 silver badges167 bronze badges
answered Feb 5 ’09 at 18:46
Mark BrackettMark Brackett
82.1k17 gold badges103 silver badges150 bronze badges
5
I know you said you would prefer a GUI, but the commandline tools will do the work nicely. See GnuWin for a port of unix tools to Windows. You’d need the patch command, obviously
You might run into a problem with the line termination, though. The GnuWin port will assume that the patchfile has DOS style line termination (CR/LF). Try to open the patchfile in a reasonably smart editor and it will convert it for you.
answered Apr 19 ’10 at 11:33
SardaukarSardaukar
24.3k4 gold badges26 silver badges30 bronze badges
4
EDIT: Looking at the replies so far, it seems that Tortoise will only do it right if it’s a file that’s already versioned. That’s not the case here. I need to be able to apply a patch to a file that did not come out of an SVN repository. I just tried using Tortoise because I happen to know that SVN uses diffs and has to know how to both create them and apply them.
You can install Cygwin, then use the command-line patch tool to apply the patch. See also this Unix man page, which applies to patch.
answered Feb 5 ’09 at 21:05
Brian ClapperBrian Clapper
23.6k7 gold badges62 silver badges65 bronze badges
1
When applying patches using TortoiseSVN, I typically save the path in the root of the checked out repository. You should then be able to right click on the patch, go to the TortoiseSVN menu, and click ApplyPatch. ApplyPatch should automatically figure out which level in the directory hierarchy the patch was created.
I have, however, had issues in the past with applying patches that contain new files, or which involve renames to files. Whatever algorithm Tortoise uses for this doesn’t seem to handle those scenarios very well. Unicode can give you similar issues.
answered Feb 5 ’09 at 18:43
tsellontsellon
2,2965 gold badges24 silver badges30 bronze badges
Not the answer you’re looking for? Browse other questions tagged windows diff gnu patch or ask your own question.
Overview
BDiff and BPatch are a pair of related Windows command line programs for creating diffs between two files and recreating files using the previously created diffs.
BDiff is used to output information about the differences between two files, with the option to output binary information. BPatch can apply binary patches created by BDiff.
For details of how to use the programs read the manual pages – there are links in the documentation section below.
These programs are modified versions of Stefan Reuther’s bdiff and bpatch utilities, based on Pascal translations of the original C source code. [Get the C source code from Google Drive].
Download
These programs are hosted on GitHub in the ddabapps/bdiff
repository. Both 32 bit and 64 bit versions of the programs can be downloaded from the repository’s Releases section.
Each release gives the option of downloading the programs in executable format or as source code. Your options are:
-
bdiff-exe32--1.0.0-rc.1.zip
— contains the 32 bit version of BDiff and BPatch along with documentation and tests. -
bdiff-exe64--1.0.0-rc.1.zip
— contains the 64 bit version of BDiff and BPatch along with documentation and tests. -
Choose
Source code.zip
to download the source code in.zip
format. -
Choose
Source code.tar.gz
to download the source code as a g-zipped tarball.
Get release v1.0.0-rc.1 from GitHub
Documentation
The following documentation is available. All the links open on GitHub.
- Read Me (markdown format text file) – contains details of how to install the programs, along with other information.
- BDiff manual page (markdown format text file) – provides details on how to use BDiff.
- BPatch manual page (markdown format text file) – provides details on how to use BPatch.
- License (markdown format text file) – the programs’ end user license agreement.
- Change Log (markdown format text file) – lists significant changes made in each release.
- Build File (markdown format text file) – provides detailed information on how to build the programs from source.
All the above files, except for the Build File, are included in the executable program download.
Bugs & Feature Requests
If you find any bugs or want to suggest a new feature, please use the GitHub project’s issue tracker. You can browse existing issues to see if a similar one has already been raised. If so please add any further information as comments. If there is no similar issue please raise new one.
You will need a GitHub account if you want to create or edit an issue.
When you have modified some the source code of some project you downloaded,
but do not want to commit your changes to the repository or don’t have write
access to the repository, the universal language for transmitting these changes
to other people is a patch in the unified diff format.
This is much better than just zipping the source tree because with a patch,
other people can apply your changes to their own source file tree without
destroying their own changes to the sources.
For example, many of the precompiled binaries I store in my externals repository include
a patch file containing the changes I’ve done (mostly just adding new project
files matching my preferences).
The archive attached to this article contains the diff.exe and patch.exe
from the gnuwin32 project, tweaked a little so that
you don’t require administrator privileges to run patch.exe.
How to Create a Patch
My preferred way of packaging patches is to put a directory containing the unchanged
files in parallel to the one containing the changed files like this:
Another thing you can see here is my naming scheme: I always add the version
number of the library to the directory name. This makes it unambiguously clear
which version of the library the patch is for.
I name the directory containing original (unmodified) state of the library
like my working directory, but with -original appended to it. This
name is completely ignored when you apply the patch, but anyone reading
the patch in an editor will immediately get the right idea which side is which!
Finally, as a matter of common courtesy, make sure your patch is clean and
doesn’t contain pointless artifacts such as build logs, temporary files or
other random stuff.
To generate a patch, open a command prompt and navigate to the directory
containing the two directories you wish to compare and enter:
diff -r -u -N -w SomeLibrary-1.2.3-original SomeLibrary-1.2.3 > SomeLibrary-1.2.3-changes.patch
Applying Patches
To apply a patch, read the patch to find out what file structure it expects.
The patch created before would require you to place an unmodified copy of the
code in a directory called SomeLibrary-1.2.3.
Then open a command prompt and navigate to the right place for the patch
(again, for the example patch above, that would be one directory above
SomeLibrary-1.2.3). Then apply the patch by piping it to patch.exe:
patch -p0 < SomeLibrary-1.2.3-changes.patch
The -p0 indicates the number of directory levels you want to strip
from the patch. With -p0, no directories will be stripped, but if
the patch contains a leading slash (which would turn this in to an absolute
path starting at the root directory), that will be removed.
BDiff / BPatch
Binary diff and patch programs for the Windows command line.
Introduction
BDiff computes the differences between two files, say file1 and file2. Output can be either a somewhat human-readable protocol in plain text, or a binary file that is readable by BPatch.
BPatch applies a binary patch generated by BDiff to file1 to recreate file2.
See the files BDiff.md
and BPatch.md
in the Docs
directory for details of how to use the programs.
BDiff and BPatch are derived from Stefan Reuther’s bdiff and bpatch v0.2 and a later bug fix by Stefan.
The original C source was translated into Object Pascal by Peter D Johnson. The programs are based on updates of the Pascal code base.
The programs should run on Windows 7 SP1 and later.
For more information see the see the project web pages.
Installation & Uninstallation
BDiff and BPatch are available as both 32 bit and 64 bit Windows console applications.
The latest release can be downloaded from the project’s GitHub releases page.
Both programs are packaged together in a zip file. There is one zip file for the 32 bit Windows version and another for the 64 bit Windows version. Download the desired version and unzip the file.
Copy the both executable files to the required location. Always place both programs in the same directory. No further installation is required.
You may want to modify the Windows PATH environment variable to include the location of the programs.
To uninstall simply delete the programs. They make no changes to the system. If you changed the PATH environment variable you may wish to adjust this.
Tests
You can test the operation of BDiff and BPatch using the Test.bat
, Test32.bat
and Test64.bat
scripts in the Test
directory. See ReadMe.md
in that directory for details.
Source Code
Pascal Source
The current source code is maintained in the delphidabbler/bdiff Git repository on GitHub. It contains releases going back to v0.2.5. Earlier versions were not under version control and are no longer available.
Note: Until February 2014 the source code was maintained in a Subversion repository. A dump of the repo is available from Google Drive.
For information on how to build the Pascal source, see Build.md
in the root of the Git repo.
C Source
The original C source code can be downloaded from Google Drive.
Copyright and License
See the file LICENSE.md
for details of copyright and the license that applies to this software.
Change Log
The change log is provided in the file CHANGELOG.md
.