Как вы относитесь к публичному репозиторию, который уже был переустановлен?

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

Очень часто опытные пользователи git скажут "не репозитории rebase, которые уже были открыты публично!" и это правильно. Но я не вижу никакой информации, которая связана с последствиями. Я знаю, что это ситуация, в которую мы не должны входить, но что мы делаем, когда это происходит?

Original: A--B--C--D--E--F
My Repo:  A--B--C--D
                    \
                     X--Y--Z
Rebased:  A--B
              \
               L--M--N--O

Итак, я fetch, это должно быть довольно просто, не так ли? И... что тогда происходит? Если я делаю `pull --rebase, он должен сработать в какой-то момент, потому что C--D отсутствует. Каковы ошибки, с которыми я столкнулся, и как их исправить?

Ответ 1

В разделе git-rebase содержится раздел "ВОССТАНОВЛЕНИЕ ОТ РЕЖИМА UPSTREAM", который описывает это довольно хорошо.

Fetching

В вашем конкретном случае позвольте пройти через то, что происходит. Если вы просто извлекаете, все, что делает, это обновление вашей ветки удаленной отслеживания. Это все еще отлично работает - он даст вам предупреждение, хотя:

From <url>:
 + abc123...def456 master    -> origin/master  (forced update)

Натяжение

Притяжение, будь то нормальное (слияние) или перезарядка, вызывает выборку внутри. Поэтому , если вы еще не выбрали, вы увидите предупреждение принудительного обновления. Если вы уже выучили, предупреждение принудительного обновления было дано раньше, и в настоящее время нет принудительного обновления. Вы также можете пропустить предупреждение принудительного обновления, если есть большой отчет о diff как часть слияния после него. После извлечения, git -pull затем вызывает слияние или переадресацию по запросу. Здесь все становится страшно. Что касается Git, то теперь совершает C и D только локальные коммиты. Не имеет значения, что в какой-то момент они были опубликованы. Таким образом, Git будет объединять/переупорядочивать, как обычно.

Слияние:

- A - B - L - M - N - O (origin/master)
   \                   \
    C - D - X - Y - Z - Q (master)

Баба:

 - A - B - L - M - N - O (origin/master) - C' - D' - X' - Y' - Z' (master)

Теперь любой из них вполне может столкнуться с конфликтами, если удаление коммитов C и D делает более поздние коммиты разными. Конфликты слияния - это единственные другие ошибки, которые вы заметили бы. Но если они были изолированы, то все идет "отлично". Если пользователь не замечает, то впоследствии может вставить эту историю и, таким образом, повторно удалить удаленные коммиты! Иногда это может быть относительно мягким. Возможно, L на самом деле C с опечаткой, зафиксированной в сообщении фиксации, а MNO идентичны DEF, только с разными родителями. Таким образом, повторное введение C и D ничего не значит, чем загромождать историю. Но, возможно, это ужасно опасно: возможно, удаление C фиксировало какую-то ужасную ошибку, и оно просто было повторно введено.

Предотвращение проблемы

И вот почему это действительно плохая идея переписать опубликованную историю. Наилучший случай заканчивается некоторыми дублируемыми фиксациями, а худший случай - это пользователь, неосознанно разбивающий все. Поэтому, если это произойдет, самое первое, что вам нужно сделать, это рассказать всем и направить их как восстановить. Следующее - следить за тем, чтобы все последующие публикации были опубликованы, чтобы убедиться, что они не возвращают старые коммиты. Если вы на другой стороне этого, получая от публичного репо, мы надеемся, вы доверяете сопровождающим достаточно, чтобы никогда не переустанавливать стабильную ветку или не говорить вам, если они это сделают. Если вы им не доверяете, тогда хорошей практикой является выборка слияния/переустановки, а не вытягивание, чтобы вы могли заметить сообщение "принудительное обновление" и действовать очень осторожно.

Восстановление локально

Сведения о том, как восстановить, различаются; Я рекомендую вышеупомянутую man-страницу. Трудно точно сказать, что вам нужно делать - это зависит от того, была ли C переписана в L (и, возможно, D в M), или они являются совершенно новыми коммитами, и о том, хотите ли вы переустановить или слить. Если вы просто хотите переустановить, будет достаточно перезагрузки XYZ на O. Если вы хотите объединиться, вы пересобираете XYZ на B или M, затем объединяете O.

Ответ 2

Вам следует сделать (как в разделе Как восстановить/пересинхронизировать после того, как кто-то нажал на rebase или reset на опубликованную ветку?"):

git checkout yourBranch
git branch old-upstreamBranch D # BEFORE fetching!

           E--F (origin/upstreamBranch)
          /
A--B--C--D (old-upstreamBranch)
          \
           X--Y--Z (yourBranch)


git fetch


     L--M--N--O (origin/upstreamBranch)
    /
A--B--C--D (old-upstreamBranch)
          \
           X--Y--Z (yourBranch)

В этот момент вы не можете просто переустановить свою ветку поверх перезаписанного upstreamBranch:, который ввел C и D, которые были явно учтены при перезаписывании upstreamBranch.

Вам нужно переустановить только yourBranch, а не коммиты перед ним.
Вот почему перед извлечением вы помещаете тег old-upstreamBranch:

git rebase --onto origin/upstreamBranch old-upstreamBranch yourBranch

                X--Y--Z (yourBranch)
               /
     L--M--N--O (origin/upstreamBranch)
    /
A--B--C--D (old-upstreamBranch)



git branch -D old-upstreamBranch 

                X--Y--Z (yourBranch)
               /
     L--M--N--O (origin/upstreamBranch)
    /
A--B

Но: С Git 2.0 вам нужно всего лишь:

fork_point=$(git merge-base --fork-point origin/upstreamBranch yourBranch)
# return D
git rebase --onto origin/upstreamBranch $fork_point yourBranch

Больше не нужно делать ветку перед извлечением!

Если вы осознаете проблему после git fetch, вы можете по-прежнему быстро перестроить свою ветку поверх перезаписываемой ветки вверх.


Поскольку совершить ad8261d из John Keeping (johnkeeping), git rebase может использовать тот же самый новый параметр --fork-point.