現在のコミットをGitリポジトリの唯一の(初期)コミットにしますか?


664

現在、ローカルのGitリポジトリーがあり、それをGithubリポジトリーにプッシュしています。

ローカルリポジトリには〜10のコミットがあり、Githubリポジトリはこれの同期複製です。

ローカルGitリポジトリからすべてのバージョン履歴を削除して、リポジトリの現在の内容が唯一のコミットとして表示されるようにします(したがって、リポジトリ内の古いバージョンのファイルは保存されません)。

次に、これらの変更をGithubにプッシュしたいと思います。

私はGitリベースを調査しましたが、これは特定のバージョンを削除するのにより適しているようです。もう1つの潜在的な解決策は、ローカルリポジトリを削除して新しいリポジトリを作成することです。

ETA:追跡されていない特定のディレクトリ/ファイルがあります-可能であれば、これらのファイルの追跡を解除したいと思います。


6
stackoverflow.com/questions/435646/…も参照してください(「Gitリポジトリの最初の2つのコミットを組み合わせるにはどうすればよいですか?」)
Anonymoose


回答:


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がそれらを処理する必要があると思いますよね?
Fred Foo

48
前に.git / configを保存し、後で復元します。
lalebarde 14

@lalebarde後で.git / configを復元する場合git commit -m "Initial commit"、そのgit remote add ...部分が既に構成にあると想定して、その部分をスキップして、プッシュに直接進むことができます。それは私のために働いた。
Buttle Butkus、2015年

24
機密データを削除しようとする場合は、このことに注意してください。新しくプッシュされたマスターブランチにコミットが1つしか存在しないと誤解を招きます。履歴はまだ存在し、そのブランチからはアクセスできません。たとえば、古いコミットを指すタグがある場合、これらのコミットにアクセスできます。実際、少しgit fooを使用している人は、このgit pushを実行した後でも、GitHubリポジトリからすべての履歴を復元できると確信しています。他のブランチまたはタグがある場合、それらは実行されません。多くのgit fooも必要です。
Robert Muil、2016年

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最後の操作としてa を追加するだけで、新鮮なレポで太陽が再び輝きます!:)
gru

2
これは古いコミットを保持しませんか?
ブラッド

4
@JonePolvora git fetch; Gitは--hard起源/マスターリセットstackoverflow.com/questions/4785107/...
エコーを

5
これを行った後、レポは空き領域になりますか?
Inuart 2014

8
@JasonGoemaatの提案を回答の最後の行として追加する必要があると思います。git gc --aggressive --prune all歴史を失うという全体のポイントがなければ、見逃されるでしょう。
TuncayGöncüoğlu2016年

93

これは私の好むアプローチです:

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

これにより、HEADのすべてを追加する1つのコミットで新しいブランチが作成されます。それは他に何も変更しないので、それは完全に安全です。


3
ベストアプローチ!クリアして、作業を行います。さらに、ブランチの名前を「master」から「local-work」に、「new_branch_name」を「master」に変更しました。マスターで、次の操作を行います。git -m local-changes git branch -m local-changes git checkout new_branch_name git branch -m master <
Valtoni Boaventura

これは本当に短くてスマートに見えますが、私がまだ理解していない、またはまだ見たことがないのはHEAD ^ {tree}だけです、誰かが説明できますか?それとは別に、これを「特定のコミットから新しいブランチを作成し、___からの特定のコミットメッセージで新しいコミットオブジェクトを作成することによって作成する」と読みました
TomKeegasi

3
gitリファレンス構文に関する質問への回答を探す最も確実な場所は、git-rev-parseドキュメントにあります。ここで起こっていることはgit-commit-tree、ツリーへの参照(リポジトリのスナップショット)が必要HEADですが、修正です。コミットに関連付けられているツリーを見つけるには、<rev>^{<type>}フォームを使用します。
dan_waterworth 2017

いい答えだ。うまくいきます。最後に言うgit push --force <remote> new_branch_name:<remote-branch>
フェリペアルバレス

31

他のオプションは、多くのコミットがある場合、多くの作業になる可能性がありますが、インタラクティブなリベースです(gitバージョンが> = 1.7.12であると想定しています)。git rebase --root -i

エディターにコミットのリストが表示されたら:

  • 最初のコミットで「pick」を「reword」に変更します
  • 他のすべてのコミットで「選択」を「修正」に変更

保存して閉じます。Gitはリベースを開始します。

最後に、その後に来たすべてのものを組み合わせた新しいルートコミットができます。

利点は、リポジトリを削除する必要がないことと、考え直しがある場合は常にフォールバックがあることです。

本当に履歴を削除したい場合は、マスターをこのコミットにリセットし、他のすべてのブランチを削除します。


