Jak na Git

V posledních pár měsících jsem se pral s verzovacím systémem Git. Teď už mám dojem, že jsem mu přišel na kloub, tak se chci podělit o to, jak se mi to povedlo. Snad to nezakřiknu.

Nakonec u mě zabral následující postup.

  1. Přečíst si knihu ProGit (anglicky nebo česky)
  2. Několik týdnu se pokoušet s Gitem pracovat, rvát si vlasy z hlavy, říkat si, že už jsem asi na ty počítače starý.
  3. Přečíst si knihu znovu a dosáhnout osvícení.

Rozhodně se tu nechystám Git popisovat, přečtěte si tu knihu. Jenom tu popíšu svůj mentální model toho, jak to celé pracuje. Musím vás varovat, že tento model se může lišit od reality, ale mě zatím pomáhá. Schválně budu ignorovat oblast změn (staging, index), budu se věnovat hlavně revizím, kolem kterých se všechno v Gitu točí.

Revize (alias snímek neboli commit) vznikne, když commitneme nějaké změny. Je důležité si uvědomit, že revize obsahuje obraz našeho projektu v okamžiku jejího vzniku. Takže to není popis rozdílů, ale opravdu snímek všech souborů tak jak v čase vytváření revize vypadaly. Interně to je samozřejmě pořešeno tak, aby to bylo rychlé a diskově nenáročné, navenek se ale Git opravdu tváří tak, že má hromadu snímků stavu projektu.

Každá taková revize má navíc jednoznačný identifikátor (hash) a také informaci o tom, které revize jí předcházely.

Toto už stačí, aby to všechno fungovalo. Máme hromadu revizí, které jsou uspořádány v jakémsi orientovaném grafu a my po tom grafu můžeme dle libosti skákat, dělat změny a vytvářet z nich nové revize. Abychom se v tom neztratili, mohou na jednotlivé revize ukazovat pojmenované identifikátory jako jsou větve a tagy. Je tu speciální identifikátor HEAD, který ukazuje na místo v grafu v které se právě nacházím. Všechny tyto identifikátory, ale nejsou nic jiného, než odkazy na revize.

Takže například kouzelný příkaz

git reset --hard <<revize>>

mi převede (téměř) všechny soubory v projektu do stavu v dané revizi a změní hodnotu odkazu HEAD. Tento příkaz si zapamatujte, ohromě se hodí, když se dostanete do potíží. Například když se vám nepovede merge a Git vám automaticky ty nepovedené změny commitne. Nemusíte mazat a naklonovat znovu celé úložištěm jako jsem to jednou v panice udělal já. Stačí se uklidnit, uvědomit si co je Git zač, najít číslo revize v které to bylo ještě v pořádku a vrátit se do ní. Docela bezpečné je vracet se do origin/master, to ukazuje na revizi, kde byl vrchol hlavní větve ve zdrojovém úložišti při poslední synchronizaci. Nepovedené změny sice někde v Gitu zůstanou hnít, ale to zas tak nevadí.

Toliko můj mentální model Gitu. Možná se ještě zmíním o tom, v čem se mi Git líbí. Zajímavé je například vyzobávání třešínek (cherrypick). To je funkce, která mi umožní vyzobnout si změny provedené v jedné revizi a aplikovat je na jinou revizi. Úžasná funkce, pokud potřebuje zároveň aplikovat jednu změnu do víc větví.

Mocná i když trochu nebezpečná zbraň je také přeskládání (rebase), které vám například umožní přehrát změny provedené v jedné větvi nad jinou větví. Dokonce vám to umožní přeházet pořadí revizí, slučovat revize dohromady, rozdělovat jednu revizi na víc atp. Ve skutečnosti se samozřejmě revize nemění, jenom se vytvoří nové, které se jen tváří jako ty původní s příslušnými změnami. Na přeskládání ale pozor, můžete tím naštvat své spolupracovníky, jejichž revize vycházejí z těch původních, nepřeskládaných.

Jinak mohu Git jen doporučit. Výsledek stojí za to, pokud přežijete dvojí přečtení jedné krátké knihy a čtrnáct dnů utrpení.

Using Jing for Relax NG validation

I just ant to jot down how to programmatically use Jing for Relax NG validation. It has a simple API but I could not find any description besides the JavaDoc. It took me some time to figure out how to use it.

If you want to validate a XML file, just use ValidationDriver. If you have a DOM Document or StAX source, just do the following

//we are using Relax NG compact format
SchemaReader schemaReader = CompactSchemaReader.getInstance();

//schema can be reused, it's thread safe
Schema schema = schemaReader.createSchema(ValidationDriver.fileInputSource(new File("your_schema.txt)), PropertyMap.EMPTY);

//can use different error handler here (try DraconianErrorHandler http://www.thaiopensource.com/relaxng/api/jing/com/thaiopensource/xml/sax/DraconianErrorHandler.html)
ErrorHandler seh = new ErrorHandlerImpl();
PropertyMapBuilder  builder = new PropertyMapBuilder();
builder.put(ValidateProperty.ERROR_HANDLER, seh);
		
//Validator is NOT thread safe
Validator validator = schema.createValidator(builder.toPropertyMap());
			
		
Source source = ...//your XML source

TransformerFactory.newInstance().newTransformer().transform(source, new SAXResult(validator.getContentHandler()));

And that’s it.

Visualizing Fork/Join

Last Friday I have participated on GPars Hackathon. Together with Jety we have picked the task “Catchy visual demos“. Neither of us had used Swing for several years and we are newbies in both Groovy and GPars. But Václav helped us to solve several Groovy mean tricks and at the end we managed to create really nice demo. It’s a visualization of Fork/Join based merge-sort. You can enjoy it here. You can really see how fork/join aka jsr166y works. Cool.

THE DEMO (alternatively you can download and execute this jar)

The source code can be downloaded here although it’s much less enjoyable than the application itself. I have to confess that I enjoyed it so much that I have rewritten it to Java and in fact that’s what you are really looking at. In Java it’s less elegant, but you do not have to download 5Mb of Groovy libraries. I also hoped that in Java it would run in sandbox without special permissions. Unfortunately you are not allowed to manipulate with threads in and untrusted app, so you have to trust my self-signed certificate. The Java source is available on GitHub.