Git дочерняя ветка удаляет обновления родительской ветки - PullRequest
0 голосов
/ 04 марта 2020

Хорошо, у меня возникла странная проблема с git ( с использованием BitBucket ).

У меня есть следующее (пример)

  • development-branch
    • child-branch

Сейчас в child-branch Я сделал много переделок, но в тот же период я ​​также исправил некоторые ошибки, которые были объединены с помощью запросов на извлечение из другой дочерней ветви в development-branch. Теперь, когда я делаю запрос на извлечение из child-branch в development-branch, он хочет удалить исправления, сделанные в development-branch. Но когда я хочу объединить development-branch в child-branch, исправления, внесенные в development-branch, не объединяются в child-branch.

Я делаю что-то не так в потоке? И есть ли способ исправить это, не проверяя вручную разницу в каждом файле?

1 Ответ

0 голосов
/ 05 марта 2020

TL; DR

Имена ветвей не работают так, как вы думаете.

Учитывая то, что вы хотите сделать, вы, вероятно, захотите рабочий процесс, ориентированный на ребазу, так как Никос C. прокомментировал . Помните, что rebase означает скопировать некоторые коммиты в новые и улучшенные коммиты, а затем переместить имя ветки, чтобы использовать новые и улучшенные коммиты, отказавшись от коммитов старых и паршивых. Это означает, что каждый , использующий перебазированную ветвь, должен быть готов отказаться от старых коммитов.

Long

Ветви или, точнее, ветка names , не иметь отношения родитель / ребенок. Имена ветвей - это просто метки, прикрепленные (или указывающие на), указывающие c коммитов .

коммитов , однако, do имеют родителя / детские отношения. В частности, последовательность команд, используемая для создания нового коммита, выглядит следующим образом:

git checkout somebranch
[edit various files]
git add file1.ext file2.ext
git commit

Шаг git checkout означает выбор названной ветви и, таким образом, выберите коммит, который выберет имя ветви . Этот копирует содержимое этого коммита - которое в Git представляет собой полный снимок всех ваших файлов, хранящихся в специальном, только для чтения, Git -сжатом сжатом формате - в оба index aka область подготовки и рабочее дерево или рабочее дерево . 1

Шаг редактирования, конечно, работает с этими файлами в рабочем дереве.

Шаг git add копирует обновленные файлы рабочего дерева - где вы внесли изменения - обратно в index / staging-area и последний шаг, git commit, создает новый моментальный снимок с его замороженными на все время копиями файла из копий, находящихся в индексе. Любые файлы, которые вы не перезаписали с помощью git add, остались такими же, как и в предыдущих git checkout, поэтому новый снимок содержит все файлы, которые не были изменены и любые обновленные файлы, которые изменены соответствующим образом.

Новый коммит, однако, является child ранее помеченного коммита. Предыдущий коммит не изменился - ничто, даже сам Git не может изменить коммит после того, как он сделан, но теперь у него есть дочерний коммит. Только что созданный дочерний коммит записывает необработанный идентификатор ha sh его parent commit. Таким образом, связь идет только в одну сторону, от ребенка к родителю. Поскольку дочерний элемент является новым коммитом, он получает новый уникальный идентификатор ha sh.

Последний и важный шаг git commit заключается в том, что записывает новый коммит ha sh ID. в название филиала . То есть метка перестает указывать на предыдущий родительский коммит и теперь указывает на дочерний коммит.

Это означает, что мы начинаем с цепочки коммитов, каждый с уникальным (и большим, и уродливым) га sh ID, который я заменим здесь одной заглавной буквой:

... <-F <-G <-H   <-- develop

name develop в настоящее время выбирает commit H.

К этому вы добавляете новое имя, feature, которое также выбирает коммит H:

...--F--G--H   <-- develop, feature

Теперь нам нужен способ запомнить , какое имя ветви вы Используешь . Для этого мы прикрепляем специальное имя HEAD к одной ветви. Если вы git checkout develop, мы добавим имя HEAD к develop:

...--F--G--H   <-- develop (HEAD), feature

Если вы git checkout feature, мы получим:

...--F--G--H   <-- develop, feature (HEAD)

В любом случае, вы ' используйте commit H, но вы делаете это через одно из двух имен, прикрепленных к / указывающих на коммит H. (Тем временем commit H указывает назад на коммит G в качестве его родителя, а G указывает на F и т. Д.)

Теперь вы изменяете некоторые файлы в вашем рабочем дереве, git add их, чтобы скопировать их обратно в область index / staging, и git commit, чтобы сделать новый коммит I. Вот что происходит, если вы используете feature:

...--F--G--H   <-- develop
            \
             I   <-- feature (HEAD)

Имя HEAD по-прежнему прикреплено к имени feature, но feature теперь указывает на новый коммит I. Новый коммит I указывает на коммит H - где feature указывал минуту назад - так что feature содержит коммиты до и включая I, а develop содержит коммиты до и включая H , Обратите внимание, что большинство коммитов по-прежнему находятся в обеих ветвях.

Если и когда вы добавляете коммиты к develop, они просто добавляют к develop:

