Удалив папку .git
, вы удалили историю. Затем, создав новый репо из этих файлов, вы начали новую историю, которая, по мнению git, не связана. Даже если вы совершили коммит до внесения каких-либо изменений, вы все равно создали новый коммит (с идентично совпадающим контентом).
Вы хотите, чтобы репозитории вашей команды сохранили историю вышестоящего репо, чтобы ваши репо знали, как ваши изменения связаны с этой историей, и затем (относительно) легко объединить в вышестоящие изменения. Затем вы контролируете видимость вверх по течению, просто контролируя, что вы толкаете к этому источнику.
Этого все еще можно достичь, но точные шаги зависят от ряда вопросов о вашем текущем состоянии. Я собираюсь сделать некоторые предположения, поэтому прокомментируйте, если эти предположения неверны, и я могу попытаться скорректировать ответ соответствующим образом.
По-видимому, вы создали свое новое репо таким образом, что у вас есть пульт с именем origin
, с которого ваши разработчики клонируют свои локальные репо. (В дальнейшем я назову это origin
, а исходный репозиторий, из которого вы сделали первый снимок, я назову upstream
.)
Я также предполагаю, что в вашем origin
идет активная разработка, так что было бы проблемой просто повторно clone
из upstream
; и что вы находитесь в точке, где вы хотите интегрировать изменения с upstream
в origin
.
Первый шаг, работающий с клоном origin
, заключается в извлечении истории upstream
.
git remote add upstream url/of/upstream/repo
git fetch upstream
Далее вам нужно интегрировать истории. Есть несколько способов сделать это. Наилучшие долгосрочные результаты будут получены при переписывании истории, хотя это требует определенной координации между вашей командой.
В идеале вы должны сказать всем в команде push
о своей работе до origin
; его не нужно полностью объединять, но все ветви должны быть push
ed. После push
своей работы они должны отказаться от своих клонов и подождать, пока вы не сообщите им, что перезапись завершена, и в этот момент они создадут новые клоны origin
(и, дополнительно, также добавят удаленный upstream
). ).
Если этот уровень координации невозможен, переписывание еще можно сделать; но, скорее всего, это будет больше работы для некоторых или для всех разработчиков, так как им придется выполнять собственную миграцию любых изменений, которых не было в origin
в то время, когда вы clone
d (или сделали ваш последний *) 1039 * до переписывания). В этом случае может быть лучше использовать опцию без перезаписи (подробнее об этом чуть позже).
После того, как fetch
отредактировал всю историю своей команды (или реплицировал ее, когда клонировал свое рабочее репо), вам нужно в дальнейшем идентифицировать фиксацию в истории upstream
, которая соответствует первоначальной фиксации в вашем репозитории origin
. ,
Если это версия с тегами, это может упростить задачу; но я предполагаю, что это было то, что было на master
в то время. Но даже в этом случае, если вы знаете, когда вы это сделали, вы сможете отследить правильный коммит и подтвердить с помощью git diff
, что он соответствует вашему первоначальному коммиту.
Итак, вы нашли этот коммит, и мы пометили его O
на следующем графике; и для удобства вы можете пометить его.
X -- X -- O -- X -- X -- X <--(upstream/master)
o -- A -- B <--(master)(origin/master)
\
C <--(someBranch)
Теперь, самое простое, что нужно сделать - это переучить o
, так что родителем его замены будет O
. Другой вариант, который дает более «чистый» результат, заключается в том, чтобы переопределить родительские значения A
и C
, заменив o
на O
. Последнее может быть немного хитрее, но не так сильно, как вы думаете; так что давайте посмотрим, как это сделать. В приведенном выше примере вы можете использовать что-то вроде
git filter-branch --parent-filter "sed \"s/$(git rev-parse master~2)/$(git rev-parse origin/master~3)/g\"" -- --branches
что должно дать вам
X -- X -- O -- X -- X -- X <--(upstream/master)
|\
| A* -- B* <--(master)
\
C* <--(someBranch)
o -- A -- B <--(refs/original/refs/heads/master)(origin/master)
\
C <--(refs/original/refs/heads/someBranch)
Затем вы можете принудительно подтолкнуть (git push -f
) все переписанные ветви к origin
или воссоздать origin
из нового репо.
Обратите внимание, что в локальном репо, в котором вы выполняли перезапись, будут новые ссылки под original/refs/heads
, представляющие местоположения ветвей перед перезаписью.Также обратите внимание, что ссылки на удаленное отслеживание для origin
еще не обновлены (пока вы не нажмете принудительное нажатие или пока не удалите пульт дистанционного управления origin
и повторно не добавите его с помощью пульта, который отражает перезапись).
Итак ... что если вы решите, что переписать нельзя?Ну, в этом случае вы, вероятно, захотите иметь одно «интеграционное репо», клонированное из origin
с добавлением upstream
.В репозитории интеграции вы устанавливаете отображение git replace
, сообщая git "всякий раз, когда вы сталкиваетесь с объектом o
, используйте вместо него объект O
".Это "документы над" проблемой.Он может иметь несколько причуд (см. git replace
документы), но в идеале вы сможете перестать полагаться на отображение replace
(и репозиторий интеграции) через некоторое время.История разработчиков не будет такой чистой, как при переписывании, но не будет необходимости в восстановлении перестроенного репозитория.
Идея в том, что со временем истории будут "Достаточно комбинированный », что мерзавец поймет, что делать без замены.Это будет связано с тем, как рассчитываются базы слияния.Рассмотрим простой случай.
X -- X -- X -- O -- A -- B -- C <--(upstream/master)
o -- D -- E <--(master)
Теперь вы хотите объединить upstream
изменения в master
.В своем репозитории для интеграции вы сказали
git replace master~2 upstream/master~3
, который мы могли бы нарисовать как
X -- X -- X -- O -- A -- B -- C <--(upstream/master)
:
o -- D -- E <--(master)
, поэтому команды git по умолчанию "see"
X -- X -- X -- O -- A -- B -- C <--(upstream/master)
\
D -- E <--(master)
Значение, есливы говорите
git checkout master
git merge upstream/master
вычисленная база слияния будет O
, а git будет "думать", что дает вам
X -- X -- X -- O -- A -- B -- C <--(upstream/master)
\ \
D ---- E ---- M <--(master)
, что на самом деле
X -- X -- X -- O -- A -- B -- C <--(upstream/master)
: \
o --- D --- E --- M <--(master)
В этом примере master
- единственная ветвь, получающая изменения от upstream
, и на этом этапе история master
возвращается к upstream/master
;поэтому в следующий раз при слиянии upstream/master
в master
база слияния должна быть C
, и замена больше не нужна (так что это может быть выполнено в любом клоне, а не в специально установленном наборе).до репо интеграции).
Теперь я упомянул, что история разработчиков не будет такой «чистой», и очевидно, что в конечном состоянии после того, как вы перестанете использовать замену, у нас будет
X -- X -- X -- O -- A -- B -- C <--(upstream/master)
\
o --- D --- E --- M <--(master)
так что "происхождение" D
, E
и M
несколько нарушено.В частности, не очевидно, как M
должен быть результатом слияния C
в E
.Это можно рассматривать как «злое слияние», хотя это не так плохо, как некоторые в том смысле, что слияние по умолчанию (без использования замены) в любом случае вызовет конфликты слияния.