Much can be said about similarities between two popular, distributed version control systems: Git and Mercurial. For the most part, choosing between them can be a matter of taste. And because Git seems to have considerably more proponents in the Cool Kids camp, it doesn’t necessarily follow it’s a better option.
But I have found at least one specific and common scenario where Git clearly outshines Hg. Suppose you have coded yourself into a dead end: the feature you planned doesn’t pan out the way you wanted it; or you have some compatibility issues you cannot easily resolve; or you just need to escape the refactoring bathtub.
In any case, you just want to step back a few commits and pretend nothing happened, for now. The mishap might be useful later, though, so it’d be nice if we left it marked for the future.
In Git, this is easily done. You would start by creating a new branch that points to your dead end:
Afterwards, both my_feature
and __my_feature__dead_end__
refer to the same, head commit. We would then move the former a little back, sticking it to one of the earlier hashes. Let’s find a suitable target:
If it looks right, we can reset the my_feature
branch so it points to this specific commit:
Our final situation would then looks like this:
which is exactly what we wanted. Note how any further commits starting from the referent of my_feature
would fork at that point, diverging from development line which has lead us into dead end.
Why the same thing is not so easily done in Mercurial?… In general, this is mostly because of its one fundamental design decision: every commit belongs to one branch, forever and for always. Branch designation is actually part of the changeset’s metadata, just like the commit message or diff. Moving things around – like we did above – is therefore equivalent to changing history and requires tools that are capable of doing so, such as hg strip
.
Now that I don’t use Mercurial at work anymore, I’ve found that despite its shortcomings (hg status
taking 10+ seconds?!) it has some few nice features. One of those is hg outgoing
, which shows you which changesets you are going to send to remote repo in your next push. A quick glance at this list will typically ensure that everything is in order, or allow to amend some commits before making them public.
In Git you can do the similar by applying a filter to git log
:
But while origin
is most often the remote you want to compare against, the master
branch is typically not the one where most of development takes place. So if we want to create a git outgoing
command, we would rather check what the current branch is and compare it with its remotely tracked equivalent:
Simply naming this script git-outgoing
and making it executable somewhere within your $PATH
(e.g. /usr/bin
) will make the git outgoing
command available:
There are few untold assumptions here, like the fact that branch names must match on both local and remote repo. If you find yourself breaking those, then you’re probably better to just use git log
directly.
As the ubiquitous spirit of laziness relaxation permeates the holiday season, a will to do any serious and productive coding seems appallingly low. Instead of fighting it, I went along and tried to write something just for pure fun.
And guess what: it worked pretty well. Well enough, in fact, that the result seems to be worthy of public exposure – which I’m hereby doing. Behold the coded4 project:
What exactly is this thing? It’s a “time sheet” for the marvelous jQuery project, reconstructed from commit timestamps in its Git repository. coded4 created this result by analyzing repo’s history, grouping changesets by contributors, and running some heuristics to approximate timespans of their coding sessions.
And of course, this can be done for any Git (or Hg) repository. Pretty neat for a mere *pops up shell and types coded4 .
* 3 hours of casual coding, eh?
W ostatnich latach rozproszone systemy kontroli wersji (w skrócie DVCS) stały się popularne, zwłaszcza w środowiskach związanych z open source. Warto je jednak znać nie tylko wtedy, gdy pracujemy nad projektami z otwartym źródłem. Jak bowiem pisałem wcześniej, mogą być one przydatne chociażby do małych jednoosobowych projektów. Ponadto bywają nierzadko używane w większych oraz mniejszych firmach. Ogólnie mamy więc duże szanse, aby prędzej lub później zetknąć się z takimi systemami.
Ten post jest adresowany przede wszystkim do osób, które nie miały wcześniej dłuższej styczności rozproszonymi systemami kontroli wersji. Ma on na celu wyjaśnienie wielu spośród tych tajemniczo brzmiących słówek, na jakie nieustannie można się natknąć, gdy pracujemy z DVCS-ami. Istnieje oczywiście szansa, że bardziej zaawansowani użytkownicy również wyciągną nowe wiadomości z lektury (albo chociaż przejrzenia) niniejszej notki. A jeśli nawet nie, to powtórzenie znanej sobie wiedzy na pewno im nie zaszkodzi ;)
Z systemami kontroli wersji (w skrócie VCS) zetknął się każdy programista i dla niemal wszystkich jest to oczywiste narzędzie pracy. Dla wielu jednak wciąż jeszcze kojarzą się one z jednym, centralnym miejscem zawierającym zawierającym bazę kodu projektu (czyli repozytorium) oraz z licznymi kopiami roboczymi, nad którymi pracują programiści. Kiedy zaś chcą połączyć swoje zmiany, wówczas wykonują tylko operację znaną jako commit.
Tak jest w systemach typu CVS czy Subversion, ale obecnie sporą popularność zyskały też inne, działające na nieco innej zasadzie. Nazywają się one rozproszonymi (distributed version control system – DVCS) i zgodnie z tym określeniem ich główną cechą jest brak wydzielonego, centralnego repozytorium. Zamiast tego może być ich dowolna ilość, która w rzeczywistych projektach może łatwo urosnąć do setek, a nawet tysięcy. Najczęściej bowiem swoje własne repozytorium ma na przykład… każdy pracujący nad projektem programista.
Obłęd? Niezupełnie. Wszystko opiera się bowiem na operacji synchronizacji dwóch repozytoriów, którą nazywa się – w zależności od punktu widzenia – push lub pull. Push oznacza wprowadzenie poprawek z naszego repozytorium do innego, zaś pull oznacza ściągnięcie zmian z innego repozytorium do własnego. Jak nietrudno zauważyć, operacja ta jest symetryczna. I chociaż techniczne wszystkie repozytoria są równoważne, w praktyce jedno z nich jest zwykle bazą projektu, zawierającą jego “autorytatywną” wersję. Ponadto z każdym repozytorium można też pracować “normalnie”, tj. przy pomocy znanych i lubianych operacji commit i update.
Działanie rozproszonych systemów kontroli wersji
Po co taka zmiana? Przydaje się ona zwłaszcza w dwóch sytuacjach:
W średniej skali (takiej, jak przedstawiona na obrazku powyżej) systemy rozproszone sprawdzają się natomiast nieco gorzej – zwłaszcza, gdy częstotliwość zmian jest duża. Wówczas często zachodzi potrzeba łączenia (merge) różnych ścieżek tworzenia aplikacji.
Tym niemniej warto się zainteresować tego rodzaju systemami kontroli wersji, by przekonać się, że poza Subversion też coś istnieje :) Przykładami rozproszonych VCS-ów są np. Mercurcial i Git.