W poprzednich częściach znęcałem się nad biblioteką ClrMd i zawartym w niej niedopatrzeniu – poważnym problemie z deadlockiem. Korzystając, że jest to Open Source dostępny na GitHubie, czuję się w obowiązku spróbować dostarczyć oficjalną poprawkę. A to świetna okazja do napisania artykuliku o tym, jak to formalnie przeprowadzić.
Mając namierzony błąd w ukochanym projekcie, zacząć musimy od rzeczy podstawowej - issue. Jeśli błąd zgłosił już ktoś inny, na razie nie musimy się tym przejmować. Ale jeśli jesteśmy “szczęściarzami” i pierwsi coś odkryliśmy, zanim przystąpimy do dalszych działań błąd taki najlepiej formalnie zgłosić.
Zgłoszenie issue
W tym celu w ramach repozytorium zakładamy Issue. W moim przypadku wygląda ono tak: https://github.com/Microsoft/clrmd/issues/44
Opisuję więc przede wszystkim kroki do zreprodukowania problemu oraz jak wygląda sam problem, z odrobiną formatowania Markdown:
## Steps to reproduce
Call following code from WPF main thread (e.g. as a Command):
```fsharp
use target = DataTarget.LoadCrashDump(@"path\to\file.dmp")
target.SymbolLocator.SymbolPath <- @"SRV*http://msdl.microsoft.com/download/symbols"
target.SymbolLocator.SymbolCache <- @"c:\symbols"
for version in target.ClrVersions do
let dacInfo = version.DacInfo
let runtime = version.CreateRuntime()
```
## Actual behavior
WPF application freezes on `version.CreateRuntime()` call because of the deadlock:
Utworzenie forka
Oczywiście aby móc, jak to się ładnie mówi, partycypować w oficjalnym repozytorium, musimy najpierw utworzyć tzw. forka tego repozytorium na nasze potrzeby i to na nim dokonywać będziemy wszelkich zmian. Jest to prosta operacja opisana w dokumentacji, do wykonania z poziomu interfejsu GitHuba.
Następnie owego forka musimy sobie lokalnie sklonować na komputer, by móc na nim popracować:
$ git clone https://github.com/kkokosa/clrmd.git
Być może tego się spodziewacie, ale po takim działaniu, nasz fork formalnie nie jest złączony z pierwotnym repozytorium. Tzn. wykonywanie git pull nie będzie aktualizowało naszego kodu uwzględniając zmiany w pierwotnym projekcie. Mój fork kkokosa/clrmd jest po prostu osobnym repozytorium i póki mu nie każemy, nie będzie patrzył na inne repozytoria - w naszym wypadku Microsoft/clrmd:
$ git remote -v
origin https://github.com/kkokosa/clrmd.git (fetch)
origin https://github.com/kkokosa/clrmd.git (push)
Synchronizowanie z mainem - konfiguracja
Tutaj dochodzimy do potrzeby lekkiej “dokonfiguracji”. Musimy repozytorium wskazać dodatkowe źródło - pierwotne repozytorium:
$ git remote add upstream https://github.com/Microsoft/clrmd.git
I dzięki temu jesteśmy podłączeni do dwóch zdalnych repozytoriów:
$ git remote -v
origin https://github.com/kkokosa/clrmd.git (fetch)
origin https://github.com/kkokosa/clrmd.git (push)
upstream https://github.com/Microsoft/clrmd.git (fetch)
upstream https://github.com/Microsoft/clrmd.git (push)
Synchronizowanie z mainem – codzienność
Co dalej? Załóżmy, że pracowaliśmy na lokalnym repozytorium forka i naprawiliśmy już błąd. Chcemy teraz zacząć przygotowywać się do wysłania zmian do głównego repozytorium. Musimy wcześniej mieć pewność, że mamy jego najbardziej aktualną wersję. Mimo powyższych zmian, nadal prosty git pull nie będzie zaciągał automatycznie informacji z obu repozytoriów. Jak tego dokonać? Znów, bardzo dobrze to zostało opisane w dokumentacji.
Z katalogu roboczego wykonujemy:
$ git fetch upstream
Pobieramy w ten sposób branche zdalnego repozytorium. W tym interesujący nas najbardziej upstream/master.
Następnie robimy checkout tego lokalnego brancha, do którego chcemy zmergdować zdalne repozytorium. Tak, to zdanie brzmi strasznie po Polsku… W moim przypadku jest to po prostu master:
$ git checkout master
Pozostaje już tylko właściwy merge zmian:
$ git merge upstream/master
I nasze repozytorium powinno zawierać najnowszą wersję Microsoft/clrmd wraz z naszymi lokalnymi zmianami.
Praca, commit, push
Teraz już postępujemy standardowo – robimy ostatnie poprawki, przygotowujemy commita i wysyłamy go do lokalnego repozytorium.
$ git add *
$ git commit -m "Fixes issue Microsoft/clrmd#44"
$ git push
Jak widać, możemy w treści commita odnosić się do np. konkretnego issue, dzięki czemu będzie on widoczny w komentarzach w głównym repozytorium, mimo że pochodzi z naszego forka. Oczywiście trzeba uważać z takim spamowaniem...
Zgłoszenie Pull Requesta
Mamy już komplet potrzebnych kroków za sobą. Stworzyliśmy issue. Naprawiliśmy na lokalnym forku błąd i wcommitowaliśmy zmiany. Pora na krok ostatni - poproszenie właścicieli repozytorium Microsoft/clrmd o uwzględnienie naszych zmian. W tym celu musimy poprzez interfejs GitHuba wystawić tzw. Pull Request:
Klikając w interfejsie wybieramy opcję Compare between forks i jako drugi wybieramy oczywiście nasz fork, w którym dokonaliśmy poprawki:
Jeśli wszystko wykonaliśmy wcześniej poprawnie, merge powinien być możliwy do przeprowadzenia automatycznie, bez konfliktów. Klikamy zatem Create Pull Request, piszemy kilka słów komentarza i... czekamy, obserwując co stanie się z Pull Requestem #45. Nawet jeśli nie zostanie zaakceptowany, być może zwrócę tym chociaż uwagę na poważny problem z deadlockiem. I może wywoła to chociaż jakąś dyskusję nad większymi zmianami API by pozbyć się owego całkowicie beznadziejnego błędu.