リベースが完了した後、私はプッシュできません:error: failed to push some refs to
Begueradj

@Begueradjリベースしたブランチをすでにプッシュしている場合は、pushを強制する必要がありますgit push --force-with-lease。--forceより破壊的ではないため、force-with-leaseが使用されます。
カール

19

ラースマンズの提案された方法の変種:

untrackfilesリストを保存します。

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の回答を基にしたスクリプトです。マスターブランチだけでなく、すべてのブランチから履歴を削除する必要があります。

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 master、またはgit push --force-with-lease?明らかに後者の方が安全です(stackoverflow.com/questions/5509543/…を参照)
Shafique Jamal

@BenRichards。面白い。ブランチ名と一致するフォルダーを使用してこれをもう一度試し、テストしてから、回答を更新します。ありがとう。
Shafique Jamal

5

浅いクローンを使用できます(git> 1.9):

git clone --depth depth remote-url

さらに読む:http : //blogs.atlassian.com/2014/05/handle-big-repositories-git/


4
このようなクローンは、新しいリポジトリにプッシュできません。
Seweryn Niemiec 2016年

1
その制限を回避する方法を知っていると便利です。これを強制的にプッシュできない理由を誰かが説明できますか?
not2qubit

あなたの質問への答え:stackoverflow.com/questions/6900103/...
マティアスM

4

git filter-branch 主要な手術ツールです。

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

--parent-filterstdinの親を取得し、書き直された親をstdoutに出力する必要があります。unix trueは正常に終了し、何も出力しないので、親はありません。 @^!あるGitはの略記「頭がコミットではなく、任意のその親の」。次に、他のすべての参照を削除し、自由にプッシュします。


3

Githubリポジトリを削除して、新しいリポジトリを作成するだけです。はるかに速く、最も簡単で安全なアプローチ。結局のところ、シングルコミットのマスターブランチだけが必要な場合、受け入れられたソリューションでこれらのすべてのコマンドを実行するために何が必要ですか?


1
主なポイントの1つは、分岐元を確認できることです。
not2qubit

私はちょうどこれをやった、それは結構です
thanos.a '21 / 12/21

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
これをありがとう。ただ、FYI:各ブランチの履歴を殺すために、あなたのスクリプトは、いくつかの更新を使用することができます-それは、次のエラーを与える:git-hash: not foundSupport for <GIT_DIR>/info/grafts is deprecated
Shafiqueジャマル

1
@ShafiqueJamalさん、ありがとうございます。小さな「git-hash」スクリプトはgit log HEAD~${1:-0} -n1 --format=%H、ここではsam.aiki.info/b/git-hashです。すべてのスクリプトを1つのスクリプトにまとめて公開することをお勧めします。再び使用する場合、「移植片」に代わる新機能を使用してその方法を理解するかもしれません。
Sam Watkins、

2

ローカルGitリポジトリからすべてのバージョン履歴を削除して、リポジトリの現在の内容が唯一のコミットとして表示されるようにします(したがって、リポジトリ内の古いバージョンのファイルは保存されません)。

より概念的な答え:

タグ/ブランチ/参照が指していなければ、gitは古いコミットを自動的にガベージコレクションします。そのため、すべてのタグ/ブランチを削除し、任意のブランチに関連付けられた新しい孤立コミットを作成するだけです-慣例により、ブランチmasterはそのコミットを指すようにします。

古い、到達不能なコミットは、低レベルのgitコマンドで掘り起こさない限り、誰にも二度と表示されません。それで十分であれば、私はそこで立ち止まって、自動GCに必要なときにそれを実行させます。それらをすぐに取り除きたい場合は、git gc(おそらくで--aggressive --prune=all)使用できます。リモートgitリポジトリの場合、ファイルシステムへのシェルアクセスがない限り、強制する方法はありません。


@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ブランチの出力は通常、スクリプト作成にはあまり適していません。配管ツールを確認することをお勧めします。
D.ベンノーブル

-1

.gitプロジェクトからフォルダーを削除し、IntelliJによるバージョン管理と再統合することで、同様の問題を解決しました。注:.gitフォルダーは非表示です。でターミナルに表示しls -a、を使用して削除できますrm -rf .git


それは彼がステップ1でやっていることです:rm -rf .git?
2017

-1

そのためには、Shallow Cloneコマンドを使用しますgit clone --depth 1 URL-リポジトリの現在のHEADのみを複製します


-2

gitから最後のコミットを削除するには、単に実行することができます

git reset --hard HEAD^ 

上から複数のコミットを削除する場合は、実行できます

git reset --hard HEAD~2 

最後の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ğlu2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.