19

私の組織は、githubを使用してソフトウェアのオープンソースバージョンをリリースする準備をしていますが、これに取り組む最善の方法がわかりません。

masterreleaseの2つのブランチがあり、masterにはリリースしないことを決定した独自のコンポーネントがいくつか含まれており、releaseには配布したいクリーンアップされたバージョンが含まれています。問題は、リリースブランチをgithubにプッシュするだけで、リビジョン履歴を調べることでプロプライエタリコンポーネントを取得できることです。

別のリポジトリを作成し、リリースのHEADをそこにコピーし、を実行して、git initそのリポジトリをgithubにプッシュすることを検討していました。ただし、将来的にマスターからリリースに特定のパッチをチェリーピックする機能を保持し、それらの変更をgithubにプッシュしたいと考えています。

2つの別々のリポジトリを維持せずにこれを行う方法はありますか?

ありがとう!

アップデート:

もう少し具体的に言うと、これは、現時点でのコミット履歴のようなものです。

--- o - o - o - o - f - o - o - f - master
             \
              c - c - c - c - c - c - c - REL - f - f

「o」はマスターのプロプライエタリブランチのコミットであり、「c」は公開されるべきではないものを削除するコミットです(多くの場合、ファイル全体を削除するのではなく、プロプライエタリコンポーネントに依存しないように既存のファイルを作り直します)。リリースにも適用されるマスターの修正であるため、厳選されています。RELは、安全に公開できると思われるコードのタグ付きバージョンであり、履歴はまったくありません(RELタグの前にすべての専有資料が削除されたわけではないため、リリースブランチの以前のバージョンでも)。

4

2 に答える 2

17

ベンジャクソンの答えはすでに一般的な考えをカバーしていますが、ここで最終的な目標についていくつかのメモ(コメントの価値以上)を追加したいと思います。

2つのブランチを簡単に作成できます。1つは完全にクリーンな(プライベートファイルなし)履歴を持ち、もう1つは完全な(プライベートファイルを含む)もので、コンテンツを適切に共有します。重要なのは、マージ方法に注意することです。過度に単純化された履歴は、次のようになります。

o - o - o - o - o - o - o (public)
 \       \           \   \
  x ----- x ----x---- x - x (private)

コミットは「oクリーン」なものであり、xいくつかの個人情報を含むも​​のです。パブリックからプライベートにマージする限り、何もリークすることなく、両方が必要なすべての共有コンテンツを持つことができます。ベンが言ったように、これには注意する必要があります-他の方法でマージすることはできません。それでも、回避することはかなり可能です-そして、あなたは自分自身をチェリーピッキングに制限する必要はありません。通常の目的のマージワークフローを使用できます。

もちろん、実際には、ワークフローはもう少し複雑になる可能性があります。独自のブランチでトピック(機能/バグ修正)を開発し、それをパブリックバージョンとプライベートバージョンの両方にマージすることができます。たまにさくらんぼを選ぶこともできます。実際には、プライベートをパブリックにマージすることを除いて、何でもあります。

フィルターブランチ

したがって、現在の問題は、リポジトリをこの状態にすることです。残念ながら、これはかなり注意が必要です。プライベートファイルとパブリックファイルの両方にアクセスするコミットがいくつか存在すると仮定すると、最も簡単な方法はfilter-branch、パブリック(クリーン)バージョンを作成するために使用することだと思います。

git branch public master   # create the public branch from current master
git filter-branch --tree-filter ... -- public    # filter it (remove private files with a tree filter)

次に、プライベートコンテンツのみを含む一時的なプライベート専用ブランチを作成します。

git branch private-temp master
git filter-branch --tree-filter ... -- private-temp    # remove public files

そして最後に、プライベートブランチを作成します。完全なバージョンが1つしかない場合は、次のように1回マージするだけです。

git branch private private-temp
git merge public

これにより、マージが1つだけの履歴が得られます。

o - o - o - o - o - o - o - o - o - o (public)
                                     \
  x -- x -- x -- x -- x -- x -- x --- x (private)

注:ここには2つの別々のルートコミットがあります。それは少し奇妙です。それを避けたい場合git rebase --root --onto <SHA1>は、private-tempブランチ全体をpublicブランチの祖先に移植するために使用できます。

