Как добавить прошлое в репозиторий git? - PullRequest
24 голосов
/ 30 июня 2010

Я получил некоторый исходный код и решил использовать для него git, так как мой коллега использовал mkdir $VERSION и т. Д. Хотя прошлое кода в настоящее время кажется неважным, я все же хотел бы поставить его под контроль git, чтобы лучше понять процесс разработки. Итак:

Какой удобный способ поместить эти прошлые версии в мое уже существующее git-репо? В настоящее время нет удаленного репо, поэтому я не возражаю переписывать историю, но решение, которое принимает во внимание удаленные репозитории, конечно, будет предпочтительным, если только тогда оно не будет намного более скопированным. Бонусные баллы за сценарий, который больше не требует взаимодействия на основе истории или каталога, либо архива.

Ответы [ 4 ]

24 голосов
/ 30 июня 2010

Для импорта старых снимков вы найдете некоторые инструменты в Git's contrib / fast-import каталог полезными. Или, если у вас уже есть каждый старый снимок в каталоге, вы можете сделать что-то вроде этого:

# Assumes the v* glob will sort in the right order
# (i.e. zero padded, fixed width numeric fields)
# For v1, v2, v10, v11, ... you might try:
#     v{1..23}     (1 through 23)
#     v?{,?}       (v+one character, then v+two characters)
#     v?{,?{,?}}   (v+{one,two,three} characters)
#     $(ls -v v*)  (GNU ls has "version sorting")
# Or, just list them directly: ``for d in foo bar baz quux; do''
(git init import)
for d in v*; do
    if mv import/.git "$d/"; then
        (cd "$d" && git add --all && git commit -m"pre-Git snapshot $d")
        mv "$d/.git" import/
    fi
done
(cd import && git checkout HEAD -- .)

Затем загрузите старую историю в ваш рабочий репозиторий:

cd work && git fetch ../import master:old-history

Если у вас есть и старая история, и ваша история на основе Git в одном и том же хранилище, у вас есть пара опций для операции prepend: пересадки и замены.

Трансплантаты - это механизм для каждого репозитория, который (возможно временно) редактирует происхождение различных существующих коммитов. Трансплантаты контролируются файлом $GIT_DIR/info/grafts (описанным в разделе «информация / трансплантаты» справочной страницы макета репозитория ).

INITIAL_SHA1=$(git rev-list --reverse master | head -1)
TIP_OF_OLD_HISTORY_SHA1=$(git rev-parse old-history)
echo $INITIAL_SHA1 $TIP_OF_OLD_HISTORY_SHA1 >> .git/info/grafts

С трансплантатом на месте (исходный первоначальный коммит не имел родителей, у него был один родитель), вы можете использовать все обычные инструменты Git для поиска и просмотра расширенной истории (например, git log должен теперь покажу вам старую историю после ваших коммитов).

Основная проблема с трансплантатами заключается в том, что они ограничены вашим хранилищем. Но если вы решите, что они должны быть постоянной частью истории, вы можете использовать git filter-branch , чтобы сделать их такими (сначала сделайте резервную копию tar / zip вашего .git dir; git filter-branch сохранит исходные ссылки, но иногда проще использовать простую резервную копию).

git filter-branch --tag-name-filter cat -- --all
rm .git/info/grafts

Механизм замены более новый (Git 1.6.5 +), но их можно отключить для каждой команды (git --no-replace-objects …) и использовать для упрощения совместного использования. Замена работает с отдельными объектами (BLOB-объектами, деревьями, коммитами или аннотированными тегами), поэтому механизм также является более общим. Механизм замены описан в git replace manpage . Из-за общности «предварительная» настройка немного сложнее (нам нужно создать новый коммит, а не просто назвать нового родителя):

# the last commit of old history branch
oldhead=$(git rev-parse --verify old-history)
# the initial commit of current branch
newinit=$(git rev-list master | tail -n 1)
# create a fake commit based on $newinit, but with a parent
# (note: at this point, $oldhead must be a full commit ID)
newfake=$(git cat-file commit "$newinit" \
        | sed "/^tree [0-9a-f]\+\$/aparent $oldhead" \
        | git hash-object -t commit -w --stdin)
# replace the initial commit with the fake one
git replace -f "$newinit" "$newfake"

Обмен этой заменой не является автоматическим. Вы должны нажать часть (или все) refs/replace, чтобы поделиться заменой.

git push some-remote 'refs/replace/*'

Если вы решили сделать замену постоянной, используйте git filter-branch (так же, как с трансплантатами; сначала сделайте резервную копию tar / zip для вашего каталога .git):

git filter-branch --tag-name-filter cat -- --all
git replace -d $INITIAL_SHA1
3 голосов
/ 30 июня 2010

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

Это сообщение: http://marc.info/?l=git&m=119636089519572, кажется, содержит лучшую документацию, которую я могу найти.Вы должны создать последовательность коммитов, относящуюся к вашей истории pre-git, а затем использовать файл .git/info/grafts, чтобы Git использовал последний коммит в этой последовательности в качестве родителя первого коммита, сгенерированного вами с помощью Git.

