使当前提交成为Git存储库中唯一的(初始)提交?


664

我目前有一个本地Git存储库,我将其推送到Github存储库。

本地存储库有大约10个提交,而Github存储库是它的同步副本。

我想做的是从本地Git存储库中删除所有版本历史记录,因此存储库中的当前内容显示为唯一的提交(因此不会存储存储库中较旧版本的文件)。

然后,我想将这些更改推送到Github。

我已经研究了Git变基,但是这似乎更适合删除特定版本。另一个可能的解决方案是删除本地存储库,然后创建一个新的存储库-尽管这可能会导致很多工作!

预计到达时间:有些特定的目录/文件无法跟踪-如果可能的话,我想保持对这些文件的跟踪。


6
另请参见stackoverflow.com/questions/435646/…(“如何合并Git存储库的前两个提交?”)
Anonymoose 2012年


Answers:


981

这是蛮力方法。它还会删除存储库的配置。

注意:如果存储库包含子模块,则此方法不起作用!如果使用子模块,则应使用例如交互式基础

第1步:删除所有历史记录(确保您已备份,无法还原

cat .git/config  # note <github-uri>
rm -rf .git

步骤2:仅使用当前内容重建Git存储库

git init
git add .
git commit -m "Initial commit"

步骤3:推送至GitHub。

git remote add origin <github-uri>
git push -u --force origin master

3
谢谢larsmans-我选择使用它作为我的解决方案。尽管初始化Git存储库会丢失旧存储库中未跟踪文件的记录,但这可能是解决我的问题的更简单方法。
kaese 2012年

5
@kaese:我认为您.gitignore应该处理这些,对吗?
弗雷德·富

48
之前保存.git / config,然后保存。
lalebarde 2014年

@lalebarde如果您之后恢复.git / config,git commit -m "Initial commit"那么您可以跳过该git remote add ...部分(假设该部分已在您的配置中),然后继续进行推送。它为我工作。
Buttle Butkus 2015年

24
如果要尝试删除敏感数据,请谨慎操作:在新推送的master分支中仅存在一次提交是令人误解的-历史将仍然存在,只是无法从该分支访问。例如,如果您有标记指向较早的提交,则可以访问这些提交。实际上,对于任何使用git foo的人,我敢肯定,在执行git push之后,他们仍然能够从GitHub存储库中恢复所有历史记录-如果您还有其他分支或标签,那么他们将无法甚至需要很多git foo。
罗伯特·穆尔

621

唯一适用于我(并使子模块保持工作状态)的解决方案是

git checkout --orphan newBranch
git add -A  # Add all files and commit them
git commit
git branch -D master  # Deletes the master branch
git branch -m master  # Rename the current branch to master
git push -f origin master  # Force push master branch to github
git gc --aggressive --prune=all     # remove the old files

.git/当我有子模块时,删除总是会引起巨大的问题。使用git rebase --root会以某种方式对我造成冲突(并且自从我有很多历史以来就花了很长时间)。


55
这应该是正确的答案!只需添加一个git push -f origin master作为最后一个操作,太阳就会在您的新仓库上再次闪耀!:)
gru 2014年

2
这不会保留旧提交吗?
布拉德2014年

4
@JonePolvora git获取; GIT中的复位-硬起源/主stackoverflow.com/questions/4785107/...
回波

5
完成此操作后,回购空间是否可用?
Inuart 2014年

8
我相信您应该在回答的最后一行加上@JasonGoemaat的建议。没有git gc --aggressive --prune all失去历史的全部点,就会错过。
TuncayGöncüoğlu的

93

这是我最喜欢的方法:

git branch new_branch_name $(echo "commit message" | git commit-tree HEAD^{tree})

这将创建一个带有一次提交的新分支,该分支将所有内容添加到HEAD中。它不会改变任何其他内容,因此是完全安全的。


3
最好的方法!清除并执行工作。此外,我对分支进行了重命名,从“主”更改为“本地工作”,将“ new_branch_name”更改为“主”。在master中,请执行以下操作:git -m local-changes git branch -m local-changes git checkout new_branch_name git branch -m master <
Valtoni Boaventura 2015年

这看起来确实很短而圆滑,我唯一不了解或尚未看到的是HEAD ^ {tree},有人可以解释吗?除此之外,我将其读为“根据给定的提交创建新分支,通过使用___给定的提交消息创建新的提交对象来创建”
TomKeegasi

