Ben Jacksons Antwort deckt bereits die allgemeine Idee ab, aber ich möchte ein paar Anmerkungen (mehr als einen Kommentar wert) über das letztendliche Ziel hinzufügen.
Sie können ganz einfach zwei Zweige haben, einen mit einem völlig sauberen Verlauf (ohne private Dateien) und einen vollständigen (mit den privaten Dateien), und den Inhalt entsprechend teilen. Der Schlüssel dazu ist, dass Sie sorgfältig darauf achten, wie Sie zusammenführen. Ein vereinfachter Verlauf könnte etwa so aussehen:
o - o - o - o - o - o - o (public)
\ \ \ \
x ----- x ----x---- x - x (private)
El o
Übertragungen sind die "sauberen", und die x
sind diejenigen, die einige private Informationen enthalten. Solange Sie von öffentlich auf privat zusammenführen, können beide alle gewünschten gemeinsamen Inhalte haben, ohne dass jemals etwas durchsickert. Wie Ben schon sagte, muss man dabei vorsichtig sein - man kann nie in die andere Richtung fusionieren. Dennoch ist es durchaus möglich, dies zu vermeiden - und Sie müssen sich nicht auf die Rosinenpickerei beschränken. Sie können Ihren normalen gewünschten Zusammenführungs-Workflow verwenden.
In der Realität könnte Ihr Arbeitsablauf natürlich etwas komplexer sein. Sie könnten ein Thema (Feature/Bugfix) in einem eigenen Zweig entwickeln und es dann sowohl in die öffentliche als auch in die private Version einfließen lassen. Sie könnten sogar ab und zu eine Auswahl treffen. Wirklich alles ist möglich, mit der Ausnahme, dass die private Version in die öffentliche Version überführt wird.
filtern-verzweigen
Ihr Problem besteht also im Moment einfach darin, Ihr Repository in diesen Zustand zu bringen. Leider kann das ziemlich schwierig sein. Unter der Annahme, dass einige Commits existieren, die sowohl private als auch öffentliche Dateien berühren, glaube ich, dass die einfachste Methode darin besteht, Folgendes zu verwenden filter-branch
um die öffentliche (saubere) Version zu erstellen:
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)
erstellen Sie dann einen temporären, privaten Zweig, der nur den privaten Inhalt enthält:
git branch private-temp master
git filter-branch --tree-filter ... -- private-temp # remove public files
Und schließlich erstellen Sie den privaten Zweig. Wenn Sie damit einverstanden sind, nur eine vollständige Version zu haben, können Sie sie einfach einmal zusammenführen:
git branch private private-temp
git merge public
So erhalten Sie eine Geschichte mit nur einer Verschmelzung:
o - o - o - o - o - o - o - o - o - o (public)
\
x -- x -- x -- x -- x -- x -- x --- x (private)
Hinweis: Es gibt hier zwei separate Root-Commits. Das ist ein wenig seltsam; wenn Sie das vermeiden wollen, können Sie git rebase --root --onto <SHA1>
um den gesamten privat-temp-Zweig auf einen Vorgänger des öffentlichen Zweigs zu verpflanzen.
Wenn Sie einige vollständige Zwischenversionen haben möchten, können Sie genau dasselbe tun, nur hier und da anhalten, um sie zusammenzuführen und neu zu basen:
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...
Damit erhalten Sie einen Verlauf in etwa wie folgt:
o - o - o - o - o - o - o - o - o - o (public)
\ \ \
x -- x -- x -- x -- x -- x -- x --- x (private)
Auch hier können Sie, wenn Sie möchten, dass sie einen gemeinsamen Vorfahren haben, eine erste git rebase --root --onto ...
um loszulegen.
Hinweis: Wenn Sie bereits Zusammenführungen in Ihrem Verlauf haben, sollten Sie die Option -p
Option bei allen Rebases, um die Zusammenführungen zu erhalten.
so tun, als ob
Edit: Wenn sich die Überarbeitung der Historie wirklich als schwierig erweist, können Sie es auch ganz einfach machen: Drücken Sie die gesamte Historie auf einen Commit zusammen, zusätzlich zu demselben Root-Commit, den Sie bereits haben. Etwas wie dies:
git checkout public
git reset --soft <root SHA1>
git commit
Das Ergebnis ist also Folgendes:
o - A' (public)
\
o - x - o - x - X - A (public@{1}, the previous position of public)
\
x - x (private)
donde A
y A'
genau den gleichen Inhalt haben und X
ist die Übergabe, bei der Sie alle privaten Inhalte aus dem öffentlichen Zweig entfernt haben.
An diesem Punkt können Sie eine einmalige Zusammenführung von öffentlich und privat durchführen und von da an den Arbeitsablauf befolgen, den ich oben in der Antwort beschrieben habe:
git checkout private
git merge -s ours public
El -s ours
weist git an, die Merge-Strategie "ours" zu verwenden. Das bedeutet, dass es alle Inhalte genau so beibehält, wie sie im privaten Zweig sind, und lediglich einen Merge-Commit aufzeichnet, der anzeigt, dass Sie den öffentlichen Zweig mit diesem zusammengeführt haben. Dadurch wird verhindert, dass Git jemals die "remove private"-Änderungen aus dem Commit X
an die Privatwirtschaft.
Wenn der Root-Commit private Informationen enthält, sollten Sie einen neuen Root-Commit erstellen, anstatt den aktuellen Commit einmal zu überschreiben.