Вы можете легко создать новую, свежую историю в 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
.