3
寻找有关git reference语法问题的答案的权威位置在git-rev-parsedocs中。这里发生的事情是git-commit-tree需要引用树(存储库的快照),但这HEAD是一个修订。为了找到与提交关联的树,我们使用<rev>^{<type>}表单。
dan_waterworth'2

好答案。效果很好。最后说git push --force <remote> new_branch_name:<remote-branch>
费利佩·阿尔瓦雷斯

31

另一个选项(如果您的提交次数很多)可能需要大量工作,它是一个交互式的基础(假设您的git版本> = 1.7.12):git rebase --root -i

在编辑器中显示提交列表时:

  • 第一次提交时将“ pick”更改为“ reword”
  • 每隔一次提交将“ pick”更改为“ fixup”

保存并关闭。Git将开始变基。

最后,您将拥有一个新的root提交,该提交是所有后续提交的组合。

这样做的好处是,您不必删除存储库,并且如果您有其他想法,则总是有一个备用。

如果您确实想核对您的历史记录,请将master重置为该提交并删除所有其他分支。


调整完成后,我无法执行以下操作:error: failed to push some refs to
Begueradj

@Begueradj如果您已经推送了您重新设置了基础的分支,那么您将需要强制推送git push --force-with-lease。使用“带租用租借”是因为其破坏性不如--force。
卡尔

19

larsmans提出的方法的变体:

保存您的取消跟踪文件列表:

git ls-files --others --exclude-standard > /tmp/my_untracked_files

保存您的git配置:

mv .git/config /tmp/

然后执行larsmans的第一步:

rm -rf .git
git init
git add .

恢复您的配置:

mv /tmp/config .git/

取消跟踪您未跟踪的文件:

cat /tmp/my_untracked_files | xargs -0 git rm --cached

然后提交:

git commit -m "Initial commit"

最后推送到您的存储库:

git push -u --force origin master

6

以下是根据@Zeelot的答案改编而成的脚本。它应该删除所有分支的历史记录,而不仅仅是master分支:

for BR in $(git branch); do   
  git checkout $BR
  git checkout --orphan ${BR}_temp
  git commit -m "Initial commit"
  git branch -D $BR
  git branch -m $BR
done;
git gc --aggressive --prune=all

它出于我的目的而工作(我不使用子模块)。


4
我认为您忘记了强迫主控完成此过程。
not2qubit

2
我必须稍作修改。git branch将在检出的分支旁边包含一个星号,然后将其选中,使其解析为所有文件或文件夹,就好像它们也是分支名称一样。相反,我使用的git branch --format="%(refname:lstrip=2)"只是分支名称。
本·理查兹

