Ответ 1
Вы можете легко создать новую свежую историю в 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
и заменить ref были добавлены в 1.6.5, поэтому вам придется использовать более старый, несколько менее гибкий механизм, известный как grafts, которые позволяют указывать альтернативных родителей для данного коммита. Вместо команды git replace
запустите:
echo $(git rev-parse master) $(git rev-parse old-master) >> .git/info/grafts
Это заставит его выглядеть локально, как будто master
commit имеет old-master
commit в качестве своего родителя, поэтому вы увидите еще одну фиксацию, чем с git replace
.