2 голосов
/ 30 июня 2010

Самым простым подходом, конечно же, является создание нового git-репо, коммит истории, предварительно добавив историю, а затем повторно применяя патчи старого репо.Но я бы предпочел решение, которое отнимает меньше времени за счет автоматизации.

0 голосов
/ 19 декабря 2018

Если вы просто хотите навсегда объединить 2 репозитория, лучшее решение - экспортировать все коммиты из второго репозитория (кроме первоначального коммита, который создал репозиторий как продолжение другого).

Iдумаю, что это лучше, потому что когда вы выполняете шаги, описанные Chris Johnsen , он преобразует ваш первоначальный коммит во второй репозиторий как коммит удаления, который удаляет несколько файлов.И если вы попытаетесь пропустить первоначальный коммит, он преобразует ваш второй коммит в коммит, который удаляет все файлы (конечно, мне пришлось попробовать его).Я не уверен, как это повлияет на способность git отслеживать историю файлов в команде как git log --follow -- file/name.txt

Вы можете экспортировать всю историю (ожидайте первый коммит, который уже присутствует в первом репозитории)ваш второй репозиторий и импортируйте его в первый репозиторий, выполнив следующие команды:

  1. Откройте командную строку Linux во втором репозитории (для экспорта последних коммитов)
  2. commit_count=$(git rev-list HEAD --count)
  3. git format-patch --full-index -$(($commit_count - 1))
  4. Переместить все файлы git patch .patch, созданные в корне вашего второго репозитория, в новый каталог с именем patches на стороне вашего первого корневого каталога репозитория
  5. Теперь откройте командную строку Linux в вашем первом репозитории (для импорта последних коммитов)
  6. git am ../patches/*.patch
  7. Если у вас возникли проблемы при применении патчей git, запустите git am --abort, затемсм. git: patch не применяется и попробуйте что-то вроде git am ../patches/*.patch --ignore-space-change --ignore-whitespace, как предложено в связанном вопросе StackOverflow.

В качестве альтернативы использованию git из командылиния, вы можете использовать интерфейс git, как SmartGit или GitExtensions

Ссылки:

  1. https://www.ivankristianto.com/create-patch-files-from-multiple-commits-in-git/
  2. Git: Как создавать патчи для слияния?
  3. https://www.ivankristianto.com/create-patch-files-from-multiple-commits-in-git/
  4. как применять несколько патчей git в одном кадре
  5. https://davidwalsh.name/git-export-patch

Для полноты здесь я представляю автоматический сценарий оболочки, который следует за Крисом Джонсеном шагами для окончательного объединения 2 хранилища.Вам нужно запустить это в первом репозитории, где вы хотели бы интегрировать историю из второго репозитория, которая продолжает историю из первого репозитория.После нескольких часов экспериментов я обнаружил, что это лучший подход.Если вы знаете, как что-то улучшить, пожалуйста, исправьте / поделитесь / прокомментируйте.

Пожалуйста, полностью сделайте резервную копию вашего первого и второго репозиториев в файл .zip перед запуском этого.

old_history=master
new_history=master-temp

old_remote_name=deathaxe
old_remote_url=second_remote_url

git remote add $old_remote_name $old_remote_url
git fetch $old_remote_name
git branch --no-track $new_history refs/remotes/$old_remote_name/$old_history
git branch --set-upstream-to=origin/$old_history $new_history

# the last commit of old history branch
oldhead=$(git rev-parse --verify $old_history)

# the initial commit of current branch
# newinit=$(git rev-list $new_history | tail -n 2 | head -n -1)
newinit=$(git rev-list $new_history | tail -n 1)

# create a fake commit based on $newinit, but with a parent
# (note: at this point, $oldhead must be a full commit ID)
newfake=$(git cat-file commit "$newinit" \
        | sed "/^tree [0-9a-f]\+\$/aparent $oldhead" \
        | git hash-object -t commit -w --stdin)

# replace the initial commit with the fake one
# git replace <last commit> <first commit>
# git replace <object> <replacement>
git replace -f "$newinit" "$newfake"

# If you decide to make the replacement permanent, use git filter-branch
# (make a tar/zip backup of your .git directory first)
git filter-branch --tag-name-filter cat -- --all
git replace -d $newinit

git push -f --tags
git push -f origin $new_history

git checkout $old_history
git branch -d $new_history
git pull --rebase

Ссылки:

  1. https://feeding.cloud.geek.nz/posts/combining-multiple-commits-into-one/
  2. https://mirrors.edge.kernel.org/pub/software/scm/git/docs/git-replace.html
  3. Удалить последнюю строку из файла в Bash
  4. Принудительная «git push» для перезаписи удаленных файлов
  5. Git принудительная push-метка, когда тег уже существует на удаленном
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...