Создание репозитория GitHub только с подмножеством истории локального репозитория - PullRequest
43 голосов
/ 20 апреля 2011

Справочная информация: Я приближаюсь к открытому поиску кода личного исследования Я работаю более двух лет.Он начал свою жизнь как репозиторий SVN, но я перешел на Git около года назад, и я хотел бы поделиться кодом на GitHub.Тем не менее, он накопил много беспорядка за эти годы, и я бы предпочел, чтобы публичная версия начала свою жизнь в ее нынешнем статусе.Тем не менее, я все еще хотел бы внести свой вклад в это и включить потенциальные вклады других людей.

Вопрос: есть ли способ "разветвить" репозиторий git так, чтобы история не сохраняласьна развилке (которая находится на GitHub), но мой локальный репозиторий все еще имеет полную историю, и я могу перетащить / перенести в GitHub?

У меня нет опыта администрирования больших репозиториев,поэтому детали очень ценятся.

Ответы [ 3 ]

70 голосов
/ 20 апреля 2011

Вы можете легко создать новую, свежую историю в Git. Допустим, вы хотите, чтобы ваша ветка master была той, которую вы отправите на GitHub, и ваша полная история будет храниться в old-master. Вы можете просто переместить ветку master в old-master, а затем запустить новую новую ветку без истории, используя git checkout --orphan:

git branch -m master old-master
git checkout --orphan master
git commit -m "Import clean version of my code"

Теперь у вас есть новая ветка master без истории, которую вы можете отправить на GitHub. Но, как вы говорите, вы хотели бы видеть всю старую историю в вашем локальном хранилище; и, вероятно, хотел бы, чтобы он не был отключен.

Вы можете сделать это, используя git replace. Замена ref - это способ указать альтернативный коммит в любое время, когда Git просматривает данный коммит. Таким образом, вы можете указать Git смотреть на последний коммит вашей старой ветки, а не на первый коммит вашей новой ветки, при просмотре истории. Для этого вам необходимо внести отключенную историю из старого репо.

git replace master old-master

Теперь у вас есть новая ветвь, в которой вы можете видеть всю свою историю, но реальные объекты коммитов отключены от старой истории, и поэтому вы можете помещать новые коммиты в GitHub без старых коммитов. Переместите ветку master на GitHub, и только новые коммиты будут отправлены на GitHub. Но взгляните на историю в gitk или git log, и вы увидите полную историю.

git push github master:master
gitk --all

Gotchas

Если вы когда-либо основываете какие-либо новые ветви на старых коммитах, вам нужно быть осторожным, чтобы сохранить историю отдельно; в противном случае новые коммиты в этих ветвях действительно будут иметь старые коммиты в своей истории, и поэтому вы будете тянуть всю историю вперед, если вы отправите ее в GitHub. Пока вы сохраняете все свои новые коммиты на основе вашего нового master, все будет в порядке.

Если вы когда-нибудь запустите git push --tags github, это подтолкнет все ваши теги, включая старые, что приведет к тому, что вся ваша старая история будет сохранена вместе с ней. Вы могли бы справиться с этим, удалив все ваши старые теги (git tag -d $(git tag -l)), или никогда не используя git push --tags, а только нажимая теги вручную, или используя два репозитория, как описано ниже.

Основная проблема, лежащая в основе обоих этих уловок, заключается в том, что если вы когда-либо нажмете любой реф, который соединяется с какой-либо из старой истории (кроме как с помощью замененных коммитов), вы будете выталкивать всю старую историю. Вероятно, лучший способ избежать этого - использовать два репозитория, один из которых содержит только новые коммиты, а другой содержит как старую, так и новую историю, для проверки всей истории. Вы делаете всю свою работу, ваши коммиты, ваши пуш-апы из GitHub, в репо только с новыми коммитами; таким образом, вы не можете случайно подтолкнуть ваши старые коммиты. Затем вы тянете все свои новые коммиты в репо, имеющий полную историю, всякий раз, когда вам нужно посмотреть на все это. Вы можете взять из GitHub или другого локального репо, в зависимости от того, что удобнее. Это будет ваш архив, но, чтобы избежать случайной публикации вашей старой истории, вы никогда не будете использовать GitHub. Вот как это можно настроить:

~$ mkdir newrepo
~$ cd newrepo
newrepo$ git init
newrepo$ git pull ~/oldrepo master
# now newrepo has just the new history; we can set up oldrepo to pull from it
newrepo$ cd ~/oldrepo
oldrepo$ git remote add newrepo ~/newrepo
oldrepo$ git remote update
oldrepo$ git branch --set-upstream master newrepo/master
# ... do work in newrepo, commit, push to GitHub, etc.
# Now if we want to look at the full history in oldrepo:
oldrepo$ git pull

Если вы используете Git старше 1.7.2

У вас нет git checkout --orphan, поэтому вам придется делать это вручную, создавая свежий репозиторий из текущей ревизии вашего существующего репозитория, а затем вытягивая свою старую отключенную историю. Вы можете сделать это, например:

oldrepo$ mkdir ~/newrepo
oldrepo$ cp $(git ls-files) ~/newrepo
oldrepo$ cd ~/newrepo
newrepo$ git init
newrepo$ git add .
newrepo$ git commit -m "Import clean version of my code"
newrepo$ git fetch ~/oldrepo master:old-master

Если вы используете Git старше 1.6.5

git replace и заменяющие ссылки были добавлены в 1.6.5, поэтому вам придется использовать более старый, несколько менее гибкий механизм, известный как grafts , который позволяет вам указать альтернативных родителей для данного совершить. Вместо команды git replace введите:

echo $(git rev-parse master) $(git rev-parse old-master) >> .git/info/grafts

Это будет выглядеть локально, как будто коммит master имеет коммит old-master в качестве родителя, поэтому вы увидите еще один коммит, чем с git replace.

2 голосов
/ 20 апреля 2011

Ответ Брайана, приведенный выше, кажется полным и хорошо осведомленным, но немного сложным.

Простое (простое) решение - сохранить два репозитория.

Частный репозиторий github, с которым вы работаетена.Вы делаете все полные истории изменений в этом хранилище.

Вторым хранилищем является общедоступный репозиторий github, в который вы публикуете только когда вы хотите «выпустить» новую версию для всех.Вы публикуете его, используя простой патч diff +, а затем фиксируете + push.

0 голосов
/ 02 июня 2014

Очень простой и интересный способ сделать это, как показано ниже -

Скажем, вы имеете в коммитах REPO-A C1-C10, где C1 - начальный коммит, а C10 - последний HEAD. И вы хотите создать новый REPO-B таким образом, чтобы он зафиксировал C4-C8 (подмножество).

ПРИМЕЧАНИЕ: Использование этого метода приведет к изменению SHA фиксации (например, от C4 'до C8' в этом случае), но изменения каждого удержания фиксации останутся такими же, и ваша первая фиксация теперь начнется со всех изменения ваших предыдущих коммитов до этого момента вместе взятые.

Что делать?


Рекурсивно копировать все на локальный компьютер

cp -R REPO-A REPO-B

При необходимости удалите все пульты дистанционного управления из вашего REPO-B, поскольку, скорее всего, вы захотите использовать это как отдельный репозиторий.

cd REPO-B
git remote -v 
git remote remove REMOTE_NAME

Принудительно переместить указатель ветви на более поздний конец вашего подмножества. Для субъекта от C4 до C8 это будет C8. Но, скорее всего, вам понадобятся подмножества до HEAD (например, от C4 до C10 или от C6 до C10), в этом случае приведенный ниже шаг не требуется.

git checkout -b temp
git branch -f master C8
git checkout master
git branch -D temp

Введите коммит SHA более раннего конца вашего подмножества в каталоге .git/info/grafts файла. В данном случае это SHA коммита C4.

git rev-parse --verify C4 >> .git/info/grafts

Выполнить фильтрацию веток git без аргументов

git filter-branch

или это что не работает

git filter-branch --all

Теперь вы можете перенести это на отдельный / новый пульт, если хотите

git remote add origin NEWREMOTE
git push -u origin master

Как это работает?


По этой ссылке вы узнаете, как на самом деле это работает - http://git.661346.n2.nabble.com/how-to-delete-the-entire-history-before-a-certain-commit-td5000540.html

Вы можете прочитать о трансплантатах на man-странице git-filter-branch (1), в gitrepository-layout (5) описание макета git-репозитория и в Gitglossary (7) Глоссарий Git.

Короче говоря, каждая строка в .git / info / grafts состоит из идентификатора объекта sha1, за ним следует разделенный пробелами список его эффективных (привитых) родителей. Таким образом, чтобы сократить историю, например. после коммита a3eb250f996bf5e нужно поставить строка, содержащая только этот SHA-1 в файле .git / info / grafts, например:

$ git rev-parse --verify a3eb250f996bf5e >> .git / info / grafts

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...