Tag Archives: gitolite

Recovering from git push fuckup

For some of my projects the central Git server is hosted with gitolite for a long time now. Some time ago I forked a repo bss (example name) to a new repo gee, where both should keep the history of bss, but head in a different direction, including different tags. While doing changes on both repos in different branches I still need to merge an old branch of bss to the recent branch of gee, but usually not in the other direction.

To not accidentally break things, my working copies of both projects only have one remote, the one designated for the original or the fork. For merge or cherry-pick operations in one or the other directions I have a special working copy with both remotes configured. Today after a cherry-pick from gee to bss I messed up pushing, and ended up with gee/master pushed to bss/master, which was wrong as hell, and cold sweat appeared on my head. Damn.

So I had to rewind bss/master to a known good state, but on the server.
WARNING: Make 100% sure you know what you do here to prevent DATA LOSS or your colleagues being mad at you or both.

First try was with the obvious git push --force which was denied by gitolite right away. If you do not know why force pushing usually is a bad thing, stop reading here and learn why it is a bad thing.

Did I warn you about possible data loss?

Second try usually works with branches which are not the default branch, like this (assuming I messed up origin/mybranch and want to push a different version):

$ git push --delete origin mybranch
$ git push --set-upstream origin mybranch

This won’t work with the default branch however with this error message:

$ git push --delete origin master
remote: error: By default, deleting the current branch is denied, because the next
remote: 'git clone' won't result in any file checked out, causing confusion.
remote:
remote: You can set 'receive.denyDeleteCurrent' configuration variable to
remote: 'warn' or 'ignore' in the remote repository to allow deleting the
remote: current branch, with or without a warning message.
remote:
remote: To squelch this message, you can set it to 'refuse'.
remote: error: refusing to delete the current branch: refs/heads/master
To ssh://git.example.com/bss
 ! [remote rejected]   master (deletion of the current branch prohibited)
Fehler: Fehler beim Versenden einiger Referenzen nach 'ssh://git.example.com/bss'

This is not a gitolite problem, but Git itself complains here. Why? For bare repositories Git has a concept of a default branch. This is the branch you get when cloning a repo without specifying a branch. It is marked by the ref named HEAD. Now the idea to recover is like this:

  1. create a new temporary branch on the same changeset you want to have as master on the server
  2. push that branch to the server
  3. change the default branch on the server
  4. do the push –delete / push dance from above (with master now not being the default branch anymore)
  5. change the server’s default branch back
  6. delete the temporary branch both local and remote

In recent Git changing the default branch is done with git symbolic-ref and that would have to be executed on the gitolite server.

Thankfully on recent gitolite V3 you can execute that through SSH, and I learned this from stackoverflow. These were the commands I used (including output), with main being my temporary branch (see above):

$ ssh git@git.example.com symbolic-ref bss HEAD
refs/heads/master
$ ssh git@git.example.com symbolic-ref bss HEAD refs/heads/main
$ ssh git@git.example.com symbolic-ref bss HEAD                
refs/heads/main

Now this is the time to delete master on the remote and push it again from a known good state. Then switch back the default branch.

$ ssh git@git.example.com symbolic-ref bss HEAD refs/heads/master
$ ssh git@git.example.com symbolic-ref bss HEAD                  
refs/heads/master

Everything fine again now. Wrote this for my future self.

HowTo: gitolite auf Debian Wheezy

Das Netz ist natürlich voll von HowTos zum Thema gitolite, in diesem hier nutze ich aber nicht die bleeding edge Version von upstream sondern das Debian-Paket. Eine gewisse Hilfe für’s Verständnis und bei der Installation war die Dokumentation für gitolite 2.x, denn in Wheezy ist Version 2.3 verpackt.

Installation

Geht los mit Installation des Pakets gitolite. Wenn der bei der Installation dpkg nichts konfiguriert haben will, dann gibt man danach nochmal ein:

dpkg-reconfigure gitolite

Dort wird dann ein SSH public key abgefragt für den Admin-Nutzer. In meinem Fall hab ich hier für die Testinstallation auf dem selben Rechner den folgenden Pfad angegeben, ansonsten kopiert man seinen public key ins Eingabefeld:

/home/adahl/.ssh/id_rsa.pub

Damit liegt jetzt hier unterhalb von /var/lib/gitolite1 folgendes:

% ls -la /var/lib/gitolite
insgesamt 28
drwxr-xr-x  5 gitolite gitolite 4096 Jun 17 13:51 ./
drwxr-xr-x 58 root     root     4096 Jun 17 13:47 ../
drwx------  8 gitolite gitolite 4096 Jun 17 13:47 .gitolite/
-rw-r--r--  1 gitolite gitolite 4217 Jun 17 13:47 .gitolite.rc
-rw-------  1 gitolite gitolite    0 Jun 17 13:47 projects.list
drwx------  4 gitolite gitolite 4096 Jun 17 13:47 repositories/
drwx------  2 gitolite gitolite 4096 Jun 17 13:47 .ssh/

Da mein public key bereits hinterlegt ist, kann ich direkt zum nächsten Schritt übergehen und das Admin-Repo clonen, welches bei der Installation angelegt wurde:

git clone gitolite@localhost:gitolite-admin

Wie man sieht, hatte ich den dedizierten Nutzer gitolite genannt, für Produktionsumgebung ist vielleicht git eine bessere Wahl.

Ganz wichtig: beim Ändern der Config muss man stets drauf achten, dass man sich nicht selbst aussperrt. Für das Repository gitolite-admin muss man Schreibrechte für irgendeinen Key behalten, auf den man auch Zugriff hat und der unterhalb von keydir im Repo liegt, sonst hat man keine Möglichkeit mehr ohne große Schmerzen diese Rechte wiederzuerlangen.

Konfiguration

Als nächstes habe ich die Datei /var/lib/gitolite/.gitolite.rc angepasst. Berücksichtigt sind hier bereits, dass ich sowohl git-daemon als auch gitweb einsetzen und die Repos auf einen anderen Rechner spiegeln will. D.h. ich habe folgende Einträge geändert:

$REPO_UMASK = 0027;
$GL_GITCONFIG_KEYS = "gitolite.mirror.*";
$REPO_BASE="/srv/repos/git";

Der letzten Zeile ist erhöhte Beachtung zu schenken. Die bereits angelegten Repositories gitolite-admin und testing aus /var/lib/gitolite/repositories müssen nämlich in den geänderten Pfad verschoben werden.2 Damit der Zugriff über gitweb funktioniert, habe ich den Nutzer www-data der Gruppe git hinzugefügt und die Rechte des Repository-Verzeichnisses gelockert:3

sudo addgroup www-data git
sudo chmod g+rx /var/lib/gitolite/repositories

Die eigentliche Konfiguration erfolgt dann wie bei gitolite üblich über das admin-Repository nachdem man es geklont hat:

git clone gitolite@myfancyserver:gitolite-admin

Wie man das im Einzelnen macht, kann man in der Doku zu gitolite v2 nachlesen. Das ist nicht Debian-spezifisch.

gitweb mit lighttpd

Für einen schnellen Überblick mit dem Webbrowser ist das Paket gitweb installiert. Als Webserver kann vermutlich irgendein Webserver dienen. Für lighttpd habe ich eine Datei /etc/lighttpd/conf-available/80-gitweb.conf angelegt und mit den für diesen Browser üblichen Mechanismen aktiviert. Der Inhalt sieht so aus:4

# -*- depends: cgi -*-
server.modules += ("mod_setenv")
setenv.add-environment = ("GITWEB_CONFIG" => "/etc/gitweb.conf")
alias.url += ( "/gitweb" => "/usr/share/gitweb" )

$HTTP["url"] =~ "^/gitweb/" {
        server.indexfiles = ("gitweb.cgi")
        cgi.assign = ( ".cgi" => "" )
}

Es ist außerdem das Modul 10-cgi.conf zu aktivieren:

sudo lighty-enable-mod cgi
sudo lighty-enable-mod gitweb

In der /etc/gitweb.conf sind auch noch Anpassungen zu tätigen. Hier meine geänderten Zeilen:

$projectroot = "/var/lib/gitolite/repositories";
$projects_list = "/var/lib/gitolite/projects.list";
@diff_opts = ('--find_renames', '--minimal');

Urspünglich war jetzt hier noch eine Beschreibung zum Mirroring, ich verschiebe das auf später und tu es in einen separaten Artikel … O:-)

  1. auch den Pfad kann man beim dpkg-reconfigure angeben. []
  2. Dieser Schritt kann natürlich entfallen, wenn man die Repositories im Standard-Pfad belässt. []
  3. Die Rechte der bei der Installation angelegten Repositories sind allerdings immernoch so restriktiv, dass sie nicht über gitweb angezeigt werden können. Bei <code>gitolite-admin</code> will man das sowieso nicht und statt <code>testing</code> legt man sich vielleicht ein weiteres Testrepository an, um zu sehen, ob das mit den Rechten beim neu anlegen richtig klappt. []
  4. Siehe auch gitweb im ArchWiki. []