@ not2qubit:谢谢你。确切的命令是什么?git push --force origin mastergit push --force-with-lease?显然,后者是更安全(见stackoverflow.com/questions/5509543/...
Shafique先生贾马尔-

@BenRichards。有趣。我将在某个时候使用与分支名称匹配的文件夹再次尝试进行测试,然后更新答案。谢谢。
沙菲克·贾马尔


4

git filter-branch 是主要手术工具。

git filter-branch --parent-filter true -- @^!

--parent-filter让父母使用stdin,并在stdout上打印重写的父母;unix true成功退出并且什么也不打印,因此:没有父母。 @^!Git的简写,意为 “负责任,但不负责任”。然后删除所有其他参考,并随意进行。


3

只需删除Github仓库并创建一个新仓库即可。迄今为止最快,最简单,最安全的方法。毕竟,当您只需要一次提交的master分支时,您将获得在接受的解决方案中执行所有这些命令的条件?


1
要点之一是能够看到它的来源。
not2qubit

我只是这样做了,这很好
thanos.a

2

下面的方法是完全可重现的,因此如果双方保持一致,则无需再次运行克隆,也只需在另一侧运行脚本即可。

git log -n1 --format=%H >.git/info/grafts
git filter-branch -f
rm .git/info/grafts

如果然后要清理它,请尝试以下脚本:

http://sam.nipl.net/b/git-gc-all-ferocious

我写了一个脚本,“杀死历史”存储库中的每个分支:

http://sam.nipl.net/b/git-kill-history

另请参阅:http : //sam.nipl.net/b/confirm


1
谢谢你 仅供参考:杀死每个分支的历史记录的脚本可能会使用某些更新-它会产生以下错误:git-hash: not foundSupport for <GIT_DIR>/info/grafts is deprecated
Shafique Jamal

1
@ShafiqueJamal,谢谢,小“混帐散”的剧本是git log HEAD~${1:-0} -n1 --format=%H,在这里,sam.aiki.info/b/git-hash这将是更好地把它们都放在一个脚本大众消费。如果我再次使用它,我可能会想出如何用替代“嫁接”的新功能来做到这一点。
山姆·沃特金斯

2

我想做的是从本地Git存储库中删除所有版本历史记录,因此存储库中的当前内容显示为唯一的提交(因此不会存储存储库中较旧版本的文件)。

更具概念性的答案:

如果没有标签/分支/引用指向旧提交,git会自动垃圾收集旧提交。因此,您只需删除所有标签/分支并创建与任何分支相关联的新的孤立提交,按照约定,您就可以让该分支master指向该提交。

除非有人使用低级git命令进行挖掘,否则任何人都不会再看到旧的,无法访问的提交。如果这对您来说足够了,我会停在那儿,让自动GC随时执行它的工作。如果您想立即摆脱它们,可以使用git gc(可能使用--aggressive --prune=all)。对于远程git存储库,除非您可以通过Shell访问其文件系统,否则您无法强制这样做。


当在@Zeelot的答案上下文中看到时,这是很好的补充。
Mogens TrasherDK

是的,Zeelot的命令基本上可以做到这一点(只是完全重新开始而有所不同,这可能对OP来说很好)。@MogensTrasherDK
AnoE

0

干得好:

#!/bin/bash
#
# By Zibri (2019)
#
# Usage: gitclean username password giturl
#
gitclean () 
{ 
    odir=$PWD;
    if [ "$#" -ne 3 ]; then
        echo "Usage: gitclean username password giturl";
        return 1;
    fi;
    temp=$(mktemp -d 2>/dev/null /dev/shm/git.XXX || mktemp -d 2>/dev/null /tmp/git.XXX);
    cd "$temp";
    url=$(echo "$3" |sed -e "s/[^/]*\/\/\([^@]*@\)\?\.*/\1/");
    git clone "https://$1:$2@$url" && { 
        cd *;
        for BR in "$(git branch|tr " " "\n"|grep -v '*')";
        do
            echo working on branch $BR;
            git checkout $BR;
            git checkout --orphan $(basename "$temp"|tr -d .);
            git add -A;
            git commit -m "Initial Commit" && { 
                git branch -D $BR;
                git branch -m $BR;
                git push -f origin $BR;
                git gc --aggressive --prune=all
            };
        done
    };
    cd $odir;
    rm -rf "$temp"
}

也在这里托管:https : //gist.github.com/Zibri/76614988478a076bbe105545a16ee743


加!不要让我在命令行中提供未隐藏的,不受保护的密码!另外,git branch的输出通常不适合脚本编写。您可能需要查看管道工具。
D. Ben Knoble

-1

通过.git从项目中删除文件夹并通过IntelliJ与版本控制重新集成,我解决了类似的问题。注意:该.git文件夹是隐藏的。您可以使用在终端中查看它ls -a,然后使用删除它rm -rf .git


那就是他在步骤1中所做的事情:rm -rf .git吗?
晚上

-1

为此,请使用Shallow Clone命令git clone --depth 1 URL-仅克隆存储库的当前HEAD


-2

要从git中删除最后一次提交,您只需运行

git reset --hard HEAD^ 

如果要从顶部删除多个提交,则可以运行

git reset --hard HEAD~2 

删除最后两个提交。您可以增加数量以删除更多提交。

更多信息在这里。

这里的Git tutoturial提供了有关如何清除存储库的帮助:

您要从历史记录中删除文件并将其添加到.gitignore中,以确保不会意外重新提交该文件。对于我们的示例,我们将从GitHub gem存储库中删除Rakefile。

git clone https://github.com/defunkt/github-gem.git

cd github-gem

git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch Rakefile' \
  --prune-empty --tag-name-filter cat -- --all

现在我们已经从历史记录中删除了文件,让我们确保我们不会意外地再次提交它。

echo "Rakefile" >> .gitignore

git add .gitignore

git commit -m "Add Rakefile to .gitignore"

如果对存储库的状态感到满意,则需要强制推送更改以覆盖远程存储库。

git push origin master --force

6
从存储库中删除文件或提交与问题完全无关(该问题要求删除历史记录,这是完全不同的事情)。OP希望获得干净的历史记录,但希望保留存储库的当前状态。
维克多·施罗德

这不会产生问题中要求的结果。您将保留最后一次提交后放弃所有更改,此后将丢失所有更改,但是问题要求保留当前文件和删除历史记录。
TuncayGöncüoğlu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.