Wpis z mikrobloga

#programowanie #pasta #pastaprogramistyczna #git

Napisałem mały skrypcik... który robi mi

git commit
co sekundę, a wszystko to odpowiednio taguje :) Zapytacie po co?

Może uda się zrobić coś jak https://asciinema.org/, ale nie dla konsoli... ale dla całych IDE i kodu w repozotyrium :)

W skrócie, asciinema jest narzędziem pozwalającym "nagrać to co się dzieje w konsoli". To co jest fajne w tym, to fakt, że filmik tak powstały można w każdym momencie zatrzymać, a to co jest wyświetlone... po prostu skopiować :)


Pomyślałem sobie, że może dało by się zrobić coś podobnego, ale na takiej zasadzie, żeby nagrywać to co się dzieje, ale w dowolnym oknie z tekstem na systemie operacyjnym, w szczególności w ide. Kojarzyłem, że na windowsa dało się coś takiego zrobić.

SO: Reading from a text field in another application's window
Unix SO: Is there a WinSpy++ like tool for Linux/X/GTK/QT?

Niestety wnioski są takie, że ciężko było by zrobić coś uniwersalnego. Pomyślałem, że może w takim razie dałoby się coś zrobić na IDE ze stajni IntelliJ.

Wtem znalazłem wtyczkę Action Tracker. Sprawdziłem ją, oraz jej źródła. Okazało się, że wtyczka ta notuje tylko eventy... w stylu

23:13:15.105: mouse clicked
23:13:15.959: mouse clicked
23:13:17.142: mouse clicked
23:13:17.989: action 'Down' via Down
23:13:18.193: action 'Down' via Down
23:13:18.546: action 'Enter' via Enter
23:13:18.704: action 'Enter' via Enter
23:13:18.864: action 'Enter' via Enter
23:13:19.122: action 'Up' via Up
23:13:19.284: action 'Up' via Up
23:13:19.999: typed 't'
23:13:20.117: typed 'e'
23:13:20.256: typed 's'
23:13:20.327: typed 't'
Wniosek: by mieć nagranie w stylu asciinema, ale dla kodu z IDE, musiałbym najpierw jakoś wczytać stan wszystkich otwartych zakładek, a potem robić diffa, wg action loga. Bez sensu... i wtedy pomyślałem, że skoro diff... to czemu by tak nie zrobić tego starym dobrym gitem? :)

No dobra, ale będzie problem. Załóżmy, że nawet git to ładnie szybko obsłuży. Nieskończona pętla, która będzie robiła commity wszystkich zmian i tagowała odpowiednim timestampem.

Jakie problemy się pojawiły? Najpierw wbrew regułom postanowiłem zastanowić się nad pierdołą i sprawdzić: jaki tag chciałbym co sekundę nadawać.

Zakładając, że nagrywałbym filmik na YT, chciałbym by patrząc na czas na filmiku, ktoś mógł łatwo ściągnąć kod z danej sekundy. YT posługuje się tagiem czasowym:
https://www.youtube.com/watch?v=0Mg2deSkgoE&t=2m47s

jednak łatwo sprawdzić, że zamiast 2m47s równie dobrze może być wygodniejsze dla mnie 00h02m47s .

Pozostaje w skrypcie napisać prosty kod, który taki timestamp w takiej formie może mi generować. Okazało się, że w linuksie jest specjalna zmienna systemowa co się zowie $SECONDS, która przybiera wartość sekund, ile sekund temu skrypt został odpalony :) Proste:

echo "$(($ELAPSED_TIME/60))m$(($ELAPSED_TIME%60))s"

i już prawie jestem w domu. No już prawie, bo dalej był problem z leading zeros, więc zrobiłem wg tego i skorzystałem z notacji printfowej.

No dobra, drugi problem: zmiany na dysk nie są zapisywane od razu. Mam w zwyczaju dość często naciskać Ctrl+S, ale to nadal było by za rzadko. Przypomniałem sobie, że IntelliJ zapisuje samoczynnie kod dość często. Postanowiłem to sprawdzić i okazało się, że jest to opcja konfigurowalna :) Ustawiłem sobie, że kod będzie mi się zapisywał na dysku sekundę po tym jak przestanę pisać. Good enough :)

