fredag den 17. april 2009

Git har ingen rename, og er stolt af det

Jeg sidder lige og fjoller rundt med mit seneste for-sjov projekt, og i den forbindelse besluttede jeg mig for at ændre på pakkestrukturen. Det er et Scala projekt, og derfor er sammenhængen imellem filnavne og klasse-/pakkenavne mere en konvention end en regel - men dels kan jeg lide den, og dels så skal man kun bryde konventioner, når man har rigtigt gode grund til det - så det endte med, at jeg også fysisk flyttede rundt på filerne.

Som tidligere nævnt her, så bruger jeg Git til versionskontrol, og da jeg ikke lige finde en oplagt måde at fortælle Git om, at det var en rename (og altså ikke en række sletning og tilføjelser, som det ellers godt kunne ligne), så tænkte jeg, at det kunne være lige meget - projektet er jo i sin spæde opstart, så tabet af historik er bestemt til at overse. Så jeg committede det bare, som det var.

Stor var min overraskelse, da jeg nu ser mit commit beskrevet af Git bl.a. på flg. måde:


Wow! Git har altså opdaget, at der var tale om en rename udfra det forhold, at filerne ligner hinanden; og den har ved samme lejlighed set igennem fingre med, at de ikke var helt ens (pakkenavnet var jo netop opdateret).

"Snedigt", tænker jeg, "den har regnet det ud i forbindelse committet", men så er det, at jeg kigger på commithistorikken på git-hub: der står det som sletninger og tilføjelser (se evt. her). Git-hub er altså ikke nær som snedig som min lokale Git. Men alt er ikke tabt, for under "blame" kan man se, at også git-hub kan spore ændringer på tværs af renames (se evt. her). Det gjorde så lige min antagelse om, at det var noget, som blev regnet ud på committidspunktet, ganske usandsynlig.

Derfor graver jeg lidt mere i det, og det viser sig sandelig, at det er korrekt - der er intet "rename" begreb i Git; og det er en design-feature. Man kan se her, hvordan Linus Torvalds argumenterer for, at man skal tracke indhold fremfor identitet - og her, hvor han illustrerer en yderligere fordel ved konceptet med et eksempel om en fil, der er blevet splittet op i to (begge links har jeg fundet på Wikipedia artikel om Git - søg efter "rename" på siden for nemt at finde det relevante afsnit).

Hvis det virker, så er det altså smart - og det ser ud til at virke! Det virker dog klart bedste, hvis man har disciplin nok til at holde sine refactorings for sig selv, så de ikke bliver blandet sammen med øvrige ændringer ... og det er i grunden heller ikke så dumt.

2 kommentarer:

Unknown sagde ...

Git sammenkæder altså historik for den slettede fil med historikken for den tilføjede fil p.b.a. indholdet i de to filer. Men hvor ens skal de to filer være for at GIT bedømmer at det er en rename operation og ikke en slet efterfulgt at en tilføj-operation? 90% lighed? 87% lighed? Jeg ville synes det var svært at vide hvor man skulle slå stregen. På den anden side forstår jeg også godt Linus argument om at CVS lignende systemer forsøger at skabe en perfekt verden og på den bekostning skaber en unødig kompleksitet.

Unknown sagde ...

Casper, du har helt ret i, at det i bedste fald må være en heuristik. Det korte svar er 50% (bevaret af den mindste fil), men du kan læse meget mere her.

Hvis du ikke har noget imod lidt kodenørderi, så kig i f.eks. diffcore-rename.c og diffcore.h, som kan findes f.eks. her.