Ответ 1
Update
Я нашел другой, немного более простой способ сделать это, используя флаг --root
git rebase
. Я попробовал это с моим репо, поэтому я знаю, что он работает (по крайней мере, для полностью линейной истории, больше об этом в конце).
Шаг 1: Создайте резервный клон вашего репо
Пусть это безопасно и сделает резервную копию, на случай, если мы закончим что-то катастрофическое и потеряем ваши данные:
git clone original-repo backup-repo
Шаг 2: Создайте сиротскую ветку (это будет ваш новый корень)
Оформить покупку сиротской ветки. Указание фиксации/ветки начальной точки является необязательным, если вы оставите этот аргумент вне, тогда Git по умолчанию будет использовать ваш текущий фиксатор в качестве начальной точки (он следует за шаблоном стандартного синтаксиса ветвления):
git checkout --orphan new-master firstCommitOfOldMaster
Я определил корень старого мастера как начальную точку, потому что он, вероятно, сделает следующий шаг быстрее (удаление файлов рабочего каталога).
Шаг 3: Удалите файлы рабочего каталога
Создание ветки-сироты new-master
от вашего старого мастера может оставить файлы в вашем рабочем каталоге и индексе, в зависимости от того, какое состояние файлов было в commit, от которого вы отделились:
Индекс и рабочее дерево настроены так, как если бы вы ранее запускали
git checkout <start_point>
. Это позволяет вам начать новую историю, которая записывает набор путей, похожих на<start_point>
, легко запускаяgit commit -a
, чтобы сделать фиксацию root. — git checkout docs
Теперь вам нужно полностью очистить состояние ветки, так что вы действительно начнете снова с чистого листа (запустите из папки верхнего уровня):
git rm -rf .
Это объяснение из документов:
Если вы хотите запустить отключенную историю, которая записывает набор путей, полностью отличающихся от одного из
<start_point>
, тогда вы должны очистить индекс и рабочее дерево сразу после создания ветки-сироты, запустивgit rm -rf .
от верхнего уровня рабочего дерева. После этого вы будете готовы подготовить свои новые файлы, заполнив рабочее дерево, скопировав их из других источников, извлеките архив и т.д.
Шаг 4: Рекомендуем более старую работу в new-master
Теперь вы захотите начать выполнять свою более старую работу в new-master
. Когда вы закончите, вы вернете своего старого мастера сверху.
Я не совсем уверен, как организованы ваши резервные папки, но я собираюсь предположить, что каждый из них является только датированной копией папки вашего проекта, названной в формате YYYY-MM-DD
, например. 2013-01-30
. Вы можете добавить содержимое каждой папки в качестве новой фиксации вручную или написать script для автоматизации процесса.
Предположим, что у вас есть все ваши резервные папки в верхней папке с именем backup
, которая находится в том же каталоге, что и ваша основная папка проекта. Затем вот некоторый псевдокод для script для автоматизации процесса компиляции каждой папки:
# Get backups and sort them from oldest to newest
backups = open("backups").toList().sort("date ascending")
for each (backup in backups)
copy files from backup to project folder
execute `git add "*"`
execute `git commit --date="#{backup.date}" --message="#{backup.date}"`
script выше будет добавлять и изменять файлы между фиксациями. Если какая-либо из ваших резервных копий удалила, переместила или переименовала файлы, script не будет записывать это. Вам нужно написать более умный script, чтобы сделать это (возможно, что-то, что использует git diff
или какой-либо другой инструмент) или записать эти изменения вручную.
Шаг 5: Восстановите старый master
на new-master
ПОСМОТРЕТЬ УДИВИТЕЛЬНУЮ ЭНЕРГИЮ, КОТОРУЮ GIT! Теперь мы собираемся ОБНОВИТЬ ВАШУ ИСТОРИЧЕСКУЮ ИСТОРИЮ! Прочитайте эти тайные слова силы и понаблюдайте за волшебством:
git rebase --onto new-master --root master
TA-DA! Возможно, вам придется разрешать конфликты между last-commit из new-master
и first-commit из master
, но кроме этого, это должно быть!
Флаг --root
на rebase
обходит проблему, с которой мы столкнулись ранее, о том, что — normal — rebase
не будет работать с первым (корневым) фиксацией репозитория Git. Флаг --root
в основном сообщает Git включить его в rebase
:
Восстановите все коммиты, достижимые с
<branch>
, вместо того, чтобы ограничивать их<upstream>
. Это позволяет вам переустановить корневой фиксатор на ветке. При использовании с--onto
он пропустит изменения, уже содержащиеся в<newbase>
(вместо<upstream>
), тогда как без--onto
он будет работать при каждом изменении. — обновить документы
Шаг 6: убедитесь, что конечная фиксация остается тем же самым
Чтобы убедиться, что мы не испортили код, нам нужно сравнить текущее конечное состояние master
с его состоянием до того, как мы сделали rebase
. Любая из следующих команд будет работать (они идентичны). В ветке master
:
git diff orig_head # "Original" head
git diff [email protected]{1} # Master at its 1st prior position
Если вы не получаете выход из diff
, это означает, что конечное состояние вашего rebased master
идентично тому, что было до rebase
. Хорошая работа!
ПРИМЕЧАНИЕ: не уверен, что происходит для слияния коммитов...
Теперь вышеуказанные шаги будут работать нормально, если у вас есть отличная линейная история, т.е. у вас нет коммитов. Я не уверен, будет ли он работать, если вы это сделаете. В моем предыдущем ответе ниже я упомянул, что использование флага --preserve-merges
с rebase
может помочь вам сохранить эти слияния, но в документах упоминается, что этот флаг также взаимодействует с флагами --onto
и --root
:
Когда [
--root
] используется вместе с--onto
и--preserve-merges
, все корневые коммиты будут перезаписаны, если вместо этого будет<newbase>
как родительский. — обновить документы
Я не совсем уверен, что это значит в данный момент... Мне, возможно, придется попробовать и посмотреть, что произойдет. Означает ли это, что если у вас более 1 корневой фиксации (например, 10), то 9 из этих корневых коммитов в конечном итоге будут переустановлены на последний, действующий как новый корень? Это не похоже на поведение, которое мы хотели бы. Мы просто хотим сохранить фиксацию слияния для того, чтобы один корень был переустановлен на другой.
Предыдущий ответ
У MGA была правильная идея с rebase
. Я не уверен, что это исправит вашу проблему, но, возможно, вам нужно сделать новую фиксацию root в своем репо, добавить ваши резервные файлы в качестве новых коммитов поверх нее, а затем перегрузить старый график фиксации сверху.
Например, git checkout
имеет флаг --orphan
в соответствии с документацией:
--orphan <new_branch>
Создайте новую сиротскую ветвь с именем<new_branch>
, начиная с<start_point>
и переключитесь на нее. Первая фиксация, сделанная в этой новой ветке, не будет иметь родителей, и она станет корнем новой истории, полностью отключенной от всех других ветвей и совершит.
Итак, вы можете попробовать
git checkout --orphan <temp_branch>
Затем закрепите на нем свои файлы резервных копий. Затем, выведя temp_branch
, сделайте
git cherry-pick <first commit from master>
git rebase --onto temp_branch <first commit from master> master
Я думаю, вам, вероятно, понадобится cherry-pick
первая фиксация, потому что второй аргумент rebase --onto
- это старая база ветки, которую вы хотите переустановить (в данном случае master
), а старая база isn ' т включены. Поэтому я считаю, что вам нужно cherry-pick
его, чтобы получить его, поскольку он не имеет базового коммита, поэтому вы не можете указать его для него в rebase
.
Также обратите внимание, что если ваша история фиксации не является линейной (т.е. имеет фиксацию слияния), то rebase
обычно не сохраняет их. Он имеет флаг --preserve-merges
, но я никогда не использовал его раньше, поэтому я не уверен, как он будет работать. Из документы:
-p
--preserve-merges
Вместо того, чтобы игнорировать слияния, попробуйте воссоздать их.Это использует механизм
--interactive
внутри, но его сочетание с опцией--interactive
явно не является хорошей идеей, если вы не знаете, что делаете (см. ниже).
Итак, возможно, все это сработает?