...--F--G--H--K   <-- develop (HEAD)
            \
             I--J   <-- feature

Нет родителя / потомка связь между именами develop и feature. Это просто метки , идентифицирующие спецификацию c коммитов.

Так работают имена ветвей и тегов в Git: они идентифицируют коммиты. Различия между именем ветки и именем тега:

  • Имена ветвей перемещение во времени. На самом деле они двигаются автоматически! Имена тегов никогда не должны перемещаться. 2

  • Имена ветвей локальны для одного конкретного хранилища . Другие репозитории могут их видеть, но когда вы получаете коммиты от кого-то , ваш Git скопирует их ответвления , например master, в ваши имена для удаленного слежения , как origin/master. Имена тегов гораздо более глобальны: если они - кем бы они ни были - имеют v1.2, и вы получаете от них ваши коммиты, ваш Git, вероятно, создаст ваш v1.2 для соответствия. 3

  • И, конечно же, имя ветви - это ответвление , а имя тега - это тег имя: есть несколько меньших дополнительные эффекты, но две вышеуказанные точки являются важными отличиями.


1 Дерево работы содержит фактические файлы в обычном обычном формате что вы и все другие не Git программы на вашем компьютере могут использовать. Эти фактические файлы не используются Git. Он использует сжатые, только для чтения, Git только зафиксированные файлы и использует индексную / промежуточную область для выполнения новых фиксаций, как показано выше.

2 Возможно перемещать тег, принудительно или удаляя его, а затем создавая его заново. Однако другое программное обеспечение может ожидать перемещения тегов , а не . Например, модульная система Go может кэшировать идентификатор ha sh тега, и если вы позже переместите тег, обновится , а не . Перемещать теги в целом - плохая идея: избегайте, если можете.

3 Это под вашим контролем, и правила по умолчанию здесь несколько сложны, но они означают, что вы получите их теги, если вы не сделали ничего особенного.


Использование rebase

Предположим, у вас есть это:

...--F--G--H--K   <-- develop
            \
             I--J   <-- feature (HEAD)

, но вы хотите это:

                ?--?   <-- feature (HEAD)
               /
...--F--G--H--K   <-- develop

Вы не можете изменить существующие коммиты. Ничто не может. Существующие коммиты I и J застряли там, где они есть. Но вы можете копировать существующие коммиты в новые и улучшенные версии. В частности, вы можете скопировать коммит I в новый улучшенный - мы назовем его I', чтобы указать, что это копия I, хотя в Git он просто получает какую-то большую, некрасивую, случайную -haking sh ID, который не имеет никакого отношения к I 's ha sh ID. Разница между I и I' составит:

  • I Родитель H, но родитель I' будет K.

Скопировав I в I', мы затем хотим получить Git копия J в J'. Результат выглядит следующим образом:

                I'-J'
               /
...--F--G--H--K   <-- develop
            \
             I--J

Обратите внимание, что я стер ярлык feature и полностью исключил имя HEAD, так как на данный момент мы готовы двигаться feature и сделать HEAD присоединенным к новому, перемещенному feature. Процесс, который выполняет эту «сборку новых коммитов», на самом деле использует то, что Git называет отсоединенным HEAD , поэтому сейчас фактическая ситуация такова:

                I'-J'  <-- HEAD
               /
...--F--G--H--K   <-- develop
            \
             I--J   <-- feature

Обратите внимание, что специальное имя HEAD не не привязан ни к какому имени ветви. Вот почему это отдельная ГОЛОВА . Между тем имя feature по-прежнему выбирает существующий коммит J.

Последнее действие команды git rebase, чтобы завершить sh перебазирование, состоит в том, чтобы отделить имя feature от существующего коммита (J) и сделать так, чтобы он указывал на тот же коммит, на который HEAD указывает (J'):

                I'-J'  <-- feature, HEAD
               /
...--F--G--H--K   <-- develop
            \
             I--J   [abandoned]

Переместив метку, rebase повторно присоединяет HEAD к метке (перемещенной) , чтобы все выглядело так, как мы хотели. И так как Git находит коммитов, это начинать с меток и работать в обратном направлении, мы не можем увидеть коммитов I и J больше Все, что мы видим:

                I'-J'  <-- feature (HEAD)
               /
...--F--G--H--K   <-- develop

Если бы мы не помнили идентификаторы коммитов ha sh I и J, мы бы подумали, что Git каким-то образом переместил старые коммиты. Это не так - старые коммиты все еще там, и каждый, у кого есть клон хранилища и кто имеет старые коммиты, все еще имеет старые коммиты. Мы только видим наши новые , хотя.

Как Никос C. отметил, что если у вас есть активный запрос на получение, использующий существующие коммиты I-J, вам, вероятно, потребуется использовать git push --force для обновления запроса на получение. Вы должны получить все хранилища Git, в которых хранятся их копии коммитов I и J, чтобы отказаться от их в пользу новой и улучшенной цепочки I'-J'. Точный способ убедить некоторых других Git перейти от старых коммитов к новым зависит от элементов управления, настроенных на , чем другие Git. GitHub позволяет простым git push --force делать свое дело.

...