Да речем, че в Git имаме следната ситуация:
Създадено хранилище:
mkdir GitTest2
cd GitTest2
git init
Извършват се някои промени в главния модул и се предават.
echo "On Master" > file
git commit -a -m "Initial commit"
Feature1 се разклонява от master и е свършена някаква работа:
git branch feature1
git checkout feature1
echo "Feature1" > featureFile
git commit -a -m "Commit for feature1"
Междувременно се открива грешка в главния код и се създава клон за поправка
git checkout master
git клон hotfix1
git checkout hotfix1
Грешката се отстранява в клона hotfix и се обединява обратно в главния клон (може би след заявка за изтегляне/преглед на кода):
echo "Bugfix" > bugfixFile
git commit -a -m "Bugfix Commit"
git checkout master
git merge --no-ff hotfix1
Разработката на функция 1 продължава:
git checkout feature1
Да кажем, че се нуждая от поправката в моя клон за функции, може би защото грешката се среща и там. Как мога да постигна това, без да дублирам предаванията в моя клон за функции?
Искам да предотвратя получаването на два нови коммита в моя клон за функции, които нямат връзка с изпълнението на функцията. Това ми се струва особено важно, ако използвам заявки за изтегляне: Всички тези коммити ще бъдат включени в заявката за изтегляне и ще трябва да бъдат прегледани, въпреки че това вече е направено (тъй като поправката вече е в главния клон).
Не мога да направя git merge master --ff-only
: "fatal: Not possible to fast-forward, aborting.", но не съм сигурен дали това ми помогна.
Как да слеем главния клон с клона с функции? Лесно:
git checkout feature1
git merge master
Няма смисъл да налагаме бързо сливане тук, тъй като то не може да бъде извършено. Извършили сте ангажимент както в клона за функции, така и в главния клон. Бързото сливане е невъзможно сега.
Погледнете GitFlow. Това е модел за разклоняване за git, който може да се следва и вие несъзнателно вече сте го направили. Той също така е разширение на Git, което добавя някои команди за новите стъпки на работния процес, които правят автоматично неща, които иначе би трябвало да правите ръчно.
И така, какво направихте правилно във вашия работен процес? Имате два клона, с които да работите, вашият клон feature1 по принцип е клонът "develop" в модела на GitFlow.
Създадохте клон hotfix от master и го сляхте обратно. И сега сте в задънена улица.
Моделът на GitFlow изисква от вас да обедините клона hotfix и в клона за разработка, който във вашия случай е "feature1".
Така че истинският отговор би бил:
git checkout feature1
git merge --no-ff hotfix1
Това добавя всички промени, които са направени в горещата поправка, към клона с функции, но само тези промени. Те могат да са в конфликт с други промени в клона за разработка, но няма да са в конфликт с главния клон, ако в крайна сметка обедините функционалния клон обратно с главния.
Бъдете много внимателни с преиздаването. Ребазирайте само ако промените, които сте направили, са останали локални за вашето хранилище, т.е. не сте прехвърлили клонове в друго хранилище. Ребазирането е чудесен инструмент, с който можете да подредите локалните си промени в полезен ред, преди да ги избутате в света, но последващото ребазиране ще обърка нещата за начинаещите в git като вас.
Би трябвало да можете да пребазирате клона си в master:
git checkout feature1
git rebase master
Управлявайте всички възникнали конфликти. Когато стигнете до коммитите с поправките на грешки (вече в master), Git ще каже, че няма промени и че може би те вече са приложени. Тогава продължавате пребазирането (като пропускате коммитите, които вече са в master) с
git rebase --skip
Ако извършите git log
на вашия клон с функции, ще видите, че коммитът за поправка на грешки се появява само веднъж, и то в частта master.
За по-подробно обсъждане погледнете документацията на книгата на Git за git rebase
(https://git-scm.com/docs/git-rebase), която разглежда точно този случай на използване.
================ Редактиране за допълнителен контекст ====================
Този отговор е предоставен специално за въпроса, зададен от @theomega, като е взета предвид неговата конкретна ситуация. Обърнете внимание на тази част:
Искам да предотвратя [...] commit-ове в моя клон за функции, които нямат връзка с изпълнението на функцията.
Преизчисляването на неговия частен клон в master е точно това, което ще доведе до този резултат. За разлика от това, сливането на master в неговия клон ще доведе точно до това, което той конкретно не иска да се случи: добавяне на commit, който не е свързан с изпълнението на функцията, върху която работи чрез своя клон.
За да се обърна към потребителите, които четат заглавието на въпроса, прескачат действителното съдържание и контекста на въпроса, а след това четат само горния отговор на сляпо, приемайки, че той винаги ще важи за техния (различен) случай на употреба, позволете ми да поясня:
git merge master
, както е в отговора на @Sven's).И накрая, ако'сте недоволни от факта, че този отговор не е най-подходящ за вашата ситуация, въпреки че е бил за @theomega, добавянето на коментар по-долу няма да'е особено полезно: Не аз контролирам кой отговор ще бъде избран, а само @theomega.
Може би ще успеете да направите "cherry-pick", за да изтеглите точните ангажименти, които ви трябват, в клона за функции.
Направете git checkout hotfix1
, за да влезете в клона hotfix1. След това направете git log
, за да получите SHA-1 хеша (голяма последователност от произволни букви и цифри, която идентифицира еднозначно даден коммит) на въпросния коммит. Копирайте го (или първите около 10 символа).
След това git checkout feature1
, за да се върнете към клона с функции.
След това git cherry-pick <SHA-1 хешът, който току-що копирахте>
Това ще вкара този и само този commit във вашия клон за функции. Тази промяна ще бъде в клона - току-що сте я "cherry-picked" включили. След това възобновете работата си, редактирайте, commit-вайте, push-вайте и т.н. до насита.
Когато в крайна сметка извършите друго сливане от един клон във вашия клон с функции (или обратното), Git ще разпознае, че вече сте сляли този конкретен commit, ще знае, че не е нужно да го прави отново, и просто "ще го прескочи".