いくつかの中間の完全なバージョンが必要な場合は、まったく同じことを行うことができます。あちこちで停止して、マージしてリベースします。

git checkout -b private <private-SHA1>  # use the SHA1 of the first ancestor of private-temp
                                        # you want to merge something from public into
git merge <public-SHA1>           # merge a corresponding commit of the public branch
git rebase private private-temp   # rebase private-temp to include the merge
git checkout private
git merge <private-SHA1>          # use the next SHA1 on private-temp you want to merge into
                                  # this is a fast-forward merge
git merge <public-SHA1>           # merge something from public
git rebase private private-temp   # and so on and so on...

これにより、次のような履歴が得られます。

o - o - o - o - o - o - o - o - o - o (public)
      \              \               \
  x -- x -- x -- x -- x -- x -- x --- x (private)

繰り返しになりますが、共通の祖先を持たせたい場合は、イニシャルgit rebase --root --onto ...を実行して開始できます。

注:履歴にすでにマージがある場合は-p、リベースでオプションを使用してマージを保持することをお勧めします。

ごまかす

編集:履歴の作り直しが本当に手に負えないことが判明した場合は、いつでも完全にそれをごまかすことができます:すでに持っている同じルートコミットの上に、履歴全体を1つのコミットに押しつぶします。このようなもの:

git checkout public
git reset --soft <root SHA1>
git commit

したがって、これで終わります:

o - A' (public)
 \
  o - x - o - x - X - A (public@{1}, the previous position of public)
               \
                x - x (private)

ここでA、とA'はまったく同じコンテンツを含みX、パブリックブランチからすべてのプライベートコンテンツを削除したコミットです。

この時点で、パブリックからプライベートへの単一のマージを実行できます。それ以降は、回答の上部で説明したワークフローに従います。

git checkout private
git merge -s ours public

-s oursgitに「ours」マージ戦略を使用するように指示します。これは、すべてのコンテンツをプライベートブランチとまったく同じように保持し、パブリックブランチをマージしたことを示すマージコミットを記録するだけであることを意味します。これにより、gitがこれらの「プライベートの削除」Xの変更をコミットからプライベートブランチに適用することを防ぎます。

ルートコミットに個人情報が含まれている場合は、現在のルートコミットの上に一度コミットするのではなく、新しいルートコミットを作成することをお勧めします。

于 2010-10-26T05:00:18.253 に答える
5

コミットのSHAは、親SHA、コミットテキスト、およびファイルツリーのSHAを含むコミットBLOBに基づいています。ツリーには、ツリー内のすべてのBLOBのSHAが含まれています。したがって、特定のコミットは、そのリビジョン内のすべてと、空のリポジトリに戻るすべての親リビジョンに依存します。リリースしたくないファイルを含むバージョンから派生したコミットがある場合(間接的には関係ありません)、そのブランチをリリースしたくありません。

git filter-branchリポジトリから機密ファイルを削除する方法についての最初の例。これは、代替履歴を作成することによってこれを行います(すべてのツリーとコミットを書き換えます)。私の答えの最初の部分を理解すれば、なぜこれが真実でなければならないのかがわかります。

filter-branchコマンドを実行して、「クリーンな」コミットから新しいコミットを作成できるはずです。履歴はやや奇妙になります(古いバージョンは現在不完全であるか壊れているため、ビルドされない可能性があります)。これにより、リポジトリ内の既存のブランチやBLOBが破壊されることはありません。ファイルblobを共有するが、ツリーやコミットは共有しないすべての新しい(並列)ものを作成します。参照していないオブジェクトを公開せずに、そのブランチを安全にプッシュできるはずです(ブランチをプッシュすると、そのブランチによって指定されたSHAとその依存関係のみがプッシュされます)。ただし、これはやや危険です。git merge「クリーンな」ブランチに移動すると、「プライベート」なブランチやオブジェクトをドラッグしてしまう可能性があります。フック(コミットまたはプッシュトリガー)を使用して、プライベートファイルがエスケープされていないことを再確認することをお勧めします。

于 2010-10-26T04:20:28.753 に答える