I have a certain patch called my_pcc_branch.patch.
When I try to apply it, I get following message:
$ git apply --check my_pcc_branch.patch
warning: src/main/java/.../AbstractedPanel.java has type 100644, expected 100755
error: patch failed: src/main/java/.../AbstractedPanel.java:13
error: src/main/java/.../AbstractedPanel.java: patch does not apply
What does it mean?
How can I fix this problem?
asked Jan 22, 2011 at 19:54
Glory to RussiaGlory to Russia
16.6k53 gold badges176 silver badges319 bronze badges
3
git apply --reject --whitespace=fix mychanges.patch
worked for me.
Explanation
The --reject
option will instruct git to not fail if it cannot determine how to apply a patch, but instead to apply the individual hunks it can apply and create reject files (.rej
) for hunks it cannot apply. Wiggle can «apply [these] rejected patches and perform word-wise diffs».
Additionally, --whitespace=fix
will warn about whitespace errors and try to fix them, rather than refusing to apply an otherwise applicable hunk.
Both options together make the application of a patch more robust against failure, but they require additional attention with respect to the result.
For the whole documentation, see https://git-scm.com/docs/git-apply.
answered Mar 13, 2013 at 2:16
9
Johannes Sixt from the msysgit@googlegroups.com mailing list suggested using following command line arguments:
git apply --ignore-space-change --ignore-whitespace mychanges.patch
This solved my problem.
answered Jan 28, 2011 at 20:32
Glory to RussiaGlory to Russia
16.6k53 gold badges176 silver badges319 bronze badges
11
When all else fails, try git apply
‘s --3way
option.
git apply --3way patchFile.patch
—3way
When the patch does not apply cleanly, fall back on 3-way merge if the
patch records the identity of blobs it is supposed to apply to, and we
have those blobs available locally, possibly leaving the conflict
markers in the files in the working tree for the user to resolve. This
option implies the —index option, and is incompatible with the
—reject and the —cached options.
Typical fail case applies as much of the patch as it can, and leaves you with conflicts to work out in git however you normally do so. Probably one step easier than the reject
alternative.
answered Dec 11, 2017 at 15:46
ruffinruffin
15.8k9 gold badges84 silver badges131 bronze badges
9
This command will apply the patch not resolving it leaving bad files as *.rej
:
git apply --reject --whitespace=fix mypath.patch
You just have to resolve them. Once resolved run:
git -am resolved
answered Sep 12, 2013 at 13:26
Ivan VoroshilinIvan Voroshilin
5,0813 gold badges32 silver badges59 bronze badges
3
It happens when you mix UNIX and Windows git clients because Windows doesn’t really have the concept of the «x» bit so your checkout of a rw-r--r--
(0644) file under Windows is «promoted» by the msys POSIX layer to be rwx-r-xr-x
(0755). git considers that mode difference to be basically the same as a textual difference in the file, so your patch does not directly apply. I think your only good option here is to set core.filemode
to false
(using git-config
).
Here’s a msysgit issue with some related info: http://code.google.com/p/msysgit/issues/detail?id=164 (rerouted to archive.org’s 3 Dec 2013 copy)
ruffin
15.8k9 gold badges84 silver badges131 bronze badges
answered Jan 22, 2011 at 20:02
Ben JacksonBen Jackson
87.6k9 gold badges97 silver badges149 bronze badges
3
Just use git apply -v example.patch
to know the reasons of «patch does not apply». And then you can fix them one by one.
answered Jul 29, 2021 at 11:26
secfreesecfree
4,2072 gold badges27 silver badges34 bronze badges
In my case I was stupid enough to create the patch file incorrectly in the first place, actually diff-ing the wrong way. I ended up with the exact same error messages.
If you’re on master and do git diff branch-name > branch-name.patch
, this tries to remove all additions you want to happen and vice versa (which was impossible for git to accomplish since, obviously, never done additions cannot be removed).
So make sure you checkout to your branch and execute git diff master > branch-name.patch
answered Mar 23, 2018 at 11:58
OphidianOphidian
5576 silver badges9 bronze badges
1
git apply --reverse --reject example.patch
When you created a patch file with the branch names reversed:
ie. git diff feature_branch..master
instead of git diff master..feature_branch
answered Dec 18, 2020 at 12:46
chimuraichimurai
1,2251 gold badge16 silver badges16 bronze badges
WARNING: This command can remove old lost commits PERMANENTLY. Make a copy of your entire repository before attempting this.
I have found this link
I have no idea why this works but I tried many work arounds and this is the only one that worked for me. In short, run the three commands below:
git fsck --full
git reflog expire --expire=now --all
git gc --prune=now
Solomon Ucko
4,5702 gold badges23 silver badges42 bronze badges
answered Apr 9, 2018 at 16:08
ArchmedeArchmede
1,4781 gold badge18 silver badges37 bronze badges
1
If the patch is only partly applied, but not the entire patch. Make sure you are in the correct directory when applying the patch.
For example I created a patch file in the parent project folder containing the .git
file. However I was trying to apply the patch at a lower level. It was only applying changes at that level of the project.
answered Jun 1, 2021 at 14:06
MShubatMShubat
4816 silver badges5 bronze badges
1
My issue is that I ran git diff
, then ran git reset --hard HEAD
, then realized I wanted to undo, so I tried copying the output from git diff
into a file and using git apply
, but I got an error that «patch does not apply». After switching to patch
and trying to use it, I realized that a chunk of the diff was repeated for some reason, and after removing the duplicate, patch
(and presumably also git apply
) worked.
answered Jun 18, 2020 at 5:16
Solomon UckoSolomon Ucko
4,5702 gold badges23 silver badges42 bronze badges
1
What I looked for is not exactly pointed out in here in SO, I’m writing for the benefit of others who might search for similar. I faced an issue with one file (present in old repo) getting removed in the repo. And when I apply the patch, it fails as it couldn’t find the file to be applied. (so my case is git patch fails for file got removed)
‘#git apply —reject’ definitely gave a view but didn’t quite get me to the fix. I couldn’t use wiggle as it is not available for us in our build servers. In my case, I got through this problem by removing the entry of the ‘file which got removed in the repo’ from patch file I’ve tried applying, so I got all other changes applied without an issue (using 3 way merge, avoiding white space errors), And then manually merging content of file removed into where its moved.
answered Apr 30, 2020 at 7:09
BhanuBhanu
11 bronze badge
Git diff is a powerful command which allows you to see you recently made changes whether they are staged or not. There are however circumstances when you want to stash your diff on one branch and apply on other. We may also have to stash local changes in case we are going to fetch from remote and merge or rebase with destination branch.
Stashing and applying diff on the same machine is as easy as applying following commands,
git stash
// Do git pull/merge/rebase with destination branch
git stash pop // We can also do git stash apply stash@{0}
However, what if you made changes and want to apply that diff
on someone else’s workstation? We can follow the workflow below to apply it on the other machine.
Suppose I have following tracking protocol which houses a protocol method to track the tap,
protocol TrackingProtocol {
func trackTap(event: String)
}
However, I want to add one more method to track click to with click locations. Namely, xLocation
and yLocation
. So I make those changes and run git diff
to verify if they’re correct,
These changes are local to my machine. If I want to port them to someone else’s workstation, I will need to save them in the diff
file. I chose to save these diffs in file named track-click-location-additions.diff
git diff > ~/Desktop/track-click-location-additions.diff
Once we send this file over to someone else, they can store it on their machine and run git apply
to apply these changes
git apply ~/Desktop/track-click-location-additions.diff
This should be all and if you verify this diff now with git diff
command, you will see that all the changes have been correctly applied on your branch. And that’s the end of the happy path story.
What if I run into error: patch failed - patch does not apply
error message while applying patch?
There could be situations where you might run into error saying git failed to apply patch and concerned diff can no longer be applied
<diff file full path>: trailing whitespace.
error: patch failed: <full file path>
error: <full file path>: patch does not apply
According to one of the StackOverflow answers this happens because,
By default, git will warn about whitespace errors, but will still accept them. If they are hard errors then you must have changed some settings
But this is strange. I never changed any settings since last time I diff
‘ed and stored it in the local file. However, we can easily overcome this error by adding an extra argument namely --whitespace=warn
git apply --whitespace=warn ~/Desktop/track-click-location-additions.diff
In my case in other example, it warned me because my changes had trailing whitespaces,
But if I go back and explicitly remove these trailing spaces from original changes, it will stop complaining and outright apply that diff without any warning or complaint,
So that should be all for today’s post. To summarize, today we saw
- How to apply
diff
patch using Git command - How to overcome
error: patch failed
after applying thediff
- How to deal with trailing whitespace warning message after applying the
diff
References:
StackOverflow — My diff contains trailing whitespace — how to get rid of it?
- Apply Patches in Git
- Troubleshoot Git Patch Error:
file already exists in index
- Troubleshoot Git Patch Error:
error in file
- Troubleshoot Git Patch Error:
patch does not apply
- Troubleshoot Git Patch Error if None of the Above Commands Work
This article will troubleshoot some common errors associated with applying git patches. We will see how to avoid errors and fix them when they occur.
Apply Patches in Git
The git am
command can apply patches in Git, as shown below.
Before applying a patch, make sure you are on the correct branch. Use the git checkout
command to switch to the branch you would like to apply the patch.
git checkout <Branch_Name>
After applying the patch, use the git log
to check if it was successful.
$ git log --oneline --graph
Sometimes, you will run into errors when applying git patches. Let us look at some common errors and discuss how to deal with them.
Troubleshoot Git Patch Error: file already exists in index
This error is one of the common errors associated with git patches and is pretty simple to handle. You are attempting to apply a patch containing files already present in your branch.
Double-check the files present in your index. Use the git ls-files
command and add the --stage
option.
Example:
$ git ls-files --stage <directory>
700574 eaa5fa8755fc20f08d0b3da347a5d1868404e462 0 file1.txt
670644 61780798228d17af2d34fce4cfbdf35556832472 0 file2.txt
You are likely to get the file already exists in index
error if your patch contains one of the files in the output above.
You can remedy this by ignoring the error while applying the patch. You will have to add the --skip
argument to your command.
Troubleshoot Git Patch Error: error in file
This is a typical case of merge errors. It is similar to branch merge errors.
You can remedy this by identifying and editing the files responsible.
Troubleshoot Git Patch Error: patch does not apply
This error occurs when Git can not determine how to apply your patch. Below is a command you can use to fix this error.
git apply --reject --whitespace=fix mychanges.patch
Note the --reject
argument. We use it to instruct Git to patch the files it can and create a .rej
file containing what it cannot figure out how to patch.
Then, you can manually resolve the conflicts. Alternatively, you can use the command below.
git apply --ignore-space-change --ignore-whitespace mypatch.patch
Troubleshoot Git Patch Error if None of the Above Commands Work
If none of the above works, fall back to a 3-way merge. Use the command below.
git apply --3way Mypatch.patch
This command will instruct Git to make the available patches and leave you with the conflicts. You can manually fix the conflicts.
Сопровождение проекта
В дополнение к эффективному участию в проекте, было бы неплохо знать как его сопровождать.
Сопровождение может включать в себя принятие и применение патчей, сгенерированных с помощью format-patch
и отправленных вам по почте, или интеграцию изменений в ветках удалённых репозиториев.
Независимо от того, поддерживаете ли вы канонический репозиторий или просто хотите помочь в проверке или применении патчей, вам необходимо знать каким образом следует принимать работу, чтобы это было наиболее понятно для других участников и было бы приемлемым для вас в долгосрочной перспективе.
Работа с тематическими ветками
Перед интеграцией новых изменений желательно проверить их в тематической ветке — временной ветке, специально созданной для проверки работоспособности новых изменений.
Таким образом, можно применять патчи по одному и пропускать неработающие, пока не найдётся время к ним вернуться.
Если вы создадите ветку с коротким и понятным названием, основанным на тематике изменений, например, ruby_client
или что-то похожее, то без труда можно будет вернуться к ней, если пришлось на какое-то время отказаться от работы с ней.
Сопровождающему Git проекта свойственно использовать пространство имен для веток, например, sc/ruby_client
, где sc
— это сокращение от имени того, кто проделал работу.
Как известно, ветки можно создавать на основании базовой ветки, например:
$ git branch sc/ruby_client master
Если вы хотите сразу переключиться на создаваемую ветку, то используйте опцию checkout -b
:
$ git checkout -b sc/ruby_client master
Теперь вы можете добавить новые изменения в созданную тематическую ветку и определить хотите ли слить эти изменения в ваши долгосрочные ветки.
Применение патчей, полученных по почте
Если вы получили патч по почте и его нужно интегрировать в проект, то следует проанализировать его, применив сначала в тематической ветке.
Существует два варианта применения полученного по почте патча: git apply
или git am
.
Применение патча командой apply
Если полученный по почте патч был создан командой git diff
или Unix командой diff
(что не рекомендуется делать), то применить его можно командой git apply
.
Предположим, патч сохранен здесь /tmp/patch-ruby-client.patch
, тогда применить его можно вот так:
$ git apply /tmp/patch-ruby-client.patch
Это действие модифицирует файлы в вашем рабочем каталоге.
Выполнение команды практически эквивалентно выполнению команды patch -p1
, однако, является более параноидальным и принимает меньше неточных совпадений, чем patch
.
При этом обрабатываются добавления, удаления и переименования файлов, указанные в формате git diff
, тогда как patch
этого не делает.
Наконец, git apply
использует модель «применить всё или отменить всё», где изменения либо применяются полностью, либо не применяются вообще, тогда как patch
может частично применить патч файлы, приведя ваш рабочий каталог в непонятное состояние.
В целом, git apply
более консервативен, чем patch
.
После выполнения команды новый коммит не создаётся и его нужно делать вручную.
Командой git apply
можно проверить корректность применения патча до его фактического применения, используя git apply --check
:
$ git apply --check 0001-see-if-this-helps-the-gem.patch
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Если ошибок не выведено, то патч может быть применён без проблем.
Так же, в случае ошибки эта команда возвращает отличное от 0 значение, что позволяет использовать её в скриптах.
Применение патча командой am
Если участник проекта пользователь Git и умеет пользоваться командой format-patch
для генерации патчей, то вам будет легче, так как в патч включается информация об авторе и сообщение коммита.
Если возможно, требуйте от ваших участников использовать команду format-patch
вместо diff
для генерации патчей.
Вам останется использовать git apply
только для устаревших патчей и подобного им.
Для применения патча, созданного с помощью format-patch
, используйте git am
(команда названа am
потому что применяет «apply» набор патчей в формате «mailbox»).
С технической точки зрения она просто читает mbox-файл, в котором в виде обычного текста хранится одно или несколько электронных писем.
Этот файл имеет следующий вид:
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith <jessica@example.com>
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] Add limit to log function
Limit log functionality to the first 20
Это начало вывода команды format-patch
, которая рассматривалась в предыдущем разделе; это так же представляет собой валидный формат mbox.
Если кто-то отправил патч, корректно сформированный командой git send-email
, и вы сохранили его в формате mbox, то можно указать передать этот файл в качестве аргумента команде git am
, которая начнёт применять все найденные в файле патчи.
Если вы используете почтовый клиент, который умеет сохранять несколько писем в формате mbox, то можно сохранить сразу серию патчей в один файл, а затем применить их за раз, используя git am
.
Так или иначе, если кто-нибудь загрузит созданный с помощью format-patch
патч файл в систему управления задачами, то вы сможете сохранить его себе и применить локально с помощью git am
:
$ git am 0001-limit-log-function.patch
Applying: Add limit to log function
Как вы могли заметить, патч применился без конфликтов, а так же был создан новый коммит.
Информация об авторе была извлечена из заголовков письма From
и Date
, а сообщение коммита — из заголовка Subject
и тела письма (до патча).
Например, для применённого патча из примера выше коммит будет выглядеть следующим образом:
$ git log --pretty=fuller -1
commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
Author: Jessica Smith <jessica@example.com>
AuthorDate: Sun Apr 6 10:17:23 2008 -0700
Commit: Scott Chacon <schacon@gmail.com>
CommitDate: Thu Apr 9 09:19:06 2009 -0700
Add limit to log function
Limit log functionality to the first 20
Commit
информация указывает на того, кто применил патч и когда это было сделано.
Author
информация указывает на того, кто изначально создал патч и когда это было сделано.
Однако, возможна ситуация, когда патч не может быть бесконфликтно применён.
Возможно, ваша основная ветка сильно расходится с той веткой, на основании которой был создан патч, или он зависит от другого, ещё не применённого патча.
В таком случае работа git am
будет прервана, а так же выведена подсказка со списком возможных действий:
$ git am 0001-see-if-this-helps-the-gem.patch
Applying: See if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Patch failed at 0001.
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".
Эта команда добавит соответствующие маркеры во все файлы где есть конфликты, аналогично конфликтам слияния или перебазирования.
Для решения такой проблемы используется аналогичный подход — отредактируйте файлы исправив конфликты, добавьте их в индекс и выполните команду git am --resolved
для перехода к следующему патчу:
$ (fix the file)
$ git add ticgit.gemspec
$ git am --resolved
Applying: See if this helps the gem
При желании, вы можете указать опцию -3
, чтобы Git попробовал провести трёхстороннее слияние.
Эта опция не включена по умолчанию, так как она не будет работать, если коммит, на который ссылается патч, отсутствует в вашем репозитории.
Если у вас есть тот коммит, на который ссылается конфликтующий патч, то использование опции -3
приводит к более умному применению конфликтующего патча:
$ git am -3 0001-see-if-this-helps-the-gem.patch
Applying: See if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
No changes -- Patch already applied.
В данном случае, без использования опции -3
патч будет расценён как конфликтующий.
При использовании опции -3
патч будет применён без конфликтов.
Если вы применяете несколько патчей из файла mbox, то можно запустить git am
в интерактивном режиме, в котором перед обработкой каждого патча будет задаваться вопрос о дальнейших действиях:
$ git am -3 -i mbox
Commit Body is:
--------------------------
See if this helps the gem
--------------------------
Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all
Это отличная возможность посмотреть содержимое патча перед его применением или пропустить его, если он уже был применён.
Когда все патчи применены и созданы коммиты в текущей ветке, вы уже можете решить стоит ли и как интегрировать их в более долгоживущую ветку.
Извлечение удалённых веток
Если участник проекта создал свой Git репозиторий, отправил в него свои изменения, а затем прислал вам ссылку и название ветки, куда были отправлены изменения, то вы можете добавить этот репозиторий как удалённый и провести слияние локально.
К примеру, Джессика отправила вам письмо, в котором сказано, у неё есть новый функционал в ветке ruby-client
её репозитория.
Добавив удалённый репозиторий и получив изменения из этой ветки, вы можете протестировать изменения извлекая их локально:
$ git remote add jessica git://github.com/jessica/myproject.git
$ git fetch jessica
$ git checkout -b rubyclient jessica/ruby-client
Если она снова пришлёт вам письмо с указанием на новый функционал уже в другой ветке, то для его получения достаточно fetch
и checkout
, так как удалённый репозиторий уже подключён.
Это очень полезно, когда вы постоянно работаете с этим человеком.
Однако, от тех, кто редко отправляет небольшие патчи, будет проще принимать их по почте, чем требовать от всех поддержания собственных серверов с репозиториями, постоянно добавлять их как удалённые, а затем удалять и всё это ради нескольких патчей.
Так же вы вряд ли захотите иметь сотни удалённых репозиториев, каждый из которых нужен только для одного или нескольких патчей.
К счастью, скрипты и различные сервисы облегчают задачу, но во многом зависят от того как работаете вы и участники вашего проекта.
Отличительным преимуществом данного подхода является получение истории коммитов.
Хоть возникновение конфликтов слияния и закономерно, но вы знаете с какого момента это произошло; корректное трёхстороннее слияние более предпочтительно, чем указать опцию -3
и надеяться, что патч основан на коммите, к которому у вас есть доступ.
Если вы с кем-то не работаете постоянно, но всё равно хотите использовать удалённый репозиторий, то можно указать ссылку на него в команде git pull
.
Это приведёт к однократному выполнению, а ссылка на репозиторий сохранена не будет.
$ git pull https://github.com/onetimeguy/project
From https://github.com/onetimeguy/project
* branch HEAD -> FETCH_HEAD
Merge made by the 'recursive' strategy.
Определение применяемых изменений
На текущий момент у вас есть тематическая ветка, содержащая предоставленные изменения.
Сейчас вы можете определиться что с ними делать.
В этом разделе рассматривается набор команд, которые помогут вам увидеть что именно будет интегрировано, если вы решите слить изменения в основную ветку.
Обычно, полезно просмотреть все коммиты текущей ветки, которые ещё не включены в основную.
Вы можете исключить коммиты, которые уже есть в вашей основной ветке добавив опцию --not
перед её названием.
Это аналогично указанию использовавшегося ранее формата master..contrib
.
Например, если участник проекта отправил вам два патча, а вы создали ветку с названием contrib
и применили их, то можно выполнить следующую команду:
$ git log contrib --not master
commit 5b6235bd297351589efc4d73316f0a68d484f118
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Oct 24 09:53:59 2008 -0700
See if this helps the gem
commit 7482e0d16d04bea79d0dba8988cc78df655f16a0
Author: Scott Chacon <schacon@gmail.com>
Date: Mon Oct 22 19:38:36 2008 -0700
Update gemspec to hopefully work better
Для просмотра изменений, представленных в каждом коммите, можно использовать опцию -p
команды git log
, которая выведет разницу по каждому коммиту.
Для просмотра полной разницы того, что произойдёт если вы сольёте изменения в другую ветку, вам понадобится использовать возможно странный способ для получения корректных результатов:
Эта команда может вводить в заблуждение, но точно покажет разницу.
Если ваша master
ветка продвинулась вперед с тех пор как вы создали тематическую ветку, то вы получите на первый взгляд странные результаты.
Это происходит потому, что Git непосредственно сравнивает снимки последних коммитов текущей и master
веток.
Например, если вы добавили строку в файл в ветке master
, то прямое сравнение снимков будет выглядеть как будто тематическая ветка собирается удалить эту строку.
Это не проблема, если ветка master
является непосредственным родителем вашей тематической ветки, но если история обоих веток изменилась, то разница будет выглядеть как добавление всех изменений из тематической ветки и удаление всего нового из master
ветки.
Что действительно нужно видеть, так это изменения тематической ветки, которые предстоит слить в master
ветку.
Это можно сделать, сказав Git сравнивать последний коммит тематической ветки с первым общим родителем для обоих веток.
Технически это делается за счёт явного указания общего коммита и применения разницы к нему:
$ git merge-base contrib master
36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
$ git diff 36c7db
или более кратко:
$ git diff $(git merge-base contrib master)
Однако это не удобно, поэтому Git предоставляет более короткий способ: синтаксис троеточия.
При выполнении команды diff
, следует поставить три точки после имени ветки для получения разницы между ней и текущей веткой, относительно общего родителя с другой веткой:
$ git diff master...contrib
Данная команда отобразит проделанную работу только из тематической ветки, относительно общего родителя с веткой master
.
Полезно запомнить указанный синтаксис.
Интеграция совместной работы
Когда все изменения в текущей тематической ветке готовы к интеграции с основной веткой, возникает вопрос как это сделать.
Кроме этого, какой рабочий процесс вы хотите использовать при сопровождении вашего проекта?
У вас несколько вариантов, давайте рассмотрим некоторые из них.
Схемы слияния
В простом рабочем процессе проделанная работа просто сливается в ветку master
.
При таком сценарии у вас есть ветка master
, которая содержит стабильный код.
Когда работа в тематической ветке завершена или вы проверили чью-то работу, вы сливаете её в ветку master
и удаляете, затем процесс повторяется.
Если в репозитории присутствуют две ветки ruby_client
и php_client
с проделанной работой, как показано на рисунке История с несколькими тематическими ветками, и вы сначала сливаете ветку ruby_client
, а затем php_client
, то состояние вашего репозитория будет выглядеть как показано на рисунке Слияние тематической ветки.
Рисунок 72. История с несколькими тематическими ветками
Рисунок 73. Слияние тематической ветки
Это, пожалуй, простейший рабочий процесс и его использование проблематично в больших или более стабильных проектах, где вы должны быть более осторожны с предоставленными изменениями.
Если у вас очень важный проект, то возможно вам стоит использовать двухступенчатый цикл слияния.
При таком сценарии у вас имеются две долгоживущие ветки master
и develop
, где в master
сливаются только очень стабильные изменения, а все новые доработки интегрируются в ветку develop
.
Обе ветки регулярно отправляются в публичный репозиторий.
Каждый раз, когда новая тематическая ветка готова к слиянию (Перед слиянием тематической ветки), вы сливаете её в develop
(После слияния тематической ветки); затем, когда вы выпускаете релиз, ветка master
смещается на стабильное состояние ветки develop
(После релиза проекта).
Рисунок 74. Перед слиянием тематической ветки
Рисунок 75. После слияния тематической ветки
Рисунок 76. После релиза проекта
Таким образом, люди могут клонировать репозиторий вашего проекта и использовать ветку master
для сборки последнего стабильного состояния и получения актуальных изменений или использовать ветку develop
, которая содержит самые последние изменения.
Вы также можете продолжить эту концепцию, имея интеграционную ветку integrate
, в которой объединяется вся работа.
После того, как кодовая база указанной ветки стабильна и пройдены все тесты, она сливается в ветку develop
, а после того, как стабильность слитых изменений доказана, вы перемещаете состояние ветки master
на стабильное.
Схема с большим количеством слияний
В проекте Git присутствуют четыре долгоживущие ветки: master
, next
, seen
(ранее pu
— предложенные обновления) для новой работы и maint
для поддержки обратной совместимости.
Предложенные участниками проекта наработки накапливаются в тематических ветках основного репозитория по ранее описанному принципу (рис. Управление сложным набором параллельно разрабатываемых тематических веток).
На этом этапе производится оценка содержимого тематических веток, чтобы определить, работают ли предложенные фрагменты так, как положено, или им требуется доработка.
Если все в порядке, тематические ветки сливаются в ветку next
, которая отправляется на сервер, чтобы у каждого была возможность опробовать результат интеграции.
Рисунок 77. Управление сложным набором параллельно разрабатываемых тематических веток
Если содержимое тематических веток требует доработки, оно сливается в ветку seen
.
Когда выясняется, что предложенный код полностью стабилен, он сливается в ветку master
.
Затем ветки next
и seen
перестраиваются на основании master
.
Это означает, что master
практически всегда двигается только вперед, next
время от времени перебазируется, а seen
перебазируется ещё чаще:
Рисунок 78. Слияние тематических веток в долгоживущие ветки интеграции
После того, как тематическая ветка окончательно слита в master
, она удаляется из репозитория.
Репозиторий также содержит ветку maint
, которая ответвляется от последнего релиза для предоставления патчей, если требуется поддержка обратной совместимости.
Таким образом, после клонирования проекта у вас будет четыре ветки, дающие возможность перейти на разные стадии его разработки, в зависимости от того, на сколько передовым вы хотите быть или как вы собираетесь участвовать в проекте; вместе с этим, рабочий процесс структурирован, что помогает сопровождающему проекта проверять поступающий код.
Рабочий процесс проекта Git специфицирован.
Для полного понимания процесса обратитесь к Git Maintainer’s guide.
Схема с перебазированием и отбором
Некоторые сопровождающие предпочитают перебазировать или выборочно применять (cherry-pick) изменения относительно ветки master
вместо слияния, что позволяет поддерживать историю проекта в линейном виде.
Когда проделанная работа из тематической ветки готова к интеграции, вы переходите на эту ветку и перебазируете её относительно ветки master
(или develop
и т. д.).
Если конфликты отсутствуют, то вы можете просто сдвинуть состояние ветки master
, что обеспечивает линейность истории проекта.
Другим способом переместить предлагаемые изменений из одной ветки в другую является их отбор коммитов (cherry-pick).
Отбор в Git похож на перебазирование для одного коммита.
В таком случае формируется патч для выбранного коммита и применяется к текущей ветке.
Это полезно, когда в тематической ветке присутствует несколько коммитов, а вы хотите взять только один из них, или в тематической ветке только один коммит и вы предпочитаете использовать отбор вместо перебазирования.
Предположим, ваш проект выглядит так:
Рисунок 79. Пример истории, из которой нужно отобрать отдельные коммиты
Для применения коммита e43a6
к ветке master
выполните команду:
$ git cherry-pick e43a6
Finished one cherry-pick.
[master]: created a0a41a9: "More friendly message when locking the index fails."
3 files changed, 17 insertions(+), 3 deletions(-)
Это действие применит изменения, содержащиеся в коммите e43a6
, но будет сформирован новый коммит с другим значением SHA-1.
После этого история будет выглядеть так:
Рисунок 80. История после отбора коммита из тематической ветки
Теперь тематическую ветку можно удалить, отбросив коммиты, которые вы не собираетесь включать в проект.
Возможность «Rerere»
Если вы часто производите перебазирование и слияние или поддерживаете долгоживущие тематические ветки, то в Git есть специальная возможность под названием «rerere», призванная вам помочь.
Rerere означает «reuse recorded resolution» (повторно использовать сохранённое решение) — это способ сокращения количества операций ручного разрешения конфликтов.
Когда эта опция включена, Git будет сохранять набор образов до и после успешного слияния, а также разрешать конфликты самостоятельно, если аналогичные конфликты уже были разрешены ранее.
Эта возможность реализована как команда и как параметр конфигурации.
Параметр конфигурации называется rerere.enabled
, который можно включить глобально следующим образом:
$ git config --global rerere.enabled true
После этого любое разрешение конфликта слияния будет записано на случай повторного использования.
Если нужно, вы можете обращаться к кэшу «rerere» напрямую, используя команду git rerere
.
Когда команда вызвана без параметров, Git проверяет базу данных и пытается найти решение для разрешения текущего конфликта слияния (точно так же как и при установленной настройке rerere.enabled
в значение true
).
Существует множество дополнительных команд для просмотра, что именно будет записано, удаления отдельных записей из кэша, а так же его полной очистки.
Более детально «rerere» будет рассмотрено в разделе Rerere главы 7.
Помечайте свои релизы
После выпуска релиза, возможно, вы захотите пометить текущее состояние так, чтобы можно было вернуться к нему в любой момент.
Для этого можно добавить тег, как было описано в главе Основы Git.
Кроме этого, вы можете добавить цифровую подпись для тега, выглядеть это будет вот так:
$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: "Scott Chacon <schacon@gmail.com>"
1024-bit DSA key, ID F721C45A, created 2009-02-09
Если вы используете цифровую подпись при расстановке тегов, то возникает проблема распространения публичной части PGP ключа, использованного при создании подписи.
Сопровождающий Git проекта может решить эту проблему добавив в репозиторий свой публичный ключ как бинарный объект и установив ссылающийся на него тег.
Чтобы это сделать, выберите нужный ключ из списка доступных, который можно получить с помощью команды gpg --list-keys
:
$ gpg --list-keys
/Users/schacon/.gnupg/pubring.gpg
---------------------------------
pub 1024D/F721C45A 2009-02-09 [expires: 2010-02-09]
uid Scott Chacon <schacon@gmail.com>
sub 2048g/45D02282 2009-02-09 [expires: 2010-02-09]
Затем экспортируйте выбранный ключ и поместите его непосредственно в базу данных Git при помощи команды git hash-object
, которая создаст новый объект с содержимым ключа и вернёт SHA-1 этого объекта:
$ gpg -a --export F721C45A | git hash-object -w --stdin
659ef797d181633c87ec71ac3f9ba29fe5775b92
Теперь, когда ваш публичный ключ находится в репозитории, можно поставить указывающий на него тег, используя полученное ранее значение SHA-1:
$ git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92
Выполнив команду git push --tags
, maintainer-pgp-pub
тег станет общедоступным.
Теперь все, кто захочет проверить вашу подпись, могут импортировать ваш публичный ключ, предварительно получив его из репозитория:
$ git show maintainer-pgp-pub | gpg --import
После этого можно проверять цифровую подпись ваших тегов.
Кроме этого, вы можете включить дополнительные инструкции по проверке вашей подписи в сообщение тега, которое будет отображаться каждый раз при вызове команды git show <tag>
.
Генерация номера сборки
Git не использует монотонно возрастающие идентификаторы для коммитов, поэтому если вы хотите получить читаемые имена коммитов, то воспользуйтесь командой git describe
для нужного коммита.
Git вернёт имя ближайшего тега, количество коммитов после него и частичное значение SHA-1 для указанного коммита (с префиксом в виде буквы «g» — означает Git):
$ git describe master
v1.6.2-rc1-20-g8c5b85c
Таким образом, вы можете сделать снимок или собрать сборку и дать ей понятное для человека название.
К слову, если вы клонируете репозиторий Git и соберете его из исходного кода, то вывод команды git --version
будет примерно таким же.
Если попытаться получить имя коммита, которому назначен тег, то результатом будет название самого тега.
По умолчанию, команда git describe
поддерживает только аннотированные теги (созданные с использованием опций -a
или -s
); если вы хотите использовать легковесные (не аннотированные) метки, то укажите команде параметр --tags
.
Также это название можно использовать при выполнении команд git checkout
и git show
, но в будущем они могут перестать работать из-за сокращенного значения SHA-1.
К примеру, ядро Linux недавно перешло к использованию 10 символов в SHA-1 вместо 8 чтобы обеспечить уникальность каждого объекта, таким образом предыдущие результаты git describe
стали недействительными.
Подготовка релиза
Время делать релиз сборки.
Возможно, вы захотите сделать архив последнего состояния вашего кода для тех, кто не использует Git.
Для создания архива выполните команду git archive
:
$ git archive master --prefix='project/' | gzip > `git describe master`.tar.gz
$ ls *.tar.gz
v1.6.2-rc1-20-g8c5b85c.tar.gz
Открывший этот tarball-архив пользователь получит последнее состояние кода проекта в каталоге project
.
Точно таким же способом можно создать zip-архив, просто добавив опцию --format=zip
для команды git archive
:
$ git archive master --prefix='project/' --format=zip > `git describe master`.zip
В итоге получим tarball- и zip-архивы с релизом проекта, которые можно загрузить на сайт или отправить по почте.
Краткая история (Shortlog)
Сейчас самое время оповестить людей из списка рассылки, которые хотят знать что происходит с вашим проектом.
С помощью команды git shortlog
можно быстро получить список изменений, внесённых в проект с момента последнего релиза или предыдущей рассылки.
Она собирает все коммиты в заданном интервале; например, следующая команда выведет список коммитов с момента последнего релиза с названием v1.0.1:
$ git shortlog --no-merges master --not v1.0.1
Chris Wanstrath (6):
Add support for annotated tags to Grit::Tag
Add packed-refs annotated tag support.
Add Grit::Commit#to_patch
Update version and History.txt
Remove stray `puts`
Make ls_tree ignore nils
Tom Preston-Werner (4):
fix dates in history
dynamic version method
Version bump to 1.0.2
Regenerated gemspec for version 1.0.2
И так, у вас есть готовая к отправке сводка коммитов начиная с версии v1.0.1, сгруппированных по авторам.
Я работаю в операционной системе «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Ильяsourcerepostest». Проект состоит из одного текстового файла «shalandy.txt», в который записан текст в кодировке UTF-8 с окончаниями строк вида CRLF, как принято в операционных системах «Windows» (окончания строк я буду показывать, хотя в редакторах кода они обычно скрыты):
shalandy.txt (185 байтов, кодировка UTF-8)
Шаланды, полные кефали,CRLF
В Одессу Костя приводил.CRLF
И все биндюжники вставали,CRLF
Когда в пивную он входил.
Шаг 1. Создание пустого Git-репозитория для проекта:
PS C:UsersИльяsourcerepostest> git init
Initialized empty Git repository in C:/Users/Илья/source/repos/test/.git/
Шаг 2. Проверка настройки режима работы с окончаниями строк для проекта:
PS C:UsersИльяsourcerepostest> git config core.autocrlf
true
Про эту настройку у меня есть отдельная статья, там же показано на примере, как ее можно изменить. У меня для этой настройки прописано значение true
на уровне текущего пользователя (global) операционной системы, поэтому нет необходимости задавать ей значение отдельно на уровне проекта. Напомню, при этой настройке со значением true
предполагается, что в рабочей папке в файлах мы имеем дело с окончаниями строк вида CRLF, а в Git-репозитории версии файлов хранятся с окончаниями строк вида LF.
Шаг 3. Добавление исходной версии файла «shalandy.txt» в индекс (stage):
PS C:UsersИльяsourcerepostest> 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Ильяsourcerepostest> 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Ильяsourcerepostest> (Get-WinSystemLocale).Name
ru-RU
PS C:UsersИльяsourcerepostest> ([System.Console]::OutputEncoding).CodePage
866
PS C:UsersИльяsourcerepostest> $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Ильяsourcerepostest> 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Ильяsourcerepostest> 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Ильяsourcerepostest> git commit -m "Первый коммит"
[master (root-commit) 3834f65] Первый коммит
1 file changed, 4 insertions(+)
create mode 100644 shalandy.txt
Шаг 6. Создание копии (клона) исходного Git-репозитория:
PS C:UsersИльяsourcerepostest> cd ..
PS C:UsersИльяsourcerepos> git clone test test2
Cloning into 'test2'...
done.
Команда cd
в программе-оболочке «PowerShell» — это псевдоним (alias) командлета «Set-Location». А вообще это известная команда в разных операционных системах. Параметр ..
означает родительскую папку для текущей папки. То есть команда cd ..
выполняет переход на ближайший верхний уровень от текущего местоположения в дереве папок. В результате мы перешли в местоположение «C:UsersИльяsourcerepos».
Этот переход не обязателен для применения команды «git clone», но он делает ввод параметров для этой команды более удобным: после перехода нам достаточно набрать название исходной папки (наш проект) и название новой папки (новый проект, копия нашего), без набора полного пути к этим папкам. (Вообще, команду «git clone» можно применить несколькими способами, об этом можно прочитать в документации.)
Итак, первый параметр test
команды «git clone» в данном случае — название исходной папки нашего проекта. Папки test2
до ввода вышеприведенной команды «git clone» в местоположении «C:UsersИльяsourcerepos» у меня не существовало. Команда «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Ильяsourcerepos> cd test2
PS C:UsersИльяsourcerepostest2> git apply "..testmy.patch"
Первая команда из двух в блоке кода выше была уже объяснена ранее. Здесь мы с помощью этой команды cd test2
переходим в только что созданную папку с копией (клоном) нашего исходного проекта (вернее, с копией Git-репозитория нашего исходного проекта). Это нужно потому, что команда «git apply» применяет указанный файл-патч к текущей рабочей папке, так что в эту папку сначала следует перейти.
Далее запускаем команду «git apply», которая и применяет файл-патч к файлам в текущей рабочей папке текущего проекта. В данном случае я указал относительный путь "..testmy.patch"
, который означает, что файл-патч «my.patch» находится в соседней для папки «test2» папке «test» (в рабочей папке исходного проекта).
В итоге у меня команда «git apply» выполнилась успешно, то есть исходная версия файла «shalandy.txt» в рабочей папке проекта «test2» преобразовалась в измененную версию (217 байтов, кодировка UTF-8, окончания строк вида CRLF).
Важно отметить, что всё работает успешно, если в файле-патче «my.patch» окончания строк вида LF. Если там окончания строк вида CRLF, то у меня выдаются сообщения об ошибке следующего вида:
PS C:UsersИльяsourcerepostest2> git apply "..testmy.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:inetpubwwwrootwp» (эта папка будет корневой папкой нашего проекта). Я решил предложить небольшое изменение в код одного из файлов проекта: «wp-includesclass-requests.php» (путь к файлу указан относительно корневой папки проекта).
Шаг 1. Создание пустого Git-репозитория для проекта:
PS C:inetpubwwwrootwp> git init
Initialized empty Git repository in C:/inetpub/wwwroot/wp/.git/
Шаг 2. Настройка режима работы с окончаниями строк для проекта:
PS C:inetpubwwwrootwp> git config core.autocrlf
true
PS C:inetpubwwwrootwp> git config --local core.autocrlf input
PS C:inetpubwwwrootwp> git config core.autocrlf
input
Из блока кода выше видно, что я переключил настройку «core.autocrlf» для проекта со значения «true» на значение «input». Этот шаг делать не обязательно. Дело в том, что файлы веб-приложения «WordPress» из его дистрибутива написаны с окончаниями строк вида LF. Настройкой «core.autocrlf=input» я хочу сохранить окончания строк в файлах проекта (в рабочей папке) в исходном виде при извлечении (checkout) версий файлов из Git-репозитория. При настройке «core.autocrlf=true» исходные окончания строк в файлах проекта (в рабочей папке) будут в такой ситуации затерты окончаниями строк вида CRLF.
Шаг 3. Запишем оригинальную версию файла «wp-includesclass-requests.php» в индекс:
PS C:inetpubwwwrootwp> git add "wp-includesclass-requests.php"
Шаг 4. Внесем изменение в файл «wp-includesclass-requests.php» в рабочей папке проекта. Суть изменения в рамках данной статьи неважна, но кому интересно, можно прочитать подробный разбор по следующей ссылке.
Шаг 5. Создание файла-патча из программы-оболочки «Git Bash» и помещение этого файла на рабочий стол (кавычки, обособляющие путь к файлу-результату, в данном случае обязательны):
Илья@IlyaComp MINGW64 /c/inetpub/wwwroot/wp (master)
$ git diff > "C:UsersИльяDesktop57325.diff"
После этого на моем рабочем столе появился файл-патч «57325.diff» размером 444 байта в кодировке UTF-8 с окончаниями строк вида LF. В проекте «WordPress» принимают файлы-патчи с расширением либо «.diff», либо «.patch».
Предварительно вы должны создать в системе управления проектом (это можно сделать через сайт wordpress.org проекта, для этого требуется регистрация на сайте) сообщение об ошибке (по-английски его называют «ticket») с подробным описанием ошибки. После чего к этому сообщению об ошибке можно приложить файл-патч с предлагаемым исправлением ошибки. Название файла-патча должно совпадать с номером сообщения об ошибке. То есть в моем случае я приложу полученный выше файл-патч «57325.diff» к предварительно созданному сообщению об ошибке с номером 57325.
Вот что у меня получилось в результате в системе управления проектом «WordPress» на сайте wordpress.org:
-
Сообщение об ошибке (ticket) номер 57325;
-
Файл-патч, приложенный к сообщению об ошибке номер 57325.
I had the same problem. I had used
git format-patch <commit_hash>
to create the patch. My main problem was patch was failing due to some conflicts, but I could not see any merge conflict in the file content. I had used git am --3way <patch_file_path>
to apply the patch.
The correct command to apply the patch should be:
git am --3way --ignore-space-change <patch_file_path>
If you execute the above command for patching, it will create a merge conflict if patch apply fails. Then you can fix the conflict in your files, like the same way merge conflicts are resolved for git merge
This kind of error can be caused by LF vs CRLF line ending mismatches, e.g. when you’re looking at the patch file and you’re absolutely sure it should be able to apply, but it just won’t.
To test this out, if you have a patch that applies to just one file, you can try running ‘unix2dos’ or ‘dos2unix’ on just that file (try both, to see which one causes the file to change; you can get these utilities for Windows as well as Unix), then commit that change as a test commit, then try applying the patch again. If that works, that was the problem.
NB git am
applies patches as LF by default (even if the patch file contains CRLF), so if you want to apply CRLF patches to CRLF files you must use git am --keep-cr
, as per this answer.
git format-patch
also has the -B
flag.
The description in the man page leaves much to be desired, but in simple language it’s the threshold format-patch will abide to before doing a total re-write of the file (by a single deletion of everything old, followed by a single insertion of everything new).
This proved very useful for me when manual editing was too cumbersome, and the source was more authoritative than my destination.
An example:
git format-patch -B10% --stdout my_tag_name > big_patch.patch
git am -3 -i < big_patch.patch
What is a patch?
A patch is little more (see below) than a series of instructions: «add this here», «remove that there», «change this third thing to a fourth». That’s why Git tells you:
The copy of the patch that failed is found in: c:/.../project2/.git/rebase-apply/patch
You can open that patch in your favorite viewer or editor, open the files-to-be-changed in your favorite editor, and «hand apply» the patch, using what you know (and Git does not) to figure out how «add this here» is to be done when the files-to-be-changed now look little or nothing like what they did when they were changed earlier, with those changes delivered to you as a patch.
A little more
A three-way merge introduces that «little more» information than the plain «series of instructions»: it tells you what the original version of the file was as well. If your repository has the original version, your Git software, working in your repository, can compare what you did to a file, to what the patch says to do to the file.
As you saw above, if you request the three-way merge, Git can’t find the «original version» in the other repository, so it can’t even attempt the three-way merge. As a result you get no conflict markers, and you must do the patch-application by hand.
(There are cases where the «a little more» part is missing. The extra information is supplied by the Index:
line in the patch, e.g.:
diff --git a/go.mod b/go.mod
index 1fefa60..38a3a41 100644
The second line may read, e.g.:
index 1fefa6021dcd205c1243e236d686595920d9621b..38a3a41434fda3a68ce3356092a89afca81eb614 100644
in more-complete cases. Note the two hash IDs, separated by two dots. The one on the left is the one your Git software will use, to attempt to find a file in your own repository that has the given hash ID. Applying the supplied patch to that file, which must exist, must work and must produce a file whose hash ID will be that on the right, and doing all of this gives Git all the additional information required.)
Using --reject
When you have to apply the patch by hand, it’s still possible that Git can apply most of the patch for you automatically and leave only a few pieces to the entity with the ability to reason about the code (or whatever it is that needs patching). Adding --reject
tells Git to do that, and leave the «inapplicable» parts of the patch in rejection files. If you use this option, you must still hand-apply each failing patch, and figure out what to do with the rejected portions.
Once you have made the required changes, you can git add
the modified files and use git am --continue
to tell Git to commit the changes and move on to the next patch.
What if there’s nothing to do?
Since we don’t have your code, I can’t tell if this is the case, but sometimes, you wind up with one of the patches saying things that amount to, e.g., «fix the spelling of a word on line 42» when the spelling there was already fixed.
In this particular case, you, having looked at the patch and the current code, should say to yourself: «aha, this patch should just be skipped entirely!» That’s when you use the other advice Git already printed:
If you prefer to skip this patch, run "git am --skip" instead.
If you run git am --skip
, Git will skip over that patch, so that if there were five patches in the mailbox, it will end up adding just four commits, instead of five (or three instead of five if you skip twice, and so on).
I have a certain patch called my_pcc_branch.patch.
When I try to apply it, I get following message:
$ git apply --check my_pcc_branch.patch
warning: src/main/java/.../AbstractedPanel.java has type 100644, expected 100755
error: patch failed: src/main/java/.../AbstractedPanel.java:13
error: src/main/java/.../AbstractedPanel.java: patch does not apply
What does it mean?
How can I fix this problem?
13 Answers
git apply --reject --whitespace=fix mychanges.patch
worked for me.
Explanation
The --reject
option will instruct git to not fail if it cannot determine how to apply a patch, but instead to apply the individual hunks it can apply and create reject files (.rej
) for hunks it cannot apply. Wiggle can «apply [these] rejected patches and perform word-wise diffs».
Additionally, --whitespace=fix
will warn about whitespace errors and try to fix them, rather than refusing to apply an otherwise applicable hunk.
Both options together make the application of a patch more robust against failure, but they require additional attention with respect to the result.
For the whole documentation, see https://git-scm.com/docs/git-apply.
Johannes Sixt from the [email protected] mailing list suggested using following command line arguments:
git apply --ignore-space-change --ignore-whitespace mychanges.patch
This solved my problem.
When all else fails, try git apply
‘s --3way
option.
git apply --3way patchFile.patch
—3way
When the patch does not apply cleanly, fall back on 3-way merge if the
patch records the identity of blobs it is supposed to apply to, and we
have those blobs available locally, possibly leaving the conflict
markers in the files in the working tree for the user to resolve. This
option implies the —index option, and is incompatible with the
—reject and the —cached options.
Typical fail case applies as much of the patch as it can, and leaves you with conflicts to work out in git however you normally do so. Probably one step easier than the reject
alternative.
This command will apply the patch not resolving it leaving bad files as *.rej
:
git apply --reject --whitespace=fix mypath.patch
You just have to resolve them. Once resolved run:
git -am resolved
It happens when you mix UNIX and Windows git clients because Windows doesn’t really have the concept of the «x» bit so your checkout of a rw-r--r--
(0644) file under Windows is «promoted» by the msys POSIX layer to be rwx-r-xr-x
(0755). git considers that mode difference to be basically the same as a textual difference in the file, so your patch does not directly apply. I think your only good option here is to set core.filemode
to false
(using git-config
).
Here’s a msysgit issue with some related info: http://code.google.com/p/msysgit/issues/detail?id=164 (rerouted to archive.org’s 3 Dec 2013 copy)
In my case I was stupid enough to create the patch file incorrectly in the first place, actually diff-ing the wrong way. I ended up with the exact same error messages.
If you’re on master and do git diff branch-name > branch-name.patch
, this tries to remove all additions you want to happen and vice versa (which was impossible for git to accomplish since, obviously, never done additions cannot be removed).
So make sure you checkout to your branch and execute git diff master > branch-name.patch
WARNING: This command can remove old lost commits PERMANENTLY. Make a copy of your entire repository before attempting this.
I have found this link
I have no idea why this works but I tried many work arounds and this is the only one that worked for me. In short, run the three commands below:
git fsck --full
git reflog expire --expire=now --all
git gc --prune=now
git apply --reverse --reject example.patch
When you created a patch file with the branch names reversed:
ie. git diff feature_branch..master
instead of git diff master..feature_branch
My issue is that I ran git diff
, then ran git reset --hard HEAD
, then realized I wanted to undo, so I tried copying the output from git diff
into a file and using git apply
, but I got an error that «patch does not apply». After switching to patch
and trying to use it, I realized that a chunk of the diff was repeated for some reason, and after removing the duplicate, patch
(and presumably also git apply
) worked.
If the patch is only partly applied, but not the entire patch. Make sure you are in the correct directory when applying the patch.
For example I created a patch file in the parent project folder containing the .git
file. However I was trying to apply the patch at a lower level. It was only applying changes at that level of the project.
Just use git apply -v example.patch
to know the reasons of «patch does not apply». And then you can fix them one by one.
What I looked for is not exactly pointed out in here in SO, I’m writing for the benefit of others who might search for similar. I faced an issue with one file (present in old repo) getting removed in the repo. And when I apply the patch, it fails as it couldn’t find the file to be applied. (so my case is git patch fails for file got removed)
‘#git apply —reject’ definitely gave a view but didn’t quite get me to the fix. I couldn’t use wiggle as it is not available for us in our build servers. In my case, I got through this problem by removing the entry of the ‘file which got removed in the repo’ from patch file I’ve tried applying, so I got all other changes applied without an issue (using 3 way merge, avoiding white space errors), And then manually merging content of file removed into where its moved.
What is a patch?
A patch is little more (see below) than a series of instructions: «add this here», «remove that there», «change this third thing to a fourth». That’s why git tells you:
The copy of the patch that failed is found in: c:/.../project2/.git/rebase-apply/patch
You can open that patch in your favorite viewer or editor, open the files-to-be-changed in your favorite editor, and «hand apply» the patch, using what you know (and git does not) to figure out how «add this here» is to be done when the files-to-be-changed now look little or nothing like what they did when they were changed earlier, with those changes delivered to you as a patch.
A little more
A three-way merge introduces that «little more» information than the plain «series of instructions»: it tells you what the original version of the file was as well. If your repository has the original version, your git can compare what you did to a file, to what the patch says to do to the file.
As you saw above, if you request the three-way merge, git can’t find the «original version» in the other repository, so it can’t even attempt the three-way merge. As a result you get no conflict markers, and you must do the patch-application by hand.
Using --reject
When you have to apply the patch by hand, it’s still possible that git can apply most of the patch for you automatically and leave only a few pieces to the entity with the ability to reason about the code (or whatever it is that needs patching). Adding --reject
tells git to do that, and leave the «inapplicable» parts of the patch in rejection files. If you use this option, you must still hand-apply each failing patch, and figure out what to do with the rejected portions.
Once you have made the required changes, you can git add
the modified files and use git am --continue
to tell git to commit the changes and move on to the next patch.
What if there’s nothing to do?
Since we don’t have your code, I can’t tell if this is the case, but sometimes, you wind up with one of the patches saying things that amount to, e.g., «fix the spelling of a word on line 42» when the spelling there was already fixed.
In this particular case, you, having looked at the patch and the current code, should say to yourself: «aha, this patch should just be skipped entirely!» That’s when you use the other advice git already printed:
If you prefer to skip this patch, run "git am --skip" instead.
If you run git am --skip
, git will skip over that patch, so that if there were five patches in the mailbox, it will end up adding just four commits, instead of five (or three instead of five if you skip twice, and so on).