Problem kolejny: jeżeli będę commitował zmiany co sekundę, to praktycznie nigdy nie będę miał możliwości sam zrobić commita w jakimś logicznym momencie. Z tej perspektywy byłby burdel. Przypomniałem sobie wtedy, że przecież mogę pushować tagi do innego repozytorium, jednak po chwili zdałem sobie sprawę, że problemem nie jest to, gdzie je #!$%@?, tylko z jakiego working directory. Wtem @frax mnie oświecił oznajmiając mi przypadkiem, że jest fajne narzędzie do duplikowania working directory, co się zowie git-new-workdir.

Okazało się to dobrym pomysłem. Nie muszę mieć dwóch repozytoriów, będę miał jedno repozytorium i dwa working directory, które będą miały wspólne repo a poprzez to listę tagów. Pomysł zatem polega, by skrypcik tagujący co sekundę pracował na drugim working directory i commitował wszystko jak leci :)

problem kolejny: no dobra, ale working directory są różne, skąd wiec drugie WD ma wiedzieć jakie zmiany ma pierwsze? Teoretycznie mógłbym użyć stasha, który w tym przypadku też jest współdzielony. Stwierdziłem jednak, że to byłby spory bałagan, który w praktyce uniemożliwiałby mi używanie stash w głównym WD.

Pomyślałem, że może rozwiązaniem była by po prostu ciągła synchronizacja jakimś rsynciem, czy czymś. Jakoś tak wygooglałem, że mi wyszło, że do ciągłej synchronizacji lepszy od rsynca będzie lsyncd. Zapewnia ciągłą synchronizację i chodzi sobie jako daemon :)

no dobra...

Więc plan jest taki, tworzyć nowy workdir... pytanie tylko gdzie je tworzyć? W bliżniaczym katalogu? w tmp? Wtedy trzebabyło by się martwić o możliwe konflikty nazw z innymi już istniejącymi katalogami, poza tym mnożyła by się struktura katalogów. Pomyślałem, że lepiej by było to nowe working direcory ukryć... w jakimś miejscu blisko repozytorium... ale w miejscu, które sam git nie śledzi... i wyszło mi, że idealnym miejscem będzie katalog .git/ pierwotnego repozytorium :)

No dobra... tylko, że wtedy kopiując pliki za pomocą lsyncd... zrobi mi się pętla nieskończona, bo będę pliki kopiował do katalogu wewnątrz i każda nowa synchronizacja kopiowała by mi coraz głębszą strukturę katalogów. No ale... na pewno da się jakoś zrobić ignora :)

Całość w zaledwie 32 liniach basha: https://github.com/noisy/recordMyTyping

prosty skrypcik... a z 5h rozkminy :)

Podczas następnego #letscode postaram się to wypróbować :)
  • 4
@noisy: ciekawa sprawa, a takie pytanie, gdybyś zamiast korzystać z drugiego work-dir w IDE pracował na jednym branchu i do niego z palca commitował i pushował z odpowiednimi komentarzami, a skrypt stałby obok i korzystając z tych źródeł pushował na bieżąco do oddzielnego brancha ? Myślałeś o takim roziwązaniu ? Jest to możliwe ?
@zly_dzien: no taki właśnie jest zamiar :) Wszystko prawilnie ma być commitowane ręcznie, z dobrymi opisami, we właściwych momentach.

Jednak, jeżeli ktoś chciałby sobie spojrzeć na dokładnie taki sam kod, jak był w 5minucie i 43 sekundzie nagrania, to sobie może zrobić clona:

git clone --branch E01-00h05m43s git@github.com:usr/repo.git
Pozostaje przygotować jakąś stronkę, która będąc podzielona na części, u góry będzie wyświetlać film... a na dole aktualizować wszystkie pliki :)
@zly_dzien: [EDIT]:
doczytałem i teraz chyba dopiero zrozumiałem. główny normany branch będzie jeden. Problem w tym, że jeżeli bym chciał zrobić drugi branch np.

master-timestamped, to nie będę w stanie się w żaden sposób odnieść do konkretnej minuty nagrania. musiałbym tworzyć takich branchy setki:
master-timestamped-00m01s,
master-timestamped-00m02s, itd.

W takim przypadku lepiej do tego celu użyć tagów. W razie czego można je pushować do innego repozytorium, ale to już jest pozostawione