Ich übernehme keine Haftung für irgendwelche Schäden, die im Zusammenhang mit diesem Programms entstehen können. Dabei spielt keine Rolle, was die Schäden hervorrief, ob unsachgemäße Verwendung, System-Inkompatiblitäten, Programmierfehler meinerseits, nachträglich vorgenommene Veränderungen, oder was auch immer. Ich garantiere lediglich, willentlich keinen Schad-Code implementiert zu haben.
Was macht Sync
Mit Sync kann man Datei-Ordner "spiegeln" - also bewirken, dass ihr Inhalt identisch ist.
Man kann das auf sehr große Systeme anwenden, etwa um ein Daten-Backup aller User-Daten eines Systems auf eine externe Festplatte zu schubsen. Oder man synchronisiert sämtliche Programmier-Projekte zwischen Desktop und Laptop. Oder zum Rechner umziehen.
Man auch kleine "Repositories" anlegen, mit nur ein paar Projekten darin, etwa auf einem Usb-Stick.
Das Back-End: RoboCopy
Das Back-End dahinter ist übrigens vergleichsweise einfach, es ist das zu Windows gehörende Kommandozeilen-Tool "RoboCopy", und mein Proggi erstellt vor allem ein paar Kommandozeilen, mit denen dieses Tool dann abgefahren wird.
Also vom Prinzip her ist dieses ein weiteres "YARCGUI" - "Yet Another RoboCopy Graphical User Interface" - mit sehr anderem, auf "Repository" ausgerichteten Oberflächen-Konzept. Das andere YarcGui kann ja beliebige RoboCopy-Ausführungs-Pläne erstellen, und auch sowas ähnliches wie ein Repository (halt ohne mergen).
Jdfs. bei mir bedeutet "Repository", dass man eine
*.syn
- Datei in einem Ordner liegen hat, in der die zu synchronisierenden Ordner gespiegelt werden sollen. Dabei kann ein Repository mehrere Clients unterstützen, also man legt fest:"Repository-
BliBlaBlu
wird auf Client1 mit C:\Progs\BliBlaBlu
synchronisiert und auf Client2 mit D:\OllerKram\HupfHupf
".Praktisch aussehen tut sowas so:
Links das Repository - dort die Pfade sind relativ zum Repository-Folder - daher kürzer und v.a. ohne Laufwerk.
Mittig der "Current Client" - das ist der aktuelle Rechner, dessen System synchronisiert werden kann. Der andere Client, "Client2" kann nicht bearbeitet werden, denn jenes Dateisystem liegt ja auf einem anneren Rechner.
Die Sync-Operationen Push, Pull, Merge
- Push erstellt im Repository den "Spiegel" des eigenen Systems. Alles abweichende wird entfernt, neu hinzugekommenes hinzugefügt, veraltetes überschrieben.
- Pull ist derselbe Vorgang andersrum, also das Repository erstellt einen Spiegel im Client.
- Merge ist modifiziertes Pull, als "Reparatur"-Funktion, wenn man was verdaddelt hat.
In so einem Fall kann zB Client2 seinen neuesten Stand pushen, und Client1 pullt den nicht, sondern merget.
Ein Merge ist ein Pull, nur werden dabei keine überflüssigen Dateien/Ordner gelöscht oder überschrieben. Sondern bei Namens-Gleichheit kriegt die Repository-Datei einen ".mergeConflict" - Dateinamens-Zusatz, bevor sie kopiert wird.
Der User muss dann selbst zusehen, wie er den Konflikt im Einzelnen auflöst. Auf jeden Fall hat er nach einem Merge u.U. erhebliche Mengen neues Material in seinem System - zunächstmal unnützes Zeug, denn sein vorheriges System besteht ja vollkommen unangetastet weiter wie zuvor - das neue ist in keiner Weise in die System-Funktionalität integriert.
Es ist auch bischen Sicherheit eingebaut, um Merge-Konflikte zu vermeiden. Zwei einfache Regeln:
- Es darf nur der Client pushen, der zuvor als letzter gepullt hat.
Denn nur wer gepullt hat, hat die neueste Version vom Repository geladen. - Andere Clients können nur pullen, wenn vorher gepusht wurde.
Denn nur wenn sie gepusht wurde, liegt die neueste Version im Repository.
Diese Regeln gewährleisten so leidlich, dass die neueste Version nur auf einem Client ist, und auf keinem anderen. Sie können allerdings nicht davor schützen, dass jmd. mit dem einen Client pullt, dann aber auf dem anderen Client weiter-arbeitet (s.o. - Merge-Konflikt)
Getting Started
- ins Projekte-Verzeichnis entpacken, im VisualStudio öffnen und als Release kompilieren. Als Release ist wichtig, denn die Debug-Version lädt nur ein Test-Repository.
- die
Empty.syn
- Datei in einen Ordner zB auf die externe Festplatte kopieren (oder wo immer das Repository einzurichten ist) - und sinnig umbenennen. - die
Empty.syn
mit Doppelklick öffnen. Da erscheint ein Dialog, über den man das Programm erstmal suchen muss, mit dem*.syn
- Dateien in Zukunft zu öffnen sind - hier dieSync.Exe
suchen, die man ja grad in 1) sich zurecht-kompiliert hat - Aussm Explorer vom Client die Ordner, die man synchronisieren möchte, auf den linken (noch leeren) Treeview ziehen. Dabei auch im weiteren nicht auf schon vorhandene Nodes droppen, sondern in die leere Fläche unter den Nodes.
Das generiert sowohl im CurrentClient als auch im Repository einen Treeview, bei dem die kindlosen Knoten (die "Leafs") grün gekennzeichnet sind. - Im Repository-Treeview kann man per KontextMenü
Delete (Save Childs)
nach Belieben Nicht-Leaf-Nodes entfernen - um die Ordner-Struktur des Repositories zu vereinfachen. Leafs-Nodes kann man übrigens gar nicht entfernen, denn ein Leaf im Repository-Tree bedeutet ja einen Kopier-Auftrag, und ist mit einem Client-Leaf verbunden. - Im Client-Tree hingegen lautet das KontextMenü
Delete Branch
, und kann nicht nur einzelne Leafs weghauen, sondern entfernt gleich den ganzen Zweig (ggfs. auch mit mehreren Leafs). Denn ein Zweig ohne Blatt (Kopier-Auftrag) ergäbe zur Synchronisierung keinen Sinn. - Jo, dann kann man erstmalig pushen, dass auch was drinne ist im Repository. Der erste Push dauert übrigens besonders lange (vlt. 5min/GByte). Wenn später nur geänderte Dateien kopiert werden gehts zackiger
- Nun kann man auch einen 2. Client anschließen ans Repository - also einen anderen Computer. Dazu zieht man von dessen Dateisystem Ordner aufs Repository - nun aber genau auf jeweils ein grün markiertes Leaf. Dadurch wird im Repository kein neuer Ordner generiert, sondern des neuen Clients Ordner verbindet sich mit dem bestehenden Repository-Leaf zu einem Kopier-Auftrag für diesen Client.
- Nochmal Bildle gucken: zB.
Repository.Client12 (2)
ist verbunden mitClient1.Client12 (2)
undClient2.Client23 (2)
. Also auf verschiedenen PCs haben die Ordner durchaus verschiedene Namen - v.a. liegen sie meist wohl in unterschiedlichen Pfaden.
Bei dem Projekt entstand eine recht umfassende, leistungsfähige, flexible, resourcensparende Node-Infrastruktur:
Kern ist die
Node(Of TNode)
-ClassWenn man das Bildle genau anschaut, erklärt sich glaub jeder ihrer 23 Member von selbst. Kurz zur
Type
-Property: die ist Datentyp NodeType
- also nebenstehende Enumeration: TopLevel, Inner, Leaf
- auch selbsterklärend, oder?Und die
Tree
-Property, die ist vom Typ Tree(Of TNode)
, und das ist eine weitere wesentliche Klasse: Dort kann man nämlich Member anlegen, die für alle Nodes eines Trees gleichermaßen relevant sind, zB eine SelectedNode
-Property ist bereits drin. Aber ich hab in Tree-erbenden Klassen auch komplette KontextMenüs angelegt, und im RepTree
noch zusätzlich zusätzlich die ganze Drag-n-Drop-Verarbeitung (Rep-Node/-Tree
sind Viewmodel des Repository-Baums).Node(Of T)
kümmert sich auch um Daten-Konsistenz: Etwa bei der InsertItem()
-Überschreibung trägt ein Node sich selbst als Parent ein im zu insertenden ChildNode.Die Resourcen-Effizienz besteht v.a. darin, dass ein Node nur für
Name
und Parent
wirklich ein individuelles Feld hinterlegt hat. Die anderen Sachen holt er sich immer gschwind vom Root-Node, welches ist der einzige Node, der einen wirklichen Verweis auf den Tree(of TNode)
hat. ZB. der IsSelected
-Property hinterliegt kein Boolean
-Feld, sondern um Node.IsSelected
zu ermitteln (oder zu setzen) rennt er intern von Parent zu Parent zum Root, holt sich den Tree, und wenn er selbst des Trees SelectedNode
ist, dann ist IsSelected
wohl True
.MenuTree - eine Anwendung der Node-Infrastruktur
Ich beerbe
Node(Of TNode)
ja 5 mal, um 1) SortedNode
zu erhalten, 2) SyncNode
mit Gemeinsamkeiten von 3) ClientNode
und 4) RepNode
, und letztere halt mit genau den Unterschiedlichkeiten.Und von 5) -
MenuTree
zeige ich mal die praktische Anwendung:VB.NET-Quellcode
- Public Class MainModel : Inherits MainModelBase(Of MainModel)
- Public Class Menu : Inherits MenuTree
- Public Shared _Load, _Save, _File, _Push, Pu_ll, _Merge As String
- Public Shared ExitNoSave As String = "E_xit Without Saving Changes", SyncOptions As String = "_Sync-Options"
- Public Sub New()
- SubMenu(_File, _Load, _Save, ExitNoSave)
- End Sub
- End Class
- Public WithEvents MainMenu As New Menu
- Private Sub MainMenu_Execute(sender As Object, e As ExecuteEventargs) Handles MainMenu.Execute
- Select Case e.ID
- Case Menu.ExitNoSave : WithoutEvents.Create(_MainWindow).Close() 'bypass _MainWindow_Closing()
- Case Menu._Load : Reload()
- Case Menu._Save : Save()
- Case Menu._Push : LaunchSync(SyncMode.Push)
- Case Menu.Pu_ll : LaunchSync(SyncMode.Pull)
- Case Menu._Merge : LaunchSync(SyncMode.Merge)
- Case Menu.SyncOptions : MessageBox.Show(My.Settings.SyncOptions, "Synchronisize-Options")
- End Select
- End Sub
Menu
(Zeilen #3 - #9), von MenuTree
erbend, mit lauter Shared
String-Membern.In
Sub New
der BasisKlasse (hier nicht gezeigt) werden per Reflection daraus MenuNode
s gebastelt, an die Xaml ein Menü binden kann, Template dran und fertig - ein für allemal. (Xaml.Menu
ist ja ein ItemsControl, und kann baumartige Strukturen anzeigen - prinzipiell wie Treeview
).Jdfs. im oben gezeigten
Menu.New()
, zeile #7, wird zusätzlich die MenuTree-Methode SubMenu()
aufgerufen, die die MenuNodes _Load, _Save, ExitNoSave
zu SubMenuItem des _File
-MenuItems macht:Ich kann also im
Mainmodel
das Menü gestalten und umbauen wie wolle, ohne am Xaml rumzufummeln.Und die vielen Shared Strings definieren einerseits die Beschriftungen der MenuItems, fungieren andererseits aber auch als ein Enum, anhand dessen ich in
MainMenu_Execute()
die Klickse den richtigen Methoden-Aufrufen zuordnen kann. Nu interessieren täte mich, ob ich der einzige bleibe, der dieses Tool nutzt, ob dem einen oder anderen meine Ausführungen was gebracht haben, oder ob mein Code jmd. ein bischen inspiriert hat.
Und ggfs. auch Fragen, oder gar BugReports.
Aber ich sage gleich: Es ist nicht idiotensicher. Wenn ihr den BilderOrdner des einen Clients mit den ProgrammierProjekten des anderen synchronisiert, sind hinterher entweder die Bilder weg oder die Programmier-Projekte
Edit: Lizenz: Beerware
Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „ErfinderDesRades“ ()