仅使用本地存储库历史记录的子集创建GitHub存储库

时间:2011-04-20 01:12:34

标签: git github fork

背景:我正在接近open sourcing a personal research code我已经工作了两年多。它起初是一个SVN存储库,但大约一年前我转移到Git,我想在GitHub上分享代码。然而,多年来它累积了很多,我宁愿公共版本以其现状开始。但是,我仍然愿意为此做出贡献并融入其他人的潜在贡献。

问题:有没有办法“分叉”一个git存储库,这样就不会在fork(它存在于GitHub上)上保留历史记录,但是我的本地存储库仍然有完整的历史记录,我可以拉/推到GitHub?

我对大型存储库的管理端没有任何经验,所以非常感谢细节。

3 个答案:

答案 0 :(得分:70)

您可以在Git中轻松创建新的历史记录。假设您希望您的master分支成为您将推送到GitHub的分支,并将您的完整历史记录存储在old-master中。您可以move your master branch to 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。但请查看gitkgit log中的历史记录,您将看到完整的历史记录。

git push github master:master
gitk --all

陷阱

如果您在旧提交上建立任何新分支,则必须小心保持历史记录分开;否则,这些分支上的新提交将在其历史中真正具有旧提交,因此如果将其推送到GitHub,您将完成整个历史记录。但是,只要您根据新的master保留所有新提交,就可以了。

如果您运行git push --tags github,则会推送所有标记,包括旧标记,这将导致您的所有旧历史记录随之拉动。您可以通过删除所有旧标记(git tag -d $(git tag -l)),或从不使用git push --tags,但只是手动推送标记,或使用两个存储库来处理此问题。

这两个问题背后的基本问题是,如果你推送任何连接到任何旧历史的ref(除了通过被替换的提交),你将推高所有旧的历史。避免这种情况的最佳方法可能是使用两个存储库,一个仅包含新提交,另一个包含旧历史和新历史,以便检查完整历史记录。你只需要新的提交就可以完成你的所有工作,你的提交,你从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

如果您使用的是超过1.7.2的Git

您没有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

如果您使用的是超过1.6.5的Git

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提交多一次提交。 / p>

答案 1 :(得分:2)

布莱恩上面的回答似乎是完整的,知识渊博,但有点复杂。

简单(ier)解决方案是保留两个存储库。

您正在使用的私有github存储库。您将所有完整历史记录推送到该存储库。

第二个存储库是一个Public github存储库,只有在想要向公众“释放”新版本时才会将其发布到该存储库。您使用简单的diff + patch发布到它,然后commit + push。

答案 2 :(得分:0)

这样做非常简单有趣的方法如下 -

假设您在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

在文件.git/info/grafts目录中输入子集前一端的提交SHA。在这种情况下,它是提交C4的SHA。

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

  

您可以在git-filter-branch(1)联机帮助页中阅读有关移植的信息   gitrepository-layout(5)git存储库布局描述,以及   gitglossary(7)git词汇表。

     

简而言之,.git / info / grafts中的每一行都包含对象的sha1 id,   其次是以空格分隔的有效(嫁接)父母名单。   所以要削减历史,例如提交a3eb250f996bf5e后,你需要把   在.git / info / grafts文件中仅包含此SHA-1的行,例如:

     

$ git rev-parse --verify a3eb250f996bf5e>>的.git /信息/移植物