Changeset - a47f8f57b347
[Not reviewed]
.hgtags
Show inline comments
 
c097458480a5972dd75d5695b61e855fd0ab371e rhodecode-0.0.0.7.0
 
8bdec09436cb7e4a764bd2ba50b84060e30eb34f rhodecode-0.0.0.7.1
 
1a18994cdc3bdd156ee93c7c0fb8d94a88f1f640 rhodecode-0.0.0.7.2
 
a3a7c3e03b76ee264a828cb1087970bb98bbffcd rhodecode-0.0.0.7.3
 
58b46f9194c347641bfc9a26697ef413a4761971 rhodecode-0.0.0.7.4
 
710e7a75bb6b8346cee3bd0ddda67592e4790268 rhodecode-0.0.0.7.5
 
ca80f8c0056211dad33483a50b913593516d7a6c rhodecode-0.0.0.7.6
 
0cf49c29c846fefeb4e1a222e4b1850e9e3eaa62 rhodecode-0.0.0.7.7
 
702c7e565c56a49c89414e81f28571c8e5b67408 rhodecode-0.0.0.7.8
 
c12f4d19c95065f313eefcd45eac9ef507f5fa55 rhodecode-0.0.0.7.9
 
558eb7c5028f24a90b5466ed16be13b213ba1fc2 rhodecode-0.0.0.8.0
 
a9814a642e11092b243ca01721254a04633a0ffc rhodecode-0.0.0.8.1
 
ccbe729908844884aea89d00fb14a6cb92e10c06 rhodecode-0.0.0.8.2
 
ca41d544dbdfd2f81bd0304168492a26276aadb6 rhodecode-0.0.0.8.3
 
2fa16ec5822da0c6fade3dd1ed9b6c0655e5dbbf rhodecode-0.0.0.8.4
 
16ba57d8fe2317c49dbd422afd07ab497687aa02 rhodecode-0.0.0.8.5
 
53128b6b9a4ddb6ee9554cbb83a082a6d1316b42 rhodecode-0.0.1.0.0rc4
 
afd98d1f817e6a6b52172735c22160239e615a6b rhodecode-0.0.1.0.0
 
bee56f209c40a6880f2f633b02227b5ee1f8ff5a rhodecode-0.0.1.0.1
 
d85b0948e53925ebbbc49e9f7967013a04f866e9 rhodecode-0.0.1.0.2
 
d9c8dddb96af521e346f05b88d515c536eef3d17 rhodecode-0.0.1.1.0
 
344f748517814ed0408a49e392dc625f4cc37fdc rhodecode-0.0.1.1.1
 
6c01c12eafb8cc72d4c4cbd121400fad755b2862 rhodecode-0.0.1.1.2
 
4fa80e0484ef5c33feaa9c39fc66916f410ba353 rhodecode-0.0.1.1.3
 
cb77867d69d3c5931712aac486c980a42ee90745 rhodecode-0.0.1.1.5
 
cb77867d69d3c5931712aac486c980a42ee90745 rhodecode-0.0.1.1.5
 
008bdfdd95c8bd31ae6d89f76c75c1f49cbcd0bc rhodecode-0.0.1.1.5
 
c5af1d3c861fb36b156224e75c2f55a97f54657d rhodecode-0.0.1.1.6
 
7327a0d1584cf28d33e738048af1f6809d499451 rhodecode-0.0.1.1.7
 
bd102f45950f779995a1beae42b6eb099cdd27b3 rhodecode-0.0.1.1.7
 
c8974135732aa0ceb841cee6df66e29f089b4963 rhodecode-0.0.1.1.8
 
c252049af24cd98eef5f4143fa3abbff3c912e29 rhodecode-0.0.1.2.0
 
0b8fba8ab90b01f811a50e6e7384989cced21d38 rhodecode-0.0.1.2.1
 
22273bec00ba2fd860c60a9277d3d7229e288e18 rhodecode-0.0.1.2.2
 
1ff606a7858dbd8a5f70b3da5cc89524bd0d84f9 rhodecode-0.0.1.2.3
 
a7a282a902b207ce34e830d643c79b7ab52e3b35 rhodecode-0.0.1.2.4
 
b6b611e7722e754abebaae6e265cbb4c823d344d rhodecode-0.0.1.2.5
 
dbc82e3362a25d2aece42060089824c4342efd17 rhodecode-0.0.1.3.0
 
79a95f338fd0115b2cdb77118f39e17d22ff505c rhodecode-0.0.1.3.1
 
9ab21c5ddb84935bea5c743b4e147ed5a398b30c rhodecode-0.0.1.3.2
 
934906f028b582a254e0028ba25e5d20dd32b9cd rhodecode-0.0.1.3.3
 
af21362474e3ab5aa0e2fbb1c872356f2c16c4f3 rhodecode-0.0.1.3.4
 
0e2792e04bd316fe64335cbe6a476031ac60b29b rhodecode-0.0.1.3.5
 
edfff9f37916389144d3a3644d0a7d7adfd79b11 rhodecode-0.0.1.3.6
 
9ae95fdeca184f2404205645f06c6597b74ef2db rhodecode-0.0.1.4.0
 
909143a4dde53c46d4f24abb426ec870471c7de1 rhodecode-0.0.1.4.1
 
d998cc84cf726798486a438763053f0e1dc1b646 rhodecode-0.0.1.4.2
 
3f5d40b9dd99ccb009ea2211ee2d4b594c634946 rhodecode-0.0.1.4.3
 
3148c08cf86f1849917e2d50f7ab7766c1550b0a rhodecode-0.0.1.4.4
 
a5f0bc867edc88be23eb808693e5393a97d4c54a rhodecode-0.0.1.5.0
 
3259dc7caea48687eab018ee646ae6ad7e7ef377 rhodecode-0.0.1.5.1
 
efe23d6c178c11d575a0214181276a3452776e48 rhodecode-0.0.1.5.2
 
1a498b11f1540f5b94b6f6009298f5dc3eaad9e9 rhodecode-0.0.1.5.3
 
3447862ad8c9ceba85857774c526e39fde3a2281 rhodecode-0.0.1.5.4
 
c15d7b336af58df9f1bbc8f8957464e7ea618d4c rhodecode-0.0.1.6.0rc1
 
78b53ee0d247f90d51b028307ff5717851b6c265 rhodecode-0.0.1.6.0
 
351ad34d56321349ff5bd38f537bd768b8efef2e rhodecode-0.0.1.7.0
 
1f71ef689d2a3c9978cea6591a1f4e9107a5ca83 rhodecode-0.0.1.7.1
 
cc48c1541c7e2e84114bf92a0f9cd4b8b1341545 0.0
 
d17e88a1a88a29f6fac948c94498129e405a40d3 0.1
 
ad0ce803b40cb17fc3988373052943e041030b02 0.2
 
c6e32714336345403adf76abb6ebf9b8116fcdc7 0.2.1
 
14f488a5dc4ca6647bc6acf12534fd137e968aa8 0.2.2
 
9b3e9e242f5c97cc0c7657e5ac93dce7de61ca16 0.3
 
9bf8eb837e785b6856ccfac264e977ce3ebe1535 0.3.1
 
a84d40e9481fcea4dafadee86b03f0dd401527d6 0.3.2
 
64ea7ea0923618a0c117acebb816a6f0d162bfdb 0.3.3
 
cf635c823ea059cc3a1581b82d8672e46b682384 0.3.4
 
4cca4cc6a0a97f4c4763317184cd41aca4297630 0.3.5
 
082c9b8f0f17bd34740eb90c69bdc4c80d4b5b31 0.3.6
 
a18445b85d407294da0b7f1d8be3bedef5ffdea6 0.3.7
 
8db761c407685e7b08b800c947890035b0d67025 0.4.0rc1
 
60f726162fd6c515bd819feb423be73cad01d7d3 0.4.0rc2
 
19086c5de05f4984d7a90cd31624c45dd893f6bb 0.4.0
 
da65398a62fff50f3d241796cbf17acdea2092ef 0.4.1
 
bfa0b0a814644f0af3f492d17a9ed169cc3b89fe 0.5.0
 
d01a8e92936dbd62c76505432f60efba432e9397 0.5.1
 
aa0a637fa6f635a5e024fa56b19ed2a2dacca857 0.5.2
 
9f5ca9088067618d79129d224c35c818bd2d2f12 0.6.0
 
a22edac2be58eaf68d1940d4dfeb88fadbabb43a 0.6.1
 
22bfca5da6f56738f6220d24bb6ce2f9bc4f9b1e 0.6.2
CONTRIBUTORS
Show inline comments
 
List of contributors to Kallithea project:
 

	
 
    Thomas De Schampheleire <thomas.de_schampheleire@nokia.com> 2014-2020
 
    Mads Kiilerich <mads@kiilerich.com> 2016-2020
 
    Asterios Dimitriou <steve@pci.gr> 2016-2017 2020
 
    Allan Nordhøy <epost@anotheragency.no> 2017-2020
 
    Anton Schur <tonich.sh@gmail.com> 2017 2020
 
    ssantos <ssantos@web.de> 2018-2020
 
    Manuel Jacob <me@manueljacob.de> 2019-2020
 
    Private <adamantine.sword@gmail.com> 2019-2020
 
    David Ignjić <ignjic@gmail.com> 2020
 
    Dennis Fink <dennis.fink@c3l.lu> 2020
 
    Étienne Gilli <etienne@gilli.io> 2020
 
    J. Lavoie <j.lavoie@net-c.ca> 2020
 
    robertus <robertuss12@gmail.com> 2020
 
    Ross Thomas <ross@lns-nevasoft.com> 2020
 
    Andrej Shadura <andrew@shadura.me> 2012 2014-2017 2019
 
    Étienne Gilli <etienne.gilli@gmail.com> 2015-2017 2019
 
    Allan Nordhøy <epost@anotheragency.no> 2017-2019
 
    ssantos <ssantos@web.de> 2018-2019
 
    Adi Kriegisch <adi@cg.tuwien.ac.at> 2019
 
    Danni Randeris <danniranderis@gmail.com> 2019
 
    Edmund Wong <ewong@crazy-cat.org> 2019
 
    Elizabeth Sherrock <lizzyd710@gmail.com> 2019
 
    Hüseyin Tunç <huseyin.tunc@bulutfon.com> 2019
 
    leela <53352@protonmail.com> 2019
 
    Manuel Jacob <me@manueljacob.de> 2019
 
    Mateusz Mendel <mendelm9@gmail.com> 2019
 
    Nathan <bonnemainsnathan@gmail.com> 2019
 
    Oleksandr Shtalinberg <o.shtalinberg@gmail.com> 2019
 
    THANOS SIOURDAKIS <siourdakisthanos@gmail.com> 2019
 
    Wolfgang Scherer <wolfgang.scherer@gmx.de> 2019
 
    Христо Станев <hstanev@gmail.com> 2019
 
    Dominik Ruf <dominikruf@gmail.com> 2012 2014-2018
 
    Michal Čihař <michal@cihar.com> 2014-2015 2018
 
    Branko Majic <branko@majic.rs> 2015 2018
 
    Chris Rule <crule@aegistg.com> 2018
 
    Jesús Sánchez <jsanchezfdz95@gmail.com> 2018
 
    Patrick Vane <patrick_vane@lowentry.com> 2018
 
    Pheng Heong Tan <phtan90@gmail.com> 2018
 
    Максим Якимчук <xpinovo@gmail.com> 2018
 
    Марс Ямбар <mjambarmeta@gmail.com> 2018
 
    Mads Kiilerich <madski@unity3d.com> 2012-2017
 
    Unity Technologies 2012-2017
 
    Søren Løvborg <sorenl@unity3d.com> 2015-2017
 
    Sam Jaques <sam.jaques@me.com> 2015 2017
 
    Alessandro Molina <alessandro.molina@axant.it> 2017
 
    Anton Schur <tonich.sh@gmail.com> 2017
 
    Ching-Chen Mao <mao@lins.fju.edu.tw> 2017
 
    Eivind Tagseth <eivindt@gmail.com> 2017
 
    FUJIWARA Katsunori <foozy@lares.dti.ne.jp> 2017
 
    Holger Schramm <info@schramm.by> 2017
 
    Karl Goetz <karl@kgoetz.id.au> 2017
 
    Lars Kruse <devel@sumpfralle.de> 2017
 
    Marko Semet <markosemet@googlemail.com> 2017
 
    Viktar Vauchkevich <victorenator@gmail.com> 2017
 
    Takumi IINO <trot.thunder@gmail.com> 2012-2016
 
    Jan Heylen <heyleke@gmail.com> 2015-2016
 
    Robert Martinez <ntttq@inboxen.org> 2015-2016
 
    Robert Rauch <mail@robertrauch.de> 2015-2016
 
    Angel Ezquerra <angel.ezquerra@gmail.com> 2016
 
    Anton Shestakov <av6@dwimlabs.net> 2016
 
    Brandon Jones <bjones14@gmail.com> 2016
 
    Kateryna Musina <kateryna@unity3d.com> 2016
 
    Konstantin Veretennicov <kveretennicov@gmail.com> 2016
 
    Oscar Curero <oscar@naiandei.net> 2016
 
    Robert James Dennington <tinytimrob@googlemail.com> 2016
 
    timeless@gmail.com 2016
 
    YFdyh000 <yfdyh000@gmail.com> 2016
 
    Aras Pranckevičius <aras@unity3d.com> 2012-2013 2015
 
    Sean Farley <sean.michael.farley@gmail.com> 2013-2015
 
    Bradley M. Kuhn <bkuhn@sfconservancy.org> 2014-2015
 
    Christian Oyarzun <oyarzun@gmail.com> 2014-2015
 
    Joseph Rivera <rivera.d.joseph@gmail.com> 2014-2015
 
    Anatoly Bubenkov <bubenkoff@gmail.com> 2015
 
    Andrew Bartlett <abartlet@catalyst.net.nz> 2015
 
    Balázs Úr <urbalazs@gmail.com> 2015
 
    Ben Finney <ben@benfinney.id.au> 2015
 
    Daniel Hobley <danielh@unity3d.com> 2015
 
    David Avigni <david.avigni@ankapi.com> 2015
 
    Denis Blanchette <dblanchette@coveo.com> 2015
 
    duanhongyi <duanhongyi@doopai.com> 2015
 
    EriCSN Chang <ericsning@gmail.com> 2015
 
    Grzegorz Krason <grzegorz.krason@gmail.com> 2015
 
    Jiří Suchan <yed@vanyli.net> 2015
 
    Kazunari Kobayashi <kobanari@nifty.com> 2015
 
    Kevin Bullock <kbullock@ringworld.org> 2015
 
    kobanari <kobanari@nifty.com> 2015
 
    Marc Abramowitz <marc@marc-abramowitz.com> 2015
 
    Marc Villetard <marc.villetard@gmail.com> 2015
 
    Matthias Zilk <matthias.zilk@gmail.com> 2015
 
    Michael Pohl <michael@mipapo.de> 2015
 
    Michael V. DePalatis <mike@depalatis.net> 2015
 
    Morten Skaaning <mortens@unity3d.com> 2015
 
    Nick High <nick@silverchip.org> 2015
 
    Niemand Jedermann <predatorix@web.de> 2015
 
    Peter Vitt <petervitt@web.de> 2015
 
    Ronny Pfannschmidt <opensource@ronnypfannschmidt.de> 2015
 
    Tuux <tuxa@galaxie.eu.org> 2015
 
    Viktar Palstsiuk <vipals@gmail.com> 2015
 
    Ante Ilic <ante@unity3d.com> 2014
 
    Calinou <calinou@opmbx.org> 2014
 
    Daniel Anderson <daniel@dattrix.com> 2014
 
    Henrik Stuart <hg@hstuart.dk> 2014
 
    Ingo von Borstel <kallithea@planetmaker.de> 2014
 
    invision70 <invision70@gmail.com> 2014
 
    Jelmer Vernooij <jelmer@samba.org> 2014
 
    Jim Hague <jim.hague@acm.org> 2014
 
    Matt Fellows <kallithea@matt-fellows.me.uk> 2014
 
    Max Roman <max@choloclos.se> 2014
 
    Na'Tosha Bard <natosha@unity3d.com> 2014
 
    Rasmus Selsmark <rasmuss@unity3d.com> 2014
 
    SkryabinD <skryabind@gmail.com> 2014
 
    Tim Freund <tim@freunds.net> 2014
 
    Travis Burtrum <android@moparisthebest.com> 2014
 
    whosaysni <whosaysni@gmail.com> 2014
 
    Zoltan Gyarmati <mr.zoltan.gyarmati@gmail.com> 2014
 
    Marcin Kuźmiński <marcin@python-works.com> 2010-2013
 
    Nemcio <areczek01@gmail.com> 2012-2013
 
    xpol <xpolife@gmail.com> 2012-2013
 
    Andrey Mivrenik <myvrenik@gmail.com> 2013
 
    Aparkar <aparkar@icloud.com> 2013
 
    ArcheR <aleclitvinov1980@gmail.com> 2013
 
    Dennis Brakhane <brakhane@googlemail.com> 2013
 
    gnustavo <gustavo@gnustavo.com> 2013
 
    Grzegorz Rożniecki <xaerxess@gmail.com> 2013
 
    Ilya Beda <ir4y.ix@gmail.com> 2013
 
    ivlevdenis <ivlevdenis.ru@gmail.com> 2013
 
    Jonathan Sternberg <jonathansternberg@gmail.com> 2013
 
    Leonardo Carneiro <leonardo@unity3d.com> 2013
 
    Magnus Ericmats <magnus.ericmats@gmail.com> 2013
 
    Martin Vium <martinv@unity3d.com> 2013
 
    Mikhail Zholobov <legal90@gmail.com> 2013
 
    mokeev1995 <mokeev_andre@mail.ru> 2013
 
    Ruslan Bekenev <furyinbox@gmail.com> 2013
 
    shirou - しろう 2013
 
    Simon Lopez <simon.lopez@slopez.org> 2013
 
    softforwinxp <softforwinxp@gmail.com> 2013
 
    stephanj <info@stephan-jauernick.de> 2013
 
    Ton Plomp <tcplomp@gmail.com> 2013
 
    zhmylove <zhmylove@narod.ru> 2013
 
    こいんとす <tkondou@gmail.com> 2013
 
    Augusto Herrmann <augusto.herrmann@planejamento.gov.br> 2011-2012
 
    Augusto Herrmann <augusto.herrmann@gmail.com> 2012
 
    Dan Sheridan <djs@adelard.com> 2012
 
    Dies Koper <diesk@fast.au.fujitsu.com> 2012
 
    Erwin Kroon <e.kroon@smartmetersolutions.nl> 2012
 
    H Waldo G <gwaldo@gmail.com> 2012
 
    hppj <hppj@postmage.biz> 2012
 
    Indra Talip <indra.talip@gmail.com> 2012
 
    mikespook <mikespook@gmail.com> 2012
 
    nansenat16 <nansenat16@null.tw> 2012
 
    Nemcio <bogdan114@g.pl> 2012
 
    Philip Jameson <philip.j@hostdime.com> 2012
 
    Raoul Thill <raoul.thill@gmail.com> 2012
 
    Stefan Engel <mail@engel-stefan.de> 2012
 
    Tony Bussieres <t.bussieres@gmail.com> 2012
 
    Vincent Caron <vcaron@bearstech.com> 2012
 
    Vincent Duvert <vincent@duvert.net> 2012
 
    Vladislav Poluhin <nuklea@gmail.com> 2012
 
    Zachary Auclair <zach101@gmail.com> 2012
 
    Ankit Solanki <ankit.solanki@gmail.com> 2011
 
    Dmitri Kuznetsov 2011
 
    Jared Bunting <jared.bunting@peachjean.com> 2011
 
    Jason Harris <jason@jasonfharris.com> 2011
 
    Les Peabody <lpeabody@gmail.com> 2011
 
    Liad Shani <liadff@gmail.com> 2011
 
    Lorenzo M. Catucci <lorenzo@sancho.ccd.uniroma2.it> 2011
 
    Matt Zuba <matt.zuba@goodwillaz.org> 2011
 
    Nicolas VINOT <aeris@imirhil.fr> 2011
 
    Shawn K. O'Shea <shawn@eth0.net> 2011
 
    Thayne Harbaugh <thayne@fusionio.com> 2011
 
    Łukasz Balcerzak <lukaszbalcerzak@gmail.com> 2010
 
    Andrew Kesterson <andrew@aklabs.net>
 
    cejones
 
    David A. Sjøen <david.sjoen@westcon.no>
 
    James Rhodes <jrhodes@redpointsoftware.com.au>
 
    Jonas Oberschweiber <jonas.oberschweiber@d-velop.de>
 
    larikale
 
    RhodeCode GmbH
 
    Sebastian Kreutzberger <sebastian@rhodecode.com>
 
    Steve Romanow <slestak989@gmail.com>
 
    SteveCohen
 
    Thomas <thomas@rhodecode.com>
 
    Thomas Waldmann <tw-public@gmx.de>
README.rst
Show inline comments
 
================
 
Kallithea README
 
================
 

	
 

	
 
About
 
-----
 

	
 
**Kallithea** is a fast and powerful management tool for Mercurial_ and Git_
 
with a built-in push/pull server, full text search and code-review. It works on
 
HTTP/HTTPS and SSH, has a built-in permission/authentication system with the ability
 
to authenticate via LDAP or ActiveDirectory. Kallithea also provides simple API
 
so it's easy to integrate with existing external systems.
 

	
 
Kallithea is similar in some respects to GitHub_ or Bitbucket_, however
 
Kallithea can be run as standalone hosted application on your own server. It is
 
open-source and focuses more on providing a customised,
 
self-administered interface for Mercurial_ and Git_ repositories. Kallithea
 
works on Unix-like systems and Windows.
 

	
 
Kallithea was forked from RhodeCode in July 2014 and has been heavily modified.
 

	
 

	
 
Installation
 
------------
 

	
 
Kallithea requires Python_ 3 and it is recommended to install it in a
 
virtualenv. Official releases of Kallithea can be installed with::
 

	
 
    pip install kallithea
 

	
 
The development repository is kept very stable and used in production by the
 
developers -- you can do the same.
 

	
 
Please visit https://docs.kallithea-scm.org/en/latest/installation.html for
 
more details.
 

	
 
There is also an experimental `Puppet module`_ for installing and setting up
 
Kallithea. Currently, only basic functionality is provided, but it is still
 
enough to get up and running quickly, especially for people without Python
 
background. See
 
https://docs.kallithea-scm.org/en/latest/installation_puppet.html for further
 
information.
 

	
 

	
 
Source code
 
-----------
 

	
 
The latest sources can be obtained from
 
https://kallithea-scm.org/repos/kallithea.
 

	
 
The issue tracker and a repository mirror can be found at Bitbucket_ on
 
https://bitbucket.org/conservancy/kallithea.
 

	
 

	
 
Kallithea features
 
------------------
 

	
 
- Has its own middleware to handle Mercurial_ and Git_ protocol requests. Each
 
  request is authenticated and logged together with IP address.
 
- Built for speed and performance. You can make multiple pulls/pushes
 
  simultaneously. Proven to work with thousands of repositories and users.
 
- Supports HTTP/HTTPS with LDAP, AD, or proxy-pass authentication.
 
- Supports SSH access with server-side public key management.
 
- Full permissions (private/read/write/admin) together with IP restrictions for
 
  each repository, additional explicit forking, repositories group and
 
  repository creation permissions.
 
- User groups for easier permission management.
 
- Repository groups let you group repos and manage them easier. They come with
 
  permission delegation features, so you can delegate groups management.
 
- Users can fork other users repos, and compare them at any time.
 
- Built-in versioned paste functionality (Gist) for sharing code snippets.
 
- Integrates easily with other systems, with custom created mappers you can
 
  connect it to almost any issue tracker, and with a JSON-RPC API you can make
 
  much more.
 
- Built-in commit API lets you add, edit and commit files right from Kallithea
 
  web interface using simple editor or upload binary files using simple form.
 
- Powerful pull request driven review system with inline commenting, changeset
 
  statuses, and notification system.
 
- Importing and syncing repositories from remote locations for Git_, Mercurial_
 
  and Subversion.
 
- Mako templates let you customize the look and feel of the application.
 
- Beautiful diffs, annotations and source code browsing all colored by
 
  pygments. Raw diffs are made in Git-diff format for both VCS systems,
 
  including Git_ binary-patches.
 
- Mercurial_ and Git_ DAG graphs and Flot-powered graphs with zooming and
 
  statistics to track activity for repositories.
 
- Admin interface with user/permission management. Admin activity journal logs
 
  pulls, pushes, forks, registrations and other actions made by all users.
 
- Server side forks. It is possible to fork a project and modify it freely
 
  without breaking the main repository.
 
- reST and Markdown README support for repositories.
 
- Full text search powered by Whoosh on the source files, commit messages, and
 
  file names. Built-in indexing daemons, with optional incremental index build
 
  (no external search servers required all in one application).
 
- Setup project descriptions/tags and info inside built in DB for easy,
 
  non-filesystem operations.
 
- Intelligent cache with invalidation after push or project change, provides
 
  high performance and always up to date data.
 
- RSS/Atom feeds, Gravatar support, downloadable sources as zip/tar/gz.
 
- Optional async tasks for speed and performance using Celery_.
 
- Backup scripts can do backup of whole app and send it over scp to desired
 
  location.
 
- Based on TurboGears2, SQLAlchemy, Whoosh, Bootstrap, and other open source
 
  libraries.
 
- Uses PostgreSQL, SQLite, or MariaDB/MySQL databases.
 

	
 

	
 
License
 
-------
 

	
 
**Kallithea** is released under the GPLv3 license. Kallithea is a `Software
 
Freedom Conservancy`_ project and thus controlled by a non-profit organization.
 
No commercial entity can take ownership of the project and change the
 
direction.
 

	
 
Kallithea started out as an effort to make sure the existing GPLv3 codebase
 
would stay available under a legal license. Kallithea thus has to stay GPLv3
 
compatible ... but we are also happy it is GPLv3 and happy to keep it that way.
 
A different license (such as AGPL) could perhaps help attract a different
 
community with a different mix of Free Software people and companies but we are
 
happy with the current focus.
 

	
 

	
 
Community
 
---------
 

	
 
**Kallithea** is maintained by its users who contribute the fixes they would
 
like to see.
 

	
 
Get in touch with the rest of the community:
 

	
 
- Join the mailing list users and developers -- see
 
  http://lists.sfconservancy.org/mailman/listinfo/kallithea-general.
 

	
 
- Use IRC and join #kallithea on FreeNode (irc.freenode.net) or use
 
  http://webchat.freenode.net/?channels=kallithea.
 

	
 
- Follow Kallithea on Twitter, **@KallitheaSCM**.
 

	
 
- Issues can be reported at `issue tracker
 
  <https://bitbucket.org/conservancy/kallithea/issues>`_.
 
- Please report issues on the mailing list. An archive of the old issue tracker
 
  is available at: https://kallithea-scm.org/bitbucket-archive/issues/index.html
 

	
 
   .. note::
 

	
 
       Please try to read the documentation before posting any issues,
 
       especially the **troubleshooting section**
 

	
 

	
 
Online documentation
 
--------------------
 

	
 
Online documentation for the current version of Kallithea is available at
 
https://docs.kallithea-scm.org/en/stable/. Documentation for the current development
 
version can be found on https://docs.kallithea-scm.org/en/default/.
 

	
 
You can also build the documentation locally: go to ``docs/`` and run::
 

	
 
   make html
 

	
 
.. note:: You need to have Sphinx_ installed to build the
 
          documentation. If you don't have Sphinx_ installed you can
 
          install it via the command: ``pip install sphinx`` .
 

	
 

	
 
Migrating from RhodeCode
 
------------------------
 

	
 
Kallithea 0.3.2 and earlier supports migrating from an existing RhodeCode
 
installation. To migrate, install Kallithea 0.3.2 and follow the
 
instructions in the 0.3.2 README to perform a one-time conversion of the
 
database from RhodeCode to Kallithea, before upgrading to this version
 
of Kallithea.
 

	
 

	
 
.. _Python: http://www.python.org/
 
.. _Sphinx: http://sphinx.pocoo.org/
 
.. _Mercurial: http://mercurial.selenic.com/
 
.. _Bitbucket: http://bitbucket.org/
 
.. _GitHub: http://github.com/
 
.. _Subversion: http://subversion.tigris.org/
 
.. _Git: http://git-scm.com/
 
.. _Celery: http://celeryproject.org/
 
.. _Software Freedom Conservancy: http://sfconservancy.org/
 
.. _Puppet module: https://forge.puppetlabs.com/rauch/kallithea
docs/contributing.rst
Show inline comments
 
.. _contributing:
 

	
 
=========================
 
Contributing to Kallithea
 
=========================
 

	
 
Kallithea is developed and maintained by its users. Please join us and scratch
 
your own itch.
 

	
 

	
 
Infrastructure
 
--------------
 

	
 
The main repository is hosted on Our Own Kallithea (aka OOK) at
 
https://kallithea-scm.org/repos/kallithea/, our self-hosted instance
 
of Kallithea.
 

	
 
For now, we use Bitbucket_ for `pull requests`_ and `issue tracking`_. The
 
issue tracker is for tracking bugs, not for support, discussion, or ideas --
 
please use the `mailing list`_ or :ref:`IRC <readme>` to reach the community.
 
Please use the `mailing list`_ to send patches or report issues.
 

	
 
We use Weblate_ to translate the user interface messages into languages other
 
than English. Join our project on `Hosted Weblate`_ to help us.
 
To register, you can use your Bitbucket or GitHub account. See :ref:`translations`
 
for more details.
 

	
 

	
 
Getting started
 
---------------
 

	
 
To get started with Kallithea development run the following commands in your
 
bash shell::
 

	
 
        hg clone https://kallithea-scm.org/repos/kallithea
 
        cd kallithea
 
        python3 -m venv venv
 
        . venv/bin/activate
 
        pip install --upgrade pip setuptools
 
        pip install --upgrade -e . -r dev_requirements.txt python-ldap python-pam
 
        kallithea-cli config-create my.ini
 
        kallithea-cli db-create -c my.ini --user=user --email=user@example.com --password=password --repos=/tmp
 
        kallithea-cli front-end-build
 
        gearbox serve -c my.ini --reload &
 
        firefox http://127.0.0.1:5000/
 

	
 
If you plan to use Bitbucket_ for sending contributions, you can also fork
 
Kallithea on Bitbucket_ first (https://bitbucket.org/conservancy/kallithea) and
 
then replace the clone step above by a clone of your fork. In this case, please
 
see :ref:`contributing-guidelines` below for configuring your fork correctly.
 

	
 

	
 
Contribution flow
 
-----------------
 

	
 
Starting from an existing Kallithea clone, make sure it is up to date with the
 
latest upstream changes::
 

	
 
        hg pull
 
        hg update
 

	
 
Review the :ref:`contributing-guidelines` and :ref:`coding-guidelines`.
 

	
 
If you are new to Mercurial, refer to Mercurial `Quick Start`_ and `Beginners
 
Guide`_ on the Mercurial wiki.
 

	
 
Now, make some changes and test them (see :ref:`contributing-tests`). Don't
 
forget to add new tests to cover new functionality or bug fixes.
 

	
 
For documentation changes, run ``make html`` from the ``docs`` directory to
 
generate the HTML result, then review them in your browser.
 

	
 
Before submitting any changes, run the cleanup script::
 

	
 
        ./scripts/run-all-cleanup
 

	
 
When you are completely ready, you can send your changes to the community for
 
review and inclusion. Most commonly used methods are sending patches to the
 
mailing list (via ``hg email``) or by creating a pull request on Bitbucket_.
 
review and inclusion, via the mailing list (via ``hg email``).
 

	
 
.. _contributing-tests:
 

	
 

	
 
Running tests
 
-------------
 

	
 
After finishing your changes make sure all tests pass cleanly. Run the testsuite
 
by invoking ``py.test`` from the project root::
 

	
 
    py.test
 

	
 
Note that on unix systems, the temporary directory (``/tmp`` or where
 
``$TMPDIR`` points) must allow executable files; Git hooks must be executable,
 
and the test suite creates repositories in the temporary directory. Linux
 
systems with /tmp mounted noexec will thus fail.
 

	
 
Tests can be run on PostgreSQL like::
 

	
 
    sudo -u postgres createuser 'kallithea-test' --pwprompt  # password password
 
    sudo -u postgres createdb 'kallithea-test' --owner 'kallithea-test'
 
    REUSE_TEST_DB='postgresql://kallithea-test:password@localhost/kallithea-test' py.test
 

	
 
Tests can be run on MariaDB/MySQL like::
 

	
 
    echo "GRANT ALL PRIVILEGES ON \`kallithea-test\`.* TO 'kallithea-test'@'localhost' IDENTIFIED BY 'password'" | sudo -u mysql mysql
 
    TEST_DB='mysql://kallithea-test:password@localhost/kallithea-test?charset=utf8mb4' py.test
 

	
 
You can also use ``tox`` to run the tests with all supported Python versions.
 

	
 
When running tests, Kallithea generates a `test.ini` based on template values
 
in `kallithea/tests/conftest.py` and populates the SQLite database specified
 
there.
 

	
 
It is possible to avoid recreating the full test database on each invocation of
 
the tests, thus eliminating the initial delay. To achieve this, run the tests as::
 

	
 
    gearbox serve -c /tmp/kallithea-test-XXX/test.ini --pid-file=test.pid --daemon
 
    KALLITHEA_WHOOSH_TEST_DISABLE=1 KALLITHEA_NO_TMP_PATH=1 py.test
 
    kill -9 $(cat test.pid)
 

	
 
In these commands, the following variables are used::
 

	
 
    KALLITHEA_WHOOSH_TEST_DISABLE=1 - skip whoosh index building and tests
 
    KALLITHEA_NO_TMP_PATH=1 - disable new temp path for tests, used mostly for testing_vcs_operations
 

	
 
You can run individual tests by specifying their path as argument to py.test.
 
py.test also has many more options, see `py.test -h`. Some useful options
 
are::
 

	
 
    -k EXPRESSION         only run tests which match the given substring
 
                          expression. An expression is a python evaluable
 
                          expression where all names are substring-matched
 
                          against test names and their parent classes. Example:
 
    -x, --exitfirst       exit instantly on first error or failed test.
 
    --lf                  rerun only the tests that failed at the last run (or
 
                          all if none failed)
 
    --ff                  run all tests but run the last failures first. This
 
                          may re-order tests and thus lead to repeated fixture
 
                          setup/teardown
 
    --pdb                 start the interactive Python debugger on errors.
 
    -s, --capture=no      don't capture stdout (any stdout output will be
 
                          printed immediately)
 

	
 
Performance tests
 
^^^^^^^^^^^^^^^^^
 

	
 
A number of performance tests are present in the test suite, but they are
 
not run in a standard test run. These tests are useful to
 
evaluate the impact of certain code changes with respect to performance.
 

	
 
To run these tests::
 

	
 
    env TEST_PERFORMANCE=1 py.test kallithea/tests/performance
 

	
 
To analyze performance, you could install pytest-profiling_, which enables the
 
--profile and --profile-svg options to py.test.
 

	
 
.. _pytest-profiling: https://github.com/manahl/pytest-plugins/tree/master/pytest-profiling
 

	
 
.. _contributing-guidelines:
 

	
 

	
 
Contribution guidelines
 
-----------------------
 

	
 
Kallithea is GPLv3 and we assume all contributions are made by the
 
committer/contributor and under GPLv3 unless explicitly stated. We do care a
 
lot about preservation of copyright and license information for existing code
 
that is brought into the project.
 

	
 
Contributions will be accepted in most formats -- such as pull requests on
 
Bitbucket, something hosted on your own Kallithea instance, or patches sent by
 
Contributions will be accepted in most formats -- such as commits hosted on your own Kallithea instance, or patches sent by
 
email to the `kallithea-general`_ mailing list.
 

	
 
When contributing via Bitbucket, please make your fork of
 
https://bitbucket.org/conservancy/kallithea/ `non-publishing`_ -- it is one of
 
the settings on "Repository details" page. This ensures your commits are in
 
"draft" phase and makes it easier for you to address feedback and for project
 
maintainers to integrate your changes.
 

	
 
.. _non-publishing: https://www.mercurial-scm.org/wiki/Phases#Publishing_Repository
 

	
 
Make sure to test your changes both manually and with the automatic tests
 
before posting.
 

	
 
We care about quality and review and keeping a clean repository history. We
 
might give feedback that requests polishing contributions until they are
 
"perfect". We might also rebase and collapse and make minor adjustments to your
 
changes when we apply them.
 

	
 
We try to make sure we have consensus on the direction the project is taking.
 
Everything non-sensitive should be discussed in public -- preferably on the
 
mailing list.  We aim at having all non-trivial changes reviewed by at least
 
one other core developer before pushing. Obvious non-controversial changes will
 
be handled more casually.
 

	
 
There is a main development branch ("default") which is generally stable so that
 
it can be (and is) used in production. There is also a "stable" branch that is
 
almost exclusively reserved for bug fixes or trivial changes. Experimental
 
changes should live elsewhere (for example in a pull request) until they are
 
ready.
 

	
 
.. _coding-guidelines:
 

	
 

	
 
Coding guidelines
 
-----------------
 

	
 
We don't have a formal coding/formatting standard. We are currently using a mix
 
of Mercurial's (https://www.mercurial-scm.org/wiki/CodingStyle), pep8, and
 
consistency with existing code. Run ``scripts/run-all-cleanup`` before
 
committing to ensure some basic code formatting consistency.
 

	
 
We support Python 3.6 and later.
 

	
 
We try to support the most common modern web browsers. IE9 is still supported
 
to the extent it is feasible, IE8 is not.
 

	
 
We primarily support Linux and OS X on the server side but Windows should also work.
 

	
 
HTML templates should use 2 spaces for indentation ... but be pragmatic. We
 
should use templates cleverly and avoid duplication. We should use reasonable
 
semantic markup with element classes and IDs that can be used for styling and testing.
 
We should only use inline styles in places where it really is semantic (such as
 
``display: none``).
 

	
 
JavaScript must use ``;`` between/after statements. Indentation 4 spaces. Inline
 
multiline functions should be indented two levels -- one for the ``()`` and one for
 
``{}``.
 
Variables holding jQuery objects should be named with a leading ``$``.
 

	
 
Commit messages should have a leading short line summarizing the changes. For
 
bug fixes, put ``(Issue #123)`` at the end of this line.
 

	
 
Use American English grammar and spelling overall. Use `English title case`_ for
 
page titles, button labels, headers, and 'labels' for fields in forms.
 

	
 
.. _English title case: https://en.wikipedia.org/wiki/Capitalization#Title_case
 

	
 
Template helpers (that is, everything in ``kallithea.lib.helpers``)
 
should only be referenced from templates. If you need to call a
 
helper from the Python code, consider moving the function somewhere
 
else (e.g. to the model).
 

	
 
Notes on the SQLAlchemy session
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 

	
 
Each HTTP request runs inside an independent SQLAlchemy session (as well
 
as in an independent database transaction). ``Session`` is the session manager
 
and factory. ``Session()`` will create a new session on-demand or return the
 
current session for the active thread. Many database operations are methods on
 
such session instances. The session will generally be removed by
 
TurboGears automatically.
 

	
 
Database model objects
 
(almost) always belong to a particular SQLAlchemy session, which means
 
that SQLAlchemy will ensure that they're kept in sync with the database
 
(but also means that they cannot be shared across requests).
 

	
 
Objects can be added to the session using ``Session().add``, but this is
 
rarely needed:
 

	
 
* When creating a database object by calling the constructor directly,
 
  it must explicitly be added to the session.
 

	
 
* When creating an object using a factory function (like
 
  ``create_repo``), the returned object has already (by convention)
 
  been added to the session, and should not be added again.
 

	
 
* When getting an object from the session (via ``Session().query`` or
 
  any of the utility functions that look up objects in the database),
 
  it's already part of the session, and should not be added again.
 
  SQLAlchemy monitors attribute modifications automatically for all
 
  objects it knows about and syncs them to the database.
 

	
 
SQLAlchemy also flushes changes to the database automatically; manually
 
calling ``Session().flush`` is usually only necessary when the Python
 
code needs the database to assign an "auto-increment" primary key ID to
 
a freshly created model object (before flushing, the ID attribute will
 
be ``None``).
 

	
 
Debugging
 
^^^^^^^^^
 

	
 
A good way to trace what Kallithea is doing is to keep an eye on the output on
 
stdout/stderr of the server process. Perhaps change ``my.ini`` to log at
 
``DEBUG`` or ``INFO`` level, especially ``[logger_kallithea]``, but perhaps
 
also other loggers. It is often easier to add additional ``log`` or ``print``
 
statements than to use a Python debugger.
 

	
 
Sometimes it is simpler to disable ``errorpage.enabled`` and perhaps also
 
``trace_errors.enable`` to expose raw errors instead of adding extra
 
processing. Enabling ``debug`` can be helpful for showing and exploring
 
tracebacks in the browser, but is also insecure and will add extra processing.
 

	
 
TurboGears2 DebugBar
 
^^^^^^^^^^^^^^^^^^^^
 

	
 
It is possible to enable the TurboGears2-provided DebugBar_, a toolbar overlayed
 
over the Kallithea web interface, allowing you to see:
 

	
 
* timing information of the current request, including profiling information
 
* request data, including GET data, POST data, cookies, headers and environment
 
  variables
 
* a list of executed database queries, including timing and result values
 

	
 
DebugBar is only activated when ``debug = true`` is set in the configuration
 
file. This is important, because the DebugBar toolbar will be visible for all
 
users, and allow them to see information they should not be allowed to see. Like
 
is anyway the case for ``debug = true``, do not use this in production!
 

	
 
To enable DebugBar, install ``tgext.debugbar`` and ``kajiki`` (typically via
 
``pip``) and restart Kallithea (in debug mode).
 

	
 

	
 
"Roadmap"
 
---------
 

	
 
We do not have a road map but are waiting for your contributions. Refer to the
 
wiki_ for some ideas of places we might want to go -- contributions in these
 
areas are very welcome.
 

	
 

	
 
Thank you for your contribution!
 
--------------------------------
 

	
 

	
 
.. _Weblate: http://weblate.org/
 
.. _issue tracking: https://bitbucket.org/conservancy/kallithea/issues?status=new&status=open
 
.. _pull requests: https://bitbucket.org/conservancy/kallithea/pull-requests
 
.. _bitbucket: http://bitbucket.org/
 
.. _mailing list: http://lists.sfconservancy.org/mailman/listinfo/kallithea-general
 
.. _kallithea-general: http://lists.sfconservancy.org/mailman/listinfo/kallithea-general
 
.. _Hosted Weblate: https://hosted.weblate.org/projects/kallithea/kallithea/
 
.. _wiki: https://bitbucket.org/conservancy/kallithea/wiki/Home
 
.. _DebugBar: https://github.com/TurboGears/tgext.debugbar
 
.. _Quick Start: https://www.mercurial-scm.org/wiki/QuickStart
 
.. _Beginners Guide: https://www.mercurial-scm.org/wiki/BeginnersGuides
docs/index.rst
Show inline comments
 
.. _index:
 

	
 
#######################
 
Kallithea Documentation
 
#######################
 

	
 
* :ref:`genindex`
 
* :ref:`search`
 

	
 

	
 
Readme
 
******
 

	
 
.. toctree::
 
   :maxdepth: 1
 

	
 
   readme
 

	
 

	
 
Administrator guide
 
*******************
 

	
 
**Installation and upgrade**
 

	
 
.. toctree::
 
   :maxdepth: 1
 

	
 
   overview
 
   installation
 
   installation_win
 
   installation_win_old
 
   installation_iis
 
   installation_puppet
 
   upgrade
 

	
 
**Setup and configuration**
 

	
 
.. toctree::
 
   :maxdepth: 1
 

	
 
   setup
 
   administrator_guide/auth
 
   administrator_guide/vcs_setup
 
   usage/email
 
   usage/customization
 

	
 
**Maintenance**
 

	
 
.. toctree::
 
   :maxdepth: 1
 

	
 
   usage/backup
 
   usage/performance
 
   usage/debugging
 
   usage/troubleshooting
 

	
 

	
 
User guide
 
**********
 

	
 
.. toctree::
 
   :maxdepth: 1
 

	
 
   usage/general
 
   usage/vcs_notes
 
   usage/statistics
 
   api/api
 

	
 

	
 
Developer guide
 
***************
 

	
 
.. toctree::
 
   :maxdepth: 1
 

	
 
   contributing
 
   dev/i18n
 
   dev/dbmigrations
 

	
 

	
 
.. _python: http://www.python.org/
 
.. _django: http://www.djangoproject.com/
 
.. _mercurial: https://www.mercurial-scm.org/
 
.. _bitbucket: http://bitbucket.org/
 
.. _subversion: http://subversion.tigris.org/
 
.. _git: http://git-scm.com/
 
.. _celery: http://celeryproject.org/
 
.. _Sphinx: http://sphinx.pocoo.org/
 
.. _vcs: http://pypi.python.org/pypi/vcs
kallithea/bin/base.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
"""
 
kallithea.bin.base
 
~~~~~~~~~~~~~~~~~~
 

	
 
Base utils for shell scripts
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: May 09, 2013
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import os
 
import pprint
 
import random
 
import sys
 
import urllib.request
 

	
 
from kallithea.lib import ext_json
 
from kallithea.lib.utils2 import ascii_bytes
 

	
 

	
 
CONFIG_NAME = '.config/kallithea'
 
FORMAT_PRETTY = 'pretty'
 
FORMAT_JSON = 'json'
 

	
 

	
 
def api_call(apikey, apihost, method=None, **kw):
 
    """
 
    Api_call wrapper for Kallithea.
 

	
 
    :param apikey:
 
    :param apihost:
 
    :param format: formatting, pretty means prints and pprint of json
 
     json returns unparsed json
 
    :param method:
 
    :returns: json response from server
 
    """
 
    def _build_data(random_id):
 
        """
 
        Builds API data with given random ID
 

	
 
        :param random_id:
 
        """
 
        return {
 
            "id": random_id,
 
            "api_key": apikey,
 
            "method": method,
 
            "args": kw
 
        }
 

	
 
    if not method:
 
        raise Exception('please specify method name !')
 
    apihost = apihost.rstrip('/')
 
    id_ = random.randrange(1, 9999)
 
    req = urllib.request.Request('%s/_admin/api' % apihost,
 
                      data=ascii_bytes(ext_json.dumps(_build_data(id_))),
 
                      headers={'content-type': 'text/plain'})
 
    ret = urllib.request.urlopen(req)
 
    raw_json = ret.read()
 
    json_data = ext_json.loads(raw_json)
 
    id_ret = json_data['id']
 
    if id_ret == id_:
 
        return json_data
 

	
 
    else:
 
        _formatted_json = pprint.pformat(json_data)
 
        raise Exception('something went wrong. '
 
                        'ID mismatch got %s, expected %s | %s' % (
 
                                            id_ret, id_, _formatted_json))
 

	
 

	
 
class RcConf(object):
 
    """
 
    Kallithea config for API
 

	
 
    conf = RcConf()
 
    conf['key']
 

	
 
    """
 

	
 
    def __init__(self, config_location=None, autoload=True, autocreate=False,
 
                 config=None):
 
        HOME = os.getenv('HOME', os.getenv('USERPROFILE')) or ''
 
        HOME_CONF = os.path.abspath(os.path.join(HOME, CONFIG_NAME))
 
        self._conf_name = HOME_CONF if not config_location else config_location
 
        self._conf = {}
 
        if autocreate:
 
            self.make_config(config)
 
        if autoload:
 
            self._conf = self.load_config()
 

	
 
    def __getitem__(self, key):
 
        return self._conf[key]
 

	
 
    def __bool__(self):
 
        if self._conf:
 
            return True
 
        return False
 

	
 
    def __eq__(self, other):
 
        return self._conf.__eq__(other)
 

	
 
    def __repr__(self):
 
        return 'RcConf<%s>' % self._conf.__repr__()
 

	
 
    def make_config(self, config):
 
        """
 
        Saves given config as a JSON dump in the _conf_name location
 

	
 
        :param config:
 
        """
 
        update = False
 
        if os.path.exists(self._conf_name):
 
            update = True
 
        with open(self._conf_name, 'wb') as f:
 
        with open(self._conf_name, 'w') as f:
 
            ext_json.dump(config, f, indent=4)
 
            f.write(b'\n')
 
            f.write('\n')
 

	
 
        if update:
 
            sys.stdout.write('Updated config in %s\n' % self._conf_name)
 
        else:
 
            sys.stdout.write('Created new config in %s\n' % self._conf_name)
 

	
 
    def update_config(self, new_config):
 
        """
 
        Reads the JSON config updates it's values with new_config and
 
        saves it back as JSON dump
 

	
 
        :param new_config:
 
        """
 
        config = {}
 
        try:
 
            with open(self._conf_name, 'rb') as conf:
 
                config = ext_json.load(conf)
 
        except IOError as e:
 
            sys.stderr.write(str(e) + '\n')
 

	
 
        config.update(new_config)
 
        self.make_config(config)
 

	
 
    def load_config(self):
 
        """
 
        Loads config from file and returns loaded JSON object
 
        """
 
        try:
 
            with open(self._conf_name, 'rb') as conf:
 
            with open(self._conf_name, 'r') as conf:
 
                return ext_json.load(conf)
 
        except IOError as e:
 
            #sys.stderr.write(str(e) + '\n')
 
            pass
kallithea/controllers/pullrequests.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
"""
 
kallithea.controllers.pullrequests
 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
pull requests controller for Kallithea for initializing pull requests
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: May 7, 2012
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 
import traceback
 

	
 
import formencode
 
import mercurial.unionrepo
 
from tg import request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPBadRequest, HTTPForbidden, HTTPFound, HTTPNotFound
 

	
 
from kallithea.config.routing import url
 
from kallithea.controllers.changeset import create_cs_pr_comment, delete_cs_pr_comment
 
from kallithea.lib import diffs
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired
 
from kallithea.lib.base import BaseRepoController, jsonify, render
 
from kallithea.lib.graphmod import graph_data
 
from kallithea.lib.page import Page
 
from kallithea.lib.utils2 import ascii_bytes, safe_bytes, safe_int
 
from kallithea.lib.vcs.exceptions import ChangesetDoesNotExistError, EmptyRepositoryError
 
from kallithea.model.changeset_status import ChangesetStatusModel
 
from kallithea.model.comment import ChangesetCommentsModel
 
from kallithea.model.db import ChangesetStatus, PullRequest, PullRequestReviewer, Repository, User
 
from kallithea.model.forms import PullRequestForm, PullRequestPostForm
 
from kallithea.model.meta import Session
 
from kallithea.model.pull_request import CreatePullRequestAction, CreatePullRequestIterationAction, PullRequestModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
def _get_reviewer(user_id):
 
    """Look up user by ID and validate it as a potential reviewer."""
 
    try:
 
        user = User.get(int(user_id))
 
    except ValueError:
 
        user = None
 

	
 
    if user is None or user.is_default_user:
 
        h.flash(_('Invalid reviewer "%s" specified') % user_id, category='error')
 
        raise HTTPBadRequest()
 

	
 
    return user
 

	
 

	
 
class PullrequestsController(BaseRepoController):
 

	
 
    def _get_repo_refs(self, repo, rev=None, branch=None, branch_rev=None):
 
        """return a structure with repo's interesting changesets, suitable for
 
        the selectors in pullrequest.html
 

	
 
        rev: a revision that must be in the list somehow and selected by default
 
        branch: a branch that must be in the list and selected by default - even if closed
 
        branch_rev: a revision of which peers should be preferred and available."""
 
        # list named branches that has been merged to this named branch - it should probably merge back
 
        peers = []
 

	
 
        if branch_rev:
 
            # a revset not restricting to merge() would be better
 
            # (especially because it would get the branch point)
 
            # ... but is currently too expensive
 
            # including branches of children could be nice too
 
            peerbranches = set()
 
            for i in repo._repo.revs(
 
                b"sort(parents(branch(id(%s)) and merge()) - branch(id(%s)), -rev)",
 
                ascii_bytes(branch_rev), ascii_bytes(branch_rev),
 
            ):
 
                for abranch in repo.get_changeset(i).branches:
 
                    if abranch not in peerbranches:
 
                        n = 'branch:%s:%s' % (abranch, repo.get_changeset(abranch).raw_id)
 
                        peers.append((n, abranch))
 
                        peerbranches.add(abranch)
 

	
 
        selected = None
 
        tiprev = repo.tags.get('tip')
 
        tipbranch = None
 

	
 
        branches = []
 
        for abranch, branchrev in repo.branches.items():
 
            n = 'branch:%s:%s' % (abranch, branchrev)
 
            desc = abranch
 
            if branchrev == tiprev:
 
                tipbranch = abranch
 
                desc = '%s (current tip)' % desc
 
            branches.append((n, desc))
 
            if rev == branchrev:
 
                selected = n
 
            if branch == abranch:
 
                if not rev:
 
                    selected = n
 
                branch = None
 
        if branch:  # branch not in list - it is probably closed
 
            branchrev = repo.closed_branches.get(branch)
 
            if branchrev:
 
                n = 'branch:%s:%s' % (branch, branchrev)
 
                branches.append((n, _('%s (closed)') % branch))
 
                selected = n
 
                branch = None
 
            if branch:
 
                log.debug('branch %r not found in %s', branch, repo)
 

	
 
        bookmarks = []
 
        for bookmark, bookmarkrev in repo.bookmarks.items():
 
            n = 'book:%s:%s' % (bookmark, bookmarkrev)
 
            bookmarks.append((n, bookmark))
 
            if rev == bookmarkrev:
 
                selected = n
 

	
 
        tags = []
 
        for tag, tagrev in repo.tags.items():
 
            if tag == 'tip':
 
                continue
 
            n = 'tag:%s:%s' % (tag, tagrev)
 
            tags.append((n, tag))
 
            # note: even if rev == tagrev, don't select the static tag - it must be chosen explicitly
 

	
 
        # prio 1: rev was selected as existing entry above
 

	
 
        # prio 2: create special entry for rev; rev _must_ be used
 
        specials = []
 
        if rev and selected is None:
 
            selected = 'rev:%s:%s' % (rev, rev)
 
            specials = [(selected, '%s: %s' % (_("Changeset"), rev[:12]))]
 

	
 
        # prio 3: most recent peer branch
 
        if peers and not selected:
 
            selected = peers[0][0]
 

	
 
        # prio 4: tip revision
 
        if not selected:
 
            if h.is_hg(repo):
 
                if tipbranch:
 
                    selected = 'branch:%s:%s' % (tipbranch, tiprev)
 
                else:
 
                    selected = 'tag:null:' + repo.EMPTY_CHANGESET
 
                    tags.append((selected, 'null'))
 
            else:
 
                if 'master' in repo.branches:
 
            else:  # Git
 
                if not repo.branches:
 
                    selected = ''  # doesn't make sense, but better than nothing
 
                elif 'master' in repo.branches:
 
                    selected = 'branch:master:%s' % repo.branches['master']
 
                else:
 
                    k, v = list(repo.branches.items())[0]
 
                    selected = 'branch:%s:%s' % (k, v)
 

	
 
        groups = [(specials, _("Special")),
 
                  (peers, _("Peer branches")),
 
                  (bookmarks, _("Bookmarks")),
 
                  (branches, _("Branches")),
 
                  (tags, _("Tags")),
 
                  ]
 
        return [g for g in groups if g[0]], selected
 

	
 
    def _is_allowed_to_change_status(self, pull_request):
 
        if pull_request.is_closed():
 
            return False
 

	
 
        owner = request.authuser.user_id == pull_request.owner_id
 
        reviewer = PullRequestReviewer.query() \
 
            .filter(PullRequestReviewer.pull_request == pull_request) \
 
            .filter(PullRequestReviewer.user_id == request.authuser.user_id) \
 
            .count() != 0
 

	
 
        return request.authuser.admin or owner or reviewer
 

	
 
    @LoginRequired(allow_default_user=True)
 
    @HasRepoPermissionLevelDecorator('read')
 
    def show_all(self, repo_name):
 
        c.from_ = request.GET.get('from_') or ''
 
        c.closed = request.GET.get('closed') or ''
 
        url_params = {}
 
        if c.from_:
 
            url_params['from_'] = 1
 
        if c.closed:
 
            url_params['closed'] = 1
 
        p = safe_int(request.GET.get('page'), 1)
 

	
 
        q = PullRequest.query(include_closed=c.closed, sorted=True)
 
        if c.from_:
 
            q = q.filter_by(org_repo=c.db_repo)
 
        else:
 
            q = q.filter_by(other_repo=c.db_repo)
 
        c.pull_requests = q.all()
 

	
 
        c.pullrequests_pager = Page(c.pull_requests, page=p, items_per_page=100, **url_params)
 

	
 
        return render('/pullrequests/pullrequest_show_all.html')
 

	
 
    @LoginRequired()
 
    def show_my(self):
 
        c.closed = request.GET.get('closed') or ''
 

	
 
        c.my_pull_requests = PullRequest.query(
 
            include_closed=c.closed,
 
            sorted=True,
 
        ).filter_by(owner_id=request.authuser.user_id).all()
 

	
 
        c.participate_in_pull_requests = []
 
        c.participate_in_pull_requests_todo = []
 
        done_status = set([ChangesetStatus.STATUS_APPROVED, ChangesetStatus.STATUS_REJECTED])
 
        for pr in PullRequest.query(
 
            include_closed=c.closed,
 
            reviewer_id=request.authuser.user_id,
 
            sorted=True,
 
        ):
 
            status = pr.user_review_status(request.authuser.user_id) # very inefficient!!!
 
            if status in done_status:
 
                c.participate_in_pull_requests.append(pr)
 
            else:
 
                c.participate_in_pull_requests_todo.append(pr)
 

	
 
        return render('/pullrequests/pullrequest_show_my.html')
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionLevelDecorator('read')
 
    def index(self):
 
        org_repo = c.db_repo
 
        org_scm_instance = org_repo.scm_instance
 
        try:
 
            org_scm_instance.get_changeset()
 
        except EmptyRepositoryError as e:
 
            h.flash(_('There are no changesets yet'),
 
                    category='warning')
 
            raise HTTPFound(location=url('summary_home', repo_name=org_repo.repo_name))
 

	
 
        org_rev = request.GET.get('rev_end')
 
        # rev_start is not directly useful - its parent could however be used
 
        # as default for other and thus give a simple compare view
 
        rev_start = request.GET.get('rev_start')
 
        other_rev = None
 
        if rev_start:
 
            starters = org_repo.get_changeset(rev_start).parents
 
            if starters:
 
                other_rev = starters[0].raw_id
 
            else:
 
                other_rev = org_repo.scm_instance.EMPTY_CHANGESET
 
        branch = request.GET.get('branch')
 

	
 
        c.cs_repos = [(org_repo.repo_name, org_repo.repo_name)]
 
        c.default_cs_repo = org_repo.repo_name
 
        c.cs_refs, c.default_cs_ref = self._get_repo_refs(org_scm_instance, rev=org_rev, branch=branch)
 

	
 
        default_cs_ref_type, default_cs_branch, default_cs_rev = c.default_cs_ref.split(':')
 
        if default_cs_ref_type != 'branch':
 
            default_cs_branch = org_repo.get_changeset(default_cs_rev).branch
 

	
 
        # add org repo to other so we can open pull request against peer branches on itself
 
        c.a_repos = [(org_repo.repo_name, '%s (self)' % org_repo.repo_name)]
 

	
 
        if org_repo.parent:
 
            # add parent of this fork also and select it.
 
            # use the same branch on destination as on source, if available.
 
            c.a_repos.append((org_repo.parent.repo_name, '%s (parent)' % org_repo.parent.repo_name))
 
            c.a_repo = org_repo.parent
 
            c.a_refs, c.default_a_ref = self._get_repo_refs(
 
                    org_repo.parent.scm_instance, branch=default_cs_branch, rev=other_rev)
 

	
 
        else:
 
            c.a_repo = org_repo
 
            c.a_refs, c.default_a_ref = self._get_repo_refs(org_scm_instance, rev=other_rev)
 

	
 
        # gather forks and add to this list ... even though it is rare to
 
        # request forks to pull from their parent
 
        for fork in org_repo.forks:
 
            c.a_repos.append((fork.repo_name, fork.repo_name))
 

	
 
        return render('/pullrequests/pullrequest.html')
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionLevelDecorator('read')
 
    @jsonify
 
    def repo_info(self, repo_name):
 
        repo = c.db_repo
 
        refs, selected_ref = self._get_repo_refs(repo.scm_instance)
 
        return {
 
            'description': repo.description.split('\n', 1)[0],
 
            'selected_ref': selected_ref,
 
            'refs': refs,
 
            }
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionLevelDecorator('read')
 
    def create(self, repo_name):
 
        repo = c.db_repo
 
        try:
 
            _form = PullRequestForm(repo.repo_id)().to_python(request.POST)
 
        except formencode.Invalid as errors:
 
            log.error(traceback.format_exc())
 
            log.error(str(errors))
 
            msg = _('Error creating pull request: %s') % errors.msg
 
            h.flash(msg, 'error')
 
            raise HTTPBadRequest
 

	
 
        # heads up: org and other might seem backward here ...
 
        org_ref = _form['org_ref'] # will have merge_rev as rev but symbolic name
 
        org_repo = Repository.guess_instance(_form['org_repo'])
 

	
 
        other_ref = _form['other_ref'] # will have symbolic name and head revision
 
        other_repo = Repository.guess_instance(_form['other_repo'])
 

	
 
        reviewers = []
 

	
 
        title = _form['pullrequest_title']
 
        description = _form['pullrequest_desc'].strip()
 
        owner = User.get(request.authuser.user_id)
 

	
 
        try:
 
            cmd = CreatePullRequestAction(org_repo, other_repo, org_ref, other_ref, title, description, owner, reviewers)
 
        except CreatePullRequestAction.ValidationError as e:
 
            h.flash(e, category='error', logf=log.error)
 
            raise HTTPNotFound
 

	
 
        try:
 
            pull_request = cmd.execute()
 
            Session().commit()
 
        except Exception:
 
            h.flash(_('Error occurred while creating pull request'),
 
                    category='error')
 
            log.error(traceback.format_exc())
 
            raise HTTPFound(location=url('pullrequest_home', repo_name=repo_name))
 

	
 
        h.flash(_('Successfully opened new pull request'),
 
                category='success')
 
        raise HTTPFound(location=pull_request.url())
 

	
 
    def create_new_iteration(self, old_pull_request, new_rev, title, description, reviewers):
 
        owner = User.get(request.authuser.user_id)
 
        new_org_rev = self._get_ref_rev(old_pull_request.org_repo, 'rev', new_rev)
 
        new_other_rev = self._get_ref_rev(old_pull_request.other_repo, old_pull_request.other_ref_parts[0], old_pull_request.other_ref_parts[1])
 
        try:
 
            cmd = CreatePullRequestIterationAction(old_pull_request, new_org_rev, new_other_rev, title, description, owner, reviewers)
 
        except CreatePullRequestAction.ValidationError as e:
 
            h.flash(e, category='error', logf=log.error)
 
            raise HTTPNotFound
 

	
 
        try:
 
            pull_request = cmd.execute()
 
            Session().commit()
 
        except Exception:
 
            h.flash(_('Error occurred while creating pull request'),
 
                    category='error')
 
            log.error(traceback.format_exc())
 
            raise HTTPFound(location=old_pull_request.url())
 

	
 
        h.flash(_('New pull request iteration created'),
 
                category='success')
 
        raise HTTPFound(location=pull_request.url())
 

	
 
    # pullrequest_post for PR editing
 
    @LoginRequired()
 
    @HasRepoPermissionLevelDecorator('read')
 
    def post(self, repo_name, pull_request_id):
 
        pull_request = PullRequest.get_or_404(pull_request_id)
 
        if pull_request.is_closed():
 
            raise HTTPForbidden()
 
        assert pull_request.other_repo.repo_name == repo_name
 
        # only owner or admin can update it
 
        owner = pull_request.owner_id == request.authuser.user_id
 
        repo_admin = h.HasRepoPermissionLevel('admin')(c.repo_name)
 
        if not (h.HasPermissionAny('hg.admin')() or repo_admin or owner):
 
            raise HTTPForbidden()
 

	
 
        _form = PullRequestPostForm()().to_python(request.POST)
 

	
 
        cur_reviewers = set(pull_request.get_reviewer_users())
 
        new_reviewers = set(_get_reviewer(s) for s in _form['review_members'])
 
        old_reviewers = set(_get_reviewer(s) for s in _form['org_review_members'])
 

	
 
        other_added = cur_reviewers - old_reviewers
 
        other_removed = old_reviewers - cur_reviewers
 

	
 
        if other_added:
 
            h.flash(_('Meanwhile, the following reviewers have been added: %s') %
 
                    (', '.join(u.username for u in other_added)),
 
                    category='warning')
 
        if other_removed:
 
            h.flash(_('Meanwhile, the following reviewers have been removed: %s') %
 
                    (', '.join(u.username for u in other_removed)),
 
                    category='warning')
 

	
 
        if _form['updaterev']:
 
            return self.create_new_iteration(pull_request,
 
                                      _form['updaterev'],
 
                                      _form['pullrequest_title'],
 
                                      _form['pullrequest_desc'],
 
                                      new_reviewers)
 

	
 
        added_reviewers = new_reviewers - old_reviewers - cur_reviewers
 
        removed_reviewers = (old_reviewers - new_reviewers) & cur_reviewers
 

	
 
        old_description = pull_request.description
 
        pull_request.title = _form['pullrequest_title']
 
        pull_request.description = _form['pullrequest_desc'].strip() or _('No description')
 
        pull_request.owner = User.get_by_username(_form['owner'])
 
        user = User.get(request.authuser.user_id)
 

	
 
        PullRequestModel().mention_from_description(user, pull_request, old_description)
 
        PullRequestModel().add_reviewers(user, pull_request, added_reviewers)
 
        PullRequestModel().remove_reviewers(user, pull_request, removed_reviewers)
 

	
 
        Session().commit()
 
        h.flash(_('Pull request updated'), category='success')
 

	
 
        raise HTTPFound(location=pull_request.url())
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionLevelDecorator('read')
 
    @jsonify
 
    def delete(self, repo_name, pull_request_id):
 
        pull_request = PullRequest.get_or_404(pull_request_id)
 
        # only owner can delete it !
 
        if pull_request.owner_id == request.authuser.user_id:
 
            PullRequestModel().delete(pull_request)
 
            Session().commit()
 
            h.flash(_('Successfully deleted pull request'),
 
                    category='success')
 
            raise HTTPFound(location=url('my_pullrequests'))
 
        raise HTTPForbidden()
 

	
 
    @LoginRequired(allow_default_user=True)
 
    @HasRepoPermissionLevelDecorator('read')
 
    def show(self, repo_name, pull_request_id, extra=None):
 
        c.pull_request = PullRequest.get_or_404(pull_request_id)
 
        c.allowed_to_change_status = self._is_allowed_to_change_status(c.pull_request)
 
        cc_model = ChangesetCommentsModel()
 
        cs_model = ChangesetStatusModel()
 

	
 
        # pull_requests repo_name we opened it against
 
        # ie. other_repo must match
 
        if repo_name != c.pull_request.other_repo.repo_name:
 
            raise HTTPNotFound
 

	
 
        # load compare data into template context
 
        c.cs_repo = c.pull_request.org_repo
 
        (c.cs_ref_type,
 
         c.cs_ref_name,
 
         c.cs_rev) = c.pull_request.org_ref.split(':')
 

	
 
        c.a_repo = c.pull_request.other_repo
 
        (c.a_ref_type,
 
         c.a_ref_name,
 
         c.a_rev) = c.pull_request.other_ref.split(':') # a_rev is ancestor
 

	
 
        org_scm_instance = c.cs_repo.scm_instance # property with expensive cache invalidation check!!!
 
        c.cs_ranges = []
 
        for x in c.pull_request.revisions:
 
            try:
 
                c.cs_ranges.append(org_scm_instance.get_changeset(x))
 
            except ChangesetDoesNotExistError:
 
                c.cs_ranges = []
 
                h.flash(_('Revision %s not found in %s') % (x, c.cs_repo.repo_name),
 
                    'error')
 
                break
 
        c.cs_ranges_org = None # not stored and not important and moving target - could be calculated ...
 
        revs = [ctx.revision for ctx in reversed(c.cs_ranges)]
 
        c.jsdata = graph_data(org_scm_instance, revs)
 

	
 
        c.is_range = False
 
        try:
 
            if c.a_ref_type == 'rev': # this looks like a free range where target is ancestor
 
                cs_a = org_scm_instance.get_changeset(c.a_rev)
 
                root_parents = c.cs_ranges[0].parents
 
                c.is_range = cs_a in root_parents
 
                #c.merge_root = len(root_parents) > 1 # a range starting with a merge might deserve a warning
 
        except ChangesetDoesNotExistError: # probably because c.a_rev not found
 
            pass
 
        except IndexError: # probably because c.cs_ranges is empty, probably because revisions are missing
 
            pass
 

	
 
        avail_revs = set()
 
        avail_show = []
 
        c.cs_branch_name = c.cs_ref_name
 
        c.a_branch_name = None
 
        other_scm_instance = c.a_repo.scm_instance
 
        c.update_msg = ""
 
        c.update_msg_other = ""
 
        try:
 
            if not c.cs_ranges:
 
                c.update_msg = _('Error: changesets not found when displaying pull request from %s.') % c.cs_rev
 
            elif org_scm_instance.alias == 'hg' and c.a_ref_name != 'ancestor':
 
                if c.cs_ref_type != 'branch':
 
                    c.cs_branch_name = org_scm_instance.get_changeset(c.cs_ref_name).branch # use ref_type ?
 
                c.a_branch_name = c.a_ref_name
 
                if c.a_ref_type != 'branch':
 
                    try:
 
                        c.a_branch_name = other_scm_instance.get_changeset(c.a_ref_name).branch # use ref_type ?
 
                    except EmptyRepositoryError:
 
                        c.a_branch_name = 'null' # not a branch name ... but close enough
 
                # candidates: descendants of old head that are on the right branch
 
                #             and not are the old head itself ...
 
                #             and nothing at all if old head is a descendant of target ref name
 
                if not c.is_range and other_scm_instance._repo.revs('present(%s)::&%s', c.cs_ranges[-1].raw_id, c.a_branch_name):
 
                    c.update_msg = _('This pull request has already been merged to %s.') % c.a_branch_name
 
                elif c.pull_request.is_closed():
 
                    c.update_msg = _('This pull request has been closed and can not be updated.')
 
                else: # look for descendants of PR head on source branch in org repo
 
                    avail_revs = org_scm_instance._repo.revs('%s:: & branch(%s)',
 
                                                             revs[0], c.cs_branch_name)
 
                    if len(avail_revs) > 1: # more than just revs[0]
 
                        # also show changesets that not are descendants but would be merged in
 
                        targethead = other_scm_instance.get_changeset(c.a_branch_name).raw_id
 
                        if org_scm_instance.path != other_scm_instance.path:
 
                            # Note: org_scm_instance.path must come first so all
 
                            # valid revision numbers are 100% org_scm compatible
 
                            # - both for avail_revs and for revset results
 
                            hgrepo = mercurial.unionrepo.makeunionrepository(org_scm_instance.baseui,
 
                                                                   safe_bytes(org_scm_instance.path),
 
                                                                   safe_bytes(other_scm_instance.path))
 
                        else:
 
                            hgrepo = org_scm_instance._repo
 
                        show = set(hgrepo.revs('::%ld & !::parents(%s) & !::%s',
 
                                               avail_revs, revs[0], targethead))
 
                        if show:
 
                            c.update_msg = _('The following additional changes are available on %s:') % c.cs_branch_name
 
                        else:
 
                            c.update_msg = _('No additional changesets found for iterating on this pull request.')
 
                    else:
 
                        show = set()
 
                        avail_revs = set() # drop revs[0]
 
                        c.update_msg = _('No additional changesets found for iterating on this pull request.')
 

	
 
                    # TODO: handle branch heads that not are tip-most
 
                    brevs = org_scm_instance._repo.revs('%s - %ld - %s', c.cs_branch_name, avail_revs, revs[0])
 
                    if brevs:
kallithea/i18n/fr/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -448,779 +448,782 @@ msgstr "Groupe de dépôts %s mis à jour"
 
msgid "Error occurred during update of repository group %s"
 
msgstr ""
 
"Une erreur est survenue durant la mise à jour du groupe de dépôts %s"
 

	
 
msgid "This group contains %s repositories and cannot be deleted"
 
msgstr "Ce groupe contient %s dépôts et ne peut être supprimé"
 

	
 
msgid "This group contains %s subgroups and cannot be deleted"
 
msgstr "Ce groupe contient %s sous-groupes et ne peut pas être supprimé"
 

	
 
msgid "Removed repository group %s"
 
msgstr "Groupe de dépôts %s supprimé"
 

	
 
msgid "Error occurred during deletion of repository group %s"
 
msgstr ""
 
"Une erreur est survenue durant la suppression du groupe de dépôts %s"
 

	
 
msgid "Cannot revoke permission for yourself as admin"
 
msgstr "Impossible de révoquer votre permission d'administrateur"
 

	
 
msgid "Repository group permissions updated"
 
msgstr "Permissions du groupe de dépôts mises à jour"
 

	
 
msgid "An error occurred during revoking of permission"
 
msgstr "Une erreur est survenue durant la révocation de la permission"
 

	
 
msgid "Error creating repository %s"
 
msgstr "Erreur de création du dépôt %s"
 

	
 
msgid "Created repository %s from %s"
 
msgstr "Dépôt %s créé depuis %s"
 

	
 
msgid "Forked repository %s as %s"
 
msgstr "dépôt %s forké en tant que %s"
 

	
 
msgid "Created repository %s"
 
msgstr "Dépôt %s créé"
 

	
 
msgid "Repository %s updated successfully"
 
msgstr "Dépôt %s mis à jour avec succès"
 

	
 
msgid "Error occurred during update of repository %s"
 
msgstr "Une erreur est survenue durant la mise à jour du dépôt %s"
 

	
 
msgid "Detached %s forks"
 
msgstr "%s forks détachés"
 

	
 
msgid "Deleted %s forks"
 
msgstr "%s forks supprimés"
 

	
 
msgid "Deleted repository %s"
 
msgstr "Dépôt %s supprimé"
 

	
 
msgid "Cannot delete repository %s which still has forks"
 
msgstr "Impossible de supprimer le dépôt %s : des forks y sont attachés"
 

	
 
msgid "An error occurred during deletion of %s"
 
msgstr "Erreur pendant la suppression de %s"
 

	
 
msgid "Repository permissions updated"
 
msgstr "Permissions du dépôt mises à jour"
 

	
 
msgid "Field validation error: %s"
 
msgstr "Erreur de validation du champ : %s"
 

	
 
msgid "An error occurred during creation of field: %r"
 
msgstr "Une erreur est survenue durant la création du champ : %r"
 

	
 
msgid "An error occurred during removal of field"
 
msgstr "Une erreur est survenue durant la suppression du champ"
 

	
 
msgid "-- Not a fork --"
 
msgstr "-- Pas un fork --"
 

	
 
msgid "Updated repository visibility in public journal"
 
msgstr "La visibilité du dépôt dans le journal public a été mise à jour"
 

	
 
msgid "An error occurred during setting this repository in public journal"
 
msgstr ""
 
"Une erreur est survenue durant la configuration du journal public pour ce "
 
"dépôt"
 

	
 
msgid "Nothing"
 
msgstr "[Aucun dépôt]"
 

	
 
msgid "Marked repository %s as fork of %s"
 
msgstr "Le dépôt %s a été marké comme fork de %s"
 

	
 
msgid "An error occurred during this operation"
 
msgstr "Une erreur est survenue durant cette opération"
 

	
 
msgid "Pulled from remote location"
 
msgstr "Les changements distants ont été récupérés"
 

	
 
msgid "An error occurred during pull from remote location"
 
msgstr "Une erreur est survenue durant le pull depuis la source distante"
 

	
 
msgid "An error occurred during deletion of repository stats"
 
msgstr ""
 
"Une erreur est survenue durant la suppression des statistiques du dépôt"
 

	
 
msgid "Updated VCS settings"
 
msgstr "Réglages des gestionnaires de versions mis à jour"
 

	
 
msgid ""
 
"Unable to activate hgsubversion support. The \"hgsubversion\" library is "
 
"missing"
 
msgstr ""
 
"Impossible d'activer la prise en charge de hgsubversion. La bibliothèque "
 
"« hgsubversion » est manquante"
 

	
 
msgid "Error occurred while updating application settings"
 
msgstr ""
 
"Une erreur est survenue durant la mise à jour des réglages de "
 
"l'application"
 

	
 
msgid "Repositories successfully rescanned. Added: %s. Removed: %s."
 
msgstr "Dépôts ré-analysés avec succès. Ajouté : %s. Supprimé : %s."
 

	
 
msgid "Invalidated %s repositories"
 
msgstr "%s dépôts invalidés"
 

	
 
msgid "Updated application settings"
 
msgstr "Réglages mis à jour"
 

	
 
msgid "Updated visualisation settings"
 
msgstr "Réglages d’affichage mis à jour"
 

	
 
msgid "Error occurred during updating visualisation settings"
 
msgstr ""
 
"Une erreur est survenue durant la mise à jour des réglages de "
 
"visualisation"
 

	
 
msgid "Please enter email address"
 
msgstr "Veuillez entrer votre adresse e-mail"
 

	
 
msgid "Send email task created"
 
msgstr "Tâche d'envoi d'e-mail créée"
 

	
 
msgid "Hook already exists"
 
msgstr "Le hook existe déjà"
 

	
 
msgid "Builtin hooks are read-only. Please use another hook name."
 
msgstr ""
 
"Les hooks intégrés sont en lecture seule. Merci de choisir un autre nom "
 
"pour le hook."
 

	
 
msgid "Added new hook"
 
msgstr "Le nouveau hook a été ajouté"
 

	
 
msgid "Updated hooks"
 
msgstr "Hooks mis à jour"
 

	
 
msgid "Error occurred during hook creation"
 
msgstr "Une erreur est survenue durant la création du hook"
 

	
 
msgid "Whoosh reindex task scheduled"
 
msgstr "La tâche de réindexation Whoosh a été planifiée"
 

	
 
msgid "Created user group %s"
 
msgstr "Groupe d'utilisateurs %s créé"
 

	
 
msgid "Error occurred during creation of user group %s"
 
msgstr ""
 
"Une erreur est survenue durant la création du groupe d'utilisateurs %s"
 

	
 
msgid "Updated user group %s"
 
msgstr "Groupe d'utilisateurs %s mis à jour"
 

	
 
msgid "Error occurred during update of user group %s"
 
msgstr ""
 
"Une erreur est survenue durant la mise à jour du groupe d'utilisateurs %s"
 

	
 
msgid "Successfully deleted user group"
 
msgstr "Groupe d'utilisateurs supprimé avec succès"
 

	
 
msgid "An error occurred during deletion of user group"
 
msgstr ""
 
"Une erreur est survenue durant la suppression du groupe d'utilisateurs"
 

	
 
msgid "Target group cannot be the same"
 
msgstr "Le groupe cible ne peut pas être le même"
 

	
 
msgid "User group permissions updated"
 
msgstr "Permissions du groupe d'utilisateurs mises à jour"
 

	
 
msgid "Updated permissions"
 
msgstr "Permissions mises à jour"
 

	
 
msgid "An error occurred during permissions saving"
 
msgstr "Une erreur est survenue durant l’enregistrement des permissions"
 

	
 
msgid "Created user %s"
 
msgstr "Utilisateur %s créé"
 

	
 
msgid "Error occurred during creation of user %s"
 
msgstr "Une erreur est survenue durant la création de l'utilisateur %s"
 

	
 
msgid "User updated successfully"
 
msgstr "L’utilisateur a été mis à jour avec succès"
 

	
 
msgid "Successfully deleted user"
 
msgstr "Utilisateur supprimé avec succès"
 

	
 
msgid "An error occurred during deletion of user"
 
msgstr "Une erreur est survenue durant la suppression de l’utilisateur"
 

	
 
msgid "The default user cannot be edited"
 
msgstr "L'utilisateur par défaut ne peut pas être modifié"
 

	
 
msgid "Added IP address %s to user whitelist"
 
msgstr "L'adresse IP %s a été ajoutée à la liste blanche"
 

	
 
msgid "An error occurred while adding IP address"
 
msgstr "Une erreur est survenue durant la sauvegarde d'IP"
 

	
 
msgid "Removed IP address from user whitelist"
 
msgstr "L'adresse IP a été supprimée de la liste blanche"
 

	
 
msgid "You need to be a registered user to perform this action"
 
msgstr ""
 
"Vous devez être un utilisateur enregistré pour effectuer cette action"
 

	
 
msgid "You need to be signed in to view this page"
 
msgstr "Vous devez être connecté pour visualiser cette page"
 

	
 
msgid ""
 
"CSRF token leak has been detected - all form tokens have been expired"
 
msgstr ""
 
"Une fuite de jeton CSRF a été détectée - tous les jetons de formulaire "
 
"sont considérés comme expirés"
 

	
 
msgid "Repository not found in the filesystem"
 
msgstr "Dépôt non trouvé sur le système de fichiers"
 

	
 
msgid "Changeset for %s %s not found in %s"
 
msgstr "Ensemble de changements pour %s %s non trouvé dans %s"
 

	
 
msgid "SSH access is disabled."
 
msgstr "L'accès SSH est désactivé."
 

	
 
msgid "Binary file"
 
msgstr "Fichier binaire"
 

	
 
msgid ""
 
"Changeset was too big and was cut off, use diff menu to display this diff"
 
msgstr ""
 
"Cet ensemble de changements était trop gros pour être affiché et a été "
 
"découpé, utilisez le menu « diff » pour afficher les différences"
 

	
 
msgid "No changes detected"
 
msgstr "Aucun changement détecté"
 

	
 
msgid "Deleted branch: %s"
 
msgstr "Branche supprimée : %s"
 

	
 
msgid "Created tag: %s"
 
msgstr "Étiquette créée : %s"
 

	
 
msgid "Changeset %s not found"
 
msgstr "Ensemble de changements %s non trouvé"
 

	
 
msgid "Show all combined changesets %s->%s"
 
msgstr "Afficher les changements combinés %s->%s"
 

	
 
msgid "Compare view"
 
msgstr "Vue de comparaison"
 

	
 
msgid "and"
 
msgstr "et"
 

	
 
msgid "%s more"
 
msgstr "%s de plus"
 

	
 
msgid "revisions"
 
msgstr "révisions"
 

	
 
msgid "Fork name %s"
 
msgstr "Nom du fork %s"
 

	
 
msgid "Pull request %s"
 
msgstr "Requête de pull %s"
 

	
 
msgid "[deleted] repository"
 
msgstr "[a supprimé] le dépôt"
 

	
 
msgid "[created] repository"
 
msgstr "[a créé] le dépôt"
 

	
 
msgid "[created] repository as fork"
 
msgstr "[a créé] le dépôt en tant que fork"
 

	
 
msgid "[forked] repository"
 
msgstr "[a forké] le dépôt"
 

	
 
msgid "[updated] repository"
 
msgstr "[a mis à jour] le dépôt"
 

	
 
msgid "[downloaded] archive from repository"
 
msgstr "[téléchargée] archive depuis le dépôt"
 

	
 
msgid "[delete] repository"
 
msgstr "[a supprimé] le dépôt"
 

	
 
msgid "[created] user"
 
msgstr "[a créé] l’utilisateur"
 

	
 
msgid "[updated] user"
 
msgstr "[a mis à jour] l’utilisateur"
 

	
 
msgid "[created] user group"
 
msgstr "[créé] groupe d'utilisateurs"
 

	
 
msgid "[updated] user group"
 
msgstr "[mis à jour] groupe d'utilisateurs"
 

	
 
msgid "[commented] on revision in repository"
 
msgstr "[a commenté] une révision du dépôt"
 

	
 
msgid "[commented] on pull request for"
 
msgstr "[a commenté] la requête de pull pour"
 

	
 
msgid "[closed] pull request for"
 
msgstr "[a fermé] la requête de pull de"
 

	
 
msgid "[pushed] into"
 
msgstr "[a pushé] dans"
 

	
 
msgid "[committed via Kallithea] into repository"
 
msgstr "[a commité via Kallithea] dans le dépôt"
 

	
 
msgid "[pulled from remote] into repository"
 
msgstr "[a pullé depuis un site distant] dans le dépôt"
 

	
 
msgid "[pulled] from"
 
msgstr "[a pullé] depuis"
 

	
 
msgid "[started following] repository"
 
msgstr "[suit maintenant] le dépôt"
 

	
 
msgid "[stopped following] repository"
 
msgstr "[ne suit plus] le dépôt"
 

	
 
msgid " and %s more"
 
msgstr " et %s de plus"
 

	
 
msgid "No files"
 
msgstr "Aucun fichier"
 

	
 
msgid "new file"
 
msgstr "nouveau fichier"
 

	
 
msgid "mod"
 
msgstr "mod"
 

	
 
msgid "del"
 
msgstr "suppr."
 

	
 
msgid "rename"
 
msgstr "renommer"
 

	
 
msgid "chmod"
 
msgstr "chmod"
 

	
 
msgid ""
 
"%s repository is not mapped to db perhaps it was created or renamed from "
 
"the filesystem please run the application again in order to rescan "
 
"repositories"
 
msgstr ""
 
"Le dépôt %s n’est pas représenté dans la base de données. Il a "
 
"probablement été créé ou renommé manuellement. Veuillez relancer "
 
"l’application pour rescanner les dépôts"
 

	
 
msgid "SSH key is missing"
 
msgstr "La clé SSH est manquante"
 

	
 
msgid ""
 
"Incorrect SSH key - it must have both a key type and a base64 part, like "
 
"'ssh-rsa ASRNeaZu4FA...xlJp='"
 
msgstr ""
 
"Clé SSH incorrecte – elle doit comporter à la fois un type de clé et une "
 
"partie base64, comme 'ssh-rsa ASRNeaZu4FA...xlJp='"
 

	
 
msgid "Incorrect SSH key - it must start with 'ssh-(rsa|dss|ed25519)'"
 
msgstr "Clé SSH incorrecte – elle doit commencer par « ssh-(rsa|dss|ed25519) »"
 
msgstr ""
 
"Clé SSH incorrecte – elle doit commencer par « ssh-(rsa|dss|ed25519) »"
 

	
 
msgid "Incorrect SSH key - unexpected characters in base64 part %r"
 
msgstr "Clé SSH incorrecte – caractères inattendus dans la partie base 64 %r"
 
msgstr ""
 
"Clé SSH incorrecte – caractères inattendus dans la partie base 64 %r"
 

	
 
msgid "Incorrect SSH key - failed to decode base64 part %r"
 
msgstr "Clé SSH incorrecte – échec du décodage de la partie base64 %r"
 

	
 
msgid "Incorrect SSH key - base64 part is not %r as claimed but %r"
 
msgstr ""
 
"Clé SSH incorrecte – la partie base 64 n'est pas %r comme il est dit mais %r"
 
"Clé SSH incorrecte – la partie base 64 n'est pas %r comme il est dit mais "
 
"%r"
 

	
 
msgid "%d year"
 
msgid_plural "%d years"
 
msgstr[0] "%d an"
 
msgstr[1] "%d ans"
 

	
 
msgid "%d month"
 
msgid_plural "%d months"
 
msgstr[0] "%d mois"
 
msgstr[1] "%d mois"
 

	
 
msgid "%d day"
 
msgid_plural "%d days"
 
msgstr[0] "%d jour"
 
msgstr[1] "%d jours"
 

	
 
msgid "%d hour"
 
msgid_plural "%d hours"
 
msgstr[0] "%d heure"
 
msgstr[1] "%d heures"
 

	
 
msgid "%d minute"
 
msgid_plural "%d minutes"
 
msgstr[0] "%d minute"
 
msgstr[1] "%d minutes"
 

	
 
msgid "%d second"
 
msgid_plural "%d seconds"
 
msgstr[0] "%d seconde"
 
msgstr[1] "%d secondes"
 

	
 
msgid "in %s"
 
msgstr "dans %s"
 

	
 
msgid "%s ago"
 
msgstr "Il y a %s"
 

	
 
msgid "in %s and %s"
 
msgstr "dans %s et %s"
 

	
 
msgid "%s and %s ago"
 
msgstr "Il y a %s et %s"
 

	
 
msgid "just now"
 
msgstr "à l’instant"
 

	
 
msgid "on line %s"
 
msgstr "à la ligne %s"
 

	
 
msgid "[Mention]"
 
msgstr "[Mention]"
 

	
 
msgid "top level"
 
msgstr "niveau supérieur"
 

	
 
msgid "Kallithea Administrator"
 
msgstr "Administrateur Kallithea"
 

	
 
msgid "Default user has no access to new repositories"
 
msgstr "L'utilisateur par défaut n'a pas accès aux nouveaux dépôts"
 

	
 
msgid "Default user has read access to new repositories"
 
msgstr "L'utilisateur par défaut a un accès en lecture aux nouveaux dépôts"
 

	
 
msgid "Default user has write access to new repositories"
 
msgstr "L'utilisateur par défaut a un accès en écriture aux nouveaux dépôts"
 

	
 
msgid "Default user has admin access to new repositories"
 
msgstr ""
 
"L'utilisateur par défaut a un accès administrateur aux nouveaux dépôts"
 

	
 
msgid "Default user has no access to new repository groups"
 
msgstr ""
 
"L'utilisateur par défaut n'a pas accès aux nouveaux groupes de dépôts"
 

	
 
msgid "Default user has read access to new repository groups"
 
msgstr ""
 
"L'utilisateur par défaut a accès en lecture seule aux nouveaux groupes de "
 
"dépôts"
 

	
 
msgid "Default user has write access to new repository groups"
 
msgstr ""
 
"L'utilisateur par défaut a accès en écriture aux nouveaux groupes de "
 
"dépôts"
 

	
 
msgid "Default user has admin access to new repository groups"
 
msgstr ""
 
"L'utilisateur par défaut a accès administrateur aux nouveaux groupes de "
 
"dépôts"
 

	
 
msgid "Default user has no access to new user groups"
 
msgstr ""
 
"L'utilisateur par défaut n'a pas accès aux nouveaux groupes d'utilisateurs"
 

	
 
msgid "Default user has read access to new user groups"
 
msgstr ""
 
"L'utilisateur par défaut a accès en lecture seule aux nouveaux groupes "
 
"d'utilisateurs"
 

	
 
msgid "Default user has write access to new user groups"
 
msgstr ""
 
"L'utilisateur par défaut a accès en écriture aux nouveaux groupes "
 
"d'utilisateurs"
 

	
 
msgid "Default user has admin access to new user groups"
 
msgstr ""
 
"L'utilisateur par défaut a un accès administrateur aux nouveaux groupes "
 
"d'utilisateurs"
 

	
 
msgid "Only admins can create repository groups"
 
msgstr "Seul un administrateur peut créer un groupe de dépôts"
 

	
 
msgid "Non-admins can create repository groups"
 
msgstr ""
 
"Les utilisateurs non-administrateurs peuvent créer des groupes de dépôts"
 

	
 
msgid "Only admins can create user groups"
 
msgstr "Seul un administrateur peut créer des groupes d'utilisateurs"
 

	
 
msgid "Non-admins can create user groups"
 
msgstr ""
 
"Les utilisateurs non-administrateurs peuvent créer des groupes "
 
"d'utilisateurs"
 

	
 
msgid "Only admins can create top level repositories"
 
msgstr "Seul un administrateur peut créer des dépôts de niveau supérieur"
 

	
 
msgid "Non-admins can create top level repositories"
 
msgstr ""
 
"Les utilisateurs non-administrateurs peuvent créer des dépôts de niveau "
 
"supérieur"
 

	
 
msgid ""
 
"Repository creation enabled with write permission to a repository group"
 
msgstr ""
 
"Création de dépôts activée avec l'accès en écriture vers un groupe de "
 
"dépôts"
 

	
 
msgid ""
 
"Repository creation disabled with write permission to a repository group"
 
msgstr ""
 
"Création de dépôts désactivée avec l'accès en écriture vers un groupe de "
 
"dépôts"
 

	
 
msgid "Only admins can fork repositories"
 
msgstr "Seul un administrateur peut faire un fork de dépôt"
 

	
 
msgid "Non-admins can fork repositories"
 
msgstr "Les utilisateurs non-administrateurs peuvent faire un fork de dépôt"
 

	
 
msgid "Registration disabled"
 
msgstr "Enregistrement désactivé"
 

	
 
msgid "User registration with manual account activation"
 
msgstr "Enregistrement des utilisateurs avec activation de compte manuelle"
 

	
 
msgid "User registration with automatic account activation"
 
msgstr ""
 
"Enregistrement des utilisateurs avec activation de compte automatique"
 

	
 
msgid "Not reviewed"
 
msgstr "Pas encore relue"
 

	
 
msgid "Under review"
 
msgstr "En cours de relecture"
 

	
 
msgid "Not approved"
 
msgstr "Non approuvée"
 

	
 
msgid "Approved"
 
msgstr "Approuvée"
 

	
 
msgid "Please enter a login"
 
msgstr "Veuillez entrer un identifiant"
 

	
 
msgid "Enter a value %(min)i characters long or more"
 
msgstr "Entrez une valeur d’au moins %(min)i caractères de long"
 

	
 
msgid "Please enter a password"
 
msgstr "Veuillez entrer un mot de passe"
 

	
 
msgid "Enter %(min)i characters or more"
 
msgstr "Entrez au moins %(min)i caractères"
 

	
 
msgid "Name must not contain only digits"
 
msgstr "Le nom ne doit pas contenir seulement des chiffres"
 

	
 
msgid ""
 
"[Comment] %(repo_name)s changeset %(short_id)s \"%(message_short)s\" on "
 
"%(branch)s"
 
msgstr ""
 
"[Commentaire] Changeset %(short_id)s « %(message_short)s » de "
 
"%(repo_name)s dans %(branch)s"
 

	
 
msgid "New user %(new_username)s registered"
 
msgstr "Nouvel utilisateur %(new_username)s enregistré"
 

	
 
msgid ""
 
"[Review] %(repo_name)s PR %(pr_nice_id)s \"%(pr_title_short)s\" from "
 
"%(pr_source_branch)s by %(pr_owner_username)s"
 
msgstr ""
 
"[Revue] %(repo_name)s PR %(pr_nice_id)s « %(pr_title_short)s » depuis "
 
"%(pr_source_branch)s par %(pr_owner_username)s"
 

	
 
msgid ""
 
"[Comment] %(repo_name)s PR %(pr_nice_id)s \"%(pr_title_short)s\" from "
 
"%(pr_source_branch)s by %(pr_owner_username)s"
 
msgstr ""
 
"[Commentaire] %(repo_name)s PR %(pr_nice_id)s « %(pr_title_short)s » "
 
"depuis %(pr_source_branch)s par %(pr_owner_username)s"
 

	
 
msgid "Closing"
 
msgstr "Fermeture"
 

	
 
msgid ""
 
"%(user)s wants you to review pull request %(pr_nice_id)s: %(pr_title)s"
 
msgstr ""
 
"%(user)s veut que vous regardiez la demande de pull %(pr_nice_id)s : "
 
"%(pr_title)s"
 

	
 
msgid "Cannot create empty pull request"
 
msgstr "Impossible de créer une requête de pull vide"
 

	
 
msgid ""
 
"Cannot create pull request - criss cross merge detected, please merge a "
 
"later %s revision to %s"
 
msgstr ""
 
"Impossible de créer la requête de pull : fusion croisée détectée, merci "
 
"de fusionner une révision plus vieille de %s vers %s"
 

	
 
msgid "You are not authorized to create the pull request"
 
msgstr "Vous n'êtes pas autorisé à créer cette requête de pull"
 

	
 
msgid "Missing changesets since the previous iteration:"
 
msgstr "Changeset manquant depuis la précédente itération :"
 

	
 
msgid "New changesets on %s %s since the previous iteration:"
 
msgstr "Nouveau changeset sur %s %s depuis la précédente itération :"
 

	
 
msgid "Ancestor didn't change - diff since previous iteration:"
 
msgstr "L'ancêtre n'a pas changé - diff depuis l'itération précédente :"
 

	
 
msgid ""
 
"This iteration is based on another %s revision and there is no simple "
 
"diff."
 
msgstr ""
 
"Cette itération est basée sur une autre révision %s et il n'y a pas de "
 
"diff simple."
 

	
 
msgid "No changes found on %s %s since previous iteration."
 
msgstr "Aucun changement constaté sur %s %s depuis l'itération précédente."
 

	
 
msgid "Closed, next iteration: %s ."
 
msgstr "Fermé, itération suivante : %s."
 

	
 
msgid "latest tip"
 
msgstr "Dernier sommet"
 

	
 
msgid "SSH key %r is invalid: %s"
 
msgstr "La clé SSH %r est invalide : %s"
 

	
 
msgid "SSH key %s is already used by %s"
 
msgstr "La clé SSH %s est déjà utilisée par %s"
 

	
 
msgid "SSH key with fingerprint %r found"
 
msgstr "Clé SSH avec l'empreinte %r trouvée"
 

	
 
msgid "New user registration"
 
msgstr "Nouveau enregistrement d'utilisateur"
 

	
 
msgid ""
 
"You can't remove this user since it is crucial for the entire application"
 
msgstr ""
 
"Vous ne pouvez pas supprimer cet utilisateur ; il est nécessaire pour le "
 
"bon fonctionnement de l’application"
 

	
 
msgid ""
 
"User \"%s\" still owns %s repositories and cannot be removed. Switch "
 
"owners or remove those repositories: %s"
 
msgstr ""
 
"L’utilisateur \"%s\" possède %s dépôts et ne peut être supprimé. Changez "
 
"les propriétaires ou supprimez ces dépôts : %s"
 

	
 
msgid ""
 
"User \"%s\" still owns %s repository groups and cannot be removed. Switch "
 
"owners or remove those repository groups: %s"
 
msgstr ""
 
"L’utilisateur \"%s\" possède %s groupes de dépôt et ne peut être "
 
"supprimé. Changez les propriétaires ou supprimez ces dépôts : %s"
 

	
 
msgid ""
 
"User \"%s\" still owns %s user groups and cannot be removed. Switch "
 
"owners or remove those user groups: %s"
 
msgstr ""
 
"L’utilisateur « %s » possède %s groupes d'utilisateurs et ne peut pas "
 
"être supprimé. Changez les propriétaires de ces groupes d'utilisateurs ou "
 
"supprimez-les : %s"
 

	
 
msgid "Password reset link"
 
msgstr "Lien de remise à zéro du mot de passe"
 

	
 
msgid "Password reset notification"
 
msgstr "Notification de réinitialisation du mot de passe"
 

	
 
msgid ""
 
"The password to your account %s has been changed using password reset "
 
"form."
 
msgstr ""
 
"Le mot de passe de votre compte %s a été changé via le formulaire de "
 
"réinitialisation du mot de passe."
 

	
 
msgid "Value cannot be an empty list"
 
msgstr "Cette valeur ne peut être une liste vide"
 

	
 
msgid "Username \"%(username)s\" already exists"
 
msgstr "Le nom d’utilisateur « %(username)s » existe déjà"
 

	
 
msgid "Username \"%(username)s\" cannot be used"
 
msgstr "Le nom d’utilisateur « %(username)s » n’est pas valide"
 

	
 
msgid ""
 
"Username may only contain alphanumeric characters underscores, periods or "
 
"dashes and must begin with an alphanumeric character or underscore"
 
msgstr ""
 
"Le nom d'utilisateur ne peut contenir que des caractères alphanumériques, "
 
"des underscores (_), points, traits d'union et doit commencer avec un "
 
"caractère alphanumérique ou un underscore"
 

	
 
msgid "The input is not valid"
 
msgstr "L'entrée n'est pas valide"
 

	
 
msgid "Username %(username)s is not valid"
 
msgstr "Le nom d’utilisateur « %(username)s » n’est pas valide"
 

	
 
msgid "Invalid user group name"
 
msgstr "Nom de groupe d'utilisateurs invalide"
 

	
 
msgid "User group \"%(usergroup)s\" already exists"
 
msgstr "Le groupe d'utilisateurs « %(usergroup)s » existe déjà"
 

	
 
msgid ""
 
"user group name may only contain alphanumeric characters underscores, "
 
"periods or dashes and must begin with alphanumeric character"
 
msgstr ""
 
"Le nom du groupe d'utilisateurs ne peut contenir que des caractères "
 
"alphanumériques, des tirets, des points, des traits d'union et doit "
 
"commencer avec un caractère alphanumérique"
 

	
 
msgid "Cannot assign this group as parent"
 
msgstr "Impossible d’assigner ce groupe en tant que parent"
 

	
 
msgid "Group \"%(group_name)s\" already exists"
 
msgstr "Le groupe « %(group_name)s » existe déjà"
 

	
 
msgid "Repository with name \"%(group_name)s\" already exists"
 
msgstr "Un dépôt portant le nom « %(group_name)s » existe déjà"
 

	
 
msgid "Invalid characters (non-ascii) in password"
 
msgstr "Caractères incorrects (non-ASCII) dans le mot de passe"
 

	
 
msgid "Invalid old password"
 
msgstr "Ancien mot de passe invalide"
 

	
 
msgid "Passwords do not match"
 
msgstr "Les mots de passe ne correspondent pas"
 

	
 
msgid "Invalid username or password"
 
msgstr "Nom d'utilisateur ou mot de passe invalide"
 

	
 
msgid "Repository name %(repo)s is not allowed"
 
msgstr "Le nom de dépôt « %(repo)s » n’est pas autorisé"
 

	
 
msgid "Repository named %(repo)s already exists"
 
msgstr "Un dépôt portant le nom « %(repo)s » existe déjà"
 

	
 
msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
 
msgstr "Le dépôt « %(repo)s » existe déjà dans le groupe « %(group)s »"
 

	
 
msgid "Repository group with name \"%(repo)s\" already exists"
 
msgstr "Un groupe de dépôts avec le nom « %(repo)s » existe déjà"
 

	
 
msgid "Invalid repository URL"
 
msgstr "URL de dépôt invalide"
 

	
 
@@ -1333,780 +1336,786 @@ msgstr "Rester connecté après un redémarrage du navigateur"
 

	
 
msgid "Forgot your password ?"
 
msgstr "Mot de passe oublié ?"
 

	
 
msgid "Don't have an account ?"
 
msgstr "Vous n’avez pas de compte ?"
 

	
 
msgid "Sign In"
 
msgstr "Connexion"
 

	
 
msgid "Password Reset"
 
msgstr "Remettre le mot de passe à zéro"
 

	
 
msgid "Reset Your Password to %s"
 
msgstr "Réinitialiser votre mot de passe à %s"
 

	
 
msgid "Reset Your Password"
 
msgstr "Réinitialiser votre mot de passe"
 

	
 
msgid "Email Address"
 
msgstr "Adresse e-mail"
 

	
 
msgid "Captcha"
 
msgstr "Captcha"
 

	
 
msgid "Send Password Reset Email"
 
msgstr "Envoyer l'E-mail de réinitialisation du mot de passe"
 

	
 
msgid ""
 
"A password reset link will be sent to the specified email address if it "
 
"is registered in the system."
 
msgstr ""
 
"Un lien de réinitialisation du mot de passe sera envoyé à l'adresse e-"
 
"mail indiquée si elle est enregistrée dans le système."
 

	
 
msgid "You are about to set a new password for the email address %s."
 
msgstr ""
 
"Vous êtes sur le point de changer le mot de passe pour l'adresse e-mail "
 
"%s."
 

	
 
msgid ""
 
"Note that you must use the same browser session for this as the one used "
 
"to request the password reset."
 
msgstr ""
 
"Vous devez utiliser la même session de navigateur pour cette opération "
 
"que celle utilisée pour la demande de réinitialisation de mot de passe."
 

	
 
msgid "Code you received in the email"
 
msgstr "Le code que vous avez reçu dans l'e-mail"
 

	
 
msgid "New Password"
 
msgstr "Nouveau mot de passe"
 

	
 
msgid "Confirm New Password"
 
msgstr "Confirmer le nouveau mot de passe"
 

	
 
msgid "Confirm"
 
msgstr "Confirmation"
 

	
 
msgid "Sign Up"
 
msgstr "Inscription"
 

	
 
msgid "Sign Up to %s"
 
msgstr "S'inscrire sur %s"
 

	
 
msgid "Re-enter password"
 
msgstr "Confirmation"
 

	
 
msgid "First Name"
 
msgstr "Prénom"
 

	
 
msgid "Last Name"
 
msgstr "Nom"
 

	
 
msgid "Email"
 
msgstr "E-mail"
 

	
 
msgid "Registered accounts are ready to use and need no further action."
 
msgstr ""
 
"Les comptes enregistrés sont prêts à être utilisés, et ne nécessitent "
 
"aucune autre action."
 

	
 
msgid "Please wait for an administrator to activate your account."
 
msgstr "Merci d'attendre qu'un administrateur active votre compte."
 

	
 
msgid "Admin Journal"
 
msgstr "Historique d’administration"
 

	
 
msgid "journal filter..."
 
msgstr "filtre du journal…"
 

	
 
msgid "Filter"
 
msgstr "Filtre"
 

	
 
msgid "%s Entry"
 
msgid_plural "%s Entries"
 
msgstr[0] "%s entrée"
 
msgstr[1] "%s entrées"
 

	
 
msgid "Action"
 
msgstr "Action"
 

	
 
msgid "Date"
 
msgstr "Date"
 

	
 
msgid "From IP"
 
msgstr "Depuis l’adresse IP"
 

	
 
msgid "No actions yet"
 
msgstr "Aucune action n’a été enregistrée pour le moment"
 

	
 
msgid "Authentication Settings"
 
msgstr "Réglages d'authentification"
 

	
 
msgid "Authentication"
 
msgstr "Authentification"
 

	
 
msgid "Authentication Plugins"
 
msgstr "Greffons d'authentification"
 

	
 
msgid "Enabled Plugins"
 
msgstr "Greffons activés"
 

	
 
msgid ""
 
"Comma-separated list of plugins; Kallithea will try user authentication "
 
"in plugin order"
 
msgstr ""
 
"Une liste séparée avec des virgules des greffons. L'ordre des greffons "
 
"est aussi celui dans lequel Kallithea va essayer d'authentifier un "
 
"utilisateur"
 

	
 
msgid "Available built-in plugins"
 
msgstr "Greffons inclus disponibles"
 

	
 
msgid "Plugin"
 
msgstr "Greffon"
 

	
 
msgid "Save"
 
msgstr "Enregistrer"
 

	
 
msgid "Repository Defaults"
 
msgstr "Réglages par défaut du dépôt"
 

	
 
msgid "Type"
 
msgstr "Type"
 

	
 
msgid "Private repository"
 
msgstr "Dépôt privé"
 

	
 
msgid ""
 
"Private repositories are only visible to people explicitly added as "
 
"collaborators."
 
msgstr ""
 
"Les dépôts privés sont visibles seulement par les utilisateurs ajoutés "
 
"comme collaborateurs."
 

	
 
msgid "Enable statistics"
 
msgstr "Activer les statistiques"
 

	
 
msgid "Enable statistics window on summary page."
 
msgstr "Afficher les statistiques sur la page du dépôt."
 

	
 
msgid "Enable downloads"
 
msgstr "Activer les téléchargements"
 

	
 
msgid "Enable download menu on summary page."
 
msgstr "Afficher le menu de téléchargements sur la page du dépôt."
 

	
 
msgid "Edit Gist"
 
msgstr "Modifier le gist"
 

	
 
msgid ""
 
"Gist was updated since you started editing. Copy your changes and click "
 
"%(here)s to reload new version."
 
msgstr ""
 
"Le gist a été mis à jour depuis que vous avez commencé à éditer. Copier "
 
"vos changements et cliquez %(here)s pour charger la nouvelle version."
 

	
 
msgid "here"
 
msgstr "ici"
 

	
 
msgid "Gist description ..."
 
msgstr "Description du gist…"
 

	
 
msgid "Gist lifetime"
 
msgstr "Durée de vie du gist"
 

	
 
msgid "Expires"
 
msgstr "Expire le"
 

	
 
msgid "Never"
 
msgstr "Jamais"
 

	
 
msgid "Update Gist"
 
msgstr "Mettre à jour le gist"
 

	
 
msgid "Cancel"
 
msgstr "Annuler"
 

	
 
msgid "Private Gists for User %s"
 
msgstr "Gists privés de l'utilisateur %s"
 

	
 
msgid "Public Gists for User %s"
 
msgstr "Gists publiques de l'utilisateur %s"
 

	
 
msgid "Public Gists"
 
msgstr "Gists publics"
 

	
 
msgid "Create New Gist"
 
msgstr "Créer un nouveau gist"
 

	
 
msgid "Created"
 
msgstr "Créé"
 

	
 
msgid "There are no gists yet"
 
msgstr "Il n'y a actuellement pas de gists"
 

	
 
msgid "New Gist"
 
msgstr "Nouveau gist"
 

	
 
msgid "Name this gist ..."
 
msgstr "Nommer ce gist…"
 

	
 
msgid "Create Private Gist"
 
msgstr "Créer un gist privé"
 

	
 
msgid "Create Public Gist"
 
msgstr "Créer un gist public"
 

	
 
msgid "Reset"
 
msgstr "Réinitialiser"
 

	
 
msgid "Gist"
 
msgstr "Gist"
 

	
 
msgid "URL"
 
msgstr "URL"
 

	
 
msgid "Public Gist"
 
msgstr "Gist public"
 

	
 
msgid "Private Gist"
 
msgstr "Gist privé"
 

	
 
msgid "Delete"
 
msgstr "Supprimer"
 

	
 
msgid "Confirm to delete this Gist"
 
msgstr "Confirmer la supprisson de ce gist"
 

	
 
msgid "Edit"
 
msgstr "Modifier"
 

	
 
msgid "Show as Raw"
 
msgstr "Montrer en brut"
 

	
 
msgid "created"
 
msgstr "créé"
 

	
 
msgid "Show as raw"
 
msgstr "Montrer en brut"
 

	
 
msgid "My Account"
 
msgstr "Mon compte"
 

	
 
msgid "Profile"
 
msgstr "Profil"
 

	
 
msgid "Email Addresses"
 
msgstr "Adresses e-mail"
 

	
 
msgid "SSH Keys"
 
msgstr "Clés SSH"
 

	
 
msgid "API Keys"
 
msgstr "Clés de l'API"
 

	
 
msgid "Owned Repositories"
 
msgstr "Dépôts possédés"
 

	
 
msgid "Watched Repositories"
 
msgstr "Dépôts surveillés"
 

	
 
msgid "Show Permissions"
 
msgstr "Afficher les permissions"
 

	
 
msgid "Built-in"
 
msgstr "Inclus"
 

	
 
msgid "Confirm to reset this API key: %s"
 
msgstr "Confirmer la remise à zéro de cette clé d'API : %s"
 

	
 
msgid "Expired"
 
msgstr "a expiré"
 

	
 
msgid "Confirm to remove this API key: %s"
 
msgstr "Confirmer la suppression de cette clé d'API : %s"
 

	
 
msgid "Remove"
 
msgstr "Supprimer"
 

	
 
msgid "No additional API keys specified"
 
msgstr "Pas de clés d'API supplémentaires spécifiées"
 

	
 
msgid "New API key"
 
msgstr "Nouvelle clé d'API"
 

	
 
msgid "Add"
 
msgstr "Ajouter"
 

	
 
msgid ""
 
"\n"
 
"API keys are used to let scripts or services access %s using your\n"
 
"account, as if you had provided the script or service with your actual\n"
 
"password.\n"
 
msgstr ""
 
"\n"
 
"Les clés API sont utilisées pour permettre à des scripts et des services "
 
"d'accéder à %s en utilisant votre compte, comme si vous aviez fourni "
 
"votre mot de passe à ces scripts ou services.\n"
 

	
 
msgid ""
 
"\n"
 
"Like passwords, API keys should therefore never be shared with others,\n"
 
"nor passed to untrusted scripts or services. If such sharing should\n"
 
"happen anyway, reset the API key on this page to prevent further use.\n"
 
msgstr ""
 
"\n"
 
"Comme les mots de passe, les clés API ne devraient donc jamais être "
 
"diffusées à des tiers, ni passés à des scripts ou services auxquels vous "
 
"ne faites pas confiance. Si cette diffusion a tout de même lieu, vous "
 
"pouvez réinitialiser la clé API sur cette page pour qu'elle ne puisse "
 
"plus être utilisée.\n"
 

	
 
msgid "Primary"
 
msgstr "Primaire"
 

	
 
msgid "Confirm to delete this email: %s"
 
msgstr "Veuillez confirmer la suppression de l’e-mail : %s"
 

	
 
msgid "No additional emails specified."
 
msgstr "Pas d'adresse e-mail supplémentaires spécifiées."
 

	
 
msgid "New email address"
 
msgstr "Nouvelle adresse e-mail"
 

	
 
msgid "Change Your Account Password"
 
msgstr "Changer le mot de passe de votre compte"
 

	
 
msgid "Current password"
 
msgstr "Mot de passe actuel"
 

	
 
msgid "New password"
 
msgstr "Nouveau mot de passe"
 

	
 
msgid "Confirm new password"
 
msgstr "Confirmer le nouveau mot de passe"
 

	
 
msgid ""
 
"This account is managed with %s and the password cannot be changed here"
 
msgstr ""
 
"Ce compte est géré avec %s et le mot de passe ne peut pas être changé ici"
 

	
 
msgid "Current IP"
 
msgstr "Adresse IP actuelle"
 

	
 
msgid "Gravatar"
 
msgstr "Gravatar"
 

	
 
msgid "Change %s avatar at"
 
msgstr "Changer l'avatar de %s sur"
 

	
 
msgid "Avatars are disabled"
 
msgstr "Les avatars sont désactivés"
 

	
 
msgid "Repositories You Own"
 
msgstr "Dépôts dont vous êtes le propriétaire"
 

	
 
msgid "Name"
 
msgstr "Nom"
 

	
 
msgid "Fingerprint"
 
msgstr "Empreinte"
 

	
 
msgid "Last Used"
 
msgstr "Dernière utilisation"
 

	
 
msgid "Confirm to remove this SSH key: %s"
 
msgstr "Confirmer la suppression de cette clé SSH : %s"
 

	
 
msgid "No SSH keys have been added"
 
msgstr "Aucune clé SSH n'a été ajoutée"
 

	
 
msgid "New SSH key"
 
msgstr "Nouvelle clé SSH"
 

	
 
msgid "Public key"
 
msgstr "Clé publique"
 

	
 
msgid "Public key (contents of e.g. ~/.ssh/id_rsa.pub)"
 
msgstr "Clé publique (contenus de par ex. ~/.ssh/id_rsa.pub)"
 

	
 
msgid "Repositories You are Watching"
 
msgstr "Dépôts que vous surveillez"
 

	
 
msgid "Default Permissions"
 
msgstr "Permissions par défaut"
 

	
 
msgid "Global"
 
msgstr "Global"
 

	
 
msgid "IP Whitelist"
 
msgstr "Liste blanche d'adresses IP"
 

	
 
msgid "Anonymous access"
 
msgstr "Accès anonyme"
 

	
 
msgid "Allow anonymous access"
 
msgstr "Permettre l'accès anonyme"
 

	
 
msgid ""
 
"Allow access to Kallithea without needing to log in. Anonymous users use "
 
"%s user permissions."
 
msgstr ""
 
"Autoriser l'accès à Kallithea sans le besoin de se connecter. Les "
 
"utilisateurs anonymes ont les permissions de l'utilisateur %s."
 

	
 
msgid ""
 
"All default permissions on each repository will be reset to chosen "
 
"permission, note that all custom default permission on repositories will "
 
"be lost"
 
msgstr ""
 
"Toutes les permissions par défaut de chaque dépôt vont être "
 
"réinitialisées aux valeurs choisies. Notez que toutes les permissions par "
 
"défaut personnalisées sur les dépôts seront perdues"
 

	
 
msgid "Apply to all existing repositories"
 
msgstr "Appliquer à tous les dépôts existants"
 

	
 
msgid "Permissions for the Default user on new repositories."
 
msgstr "Permissions pour l'utilisateur par défaut sur les nouveaux dépôts."
 

	
 
msgid "Repository group"
 
msgstr "Groupe de dépôt"
 

	
 
msgid ""
 
"All default permissions on each repository group will be reset to chosen "
 
"permission, note that all custom default permission on repository groups "
 
"will be lost"
 
msgstr ""
 
"Toutes les permissions par défaut de chaque groupe de dépôts vont être "
 
"réinitialisées aux valeurs choisies. Notez que toutes les permissions par "
 
"défaut personnalisées sur les groupes de dépôts seront perdues"
 

	
 
msgid "Apply to all existing repository groups"
 
msgstr "Appliquer à tous les groupes de dépôts existants"
 

	
 
msgid "Permissions for the Default user on new repository groups."
 
msgstr ""
 
"Permissions pour l'utilisateur par défaut sur les nouveaux groupes de "
 
"dépôts."
 

	
 
msgid "User group"
 
msgstr "Groupe d'utilisateurs"
 

	
 
msgid ""
 
"All default permissions on each user group will be reset to chosen "
 
"permission, note that all custom default permission on user groups will "
 
"be lost"
 
msgstr ""
 
"Toutes les permissions par défaut de chaque groupe d'utilisateurs vont "
 
"être réinitialisées aux valeurs choisies. Notez que toutes les "
 
"permissions par défaut personnalisées sur les groupes d'utilisateurs "
 
"seront perdues"
 

	
 
msgid "Apply to all existing user groups"
 
msgstr "Appliquer à tous les groupes d'utilisateurs existants"
 

	
 
msgid "Permissions for the Default user on new user groups."
 
msgstr ""
 
"Permissions pour l'utilisateur par défaut sur les nouveaux groupes "
 
"d'utilisateurs."
 

	
 
msgid "Top level repository creation"
 
msgstr "Création de dépôt de niveau supérieur"
 

	
 
msgid ""
 
"Enable this to allow non-admins to create repositories at the top level."
 
msgstr ""
 
"Activer pour autoriser les non-administrateurs à créer des dépôts au "
 
"niveau supérieur."
 

	
 
msgid ""
 
"Note: This will also give all users API access to create repositories "
 
"everywhere. That might change in future versions."
 
msgstr ""
 
"Note : Cela autorisera également tous les utilisateurs à utiliser l'API "
 
"pour créer des dépôts partout. Ce comportement peut changer dans des "
 
"versions futures."
 

	
 
msgid "Repository creation with group write access"
 
msgstr "Création de dépôts avec l'accès en écriture du groupe"
 

	
 
msgid ""
 
"With this, write permission to a repository group allows creating "
 
"repositories inside that group. Without this, group write permissions "
 
"mean nothing."
 
msgstr ""
 
"Avec ceci, le droit d'écriture dans un groupe de dépôt donne le droit de "
 
"créer des dépôts dans ce groupe. Sans ceci, le droit d'écriture pour les "
 
"groupes n'a pas d'impact."
 

	
 
msgid "User group creation"
 
msgstr "Création de groupes d'utilisateurs"
 

	
 
msgid "Enable this to allow non-admins to create user groups."
 
msgstr ""
 
"Activer pour autoriser les non-administrateurs à créer des groupes "
 
"d'utilisateurs."
 

	
 
msgid "Repository forking"
 
msgstr "Fork de dépôt"
 

	
 
msgid "Enable this to allow non-admins to fork repositories."
 
msgstr ""
 
"Activer pour autoriser les non-administrateurs à faire des fork de dépôt."
 

	
 
msgid "Registration"
 
msgstr "Enregistrement"
 

	
 
msgid "External auth account activation"
 
msgstr "Activation de l'authentification externe"
 

	
 
msgid "Confirm to delete this IP address: %s"
 
msgstr "Confirmer la suppression de cette adresse IP : %s"
 

	
 
msgid "All IP addresses are allowed."
 
msgstr "Toutes les adresses IP sont autorisées."
 

	
 
msgid "New IP address"
 
msgstr "Nouvelle adresse IP"
 

	
 
msgid "Repository Groups"
 
msgstr "Groupes de dépôts"
 

	
 
msgid "Group name"
 
msgstr "Nom de groupe"
 

	
 
msgid "Group parent"
 
msgstr "Parent du groupe"
 

	
 
msgid "Copy parent group permissions"
 
msgstr "Copier les permissions du groupe parent"
 

	
 
msgid "Copy permission set from parent repository group."
 
msgstr "Copier les permissions à partir du groupe de dépôts parent."
 

	
 
msgid "%s Repository Group Settings"
 
msgstr "Options du groupe de dépôts %s"
 

	
 
msgid "Add Child Group"
 
msgstr "Ajouter un groupe enfant"
 

	
 
msgid "Settings"
 
msgstr "Options"
 

	
 
msgid "Advanced"
 
msgstr "Avancé"
 

	
 
msgid "Permissions"
 
msgstr "Permissions"
 

	
 
msgid "Repository Group: %s"
 
msgstr "Groupe de dépôts : %s"
 

	
 
msgid "Top level repositories"
 
msgstr "Dépôts de niveau supérieur"
 

	
 
msgid "Total repositories"
 
msgstr "Dépôts totaux"
 

	
 
msgid "Children groups"
 
msgstr "Groupes enfants"
 

	
 
msgid "Created on"
 
msgstr "Créé le"
 

	
 
msgid "Confirm to delete this group: %s with %s repository"
 
msgid_plural "Confirm to delete this group: %s with %s repositories"
 
msgstr[0] "Confirmer la suppression de ce groupe : %s avec %s dépôt"
 
msgstr[1] "Confirmer la suppression de ce groupe : %s avec %s dépôts"
 

	
 
msgid "Delete this repository group"
 
msgstr "Supprimer ce groupe de dépôts"
 

	
 
msgid "Not visible"
 
msgstr "Non visible"
 

	
 
msgid "Visible"
 
msgstr "Visible"
 

	
 
msgid "Add repos"
 
msgstr "Ajouter un dépôt"
 

	
 
msgid "Add/Edit groups"
 
msgstr "Ajouter/Modifier les groupes d'utilisateurs"
 

	
 
msgid "User/User Group"
 
msgstr "Utilisateur/groupe d'utilisateurs"
 

	
 
msgid "Default"
 
msgstr "Par défaut"
 

	
 
msgid "Revoke"
 
msgstr "Révoquer"
 

	
 
msgid "Add new"
 
msgstr "Ajouter un nouveau"
 

	
 
msgid "Apply to children"
 
msgstr "Appliquer aux enfants"
 

	
 
msgid "Both"
 
msgstr "Les deux"
 

	
 
msgid ""
 
"Set or revoke permission to all children of that group, including non-"
 
"private repositories and other groups if selected."
 
msgstr ""
 
"Ajouter ou révoquer la permission pour tous les enfants de ce groupe, y "
 
"compris les dépôts non-privés et les autres groupes si sélectionné."
 

	
 
msgid "Remove this group"
 
msgstr "Supprimer ce groupe"
 

	
 
msgid "Confirm to delete this group"
 
msgstr "Confirmer la suppression de ce groupe"
 

	
 
msgid "Repository group %s"
 
msgstr "Groupe de dépôts %s"
 

	
 
msgid "Repository Groups Administration"
 
msgstr "Administration des groupes de dépôts"
 

	
 
msgid "Number of Top-level Repositories"
 
msgstr "Nombre de dépôts de niveau supérieur"
 

	
 
msgid "Clone remote repository"
 
msgstr "Cloner le dépôt distant"
 

	
 
msgid ""
 
"Optional: URL of a remote repository. If set, the repository will be "
 
"created as a clone from this URL."
 
msgstr ""
 
"Optionnel : URL d'un dépôt distant. Si renseigné, le dépôt sera créé "
 
"comme un clone à partir de cette URL."
 

	
 
msgid ""
 
"Keep it short and to the point. Use a README file for longer descriptions."
 
msgstr ""
 
"Gardez cette description précise et concise. Utilisez un fichier README "
 
"pour des descriptions plus détaillées."
 

	
 
msgid "Optionally select a group to put this repository into."
 
msgstr "Sélectionnez un groupe (optionel) dans lequel sera placé le dépôt."
 

	
 
msgid "Type of repository to create."
 
msgstr "Type de dépôt à créer."
 

	
 
msgid "Landing revision"
 
msgstr "Révision d’arrivée"
 

	
 
msgid ""
 
"Default revision for files page, downloads, full text search index and "
 
"readme generation"
 
msgstr ""
 
"Révision par défaut pour les pages de fichiers, de téléchargement, de "
 
"l'index de recherche dans le texte complet, et de génération de "
 
"documentation (readme)"
 

	
 
msgid "%s Creating Repository"
 
msgstr "Création du dépôt %s"
 

	
 
msgid "Creating repository"
 
msgstr "Création du dépôt"
 

	
 
msgid ""
 
"Repository \"%(repo_name)s\" is being created, you will be redirected "
 
"when this process is finished.repo_name"
 
msgstr ""
 
"Le dépôt « %(repo_name)s » est en cours de création, vous allez être "
 
"redirigé quand cette opération sera terminée."
 

	
 
msgid ""
 
"We're sorry but error occurred during this operation. Please check your "
 
"Kallithea server logs, or contact administrator."
 
msgstr ""
 
"Désolé, une erreur est survenue pendant l'opération. Vérifiez les "
 
"journaux du serveur Kallithea, ou contactez votre administrateur."
 

	
 
msgid "%s Repository Settings"
 
msgstr "Réglages du dépôt %s"
 

	
 
msgid "Extra Fields"
 
msgstr "Champs supplémentaires"
 

	
 
msgid "Remote"
 
msgstr "Dépôt distant"
 

	
 
msgid "Statistics"
 
msgstr "Statistiques"
 

	
 
msgid "Parent"
 
msgstr "Parent"
 

	
 
msgid "Set"
 
msgstr "Appliquer"
 

	
 
msgid "Manually set this repository as a fork of another from the list."
 
msgstr ""
 
"Marquer manuellement ce dépôt comme fork d’un autre dépôt de la liste."
 

	
 
msgid "Public Journal Visibility"
 
msgstr "Visibilité du journal public"
 

	
 
msgid "Remove from public journal"
 
msgstr "Supprimer du journal public"
 

	
 
msgid "Add to Public Journal"
 
msgstr "Ajouter au journal public"
 

	
 
msgid ""
 
"All actions done in this repository will be visible to everyone in the "
 
"public journal."
 
msgstr ""
 
"Les actions réalisées sur ce dépôt seront visibles à tous depuis le "
 
"journal public."
 

	
 
msgid "Confirm to delete this repository: %s"
 
msgstr "Voulez-vous vraiment supprimer le dépôt %s ?"
 

	
 
msgid "Delete this Repository"
 
msgstr "Supprimer ce dépôt"
 

	
 
msgid "This repository has %s fork"
 
msgid_plural "This repository has %s forks"
 
msgstr[0] "Ce dépôt a %s fork"
 
msgstr[1] "Ce dépôt a %s forks"
 

	
 
msgid "Detach forks"
 
msgstr "Détacher les forks"
 

	
 
msgid "Delete forks"
 
msgstr "Supprimer les forks"
 

	
 
msgid ""
 
"The deleted repository will be moved away and hidden until the "
 
"administrator expires it. The administrator can both permanently delete "
 
"it or restore it."
 
msgstr ""
 
"Le dépôt supprimé sera mis de côté et caché jusqu'à ce que "
 
"l'administrateur le fasse expirer. L'administrateur peut soit le "
 
"supprimer définitivement, soit le restaurer."
 

	
 
msgid "Label"
 
msgstr "Libellé"
 

	
 
msgid "Key"
 
msgstr "Clé"
 

	
 
msgid "Confirm to delete this field: %s"
 
msgstr "Voulez-vous vraiment supprimer ce champ : %s ?"
 

	
 
msgid "New field key"
 
msgstr "Clé du nouveau champ"
 

	
 
msgid "New field label"
 
msgstr "Libellé du nouveau champ"
 

	
 
msgid "Enter short label"
 
msgstr "Saisir un libellé court"
 

	
 
msgid "New field description"
 
msgstr "Description du nouveau champ"
 

	
 
msgid "Enter description of a field"
kallithea/i18n/nb_NO/LC_MESSAGES/kallithea.po
Show inline comments
 
# Copyright (C) 2017 Various authors, licensing as GPLv3
 
# This file is distributed under the same license as the Kallithea project.
 

	
 
msgid ""
 
msgstr ""
 
"Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
 
"Language: nb_NO\n"
 
"MIME-Version: 1.0\n"
 
"Content-Type: text/plain; charset=UTF-8\n"
 
"Content-Transfer-Encoding: 8bit\n"
 
"Plural-Forms: nplurals=2; plural=n != 1;\n"
 

	
 
msgid "There are no changesets yet"
 
msgstr "Ingen endringssett enda"
 

	
 
msgid "None"
 
msgstr "Ingen"
 

	
 
msgid "(closed)"
 
msgstr "(lukket)"
 

	
 
msgid "Show whitespace"
 
msgstr "Vis blanktegn"
 

	
 
msgid "Ignore whitespace"
 
msgstr "Ignorer blanktegn"
 

	
 
msgid "Increase diff context to %(num)s lines"
 
msgstr "Øk diff-bindeleddsinformasjon til %(num)s linjer"
 

	
 
msgid "Successfully deleted pull request %s"
 
msgstr "Slettet flettingsforespørsel %s"
 

	
 
msgid "Such revision does not exist for this repository"
 
msgstr "En slik revisjon funnes ikke for denne pakkebrønnen"
 

	
 
msgid "Could not find other repository %s"
 
msgstr "Fant ikke annen pakkebrønn %s"
 

	
 
msgid "Cannot compare repositories of different types"
 
msgstr "Kan ikke sammenligne pakkebrønner av forskjellige typer"
 

	
 
msgid "Cannot compare repositories without using common ancestor"
 
msgstr "Kan ikke sammenligne pakkebrønner uten bruk av felles opphav"
 

	
 
msgid "No response"
 
msgstr "Ingen respons"
 

	
 
msgid "Unknown error"
 
msgstr "Ukjent feil"
 

	
 
msgid ""
 
"The request could not be understood by the server due to malformed syntax."
 
msgstr ""
 
"Forespørselen kunne ikke forstås av tjeneren som følge av feilaktig "
 
"syntaks."
 

	
 
msgid "Unauthorized access to resource"
 
msgstr "Uautorisert tilgang til ressurs"
 

	
 
msgid "You don't have permission to view this page"
 
msgstr "Du har ikke tilgang til å se denne siden"
 

	
 
msgid "The resource could not be found"
 
msgstr "Kunne ikke finne ressursen"
 

	
 
msgid ""
 
"The server encountered an unexpected condition which prevented it from "
 
"fulfilling the request."
 
msgstr ""
 
"Tjeneren støtte på en uventet tilstand som forhindret utøvelse av "
 
"forespørsel."
 

	
 
msgid "%s committed on %s"
 
msgstr "%s sendte inn %s"
 

	
 
msgid "Changeset was too big and was cut off..."
 
msgstr "Endringsettet var for stort og har blitt avskåret…"
 

	
 
msgid "Click here to add new file"
 
msgstr "Klikk her for å legge til ny fil"
 

	
 
msgid "Deleted file %s via Kallithea"
 
msgstr "Slettet filen %s via Kallithea"
 

	
 
msgid "Successfully deleted file %s"
 
msgstr "Filen %s ble slettet"
 

	
 
msgid "Error occurred during commit"
 
msgstr "Feil inntraff under innsendelse"
 

	
 
msgid "You can only edit files with revision being a valid branch"
 
msgstr ""
 
"Du kan bare redigere filer med en revisjon som er en gyldig avgrening"
 

	
 
msgid "Edited file %s via Kallithea"
 
msgstr "Filen %s ble endret via Kallithea"
 

	
 
msgid "No changes"
 
msgstr "Ingen endringer"
 

	
 
msgid "Successfully committed to %s"
 
msgstr "Innsendt til %s"
 

	
 
msgid "Added file via Kallithea"
 
msgstr "Fil lagt til via Kallithea"
 

	
 
msgid "No content"
 
msgstr "Inget innhold"
 

	
 
msgid "No filename"
 
msgstr "Inget filnavn"
 

	
 
msgid "Location must be relative path and must not contain .. in path"
 
msgstr ""
 
"Plasseringen må være en relativ sti, og kan ikke inneholde .. i stien"
 

	
 
msgid "Downloads disabled"
 
msgstr "Nedlastinger avskrudd"
 

	
 
msgid "Unknown revision %s"
 
msgstr "Ukjent revisjon %s"
 

	
 
msgid "Empty repository"
 
msgstr "Tom pakkebrønn"
 

	
 
msgid "Unknown archive type"
 
msgstr "Ukjent arkivtype"
 

	
 
msgid "Changesets"
 
msgstr "Endringssett"
 

	
 
msgid "Branches"
 
msgstr "Forgreninger"
 

	
 
msgid "Tags"
 
msgstr "Etiketter"
 

	
 
msgid "An error occurred during repository forking %s"
 
msgstr "En uventet feil inntraff under forgrening av pakkebrønnen %s"
 

	
 
msgid "Groups"
 
msgstr "Grupper"
 

	
 
msgid "Repositories"
 
msgstr "Pakkebrønner"
 

	
 
msgid "Closed Branches"
 
msgstr "Lukkede forgreninger"
 

	
 
msgid "Tag"
 
msgstr "Etikett"
 

	
 
msgid "Bookmark"
 
msgstr "Bokmerke"
 

	
 
msgid "Public Journal"
 
msgstr "Offentlig loggbok"
 

	
 
msgid "Journal"
 
msgstr "Loggbok"
 

	
 
msgid "A password reset confirmation code has been sent"
 
msgstr "Passordbekreftelseskode sendt"
 

	
 
msgid "Invalid password reset token"
 
msgstr "Ugyldig passordtilbakestillingssymbol"
 

	
 
msgid "Successfully updated password"
 
msgstr "Passord oppdatert"
 

	
 
msgid "%s (closed)"
 
msgstr "%s (lukket)"
 

	
 
msgid "Changeset"
 
msgstr "Endringssett"
 

	
 
msgid "Peer branches"
 
msgstr "Likemennsforgreninger"
 

	
 
msgid "Bookmarks"
 
msgstr "Bokmerker"
 

	
 
msgid "Error creating pull request: %s"
 
msgstr "Feil ved opprettelse av ny innsendelsesforespørsel: %s"
 

	
 
msgid "Error occurred while creating pull request"
 
msgstr "Feil inntraff under opprettelse av innsendelsesforespørsel"
 

	
 
msgid "Successfully opened new pull request"
 
msgstr "Åpnet en ny innsendelsesforespørsel"
 

	
 
msgid "No description"
 
msgstr "Ingen beskrivelse"
 

	
 
msgid "Pull request updated"
 
msgstr "Innsendingsforespørsel oppdatert"
 

	
 
msgid "Successfully deleted pull request"
 
msgstr "Slettet innsendingsforespørsel"
 

	
 
msgid "This pull request has already been merged to %s."
 
msgstr "Denne innsendingsforespørselen har allerede blitt flettet inn i %s."
 

	
 
msgid "This pull request has been closed and can not be updated."
 
msgstr ""
 
"Denne innsendingsforespørselen har blitt lukket, og kan ikke oppdateres."
 

	
 
msgid "Note: Branch %s has another head: %s."
 
msgstr "Merk: Forgreningen %s har et annet hode: %s."
 

	
 
msgid "Invalid search query. Try quoting it."
 
msgstr "Ugyldig søkespørring. Prøv å sette den i sistattegn."
 

	
 
msgid "The server has no search index."
 
msgstr "Tjeneren har ingen søkeindeks."
 

	
 
msgid "An error occurred during search operation."
 
msgstr "Feil inntraff under søkeoperasjon."
 

	
 
msgid "No data ready yet"
 
msgstr "Ingen data klar enda"
 

	
 
msgid "Statistics are disabled for this repository"
 
msgstr "Statistikk er avskrudd for denne pakkebrønnen"
 

	
 
msgid "Auth settings updated successfully"
 
msgstr "Autentiseringsinnstillinger oppdatert"
 

	
 
msgid "error occurred during update of auth settings"
 
msgstr "feil inntraff under oppdatering av autentiseringsinnstillinger"
 

	
 
msgid "Default settings updated successfully"
 
msgstr "Forvalgte innstillinger oppdatert"
 

	
 
msgid "Error occurred during update of defaults"
 
msgstr "Feil inntraff under oppdatering av forvalg"
 

	
 
msgid "Forever"
 
msgstr "For alltid"
 

	
 
msgid "5 minutes"
 
msgstr "Fem minutter"
 

	
 
msgid "1 hour"
 
msgstr "Én time"
 

	
 
msgid "1 day"
 
msgstr "Én dag"
 

	
 
msgid "1 month"
 
msgstr "Én måned"
 

	
 
msgid "Lifetime"
 
msgstr "Livstid"
 

	
 
msgid "Error occurred during gist creation"
 
msgstr "Feil inntraff under gist-opprettelse"
 

	
 
msgid "Deleted gist %s"
 
msgstr "Slettet gist-en %s"
 

	
 
msgid "Unmodified"
 
msgstr "Uendret"
 

	
 
msgid "Successfully updated gist content"
 
msgstr "Oppdaterte gist-innhold"
 

	
 
msgid "Successfully updated gist data"
 
msgstr "Oppdaterte gist-data"
 

	
 
msgid "You can't edit this user since it's crucial for entire application"
 
msgstr ""
 
"Du kan ikke endre denne brukeren siden den er avgjørende for hele "
 
"programmet"
 

	
 
msgid "Your account was updated successfully"
 
msgstr "Kontoen din ble oppdatert"
 

	
 
msgid "Error occurred during update of user password"
 
msgstr "Feil inntraff under oppdatering av brukerpassord"
 

	
 
msgid "API key successfully created"
 
msgstr "API-nøkkel opprettet"
 

	
 
msgid "API key successfully reset"
 
msgstr "API-nøkkel tilbakestilt"
 

	
 
msgid "API key successfully deleted"
 
msgstr "API-nøkkel slettet"
 

	
 
msgid "SSH key %s successfully added"
 
msgstr "SSH-nøkkel %s lagt til"
 

	
 
msgid "Read"
 
msgstr "LEs"
 

	
 
msgid "Write"
 
msgstr "Skriv"
 

	
 
msgid "Admin"
 
msgstr "Admin"
 

	
 
msgid "Disabled"
 
msgstr "Avskrudd"
 

	
 
msgid "Allowed with manual account activation"
 
msgstr "Tillatt med manuell kontoaktivering"
 

	
 
msgid "Allowed with automatic account activation"
 
msgstr "Tillatt med automatisk kontoaktivering"
 

	
 
msgid "Manual activation of external account"
 
msgstr "Manuell aktivering av ekstern konto"
 

	
 
msgid "Automatic activation of external account"
 
msgstr "Automatisk aktivering av ekstern konto"
 

	
 
msgid "Enabled"
 
msgstr "Påskrudd"
 

	
 
msgid "Error occurred during update of permissions"
 
msgstr "Feil inntraff under oppdatering av tilganger"
 

	
 
msgid "Error occurred during creation of repository group %s"
 
msgstr "Feil inntraff under opprettelse av pakkebrønnsgruppen %s"
 

	
 
msgid "Created repository group %s"
 
msgstr "Opprettet pakkebrønnsgruppen %s"
 

	
 
msgid "Updated repository group %s"
 
msgstr "Oppdaterte pakkebrønnsgruppen %s"
 

	
 
msgid "Error occurred during update of repository group %s"
 
msgstr "Feil inntraff under oppdatering av pakkebrønnsgruppen %s"
 

	
 
msgid "This group contains %s repositories and cannot be deleted"
 
msgstr "Denne gruppen inneholder %s pakkebrønner og kan ikke slettes"
 

	
 
msgid "This group contains %s subgroups and cannot be deleted"
 
msgstr "Denne grunnen inneholder %s undergrupper og kan ikke slettes"
 

	
 
msgid "Removed repository group %s"
 
msgstr "Fjernet pakkebrønnsgruppen %s"
 

	
 
msgid "Error occurred during deletion of repository group %s"
 
msgstr "Feil inntraff under sletting av pakkebrønnsgruppen %s"
 

	
 
msgid "Repository group permissions updated"
 
msgstr "Pakkebrønnsgruppetilganger oppdatert"
 

	
 
msgid "An error occurred during revoking of permission"
 
msgstr "En feil inntraff under tilbakekalling av tilgang"
 

	
 
msgid "Error creating repository %s"
 
msgstr "Feil under opprettelse av pakkebrønnen %s"
 

	
 
msgid "Created repository %s from %s"
 
msgstr "Opprettet pakkebrønnen %s fra %s"
 

	
 
msgid "Forked repository %s as %s"
 
msgstr "Forgrenet pakkebrønnen %s som %s"
 

	
 
msgid "Created repository %s"
 
msgstr "Opprettet pakkebrønnen %s"
 

	
 
msgid "Repository %s updated successfully"
 
msgstr "Pakkebrønnen %s ble oppdatert"
 

	
 
msgid "Error occurred during update of repository %s"
 
msgstr "Feil under oppdatering av pakkebrønnen %s"
 

	
 
msgid "Detached %s forks"
 
msgstr "Avhektet %s forgreninger"
 

	
 
msgid "Deleted %s forks"
 
msgstr "Slettet %s forgreninger"
 

	
 
msgid "Deleted repository %s"
 
msgstr "Slettet pakkebrønnen %s"
 

	
 
msgid "Cannot delete repository %s which still has forks"
 
msgstr "Kan ikke slette pakkebrønne %s, som fremdeles har forgreninger"
 

	
 
msgid "An error occurred during deletion of %s"
 
msgstr "En feil inntraff under sletting av %s"
 

	
 
msgid "Repository permissions updated"
 
msgstr "Pakkebrønnstilganger oppdatert"
 

	
 
msgid "An error occurred during removal of field"
 
msgstr "Feil inntraff under fjerning av felt"
 

	
 
msgid "-- Not a fork --"
 
msgstr "-- Ikke en forgrening --"
 

	
 
msgid "Updated repository visibility in public journal"
 
msgstr "Oppdaterte pakkebrønnssynlighet i offentlig loggbok"
 

	
 
msgid "Nothing"
 
msgstr "Ingenting"
 

	
 
msgid "Marked repository %s as fork of %s"
 
msgstr "Markerte pakkebrønnen %s som en forgrening av %s"
 

	
 
msgid "An error occurred during this operation"
 
msgstr "En feil inntraff under denne operasjonen"
 

	
 
msgid "Updated VCS settings"
 
msgstr "Oppdaterte VCS-innstillinger"
 

	
 
msgid "Please enter email address"
 
msgstr "Skriv inn e-postadresse"
 

	
 
msgid "Created user group %s"
 
msgstr "Opprettet brukergruppe %s"
 

	
 
msgid "Updated user group %s"
 
msgstr "Oppdaterte brukergruppe %s"
 

	
 
msgid "Created user %s"
 
msgstr "Opprettet brukeren %s"
 

	
 
msgid "The default user cannot be edited"
 
msgstr "Forvalgt bruker kan ikke redigeres"
 

	
 
msgid "Added IP address %s to user whitelist"
 
msgstr "La til IP-adressen %s i brukerhvitlisten"
 

	
 
msgid "An error occurred while adding IP address"
 
msgstr "Kunne ikke legge til IP-adresse"
 

	
 
msgid "Removed IP address from user whitelist"
 
msgstr "Fjernet IP-adressen fra brukerhvitlisten"
 

	
 
msgid "Binary file"
 
msgstr "Binærfil"
 

	
 
msgid "Fork name %s"
 
msgstr "Forgreningsnavn %s"
 

	
 
msgid "Pull request %s"
 
msgstr "Flettingsforespørsel %s"
 

	
 
msgid "[created] repository as fork"
 
msgstr "[opprettet] pakkebrønn som forgrening"
 

	
 
msgid "[created] user"
 
msgstr "[opprettet] bruker"
 

	
 
msgid "[updated] user"
 
msgstr "[oppdaterte] bruker"
 

	
 
msgid "[created] user group"
 
msgstr "[opprettet] brukergruppe"
 

	
 
msgid "[updated] user group"
 
msgstr "[oppdaterte] brukergruppe"
 

	
 
msgid "[commented] on pull request for"
 
msgstr "[kommenterte] flettingsforespørsel for"
 

	
 
msgid "[closed] pull request for"
 
msgstr "[lukket] flettingsforespørsel for"
 

	
 
msgid "[pushed] into"
 
msgstr "[dyttet] til"
 

	
 
msgid "No files"
 
msgstr "Ingen filer"
 

	
 
msgid "new file"
 
msgstr "ny fil"
 

	
 
msgid "%d year"
 
msgid_plural "%d years"
 
msgstr[0] "%d år"
 
msgstr[1] "%d år"
 

	
 
msgid "%d month"
 
msgid_plural "%d months"
 
msgstr[0] "%d måned"
 
msgstr[1] "%d måneder"
 

	
 
msgid "%d day"
 
msgid_plural "%d days"
 
msgstr[0] "%d dag"
 
msgstr[1] "%d dager"
 

	
 
msgid "%d hour"
 
msgid_plural "%d hours"
 
msgstr[0] "%d time"
 
msgstr[1] "%d timer"
 

	
 
msgid "%d minute"
 
msgid_plural "%d minutes"
 
msgstr[0] "%d minutt"
 
msgstr[1] "%d minutter"
 

	
 
msgid "%d second"
 
msgid_plural "%d seconds"
 
msgstr[0] "%d sekund"
 
msgstr[1] "%d sekunder"
 

	
 
msgid "in %s"
 
msgstr "om %s"
 

	
 
msgid "%s ago"
 
msgstr "for %s siden"
 

	
 
msgid "in %s and %s"
 
msgstr "om %s og %s"
 

	
 
msgid "%s and %s ago"
 
msgstr "%s og %s siden"
 

	
 
msgid "just now"
 
msgstr "akkurat nå"
 

	
 
msgid "on line %s"
 
msgstr "på linje %s"
 

	
 
msgid "top level"
 
msgstr "toppnivå"
 

	
 
msgid "Kallithea Administrator"
 
msgstr "Kallithea-administrator"
 

	
 
msgid "Not approved"
 
msgstr "Ikke godkjent"
 

	
 
msgid "Approved"
 
msgstr "Godkjent"
 

	
 
msgid "Please enter a password"
 
msgstr "Skriv inn ett passord"
 

	
 
msgid "Closing"
 
msgstr "Lukker"
 

	
 
msgid "Username \"%(username)s\" already exists"
 
msgstr "Brukernavnet \"%(username)s\" finnes allerede"
 

	
 
msgid "Username \"%(username)s\" cannot be used"
 
msgstr "Brukernavnet \"%(username)s\" kan ikke brukes"
 

	
 
msgid "Invalid user group name"
 
msgstr "Ugyldig brukergruppenavn"
 

	
 
msgid "User group \"%(usergroup)s\" already exists"
 
msgstr "Brukergruppen \"%(usergroup)s\" finnes allerede"
 

	
 
msgid "Invalid old password"
 
msgstr "Ugyldig gammelt passord"
 

	
 
msgid "Passwords do not match"
 
msgstr "Passordene samsvarer ikke"
 

	
 
msgid "Invalid username or password"
 
msgstr "Ugyldig brukernavn eller passord"
 

	
 
msgid "Invalid repository URL"
 
msgstr "Ugyldig pakkebrønnsnettadresse"
 

	
 
msgid "This is not a valid path"
 
msgstr "Dette er ikke en gyldig sti"
 

	
 
msgid "This email address is already in use"
 
msgstr "E-postadressen er allerede i bruk"
 

	
 
msgid "Please enter a valid IPv4 or IPv6 address"
 
msgstr "Skriv inn en gyldig IPv4- eller IPv6-adresse"
 

	
 
msgid "About"
 
msgstr "Om"
 

	
 
msgid "Add Repository"
 
msgstr "Legg til pakkebrønn"
 

	
 
msgid "Add Repository Group"
 
msgstr "Legg til pakkebrønnsgruppe"
 

	
 
msgid "You have admin right to this group, and can edit it"
 
msgstr ""
 
"Du har administratorrettigheter til denne gruppen, og kan redigere den"
 

	
 
msgid "Edit Repository Group"
 
msgstr "Rediger pakkebrønnsgruppe"
 

	
 
msgid "Repository"
 
msgstr "Pakkebrønn"
 

	
 
msgid "Description"
 
msgstr "Beskrivelse"
 

	
 
msgid "Last Change"
 
msgstr "Siste endring"
 

	
 
msgid "Tip"
 
msgstr "Tips"
 

	
 
msgid "Owner"
 
msgstr "Eier"
 

	
 
msgid "Log In"
 
msgstr "Logg inn"
 

	
 
msgid "Log In to %s"
 
msgstr "Logg inn på %s"
 

	
 
msgid "Username"
 
msgstr "Brukernavn"
 

	
 
msgid "Password"
 
msgstr "Passord"
 

	
 
msgid "Forgot your password ?"
 
msgstr "Glemt passordet ditt?"
 

	
 
msgid "Password Reset"
 
msgstr "Passordstilbakestilling"
 

	
 
msgid "Reset Your Password to %s"
 
msgstr "Tilbakestill passordet ditt til %s"
 

	
 
msgid "Reset Your Password"
 
msgstr "Tilbakestill passordet ditt"
 

	
 
msgid "Email Address"
 
msgstr "E-postadresse"
 

	
 
msgid "Send Password Reset Email"
 
msgstr "Send e-post for passordtilbakestilling"
 

	
 
msgid ""
 
"A password reset link will be sent to the specified email address if it "
 
"is registered in the system."
 
msgstr ""
 
"En passordtilbakestillingslenke vil bli sendt til angitt e-postadresse "
 
"hvis den er registrert i systemet."
 

	
 
msgid "You are about to set a new password for the email address %s."
 
msgstr "Du er i ferd med å sette et nytt passord for e-postadressen %s."
 

	
 
msgid "Code you received in the email"
 
msgstr "Koden du mottok per e-post"
 

	
 
msgid "New Password"
 
msgstr "Nytt passord"
 

	
 
msgid "Confirm New Password"
 
msgstr "Bekreft nytt passord"
 

	
 
msgid "Confirm"
 
msgstr "Bekreft"
 

	
 
msgid "Sign Up"
 
msgstr "Registrer"
 

	
 
msgid "Sign Up to %s"
 
msgstr "Registrer på %s"
 

	
 
msgid "Re-enter password"
 
msgstr "Skriv inn passord på ny"
 

	
 
msgid "First Name"
 
msgstr "Fornavn"
 

	
 
msgid "Last Name"
 
msgstr "Etternavn"
 

	
 
msgid "Email"
 
msgstr "E-post"
 

	
 
msgid "Filter"
 
msgstr "Filter"
 

	
 
msgid "%s Entry"
 
@@ -783,384 +786,387 @@ msgid "Confirm to remove this API key: %
 
msgstr "Bekreft fjerning av denne API-nøklen: %s"
 

	
 
msgid "Remove"
 
msgstr "Fjern"
 

	
 
msgid "No additional API keys specified"
 
msgstr "Ingen ytterligere API-nøkler angitt"
 

	
 
msgid "New API key"
 
msgstr "Ny API-nøkkel"
 

	
 
msgid "Add"
 
msgstr "Legg til"
 

	
 
msgid "New email address"
 
msgstr "Ny e-postadresse"
 

	
 
msgid "Change Your Account Password"
 
msgstr "Endre ditt kontopassord"
 

	
 
msgid "Current password"
 
msgstr "Nåværende passord"
 

	
 
msgid "New password"
 
msgstr "Nytt passord"
 

	
 
msgid "Confirm new password"
 
msgstr "Bekreft nytt passord"
 

	
 
msgid "Current IP"
 
msgstr "Nåværende IP"
 

	
 
msgid "Gravatar"
 
msgstr "Gravatar"
 

	
 
msgid "Change %s avatar at"
 
msgstr "Endre %s-avatar på"
 

	
 
msgid "Avatars are disabled"
 
msgstr "Avatarer er avskrudd"
 

	
 
msgid "Repositories You Own"
 
msgstr "Pakkebrønner du eier"
 

	
 
msgid "Name"
 
msgstr "Navn"
 

	
 
msgid "Repositories You are Watching"
 
msgstr "Pakkebrønner du holder oppsyn med"
 

	
 
msgid "Default Permissions"
 
msgstr "Forvalgte tilganger"
 

	
 
msgid "IP Whitelist"
 
msgstr "IP-hvitliste"
 

	
 
msgid "Anonymous access"
 
msgstr "Anonym tilgang"
 

	
 
msgid "Allow anonymous access"
 
msgstr "Tillat anonym tilgang"
 

	
 
msgid "User group"
 
msgstr "Brukergruppe"
 

	
 
msgid "Registration"
 
msgstr "Registrering"
 

	
 
msgid "New IP address"
 
msgstr "Ny IP-adresse"
 

	
 
msgid "Repository Groups"
 
msgstr "Pakkebrønnsgrupper"
 

	
 
msgid "Group name"
 
msgstr "Gruppenavn"
 

	
 
msgid "Settings"
 
msgstr "Innstillinger"
 

	
 
msgid "Advanced"
 
msgstr "Avansert"
 

	
 
msgid "Permissions"
 
msgstr "Tilganger"
 

	
 
msgid "Created on"
 
msgstr "Opprettet den"
 

	
 
msgid "Delete this repository group"
 
msgstr "Slett denne pakkebrønnsgruppen"
 

	
 
msgid "Not visible"
 
msgstr "Ikke synlig"
 

	
 
msgid "Visible"
 
msgstr "Synlig"
 

	
 
msgid "Add repos"
 
msgstr "Legg til pakkebrønner"
 

	
 
msgid "Add/Edit groups"
 
msgstr "Legg til/rediger grupper"
 

	
 
msgid "User/User Group"
 
msgstr "Bruker/brukergruppe"
 

	
 
msgid "Default"
 
msgstr "Forvalg"
 

	
 
msgid "Revoke"
 
msgstr "Tilbakekall"
 

	
 
msgid "Add new"
 
msgstr "Legg til ny"
 

	
 
msgid "Both"
 
msgstr "Begge"
 

	
 
msgid "Remove this group"
 
msgstr "Fjern denne gruppen"
 

	
 
msgid "Confirm to delete this group"
 
msgstr "Bekreft sletting av denne gruppen"
 

	
 
msgid "Repository group %s"
 
msgstr "Pakkebrønnsgruppe %s"
 

	
 
msgid "Extra Fields"
 
msgstr "Ekstra felter"
 

	
 
msgid "Statistics"
 
msgstr "Statistikk"
 

	
 
msgid "Set"
 
msgstr "Sett"
 

	
 
msgid "This repository has %s fork"
 
msgid_plural "This repository has %s forks"
 
msgstr[0] "Denne pakkebrønnen har %s forgrening"
 
msgstr[1] "Denne pakkebrønnen har %s forgreninger"
 

	
 
msgid "Delete forks"
 
msgstr "Slett forgreninger"
 

	
 
msgid "Label"
 
msgstr "Etikett"
 

	
 
msgid "Key"
 
msgstr "Nøkkel"
 

	
 
msgid "Confirm to delete this field: %s"
 
msgstr "Bekreft sletting av dette feltet: %s"
 

	
 
msgid "New field key"
 
msgstr "Ny feltnøkkel"
 

	
 
msgid "New field label"
 
msgstr "Ny feltetikett"
 

	
 
msgid "Enter short label"
 
msgstr "Skriv inn kort etikett"
 

	
 
msgid "New field description"
 
msgstr "Ny feltbeskrivelse"
 

	
 
msgid "Enter description of a field"
 
msgstr "Skriv inn beskrivelse for et felt"
 

	
 
msgid "Extra fields are disabled."
 
msgstr "Ekstra felter avskrudd."
 

	
 
msgid "Private Repository"
 
msgstr "Privat pakkebrønn"
 

	
 
msgid "Fork of repository"
 
msgstr "Forgrening av pakkebrønn"
 

	
 
msgid "Type name of user"
 
msgstr "Skriv inn brukerens navn"
 

	
 
msgid "Processed commits"
 
msgstr "Behandlede innsendelser"
 

	
 
msgid "Processed progress"
 
msgstr "Behandlingsframdrift"
 

	
 
msgid "Reset Statistics"
 
msgstr "Tilbakestill statistikk"
 

	
 
msgid "Settings Administration"
 
msgstr "Innstillingsadministrasjon"
 

	
 
msgid "VCS"
 
msgstr "VKS"
 

	
 
msgid "System Info"
 
msgstr "Systeminfo"
 

	
 
msgid "Send test email to"
 
msgstr "Send test-e-post til"
 

	
 
msgid "Send"
 
msgstr "Send"
 

	
 
msgid "Save Settings"
 
msgstr "Lagre innstillinger"
 

	
 
msgid "Python version"
 
msgstr "Python-versjon"
 

	
 
msgid "Platform"
 
msgstr "Plattform"
 

	
 
msgid "Git version"
 
msgstr "Git-versjon"
 

	
 
msgid "Git path"
 
msgstr "Git-sti"
 

	
 
msgid "Python Packages"
 
msgstr "Python-pakker"
 

	
 
msgid "General"
 
msgstr "Generelt"
 

	
 
msgid "Show user Gravatars"
 
msgstr "Vis bruker-Gravatarer"
 

	
 
msgid "Add user group"
 
msgstr "Legg til brukergruppe"
 

	
 
msgid "User Groups"
 
msgstr "Brukergrupper"
 

	
 
msgid "Add User Group"
 
msgstr "Legg til brukergruppe"
 

	
 
msgid "Active"
 
msgstr "Aktiv"
 

	
 
msgid "%s user group settings"
 
msgstr "%s brukergruppeinnstillinger"
 

	
 
msgid "Show Members"
 
msgstr "Vis medlemmer"
 

	
 
msgid "User Group: %s"
 
msgstr "Brukergruppe: %s"
 

	
 
msgid "Members"
 
msgstr "Medlemmer"
 

	
 
msgid "Delete this user group"
 
msgstr "Slett denne brukergruppen"
 

	
 
msgid "No members yet"
 
msgstr "Ingen medlemmer enda"
 

	
 
msgid "Chosen group members"
 
msgstr "Valgte gruppemedlemmer"
 

	
 
msgid "Available members"
 
msgstr "Tilgjengelige medlemmer"
 

	
 
msgid "User Groups Administration"
 
msgstr "Brukergruppeadministrasjon"
 

	
 
msgid "Add user"
 
msgstr "Legg til bruker"
 

	
 
msgid "Users"
 
msgstr "Brukere"
 

	
 
msgid "Add User"
 
msgstr "Legg til bruker"
 

	
 
msgid "Password confirmation"
 
msgstr "Passordbekreftelse"
 

	
 
msgid "%s user settings"
 
msgstr "%s brukerinnstillinger"
 

	
 
msgid "Emails"
 
msgstr "E-poster"
 

	
 
msgid "User: %s"
 
msgstr "Bruker: %s"
 

	
 
msgid "Last Login"
 
msgstr "Siste innlogging"
 

	
 
msgid "Member of User Groups"
 
msgstr "Medlem av brukergrupper"
 

	
 
msgid "Delete this user"
 
msgstr "Slett denne brukeren"
 

	
 
msgid "Inherited from %s"
 
msgstr "Nedarvet fra %s"
 

	
 
msgid "Support"
 
msgstr "Støtte"
 

	
 
msgid "Mercurial repository"
 
msgstr "Merkurial-pakkebrønn"
 

	
 
msgid "Git repository"
 
msgstr "Git-pakkebrønn"
 

	
 
msgid "Create Fork"
 
msgstr "Opprett ei forgrening"
 

	
 
msgid "Summary"
 
msgstr "Sammendrag"
 

	
 
msgid "Changelog"
 
msgstr "Endringslogg"
 

	
 
msgid "Files"
 
msgstr "Filer"
 

	
 
msgid "Compare Fork"
 
msgstr "Sammenlign forgrening"
 

	
 
msgid "Compare"
 
msgstr "Sammenlign"
 

	
 
msgid "Search"
 
msgstr "Søk"
 

	
 
msgid "Follow"
 
msgstr "Følg"
 

	
 
msgid "Fork"
 
msgstr "Forgren"
 

	
 
msgid "Switch To"
 
msgstr "Bytt til"
 

	
 
msgid "Show recent activity"
 
msgstr "Vis nylig aktivitet"
 

	
 
msgid "All Public Gists"
 
msgstr "Alle offentlige Gist-er"
 

	
 
msgid "My Public Gists"
 
msgstr "Mine offentlige Gist-er"
 

	
 
msgid "My Private Gists"
 
msgstr "Mine private Gist-er"
 

	
 
msgid "Not Logged In"
 
msgstr "Ikke innlogget"
 

	
 
msgid "Forgot password?"
 
msgstr "Glemt passordet?"
 

	
 
msgid "Don't have an account?"
 
msgstr "Mangler du konto?"
 

	
 
msgid "Log Out"
 
msgstr "Logg ut"
 

	
 
msgid "Show"
 
msgstr "Vis"
 

	
 
msgid "Permission"
 
msgstr "Tilgang"
 

	
 
msgid "Edit Permission"
 
msgstr "Rediger tilgang"
 

	
 
msgid "Retry"
 
msgstr "Prøv igjen"
 

	
 
msgid "Add Another Comment"
 
msgstr "Legg til enda en kommentar"
 

	
 
msgid "Group"
 
msgstr "Gruppe"
 

	
 
msgid "Select changeset"
 
msgstr "Velg endringssett"
 

	
 
msgid "%s comments"
 
msgstr "%s kommentarer"
kallithea/i18n/pl/LC_MESSAGES/kallithea.po
Show inline comments
 
# Copyright (C) 2014 RhodeCode GmbH, and others.
 
# This file is distributed under the same license as the Kallithea project.
 

	
 
msgid ""
 
msgstr ""
 
"Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
 
"Language: pl\n"
 
"MIME-Version: 1.0\n"
 
"Content-Type: text/plain; charset=UTF-8\n"
 
"Content-Transfer-Encoding: 8bit\n"
 
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n"
 
"%100<10 || n%100>=20) ? 1 : 2;\n"
 

	
 
msgid "There are no changesets yet"
 
msgstr "Brak zestawienia zmian"
 

	
 
msgid "None"
 
msgstr "Brak"
 

	
 
msgid "(closed)"
 
msgstr "(zamknięty)"
 

	
 
msgid "Show whitespace"
 
msgstr "pokazuj spacje"
 

	
 
msgid "Ignore whitespace"
 
msgstr "Ignoruj pokazywanie spacji"
 

	
 
msgid "Successfully deleted pull request %s"
 
msgstr ""
 
"Prośba o skasowanie połączenia gałęzi %s została wykonana prawidłowo"
 

	
 
msgid "Could not find other repository %s"
 
msgstr "Nie można znaleźć innego repozytorium %s"
 

	
 
msgid "Cannot compare repositories of different types"
 
msgstr "Nie można porównać repozytoriów o różnych typach"
 

	
 
msgid "No response"
 
msgstr "Brak odpowiedzi"
 

	
 
msgid "Unknown error"
 
msgstr "Nieznany błąd"
 

	
 
msgid ""
 
"The request could not be understood by the server due to malformed syntax."
 
msgstr ""
 
"Żądanie nie może być rozumiane przez serwer z powodu zniekształconej "
 
"składni."
 

	
 
msgid "Unauthorized access to resource"
 
msgstr "Nieautoryzowany dostęp do zasobów"
 

	
 
msgid "You don't have permission to view this page"
 
msgstr "Nie masz uprawnień do przeglądania tej strony"
 

	
 
msgid "The resource could not be found"
 
msgstr "Zasób nie został znaleziony"
 

	
 
msgid ""
 
"The server encountered an unexpected condition which prevented it from "
 
"fulfilling the request."
 
msgstr ""
 
"Serwer napotkał niespodziewany warunek, który uniemożliwia spełnienie "
 
"żądania."
 

	
 
msgid "%s committed on %s"
 
msgstr "%s zakomitowal w %s"
 

	
 
msgid "Changeset was too big and was cut off..."
 
msgstr "Lista zmian była zbyt duża i została ucięta..."
 

	
 
msgid "%s %s feed"
 
msgstr "%s %s zasilać"
 

	
 
msgid "Changes on %s repository"
 
msgstr "Zmiany w %s repozytorium"
 

	
 
msgid "Click here to add new file"
 
msgstr "Kliknij tutaj, by dodać nowy plik"
 

	
 
msgid "There are no files yet."
 
msgstr "Nie ma jeszcze gałęzi."
 

	
 
msgid "Error occurred during commit"
 
msgstr "Wystąpił błąd w trakcie zatwierdzania"
 

	
 
msgid "Edited file %s via Kallithea"
 
msgstr "Edytowanie %s w Kallithea"
 

	
 
msgid "No changes"
 
msgstr "Bez zmian"
 

	
 
msgid "Successfully committed to %s"
 
msgstr "Committ wykonany do %s"
 

	
 
msgid "Added file via Kallithea"
 
msgstr "Dodano %s poprzez Kallithea"
 

	
 
msgid "No content"
 
msgstr "Brak treści"
 

	
 
msgid "No filename"
 
msgstr "Brak nazwy pliku"
 

	
 
msgid "Location must be relative path and must not contain .. in path"
 
msgstr ""
 
"Lokalizacja musi być ścieżką względną i nie może zawierać .. ścieżki"
 

	
 
msgid "Downloads disabled"
 
msgstr "Pobieranie wyłączone"
 

	
 
msgid "Unknown revision %s"
 
msgstr "Nieznana wersja %s"
 

	
 
msgid "Empty repository"
 
msgstr "Puste repozytorium"
 

	
 
msgid "Unknown archive type"
 
msgstr "Nieznany typ archiwum"
 

	
 
msgid "Changesets"
 
msgstr "Różnice"
 

	
 
msgid "Branches"
 
msgstr "Gałęzie"
 

	
 
msgid "Tags"
 
msgstr "Etykiety"
 

	
 
msgid "An error occurred during repository forking %s"
 
msgstr "Wystąpił błąd podczas rozgałęzienia %s repozytorium"
 

	
 
msgid "Groups"
 
msgstr "Grupy"
 

	
 
msgid "Repositories"
 
msgstr "Repozytoria"
 

	
 
msgid "Branch"
 
msgstr "gałąź"
 

	
 
msgid "Closed Branches"
 
msgstr "Zamknięte Gałęzie"
 

	
 
msgid "Tag"
 
msgstr "Tag"
 

	
 
msgid "Bookmark"
 
msgstr "Zakładka"
 

	
 
msgid "Public Journal"
 
msgstr "Dziennik Publiczny"
 

	
 
msgid "Journal"
 
msgstr "Dziennik"
 

	
 
msgid "You have successfully registered with %s"
 
msgstr "Udało Ci się zarejestrować w %s"
 

	
 
msgid "A password reset confirmation code has been sent"
 
msgstr "Twój link zresetowania hasła został wysłany"
 

	
 
msgid "Invalid password reset token"
 
msgstr "Nieprawidłowy token resetowania hasła"
 

	
 
msgid "Successfully updated password"
 
msgstr "Pomyślnie zaktualizowano hasło"
 

	
 
msgid "%s (closed)"
 
msgstr "%s (zamknięty)"
 

	
 
msgid "Changeset"
 
msgstr "Grupy zmian"
 

	
 
msgid "Special"
 
msgstr "Specjalne"
 

	
 
msgid "Peer branches"
 
msgstr "gałęzie"
 

	
 
msgid "Bookmarks"
 
msgstr "Zakładki"
 

	
 
msgid "Error occurred while creating pull request"
 
msgstr "Wystąpił błąd podczas prośby o połączenie gałęzi"
 

	
 
msgid "Successfully opened new pull request"
 
msgstr "Prośba o wykonanie połączenia gałęzi została wykonana prawidłowo"
 

	
 
msgid "No description"
 
msgstr "Brak opisu"
 

	
 
msgid "Pull request updated"
 
msgstr "Połączone gałęzie zaktualizowane"
 

	
 
msgid "Successfully deleted pull request"
 
msgstr "Prośba o skasowanie połączenia gałęzi została wykonana prawidłowo"
 

	
 
msgid "Invalid search query. Try quoting it."
 
msgstr "Nieprawidłowe zapytanie. Spróbuj zacytować je."
 

	
 
msgid "An error occurred during search operation."
 
msgstr "Wystąpił błąd podczas operacji wyszukiwania."
 

	
 
msgid "No data ready yet"
 
msgstr "Żadne dane nie zostały załadowane"
 

	
 
msgid "Statistics are disabled for this repository"
 
msgstr "Statystyki są wyłączone dla tego repozytorium"
 

	
 
msgid "Auth settings updated successfully"
 
msgstr "Ustawienia autentykacji poprawnie zaktualizowane"
 

	
 
msgid "error occurred during update of auth settings"
 
msgstr "wystąpił błąd podczas uaktualniania ustawień autentykacji"
 

	
 
msgid "Default settings updated successfully"
 
msgstr "Domyślne ustawienia zostały pomyślnie zaktualizowane"
 

	
 
msgid "Error occurred during update of defaults"
 
msgstr "wystąpił błąd podczas aktualizacji wartości domyślnych"
 

	
 
msgid "Forever"
 
msgstr "na zawsze"
 

	
 
msgid "5 minutes"
 
msgstr "5 minut"
 

	
 
msgid "1 hour"
 
msgstr "1 godzina"
 

	
 
msgid "1 day"
 
msgstr "1 dzień"
 

	
 
msgid "1 month"
 
msgstr "1 miesiąc"
 

	
 
msgid "Lifetime"
 
msgstr "Czas życia"
 

	
 
msgid "Error occurred during gist creation"
 
msgstr "Wystąpił błąd podczas tworzenia gist"
 

	
 
msgid "Deleted gist %s"
 
msgstr "Usuń gist %s"
 

	
 
msgid "Unmodified"
 
msgstr "Niemodyfikowany"
 

	
 
msgid "You can't edit this user since it's crucial for entire application"
 
msgstr ""
 
"Nie możesz edytować tego użytkownika ponieważ jest kluczowy dla całej "
 
"aplikacji"
 

	
 
msgid "Your account was updated successfully"
 
msgstr "Twoje konto zostało pomyślnie zaktualizowane"
 

	
 
msgid "Error occurred during update of user %s"
 
msgstr "wystąpił błąd podczas aktualizacji użytkownika %s"
 

	
 
msgid "Error occurred during update of user password"
 
msgstr "Wystąpił błąd w trakcie aktualizacji hasła użytkownika"
 

	
 
msgid "Added email %s to user"
 
msgstr "Dodano e-mail %s do użytkownika"
 

	
 
msgid "An error occurred during email saving"
 
msgstr "Wystąpił błąd podczas zapisywania e-maila"
 

	
 
msgid "Removed email from user"
 
msgstr "Usunięto e-mail użytkownikowi"
 

	
 
msgid "Read"
 
msgstr "Odczyt"
 

	
 
msgid "Write"
 
msgstr "Zapis"
 

	
 
msgid "Admin"
 
msgstr "Administracja"
 

	
 
msgid "Disabled"
 
msgstr "Wyłączone"
 

	
 
msgid "Allowed with manual account activation"
 
msgstr "Dozwolona z ręczną aktywacją konta"
 

	
 
msgid "Allowed with automatic account activation"
 
msgstr "Dozwolona z automatyczną aktywacją konta"
 

	
 
msgid "Manual activation of external account"
 
msgstr "Ręczna aktywacja nowego konta"
 

	
 
msgid "Automatic activation of external account"
 
msgstr "Automatyczna aktywacja nowego konta"
 

	
 
msgid "Enabled"
 
msgstr "Włączone"
 

	
 
msgid "Global permissions updated successfully"
 
msgstr "Globalne uprawnienia zaktualizowane poprawnie"
 

	
 
msgid "Error occurred during update of permissions"
 
msgstr "Wystąpił błąd podczas aktualizacji uprawnień"
 

	
 
msgid "Error occurred during creation of repository group %s"
 
msgstr "Wystąpił błąd podczas tworzenia grupy repo %s"
 

	
 
msgid "Created repository group %s"
 
msgstr "Utworzono grupę repo %s"
 

	
 
msgid "Updated repository group %s"
 
msgstr "Zaktualizowano grupę repo %s"
 

	
 
msgid "Error occurred during update of repository group %s"
 
msgstr "Wystąpił błąd podczas aktualizacji grupy repo %s"
 

	
 
msgid "This group contains %s repositories and cannot be deleted"
 
msgstr "Ta grupa zawiera %s repozytorium i nie może być usunięta"
 

	
 
msgid "This group contains %s subgroups and cannot be deleted"
 
msgstr "Ta grupa zawiera %s repozytorium i nie może być usunięta"
 

	
 
msgid "Removed repository group %s"
 
msgstr "Usunięto grupę repo %s"
 

	
 
msgid "Error occurred during deletion of repository group %s"
 
msgstr "Wystąpił błąd podczas usuwania z repozytorium grupy %s"
 

	
 
msgid "Cannot revoke permission for yourself as admin"
 
msgstr "Nie można cofnąć zezwolenia dla admina jako admin"
 

	
 
msgid "Repository group permissions updated"
 
msgstr "Aktualizacja uprawnień grup repozytorium"
 

	
 
msgid "An error occurred during revoking of permission"
 
msgstr "Wystąpił błąd podczas cofania zezwolenia"
 

	
 
msgid "Error creating repository %s"
 
msgstr "Błąd podczas tworzenia repozytorium %s"
 

	
 
msgid "Created repository %s from %s"
 
msgstr "utworzone repozytorium %s z %s"
 

	
 
msgid "Forked repository %s as %s"
 
msgstr "Gałęzi %s w repozytorium %s"
 

	
 
msgid "Created repository %s"
 
msgstr "Utworzone repozytorium %s"
 

	
 
msgid "Repository %s updated successfully"
 
msgstr "Repozytorium %s zostało pomyślnie zaktualizowane"
 

	
 
msgid "Error occurred during update of repository %s"
 
msgstr "Wystąpił błąd podczas aktualizacji repozytorium %s"
 

	
 
msgid "Detached %s forks"
 
msgstr "Oderwane rozgałęzienie %s"
 

	
 
msgid "Deleted %s forks"
 
msgstr "Usunięte rozgałęzienia %s"
 

	
 
msgid "Deleted repository %s"
 
msgstr "Usunięte repozytorium %s"
 

	
 
msgid "Cannot delete repository %s which still has forks"
 
msgstr ""
 
"Nie można usunąć repozytorium %s nadal zawiera załączniki rozgałęzienia"
 

	
 
msgid "An error occurred during deletion of %s"
 
msgstr "Wystąpił błąd podczas usuwania %s"
 

	
 
msgid "Repository permissions updated"
 
msgstr "Uprawnienia repozytorium zostały zaktualizowane"
 

	
 
msgid "An error occurred during creation of field: %r"
 
msgstr "Wystąpił błąd podczas tworzenia pola: %r"
 

	
 
msgid "An error occurred during removal of field"
 
msgstr "Wystąpił błąd podczas usuwania pola"
 

	
 
msgid "-- Not a fork --"
 
msgstr "-- Brak rozgałęzienia --"
 

	
 
msgid "Updated repository visibility in public journal"
 
msgstr "Zaktualizowano widoczność stron w publicznym dzienniku"
 

	
 
msgid "An error occurred during setting this repository in public journal"
 
msgstr ""
 
"Wystąpił błąd podczas ustawiania tego repozytorium w dzienniku publicznym"
 

	
 
msgid "Nothing"
 
msgstr "Brak"
 

	
 
msgid "Marked repository %s as fork of %s"
 
msgstr "Oznaczono %s repo jako rozwidlenie %s"
 

	
 
msgid "An error occurred during this operation"
 
msgstr "Wystąpił błąd podczas tej operacji"
 

	
 
msgid "Pulled from remote location"
 
msgstr "Pobieranie z lokalizacji zdalnej"
 

	
 
msgid "An error occurred during pull from remote location"
 
msgstr "Wystąpił błąd podczas pobierania z lokalizacji zdalnej"
 

	
 
msgid "An error occurred during deletion of repository stats"
 
msgstr "Wystąpił błąd podczas usuwania z repozytorium statystyk"
 

	
 
msgid "Updated VCS settings"
 
msgstr "Aktualizacja ustawień VCS"
 

	
 
msgid "Error occurred while updating application settings"
 
msgstr "Wystąpił błąd podczas aktualizacji ustawień aplikacji"
 

	
 
msgid "Repositories successfully rescanned. Added: %s. Removed: %s."
 
msgstr ""
 
"Repozytoria z powodzeniem zostały ponownie zeskanowane dodano: %s, "
 
"usunięto: %s."
 

	
 
msgid "Invalidated %s repositories"
kallithea/i18n/pt/LC_MESSAGES/kallithea.po
Show inline comments
 
new file 100644
 
# Copyright (C) 2020 Various authors, licensing as GPLv3
 
# This file is distributed under the same license as the Kallithea project.
 

	
 
msgid ""
 
msgstr ""
 
"Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
 
"Language: pt\n"
 
"MIME-Version: 1.0\n"
 
"Content-Type: text/plain; charset=UTF-8\n"
 
"Content-Transfer-Encoding: 8bit\n"
 
"Plural-Forms: nplurals=2; plural=n > 1;\n"
 

	
 
msgid "There are no changesets yet"
 
msgstr "Não há nenhum changeset ainda"
 

	
 
msgid "None"
 
msgstr "Nenhum"
 

	
 
msgid "(closed)"
 
msgstr "(fechado)"
 

	
 
msgid "Show whitespace"
 
msgstr "Mostrar espaços em branco"
 

	
 
msgid "Ignore whitespace"
 
msgstr "Ignorar espaços em branco"
 

	
 
msgid ""
 
"The request could not be understood by the server due to malformed syntax."
 
msgstr ""
 
"A requisição não pôde ser compreendida pelo servidor devido à sintaxe mal "
 
"formada."
 

	
 
msgid "Unauthorized access to resource"
 
msgstr "Acesso não autorizado ao recurso"
 

	
 
msgid "You don't have permission to view this page"
 
msgstr "Não tem permissão para ver esta página"
 

	
 
msgid "The resource could not be found"
 
msgstr "O recurso não pôde ser encontrado"
 

	
 
msgid ""
 
"The server encountered an unexpected condition which prevented it from "
 
"fulfilling the request."
 
msgstr ""
 
"O servidor encontrou uma condição inesperada que o impediu de satisfazer "
 
"a requisição."
 

	
 
msgid "%s committed on %s"
 
msgstr "%s commitados em %s"
 

	
 
msgid "Changeset was too big and was cut off..."
 
msgstr "Conjunto de mudanças era grande demais e foi cortado..."
 

	
 
msgid "%s %s feed"
 
msgstr "%s - feed %s"
 

	
 
msgid "Changes on %s repository"
 
msgstr "Modificações no repositório %s"
 

	
 
msgid "Click here to add new file"
 
msgstr "Clique aqui para adicionar um novo ficheiro"
 

	
 
msgid "Error occurred during commit"
 
msgstr "Ocorreu um erro ao realizar commit"
 

	
 
msgid "Edited file %s via Kallithea"
 
msgstr "Ficheiro %s editado via Kallithea"
 

	
 
msgid "No changes"
 
msgstr "Sem modificações"
 

	
 
msgid "Successfully committed to %s"
 
msgstr "Commit realizado com sucesso para %s"
 

	
 
msgid "Added file via Kallithea"
 
msgstr "Ficheiro adicionado via Kallithea"
 

	
 
msgid "No content"
 
msgstr "Nenhum conteúdo"
 

	
 
msgid "No filename"
 
msgstr "Nenhum nome de ficheiro"
 

	
 
msgid "Location must be relative path and must not contain .. in path"
 
msgstr "O caminho deve ser relativo e não pode conter .."
 

	
 
msgid "Downloads disabled"
 
msgstr "Downloads desativados"
 

	
 
msgid "Unknown revision %s"
 
msgstr "Revisão desconhecida %s"
 

	
 
msgid "Empty repository"
 
msgstr "Repositório vazio"
 

	
 
msgid "Unknown archive type"
 
msgstr "Tipo de archivo desconhecido"
 

	
 
msgid "Changesets"
 
msgstr "Conjuntos de mudanças"
 

	
 
msgid "Branches"
 
msgstr "Ramos"
 

	
 
msgid "Tags"
 
msgstr "Etiquetas"
 

	
 
msgid "An error occurred during repository forking %s"
 
msgstr "Ocorreu um erro ao bifurcar o repositório %s"
 

	
 
msgid "Repositories"
 
msgstr "Repositórios"
 

	
 
msgid "Branch"
 
msgstr "Ramo"
 

	
 
msgid "Closed Branches"
 
msgstr "Ramos Fechados"
 

	
 
msgid "Public Journal"
 
msgstr "Diário Público"
 

	
 
msgid "Journal"
 
msgstr "Diário"
 

	
 
msgid "You have successfully registered with %s"
 
msgstr "Foi registado no %s com sucesso"
 

	
 
msgid "Changeset"
 
msgstr "Conjunto de Mudanças"
 

	
 
msgid "Special"
 
msgstr "Especial"
 

	
 
msgid "Peer branches"
 
msgstr "Ramos pares"
 

	
 
msgid "Bookmarks"
 
msgstr "Marcadores"
 

	
 
msgid "Successfully opened new pull request"
 
msgstr "Novo pull request criado com sucesso"
 

	
 
msgid "Successfully deleted pull request"
 
msgstr "Pull request excluído com sucesso"
 

	
 
msgid "Invalid search query. Try quoting it."
 
msgstr "Consulta de busca inválida. Tente usar aspas."
 

	
 
msgid "Statistics are disabled for this repository"
 
msgstr "As estatísticas estão desabillitadas para este repositório"
 

	
 
msgid "Default settings updated successfully"
 
msgstr "Configurações predefinidas atualizadas com sucesso"
 

	
 
msgid "Error occurred during update of defaults"
 
msgstr "Ocorreu um erro durnge a atualização dos padrões"
 

	
 
msgid "5 minutes"
 
msgstr "cinco minutos"
 

	
 
msgid "1 hour"
 
msgstr "uma hora"
 

	
 
msgid "1 day"
 
msgstr "um dia"
 

	
 
msgid "1 month"
 
msgstr "um mês"
 

	
 
msgid "Error occurred during gist creation"
 
msgstr "Ocorreu um erro durante a criação de um gist"
 

	
 
msgid "Deleted gist %s"
 
msgstr "Gist %s excluído"
 

	
 
msgid "You can't edit this user since it's crucial for entire application"
 
msgstr ""
 
"Não pode editar esse utilizador pois ele é crucial para toda a aplicação"
 

	
 
msgid "Your account was updated successfully"
 
msgstr "Sua conta foi atualizada com sucesso"
 

	
 
msgid "Error occurred during update of user %s"
 
msgstr "Ocorreu um erro durante a atualização do utilizador %s"
 

	
 
msgid "Added email %s to user"
 
msgstr "Email %s adicionado ao utilizador"
 

	
 
msgid "An error occurred during email saving"
 
msgstr "Ocorreu um erro durante o salvamento do email"
 

	
 
msgid "Removed email from user"
 
msgstr "Email removido do utilizador"
 

	
 
msgid "Read"
 
msgstr "Ler"
 

	
 
msgid "Write"
 
msgstr "Gravar"
 

	
 
msgid "Admin"
 
msgstr "Administrador"
 

	
 
msgid "Disabled"
 
msgstr "Desativado"
 

	
 
msgid "Allowed with manual account activation"
 
msgstr "Permitido com ativação manual de conta"
 

	
 
msgid "Allowed with automatic account activation"
 
msgstr "Permitido com ativação automática de conta"
 

	
 
msgid "Manual activation of external account"
 
msgstr "Ativação manual de conta externa"
 

	
 
msgid "Automatic activation of external account"
 
msgstr "Ativação automática de conta externa"
 

	
 
msgid "Enabled"
 
msgstr "Ativado"
 

	
 
msgid "Error occurred during update of permissions"
 
msgstr "Ocorreu um erro durante a atualização das permissões"
 

	
 
msgid "Error occurred during creation of repository group %s"
 
msgstr "Ocorreu um erro durante a criação do grupo de repositórios %s"
 

	
 
msgid "Created repository group %s"
 
msgstr "Grupo de repositórios %s criado"
 

	
 
msgid "Updated repository group %s"
 
msgstr "Grupo de repositórios %s atualizado"
 

	
 
msgid "Error occurred during update of repository group %s"
 
msgstr "Ocorreu um erro durante a atualização do grupo de repositórios %s"
 

	
 
msgid "This group contains %s repositories and cannot be deleted"
 
msgstr "Esse grupo contém %s repositórios e não pode ser excluído"
 

	
 
msgid "This group contains %s subgroups and cannot be deleted"
 
msgstr "Este grupo contém %s subgrupos e não pode ser excluído"
 

	
 
msgid "Removed repository group %s"
 
msgstr "Grupo de repositórios %s excluído"
 

	
 
msgid "Error occurred during deletion of repository group %s"
 
msgstr "Ocorreu um erro durante a exclusão do grupo de repositórios %s"
 

	
 
msgid "Cannot revoke permission for yourself as admin"
 
msgstr "Não pode revocar sua própria permissão de administrador"
 

	
 
msgid "Repository group permissions updated"
 
msgstr "Permissões atualizadas do Grupo de Repositórios"
 

	
 
msgid "An error occurred during revoking of permission"
 
msgstr "Ocorreu um erro durante a revocação das permissões"
 

	
 
msgid "Error creating repository %s"
 
msgstr "Erro ao criar repositório %s"
 

	
 
msgid "Created repository %s from %s"
 
msgstr "Repositório %s criado de %s"
 

	
 
msgid "Forked repository %s as %s"
 
msgstr "Repositório %s bifurcado como %s"
 

	
 
msgid "Created repository %s"
 
msgstr "Repositório %s criado"
 

	
 
msgid "Repository %s updated successfully"
 
msgstr "Repositório %s atualizado com sucesso"
 

	
 
msgid "Error occurred during update of repository %s"
 
msgstr "Ocorreu um erro durante a atualização do repositório %s"
 

	
 
msgid "Deleted %s forks"
 
msgstr "%s bifurcações excluídas"
 

	
 
msgid "Deleted repository %s"
 
msgstr "Repositório %s excluído"
 

	
 
msgid "An error occurred during deletion of %s"
 
msgstr "Ocorreu um erro durante a exclusão de %s"
 

	
 
msgid "Repository permissions updated"
 
msgstr "Permissões do repositório atualizadas"
 

	
 
msgid "An error occurred during removal of field"
 
msgstr "Ocorreu um erro durante a remoção do campo"
 

	
 
msgid "Updated repository visibility in public journal"
 
msgstr "Atualizada a visibilidade do repositório no diário público"
 

	
 
msgid "An error occurred during setting this repository in public journal"
 
msgstr "Ocorreu um erro ao ajustar esse repositório no diário público"
 

	
 
msgid "Nothing"
 
msgstr "Nada"
 

	
 
msgid "Marked repository %s as fork of %s"
 
msgstr "Marcado repositório %s como bifurcação de %s"
 

	
 
msgid "An error occurred during this operation"
 
msgstr "Ocorreu um erro durante essa operação"
 

	
 
msgid "Pulled from remote location"
 
msgstr "Realizado pull de localização remota"
 

	
 
msgid "An error occurred during pull from remote location"
 
msgstr "Ocorreu um erro ao realizar pull de localização remota"
 

	
 
msgid "An error occurred during deletion of repository stats"
 
msgstr "Ocorreu um erro ao apagar as estatísticas de repositório"
 

	
 
msgid "Updated VCS settings"
 
msgstr "Configurações de VCS atualizadas"
 

	
 
msgid "Error occurred while updating application settings"
 
msgstr ""
 
"Ocorreu um erro durante a atualização das configurações da aplicação"
 

	
 
msgid "Updated application settings"
 
msgstr "Configurações da aplicação atualizadas"
 

	
 
msgid "Updated visualisation settings"
 
msgstr "Configurações de visualização atualizadas"
 

	
 
msgid "Error occurred during updating visualisation settings"
 
msgstr ""
 
"Ocorreu um erro durante a atualização das configurações de visualização"
 

	
 
msgid "Added new hook"
 
msgstr "Adicionado novo gancho"
 

	
 
msgid "Updated hooks"
 
msgstr "Atualizados os ganchos"
 

	
 
msgid "Error occurred during hook creation"
 
msgstr "Ocorreu um erro durante a criação do hook"
 

	
 
msgid "Whoosh reindex task scheduled"
 
msgstr "Tarefa de reindexação do whoosh agendada"
 

	
 
msgid "Created user group %s"
 
msgstr "Grupo de utilizadores %s criado"
 

	
 
msgid "Error occurred during creation of user group %s"
 
msgstr "Ocorreu um erro durante a criação do grupo de utilizadores %s"
 

	
 
msgid "Updated user group %s"
 
msgstr "Grupo de utilizadores %s atualizado"
 

	
 
msgid "Error occurred during update of user group %s"
 
msgstr "Ocorreu um erro durante a atualização do grupo de utilizadores %s"
 

	
 
msgid "Successfully deleted user group"
 
msgstr "Grupo de utilizadores excluído com sucesso"
 

	
 
msgid "An error occurred during deletion of user group"
 
msgstr "Ocorreu um erro durante a exclusão do grupo de utilizadores"
 

	
 
msgid "Target group cannot be the same"
 
msgstr "O grupo destino não pode ser o mesmo"
 

	
 
msgid "User group permissions updated"
 
msgstr "Permissões do Grupo de Utilizadores atualizadas"
 

	
 
msgid "Updated permissions"
 
msgstr "Permissões atualizadas"
 

	
 
msgid "An error occurred during permissions saving"
 
msgstr "Ocorreu um erro durante o salvamento das permissões"
 

	
 
msgid "Created user %s"
 
msgstr "Utilizador %s criado"
 

	
 
msgid "Error occurred during creation of user %s"
 
msgstr "Ocorreu um erro durante a criação do utilizador %s"
 

	
 
msgid "User updated successfully"
 
msgstr "Utilizador atualizado com sucesso"
 

	
 
msgid "Successfully deleted user"
 
msgstr "Utilizador excluído com sucesso"
 

	
 
msgid "An error occurred during deletion of user"
 
msgstr "Ocorreu um erro ao apagar o utilizador"
 

	
 
msgid "An error occurred while adding IP address"
 
msgstr "Ocorreu um erro durante o salvamento do IP"
 

	
 
msgid "You need to be a registered user to perform this action"
 
msgstr "Precisa ser um utilizador registado para realizar essa ação"
 

	
 
msgid "You need to be signed in to view this page"
 
msgstr "Precisa estar logado para ver essa página"
 

	
 
msgid "Binary file"
 
msgstr "Ficheiro binário"
 

	
 
msgid ""
 
"Changeset was too big and was cut off, use diff menu to display this diff"
 
msgstr ""
 
"Conjunto de mudanças é grande demais e foi cortado, use o menu de "
 
"diferenças para ver as diferenças"
 

	
 
msgid "No changes detected"
 
msgstr "Nenhuma alteração detectada"
 

	
 
msgid "Deleted branch: %s"
 
msgstr "Excluído ramo: %s"
 

	
 
msgid "Created tag: %s"
 
msgstr "Tag criada: %s"
 

	
 
msgid "Show all combined changesets %s->%s"
 
msgstr "Ver todos os conjuntos de mudanças combinados %s->%s"
 

	
 
msgid "and"
 
msgstr "e"
 

	
 
msgid "%s more"
 
msgstr "%s mais"
 

	
 
msgid "revisions"
 
msgstr "revisões"
 

	
 
msgid "[deleted] repository"
 
msgstr "repositório [excluído]"
 

	
 
msgid "[created] repository"
 
msgstr "repositório [criado]"
 

	
 
msgid "[created] repository as fork"
 
msgstr "repositório [criado] como uma bifurcação"
 

	
 
msgid "[forked] repository"
 
msgstr "repositório [bifurcado]"
 

	
 
msgid "[updated] repository"
 
msgstr "repositório [atualizado]"
 

	
 
msgid "[downloaded] archive from repository"
 
msgstr "[descarregado] archivo do repositório"
 

	
 
msgid "[delete] repository"
 
msgstr "[apagar] repositório"
 

	
 
msgid "[created] user"
 
msgstr "utilizador [criado]"
 

	
 
msgid "[updated] user"
 
msgstr "utilizador [atualizado]"
 

	
 
msgid "[created] user group"
 
msgstr "[criado] grupo de utilizadores"
 

	
 
msgid "[updated] user group"
 
msgstr "[atualizado] grupo de utilizadores"
 

	
 
msgid "[commented] on revision in repository"
 
msgstr "[comentado] em revisão no repositório"
 

	
 
msgid "[commented] on pull request for"
 
msgstr "[comentado] no pull request para"
 

	
 
msgid "[closed] pull request for"
 
msgstr "[fechado] pull request para"
 

	
 
msgid "[pushed] into"
 
msgstr "[realizado push] para"
 

	
 
msgid "[committed via Kallithea] into repository"
 
msgstr "[commitado via Kallithea] no repositório"
 

	
 
msgid "[pulled from remote] into repository"
 
msgstr "[pulled do remote] no repositório"
 

	
 
msgid "[pulled] from"
 
msgstr "[realizado pull] a partir de"
 

	
 
msgid "[started following] repository"
 
msgstr "[passou a seguir] o repositório"
 

	
 
msgid "[stopped following] repository"
 
msgstr "[parou de seguir] o repositório"
 

	
 
msgid " and %s more"
 
msgstr " e mais %s"
 

	
 
msgid "No files"
 
msgstr "Nenhum ficheiro"
 

	
 
msgid "new file"
 
msgstr "novo ficheiro"
 

	
 
msgid "mod"
 
msgstr "mod"
 

	
 
msgid "del"
 
msgstr "apagar"
 

	
 
msgid "rename"
 
msgstr "renomear"
 

	
 
msgid "chmod"
 
msgstr "chmod"
 

	
 
msgid ""
 
"%s repository is not mapped to db perhaps it was created or renamed from "
 
"the filesystem please run the application again in order to rescan "
 
"repositories"
 
msgstr ""
 
"O repositório %s não está mapeado ao BD. Talvez ele tenha sido criado ou "
 
"renomeado a partir do sistema de ficheiros. Por favor, execute a "
 
"aplicação outra vez para varrer novamente por repositórios"
 

	
 
msgid "%d year"
 
msgid_plural "%d years"
 
msgstr[0] "%d ano"
 
msgstr[1] "%d anos"
 

	
 
msgid "%d month"
 
msgid_plural "%d months"
 
msgstr[0] "%d mês"
 
msgstr[1] "%d meses"
 

	
 
msgid "%d day"
 
msgid_plural "%d days"
 
msgstr[0] "%d dia"
 
msgstr[1] "%d dias"
 

	
 
msgid "%d hour"
 
msgid_plural "%d hours"
 
msgstr[0] "%d hora"
 
msgstr[1] "%d horas"
 

	
 
msgid "%d minute"
 
msgid_plural "%d minutes"
 
msgstr[0] "%d minuto"
 
msgstr[1] "%d minutos"
 

	
 
msgid "%d second"
 
msgid_plural "%d seconds"
 
msgstr[0] "%d segundo"
 
msgstr[1] "%d segundos"
 

	
 
msgid "in %s"
 
msgstr "em %s"
 

	
 
msgid "%s ago"
 
msgstr "%s atrás"
 

	
 
msgid "in %s and %s"
 
msgstr "em %s e %s"
 

	
 
msgid "%s and %s ago"
 
msgstr "%s e %s atrás"
 

	
 
msgid "just now"
 
msgstr "agora há pouco"
 

	
 
msgid "on line %s"
 
msgstr "na linha %s"
 

	
 
msgid "[Mention]"
 
msgstr "[Menção]"
 

	
 
msgid "top level"
 
msgstr "nível superior"
 

	
 
msgid "Kallithea Administrator"
 
msgstr "Administrador do Kallithea"
 

	
 
msgid "Registration disabled"
 
msgstr "Registo desativado"
 

	
 
msgid "Approved"
 
msgstr "Aprovado"
 

	
 
msgid "Please enter a login"
 
msgstr "Por favor entre um login"
 

	
 
msgid "Enter a value %(min)i characters long or more"
 
msgstr "Entre um valor com %(min)i caracteres ou mais"
 

	
 
msgid "Please enter a password"
 
msgstr "Por favor entre com uma palavra-passe"
 

	
 
msgid "Enter %(min)i characters or more"
 
msgstr "Entre com %(min)i caracteres ou mais"
 

	
 
msgid "latest tip"
 
msgstr "tip mais recente"
 

	
 
msgid "New user registration"
 
msgstr "Novo registo de utilizador"
 

	
 
msgid "Password reset link"
 
msgstr "Ligação para trocar palavra-passe"
 

	
 
msgid "Value cannot be an empty list"
 
msgstr "O valor não pode ser uma lista vazia"
 

	
 
msgid "Username \"%(username)s\" already exists"
 
msgstr "O username \\\"%(username)s\\\" já existe"
 

	
 
msgid "Username %(username)s is not valid"
 
msgstr "O username \"%(username)s\" não é válido"
 

	
 
msgid "Invalid user group name"
 
msgstr "Nome inválido de grupo de utilizadores"
 

	
 
msgid "User group \"%(usergroup)s\" already exists"
 
msgstr "O grupo de utilizadores \"%(usergroup)s\" já existe"
 

	
 
msgid ""
 
"user group name may only contain alphanumeric characters underscores, "
 
"periods or dashes and must begin with alphanumeric character"
 
msgstr ""
 
"O nome de um grupo de utilizadores só pode conter characters alfa-"
 
"numéricos, underscores, pontos ou hífens, e deve começar om um caractere "
 
"alfa-numérico"
 

	
 
msgid "Cannot assign this group as parent"
 
msgstr "Não é possível associar esse grupo como progenitor"
 

	
 
msgid "Group \"%(group_name)s\" already exists"
 
msgstr "O grupo \\\"%(group_name)s\\\" já existe"
 

	
 
msgid "Repository with name \"%(group_name)s\" already exists"
 
msgstr "Um repositório com o nome \"%(group_name)s\" já existe"
 

	
 
msgid "Invalid characters (non-ascii) in password"
 
msgstr "Caracteres inválidos (não-ascii) na palavra-passe"
 

	
 
msgid "Passwords do not match"
 
msgstr "Palavras-passe não conferem"
 

	
 
msgid "Repository named %(repo)s already exists"
 
msgstr "Um repositório chamado %(repo)s já existe"
 

	
 
msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
 
msgstr "Um repositório \"%(repo)s\" já existe no grupo \"%(group)s\""
 

	
 
msgid "Repository group with name \"%(repo)s\" already exists"
 
msgstr "Um Grupo de Repositórios chamado \"%(repo)s\" já existe"
 

	
 
msgid "Fork has to be the same type as parent"
 
msgstr "A bifurcação deve ser do mesmo tipo que o pai"
 

	
 
msgid "You don't have permissions to create repository in this group"
 
msgstr "Não tem permissão para criar um repositório neste grupo"
 

	
 
msgid "no permission to create repository in root location"
 
msgstr "Não tem permissão para criar um repositório na raiz"
 

	
 
msgid "You don't have permissions to create a group in this location"
 
msgstr "Não tem permissão para criar um grupo neste local"
 

	
 
msgid "This username or user group name is not valid"
 
msgstr "Este nome de utilizador ou de grupo de utilizadores não é válido"
 

	
 
msgid "This is not a valid path"
 
msgstr "Esse não é um caminho válido"
 

	
 
msgid ""
 
"The LDAP Login attribute of the CN must be specified - this is the name "
 
"of the attribute that is equivalent to \"username\""
 
msgstr ""
 
"O atributo de login LDAP do CN deve ser especificado - isto é o nome do "
 
"atributo que é equivalente ao 'nome de utilizador'"
 

	
 
msgid "Please enter a valid IPv4 or IPv6 address"
 
msgstr "Por favor, forneça um endereço válido IPv4 ou IPv6"
 

	
 
msgid ""
 
"The network size (bits) must be within the range of 0-32 (not %(bits)r)"
 
msgstr ""
 
"O tamanho da rede (bits) deve estar no intervalo 0-32 (não %(bits)r)"
 

	
 
msgid "Key name can only consist of letters, underscore, dash or numbers"
 
msgstr "O nome da chave só pode conter letras, underscore, hífen ou dígitos"
 

	
 
msgid "Filename cannot be inside a directory"
 
msgstr "O nome de ficheiro não pode estar dentro de um diretório"
 

	
 
msgid "You have admin right to this group, and can edit it"
 
msgstr "Tem direitos de administrador neste grupo e pode editá-lo"
 

	
 
msgid "Repository"
 
msgstr "Repositório"
 

	
 
msgid "Description"
 
msgstr "Descrição"
 

	
 
msgid "Last Change"
 
msgstr "Última Alteração"
 

	
 
msgid "Tip"
 
msgstr "Ponta"
 

	
 
msgid "Owner"
 
msgstr "Dono"
 

	
 
msgid "Log In"
 
msgstr "Entrar"
 

	
 
msgid "Log In to %s"
 
msgstr "Log in em %s"
 

	
 
msgid "Username"
 
msgstr "Nome de utilizador"
 

	
 
msgid "Password"
 
msgstr "Palavra-passe"
 

	
 
msgid "Forgot your password ?"
 
msgstr "Esqueceu sua palavra-passe ?"
 

	
 
msgid "Don't have an account ?"
 
msgstr "Não possui uma conta ?"
 

	
 
msgid "Sign In"
 
msgstr "Entrar"
 

	
 
msgid "Password Reset"
 
msgstr "Palavra-passe Trocada"
 

	
 
msgid "Sign Up"
 
msgstr "Inscrever-se"
 

	
 
msgid "Re-enter password"
 
msgstr "Repita a palavra-passe"
 

	
 
msgid "First Name"
 
msgstr "Primeiro Nome"
 

	
 
msgid "Last Name"
 
msgstr "Último Nome"
 

	
 
msgid "Email"
 
msgstr "E-mail"
 

	
 
msgid "journal filter..."
 
msgstr "filtro de diário..."
 

	
 
msgid "Action"
 
msgstr "Ação"
 

	
 
msgid "Date"
 
msgstr "Data"
 

	
 
msgid "From IP"
 
msgstr "A partir do IP"
 

	
 
msgid "No actions yet"
 
msgstr "Ainda não há ações"
 

	
 
msgid "Save"
 
msgstr "Gravar"
 

	
 
msgid "Type"
 
msgstr "Tipo"
 

	
 
msgid "Private repository"
 
msgstr "Repositório privado"
 

	
 
msgid ""
 
"Private repositories are only visible to people explicitly added as "
 
"collaborators."
 
msgstr ""
 
"Repositórios privados são visíveis somente por pessoas explicitamente "
 
"adicionadas como colaboradores."
 

	
 
msgid "Enable statistics"
 
msgstr "Ativar estatísticas"
 

	
 
msgid "Enable statistics window on summary page."
 
msgstr "Ativar janela de estatísticas na página de sumário."
 

	
 
msgid "Enable downloads"
 
msgstr "Ativar downloads"
 

	
 
msgid "Enable download menu on summary page."
 
msgstr "Ativar menu de descarregar na página de sumário."
 

	
 
msgid "Gist description ..."
 
msgstr "Descrição do gist ..."
 

	
 
msgid "Gist lifetime"
 
msgstr "Tempo de vida do Gist"
 

	
 
msgid "Expires"
 
msgstr "Expira"
 

	
 
msgid "Cancel"
 
msgstr "Cancelar"
 

	
 
msgid "Public Gists"
 
msgstr "Gists Públicos"
 

	
 
msgid "Created"
 
msgstr "Criado"
 

	
 
msgid "There are no gists yet"
 
msgstr "Não há nenhum gist ainda"
 

	
 
msgid "Reset"
 
msgstr "Limpar"
 

	
 
msgid "Gist"
 
msgstr "Gist"
 

	
 
msgid "URL"
 
msgstr "URL"
 

	
 
msgid "Delete"
 
msgstr "Apagar"
 

	
 
msgid "Edit"
 
msgstr "Editar"
 

	
 
msgid "created"
 
msgstr "criado"
 

	
 
msgid "Show as raw"
 
msgstr "Mostrar original"
 

	
 
msgid "My Account"
 
msgstr "Minha Conta"
 

	
 
msgid "Add"
 
msgstr "Adicionar"
 

	
 
msgid "Confirm to delete this email: %s"
 
msgstr "Confirme para apagar este email: %s"
 

	
 
msgid "New email address"
 
msgstr "Novo endereço de email"
 

	
 
msgid "New password"
 
msgstr "Nova palavra-passe"
 

	
 
msgid "Name"
 
msgstr "Nome"
 

	
 
msgid "Anonymous access"
 
msgstr "Acesso anônimo"
 

	
 
msgid ""
 
"All default permissions on each repository will be reset to chosen "
 
"permission, note that all custom default permission on repositories will "
 
"be lost"
 
msgstr ""
 
"Todas as permissões predefinidos em cada repositório serão modificadas "
 
"para a permissão escolhida, note que todas as permissões predefinidas "
 
"customizadas nos repositórios serão perdidas"
 

	
 
msgid "Repository group"
 
msgstr "Grupo de repositórios"
 

	
 
msgid ""
 
"All default permissions on each repository group will be reset to chosen "
 
"permission, note that all custom default permission on repository groups "
 
"will be lost"
 
msgstr ""
 
"Todas as permissões predefinidas em cada grupo de repositórios serão "
 
"modificadas para a permissão escolhida, note que todas as permissões "
 
"predefinidas customizadas em grupos de repositórios serão perdidas"
 

	
 
msgid "User group"
 
msgstr "Grupo de utilizadores"
 

	
 
msgid "User group creation"
 
msgstr "Criação de grupo de utilizadores"
 

	
 
msgid "Repository forking"
 
msgstr "Bifurcação de repositório"
 

	
 
msgid "Registration"
 
msgstr "Registo"
 

	
 
msgid "External auth account activation"
 
msgstr "Ativação de autenticação de conta externa"
 

	
 
msgid "New IP address"
 
msgstr "Novo endereço IP"
 

	
 
msgid "Group name"
 
msgstr "Nome do grupo"
 

	
 
msgid "Group parent"
 
msgstr "Progenitor do grupo"
 

	
 
msgid "Settings"
 
msgstr "Configurações"
 

	
 
msgid "Permissions"
 
msgstr "Permissões"
 

	
 
msgid "Created on"
 
msgstr "Criado em"
 

	
 
msgid "Confirm to delete this group: %s with %s repository"
 
msgid_plural "Confirm to delete this group: %s with %s repositories"
 
msgstr[0] "Confirme para apagar este grupo: %s com %s repositório"
 
msgstr[1] "Confirme para apagar este grupo: %s com %s repositórios"
 

	
 
msgid "Add new"
 
msgstr "Adicionar novo"
 

	
 
msgid ""
 
"Keep it short and to the point. Use a README file for longer descriptions."
 
msgstr ""
 
"Seja sucinto e objetivo. Use um ficheiro README para descrições mais "
 
"longas."
 

	
 
msgid "Optionally select a group to put this repository into."
 
msgstr "Opcionalmente selecione um grupo no qual pôr esse repositório."
 

	
 
msgid "Type of repository to create."
 
msgstr "Tipo de repositório a criar."
 

	
 
msgid "Landing revision"
 
msgstr "Revisão de pouso"
 

	
 
msgid "Remote"
 
msgstr "Remoto"
 

	
 
msgid "Statistics"
 
msgstr "Estatísticas"
 

	
 
msgid "Remove from public journal"
 
msgstr "Remover do diário público"
 

	
 
msgid "Confirm to delete this repository: %s"
 
msgstr "Confirma apagar esse repositório: %s"
 

	
 
msgid "Detach forks"
 
msgstr "Desassociar bifurcações"
 

	
 
msgid "Delete forks"
 
msgstr "Apagar bifurcações"
 

	
 
msgid "Key"
 
msgstr "Chave"
 

	
 
msgid "Confirm to delete this field: %s"
 
msgstr "Confirme para apagar este campo: %s"
 

	
 
msgid "New field key"
 
msgstr "Próxima chave de campo"
 

	
 
msgid "New field label"
 
msgstr "Próximo rótulo de campo"
 

	
 
msgid "Enter short label"
 
msgstr "Entre com o rótulo curto"
 

	
 
msgid "New field description"
 
msgstr "Nova descrição de campo"
 

	
 
msgid "Enter description of a field"
 
msgstr "Entre com a descrição de um campo"
 

	
 
msgid "Default revision for files page, downloads, whoosh and readme"
 
msgstr ""
 
"Revisão predefinida para página de ficheiros, descarregamentos, whoosh e "
 
"readme"
 

	
 
msgid "Change owner of this repository."
 
msgstr "Mudar o dono desse repositório."
 

	
 
msgid "Hooks"
 
msgstr "Ganchos"
 

	
 
msgid "Send"
 
msgstr "Enviar"
 

	
 
msgid "Site branding"
 
msgstr "Marca do site"
 

	
 
msgid "HTTP authentication realm"
 
msgstr "Realm de autenticação HTTP"
 

	
 
msgid "Failed to remove hook"
 
msgstr "Falha ao remover gancho"
 

	
 
msgid "Invalidate cache for all repositories"
 
msgstr "Invalidar o cache para todos os repositórios"
 

	
 
msgid "Index build option"
 
msgstr "Opção de construção do índice"
 

	
 
msgid "Build from scratch"
 
msgstr "Construir do zero"
 

	
 
msgid "Reindex"
 
msgstr "Reindexar"
 

	
 
msgid "Show repository size after push"
 
msgstr "Mostrar tamanho do repositório após o push"
 

	
 
msgid "Update repository after push (hg update)"
 
msgstr "Atualizar repositório após realizar push (hg update)"
 

	
 
msgid "Enable largefiles extension"
 
msgstr "Ativar extensão largefiles"
 

	
 
msgid "Enable hgsubversion extension"
 
msgstr "Ativar extensão hgsubversion"
 

	
 
msgid ""
 
"Click to unlock. You must restart Kallithea in order to make this setting "
 
"take effect."
 
msgstr ""
 
"Clique para destravar. Deve reiniciar o Kallithea para que esta "
 
"configuração tenha efeito."
 

	
 
msgid "General"
 
msgstr "Geral"
 

	
 
msgid "Use repository extra fields"
 
msgstr "Usar campos extras do repositório"
 

	
 
msgid "Allows storing additional customized fields per repository."
 
msgstr "Permite armazenar campos customizados adicionais por repositório."
 

	
 
msgid "Show Kallithea version"
 
msgstr "Mostrar versão do Kallithea"
 

	
 
msgid "Icons"
 
msgstr "Ícones"
 

	
 
msgid "Show public repository icon on repositories"
 
msgstr "Mostrar ícone de repositório público nos repositórios"
 

	
 
msgid "Show private repository icon on repositories"
 
msgstr "Mostrar ícone de repositório privado nos repositórios"
 

	
 
msgid "Add user group"
 
msgstr "Adicionar grupo de utilizadores"
 

	
 
msgid "Active"
 
msgstr "Ativo"
 

	
 
msgid "Members"
 
msgstr "Membros"
 

	
 
msgid "Confirm to delete this user group: %s"
 
msgstr "Confirme para apagar este grupo de utilizador: %s"
 

	
 
msgid "No members yet"
 
msgstr "Nenhum membro ainda"
 

	
 
msgid "Chosen group members"
 
msgstr "Membros escolhidos do grupo"
 

	
 
msgid "Available members"
 
msgstr "Membros disponíveis"
 

	
 
msgid "Add user"
 
msgstr "Adicionar utilizador"
 

	
 
msgid "Users"
 
msgstr "Utilizadores"
 

	
 
msgid "Password confirmation"
 
msgstr "Confirmação de palavra-passe"
 

	
 
msgid "Confirm to delete this user: %s"
 
msgstr "Confirma apagar este utilizador: %s"
 

	
 
msgid "New password confirmation"
 
msgstr "Confirmação de nova palavra-passe"
 

	
 
msgid "Server instance: %s"
 
msgstr "Instância de servidor: %s"
 

	
 
msgid "Mercurial repository"
 
msgstr "Repositório Mercurial"
 

	
 
msgid "Git repository"
 
msgstr "Repositório Git"
 

	
 
msgid "Summary"
 
msgstr "Sumário"
 

	
 
msgid "Changelog"
 
msgstr "Registo de alterações"
 

	
 
msgid "Files"
 
msgstr "Ficheiros"
 

	
 
msgid "Show Pull Requests for %s"
 
msgstr "Mostrar Pull Requests para %s"
 

	
 
msgid "Pull Requests"
 
msgstr "Pull Requests"
 

	
 
msgid "Options"
 
msgstr "Opções"
 

	
 
msgid "Compare"
 
msgstr "Compare"
 

	
 
msgid "Search"
 
msgstr "Pesquisar"
 

	
 
msgid "Follow"
 
msgstr "Seguir"
 

	
 
msgid "Unfollow"
 
msgstr "Parar de seguir"
 

	
 
msgid "Fork"
 
msgstr "Bifurcação"
 

	
 
msgid "Create Pull Request"
 
msgstr "Criar Pull Request"
 

	
 
msgid "Switch To"
 
msgstr "Trocar Para"
 

	
 
msgid "Show recent activity"
 
msgstr "Mostrar atividade recente"
 

	
 
msgid "Public journal"
 
msgstr "Diário público"
 

	
 
msgid "Show public gists"
 
msgstr "Mostrar gists públicos"
 

	
 
msgid "Gists"
 
msgstr "Gists"
 

	
 
msgid "Search in repositories"
 
msgstr "Buscar nos repositórios"
 

	
 
msgid "Log Out"
 
msgstr "Sair"
 

	
 
msgid "Create repositories"
 
msgstr "Criar repositórios"
 

	
 
msgid "Select this option to allow repository creation for this user"
 
msgstr ""
 
"Selecione esta opção para permitir a criação de repositórios para este "
 
"utilizador"
 

	
 
msgid "Create user groups"
 
msgstr "Criar grupos de utilizadores"
 

	
 
msgid "Select this option to allow user group creation for this user"
 
msgstr ""
 
"Selecione esta opção para permitir a criação de grupos de utilizadores "
 
"para este utilizador"
 

	
 
msgid "Fork repositories"
 
msgstr "Bifurcar repositórios"
 

	
 
msgid "Select this option to allow repository forking for this user"
 
msgstr ""
 
"Selecione esta opção para permitir a bifurcação de repositórios para este "
 
"utilizador"
 

	
 
msgid "Show"
 
msgstr "Mostrar"
 

	
 
msgid "No permissions defined yet"
 
msgstr "Nenhuma permissão definida ainda"
 

	
 
msgid "Permission"
 
msgstr "Permissão"
 

	
 
msgid "Edit Permission"
 
msgstr "Editar Permissão"
 

	
 
msgid "Stop following this repository"
 
msgstr "Parar de seguir este repositório"
 

	
 
msgid "Start following this repository"
 
msgstr "Passar a seguir este repositório"
 

	
 
msgid "Group"
 
msgstr "Grupo"
 

	
 
msgid "Loading ..."
 
msgstr "Carregando..."
 

	
 
msgid "loading ..."
 
msgstr "carregando ..."
 

	
 
msgid "Search truncated"
 
msgstr "Busca truncada"
 

	
 
msgid "No matching files"
 
msgstr "Nenhum ficheiro encontrado"
 

	
 
msgid "Failed to revoke permission"
 
msgstr "Falhou ao revocar a permissão"
 

	
 
msgid "Click to sort ascending"
 
msgstr "Clique para ordenar em ordem crescente"
 

	
 
msgid "Click to sort descending"
 
msgstr "Clique para ordenar em ordem descrescente"
 

	
 
msgid "No records found."
 
msgstr "Nenhum registo encontrado."
 

	
 
msgid "Data error."
 
msgstr "Erro de dados."
 

	
 
msgid "Loading..."
 
msgstr "Carregando..."
 

	
 
msgid "%s Changelog"
 
msgstr "%s Changelog"
 

	
 
msgid "showing %d out of %d revision"
 
msgid_plural "showing %d out of %d revisions"
 
msgstr[0] "mostrando %d de %d revisão"
 
msgstr[1] "mostrando %d de %d revisões"
 

	
 
msgid "Clear selection"
 
msgstr "Deselecionar seleção"
 

	
 
msgid "Compare fork with %s"
 
msgstr "Comparar bifurcação com %s"
 

	
 
msgid "There are no changes yet"
 
msgstr "Ainda não há alteações"
 

	
 
msgid "Removed"
 
msgstr "Removido"
 

	
 
msgid "Changed"
 
msgstr "Modificado"
 

	
 
msgid "Added"
 
msgstr "Adicionado"
 

	
 
msgid "Affected %s files"
 
msgstr "Afetados %s ficheiros"
 

	
 
msgid "Bookmark %s"
 
msgstr "Bookmark %s"
 

	
 
msgid "Tag %s"
 
msgstr "Tag %s"
 

	
 
msgid "Branch %s"
 
msgstr "Ramo %s"
 

	
 
msgid "%s Changeset"
 
msgstr "%s Changeset"
 

	
 
msgid "Changeset status"
 
msgstr "Estado do changeset"
 

	
 
msgid "Raw diff"
 
msgstr "Diff cru"
 

	
 
msgid "Patch diff"
 
msgstr "D"
 

	
 
msgid "Download diff"
 
msgstr "Descarregar diff"
 

	
 
msgid "%s file changed"
 
msgid_plural "%s files changed"
 
msgstr[0] "%s ficheiro modificado"
 
msgstr[1] "%s ficheiros modificados"
 

	
 
msgid "%s file changed with %s insertions and %s deletions"
 
msgid_plural "%s files changed with %s insertions and %s deletions"
 
msgstr[0] "%s ficheiro modificado com %s inserções e %s exclusões"
 
msgstr[1] "%s ficheiros modificados com %s inserções e %s exclusões"
 

	
 
msgid "Vote for pull request status"
 
msgstr "Vote para estado do pull request"
 

	
 
msgid "Comment"
 
msgstr "Comentário"
 

	
 
msgid "You need to be logged in to comment."
 
msgstr "Precisa estar logado para comentar."
 

	
 
msgid "Login now"
 
msgstr "Entrar agora"
 

	
 
msgid "Hide"
 
msgstr "Ocultar"
 

	
 
msgid "%d comment"
 
msgid_plural "%d comments"
 
msgstr[0] "%d comentário"
 
msgstr[1] "%d comentários"
 

	
 
msgid "%s Changesets"
 
msgstr "%s Changesets"
 

	
 
msgid "Changeset status: %s"
 
msgstr "Estado do changeset: %s"
 

	
 
msgid "Files affected"
 
msgstr "Ficheiros afetados"
 

	
 
msgid "Show full diff for this file"
 
msgstr "Mostrar diff completo para este ficheiro"
 

	
 
msgid "Show full side-by-side diff for this file"
 
msgstr "Mostrar diff completo lado-a-lado para este ficheiro"
 

	
 
msgid "Show inline comments"
 
msgstr "Mostrar comentários inline"
 

	
 
msgid "No changesets"
 
msgstr "Nenhum changeset"
 

	
 
msgid "%s Compare"
 
msgstr "%s Comparar"
 

	
 
msgid "Showing %s commit"
 
msgid_plural "Showing %s commits"
 
msgstr[0] "Mostrando %s commit"
 
msgstr[1] "Mostrando %s commits"
 

	
 
msgid "Show full diff"
 
msgstr "Mostrar diff completo"
 

	
 
msgid "Public repository"
 
msgstr "Repositório público"
 

	
 
msgid "No changesets yet"
 
msgstr "Nenhum conjunto de alterações ainda."
 

	
 
msgid "Subscribe to %s rss feed"
 
msgstr "Assinar o feed rss de %s"
 

	
 
msgid "Subscribe to %s atom feed"
 
msgstr "Assinar o feed atom de %s"
 

	
 
msgid "Hello %s"
 
msgstr "Olá %s"
 

	
 
msgid "%s File side-by-side diff"
 
msgstr "Ficheiro %s diff lado-a-lado"
 

	
 
msgid "File diff"
 
msgstr "Diff do ficheiro"
 

	
 
msgid "%s File Diff"
 
msgstr "%s Diff de Ficheiro"
 

	
 
msgid "%s Files"
 
msgstr "%s Ficheiros"
 

	
 
msgid "%s Files Add"
 
msgstr "%s Adicionar Ficheiros"
 

	
 
msgid "Location"
 
msgstr "Local"
 

	
 
msgid "or"
 
msgstr "ou"
 

	
 
msgid "Loading file list..."
 
msgstr "Carregando lista de ficheiros..."
 

	
 
msgid "Size"
 
msgstr "Tamanho"
 

	
 
msgid "Last Revision"
 
msgstr "Última revisão"
 

	
 
msgid "Edit file"
 
msgstr "Editar ficheiro"
 

	
 
msgid "Source"
 
msgstr "Fonte"
 

	
 
msgid "%s author"
 
msgid_plural "%s authors"
 
msgstr[0] "%s autor"
 
msgstr[1] "%s autores"
 

	
 
msgid "Editing files allowed only when on branch head revision"
 
msgstr ""
 
"A edição de ficheiros só é permitida quando se está na revisão mais "
 
"recente do ramo"
 

	
 
msgid "Binary file (%s)"
 
msgstr "Ficheiro binário (%s)"
 

	
 
msgid "annotation"
 
msgstr "anotação"
 

	
 
msgid "No files at given path"
 
msgstr "Nenhum ficheiro no caminho especificado"
 

	
 
msgid "%s Followers"
 
msgstr "%s Seguidores"
 

	
 
msgid "Followers"
 
msgstr "Seguidores"
 

	
 
msgid "Started following -"
 
msgstr "Começou a seguir -"
 

	
 
msgid "Fork name"
 
msgstr "Nome da bifurcação"
 

	
 
msgid "Private"
 
msgstr "Privado"
 

	
 
msgid "Copy permissions"
 
msgstr "Copiar permissões"
 

	
 
msgid "Copy permissions from forked repository"
 
msgstr "Copiar permissões do repositório bifurcado"
 

	
 
msgid "Update after clone"
 
msgstr "Atualizar após clonar"
 

	
 
msgid "Checkout source after making a clone"
 
msgstr "Checkout fontes depois de criar o clone"
 

	
 
msgid "%s Forks"
 
msgstr "%s Bifurcações"
 

	
 
msgid "Forks"
 
msgstr "Bifurcações"
 

	
 
msgid "Forked"
 
msgstr "Bifurcado"
 

	
 
msgid "There are no forks yet"
 
msgstr "Ainda não há bifurcações"
 

	
 
msgid "ATOM journal feed"
 
msgstr "ATOM feed do diário"
 

	
 
msgid "RSS journal feed"
 
msgstr "RSS feed do diário"
 

	
 
msgid "No entries yet"
 
msgstr "Ainda não há entradas"
 

	
 
msgid "ATOM public journal feed"
 
msgstr "ATOM feed do diário público"
 

	
 
msgid "RSS public journal feed"
 
msgstr "RSS feed do diário público"
 

	
 
msgid "Title"
 
msgstr "Título"
 

	
 
msgid "Write a short description on this pull request"
 
msgstr "Escreva uma breve descrição para este pull request"
 

	
 
msgid "Changeset flow"
 
msgstr "Fluxo de changesets"
 

	
 
msgid "Origin repository"
 
msgstr "Repositório origem"
 

	
 
msgid "Revision"
 
msgstr "Revisão"
 

	
 
msgid "Destination repository"
 
msgstr "Repositório de destino"
 

	
 
msgid "Age"
 
msgstr "Idade"
 

	
 
msgid "Closed"
 
msgstr "Fechado"
 

	
 
msgid "Confirm to delete this pull request"
 
msgstr "Confirme para apagar este pull request"
 

	
 
msgid "Pull request status calculated from votes"
 
msgstr "Estado do pull request calculado a partir dos votos"
 

	
 
msgid "Pull changes"
 
msgstr "Puxar mudanças"
 

	
 
msgid "%s Pull Requests"
 
msgstr "%s Pull Requests"
 

	
 
msgid "Search term"
 
msgstr "Termo de pesquisa"
 

	
 
msgid "Search in"
 
msgstr "Pesquisando em"
 

	
 
msgid "File contents"
 
msgstr "Conteúdo dos ficheiros"
 

	
 
msgid "Commit messages"
 
msgstr "Mensagens de commit"
 

	
 
msgid "File names"
 
msgstr "Nomes dos ficheiros"
 

	
 
msgid "Permission denied"
 
msgstr "Permissão negada"
 

	
 
msgid "%s ATOM feed"
 
msgstr "%s ATOM feed"
 

	
 
msgid "%s RSS feed"
 
msgstr "%s RSS feed"
 

	
 
msgid "Enable"
 
msgstr "Ativar"
 

	
 
msgid "Stats gathered: "
 
msgstr "Estatísticas coletadas:"
 

	
 
msgid "files"
 
msgstr "ficheiros"
 

	
 
msgid "Show more"
 
msgstr "Mostrar mais"
 

	
 
msgid "commits"
 
msgstr "commits"
 

	
 
msgid "files added"
 
msgstr "ficheiros adicionados"
 

	
 
msgid "files changed"
 
msgstr "ficheiros alterados"
 

	
 
msgid "files removed"
 
msgstr "ficheiros removidos"
 

	
 
msgid "commit"
 
msgstr "commit"
 

	
 
msgid "file added"
 
msgstr "ficheiro adicionado"
 

	
 
msgid "file changed"
 
msgstr "ficheiro alterado"
 

	
 
msgid "file removed"
 
msgstr "ficheiro removido"
 

	
 
msgid "%s Summary"
 
msgstr "%s Sumário"
 

	
 
msgid "Fork of"
 
msgstr "Bifurcação de"
 

	
 
msgid "Clone from"
 
msgstr "Clonar de"
 

	
 
msgid "Trending files"
 
msgstr "Tendências em ficheiros"
 

	
 
msgid "Download"
 
msgstr "Download"
 

	
 
msgid "There are no downloads yet"
 
msgstr "Ainda não há downloads"
 

	
 
msgid "Downloads are disabled for this repository"
 
msgstr "Downloads estão desativados para este repositório"
 

	
 
msgid "Download as zip"
 
msgstr "Download como zip"
 

	
 
msgid "Check this to download archive with subrepos"
 
msgstr "Marque isto para descarregar o arquivo com subrepositórios"
 

	
 
msgid "Feed"
 
msgstr "Feed"
 

	
 
msgid "Add or upload files directly via Kallithea"
 
msgstr "Adicionar ou enviar ficheiros diretamente pelo Kallithea"
 

	
 
msgid "Existing repository?"
 
msgstr "Repositório existente?"
 

	
 
msgid "Download %s as %s"
 
msgstr "Descarregar %s como %s"
kallithea/model/pull_request.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
"""
 
kallithea.model.pull_request
 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
pull request model for Kallithea
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Jun 6, 2012
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import datetime
 
import logging
 
import re
 

	
 
from tg import request
 
from tg.i18n import ugettext as _
 

	
 
from kallithea.lib import helpers as h
 
from kallithea.lib.utils2 import ascii_bytes, extract_mentioned_users
 
from kallithea.model.db import ChangesetStatus, PullRequest, PullRequestReviewer, User
 
from kallithea.model.meta import Session
 
from kallithea.model.notification import NotificationModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
def _assert_valid_reviewers(seq):
 
    """Sanity check: elements are actual User objects, and not the default user."""
 
    assert not any(user.is_default_user for user in seq)
 

	
 

	
 
class PullRequestModel(object):
 

	
 
    def add_reviewers(self, user, pr, reviewers, mention_recipients=None):
 
        """Add reviewer and send notification to them.
 
        """
 
        reviewers = set(reviewers)
 
        _assert_valid_reviewers(reviewers)
 
        if mention_recipients is not None:
 
            mention_recipients = set(mention_recipients) - reviewers
 
            _assert_valid_reviewers(mention_recipients)
 

	
 
        # members
 
        for reviewer in reviewers:
 
            prr = PullRequestReviewer(reviewer, pr)
 
            Session().add(prr)
 

	
 
        # notification to reviewers
 
        pr_url = pr.url(canonical=True)
 
        threading = ['%s-pr-%s@%s' % (pr.other_repo.repo_name,
 
                                      pr.pull_request_id,
 
                                      h.canonical_hostname())]
 
        subject = h.link_to(
 
            _('%(user)s wants you to review pull request %(pr_nice_id)s: %(pr_title)s') %
 
                {'user': user.username,
 
                 'pr_title': pr.title,
 
                 'pr_nice_id': pr.nice_id()},
 
            pr_url)
 
        body = pr.description
 
        _org_ref_type, org_ref_name, _org_rev = pr.org_ref.split(':')
 
        _other_ref_type, other_ref_name, _other_rev = pr.other_ref.split(':')
 
        revision_data = [(x.raw_id, x.message)
 
                         for x in map(pr.org_repo.get_changeset, pr.revisions)]
 
        email_kwargs = {
 
            'pr_title': pr.title,
 
            'pr_title_short': h.shorter(pr.title, 50),
 
            'pr_user_created': user.full_name_and_username,
 
            'pr_repo_url': h.canonical_url('summary_home', repo_name=pr.other_repo.repo_name),
 
            'pr_url': pr_url,
 
            'pr_revisions': revision_data,
 
            'repo_name': pr.other_repo.repo_name,
 
            'org_repo_name': pr.org_repo.repo_name,
 
            'pr_nice_id': pr.nice_id(),
 
            'pr_target_repo': h.canonical_url('summary_home',
 
                               repo_name=pr.other_repo.repo_name),
 
            'pr_target_branch': other_ref_name,
 
            'pr_source_repo': h.canonical_url('summary_home',
 
                               repo_name=pr.org_repo.repo_name),
 
            'pr_source_branch': org_ref_name,
 
            'pr_owner': pr.owner,
 
            'pr_owner_username': pr.owner.username,
 
            'pr_username': user.username,
 
            'threading': threading,
 
            'is_mention': False,
 
            }
 
        if reviewers:
 
            NotificationModel().create(created_by=user, subject=subject, body=body,
 
                                       recipients=reviewers,
 
                                       type_=NotificationModel.TYPE_PULL_REQUEST,
 
                                       email_kwargs=email_kwargs)
 

	
 
        if mention_recipients:
 
            email_kwargs['is_mention'] = True
 
            subject = _('[Mention]') + ' ' + subject
 
            # FIXME: this subject is wrong and unused!
 
            NotificationModel().create(created_by=user, subject=subject, body=body,
 
                                       recipients=mention_recipients,
 
                                       type_=NotificationModel.TYPE_PULL_REQUEST,
 
                                       email_kwargs=email_kwargs)
 

	
 
    def mention_from_description(self, user, pr, old_description=''):
 
        mention_recipients = (extract_mentioned_users(pr.description) -
 
                              extract_mentioned_users(old_description))
 

	
 
        log.debug("Mentioning %s", mention_recipients)
 
        self.add_reviewers(user, pr, set(), mention_recipients)
 

	
 
    def remove_reviewers(self, user, pull_request, reviewers):
 
        """Remove specified users from being reviewers of the PR."""
 
        if not reviewers:
 
            return # avoid SQLAlchemy warning about empty sequence for IN-predicate
 

	
 
        PullRequestReviewer.query() \
 
            .filter_by(pull_request=pull_request) \
 
            .filter(PullRequestReviewer.user_id.in_(r.user_id for r in reviewers)) \
 
            .delete(synchronize_session='fetch') # the default of 'evaluate' is not available
 

	
 
    def delete(self, pull_request):
 
        pull_request = PullRequest.guess_instance(pull_request)
 
        Session().delete(pull_request)
 
        if pull_request.org_repo.scm_instance.alias == 'git':
 
            # remove a ref under refs/pull/ so that commits can be garbage-collected
 
            try:
 
                del pull_request.org_repo.scm_instance._repo["refs/pull/%d/head" % pull_request.pull_request_id]
 
                del pull_request.org_repo.scm_instance._repo[b"refs/pull/%d/head" % pull_request.pull_request_id]
 
            except KeyError:
 
                pass
 

	
 
    def close_pull_request(self, pull_request):
 
        pull_request = PullRequest.guess_instance(pull_request)
 
        pull_request.status = PullRequest.STATUS_CLOSED
 
        pull_request.updated_on = datetime.datetime.now()
 

	
 

	
 
class CreatePullRequestAction(object):
 

	
 
    class ValidationError(Exception):
 
        pass
 

	
 
    class Empty(ValidationError):
 
        pass
 

	
 
    class AmbiguousAncestor(ValidationError):
 
        pass
 

	
 
    class Unauthorized(ValidationError):
 
        pass
 

	
 
    @staticmethod
 
    def is_user_authorized(org_repo, other_repo):
 
        """Performs authorization check with only the minimum amount of
 
        information needed for such a check, rather than a full command
 
        object.
 
        """
 
        if (h.HasRepoPermissionLevel('read')(org_repo.repo_name) and
 
            h.HasRepoPermissionLevel('read')(other_repo.repo_name)
 
        ):
 
            return True
 

	
 
        return False
 

	
 
    def __init__(self, org_repo, other_repo, org_ref, other_ref, title, description, owner, reviewers):
 
        from kallithea.controllers.compare import CompareController
 
        reviewers = set(reviewers)
 
        _assert_valid_reviewers(reviewers)
 

	
 
        (org_ref_type,
 
         org_ref_name,
 
         org_rev) = org_ref.split(':')
 
        org_display = h.short_ref(org_ref_type, org_ref_name)
 
        if org_ref_type == 'rev':
 
            cs = org_repo.scm_instance.get_changeset(org_rev)
 
            org_ref = 'branch:%s:%s' % (cs.branch, cs.raw_id)
 

	
 
        (other_ref_type,
 
         other_ref_name,
 
         other_rev) = other_ref.split(':')
 
        if other_ref_type == 'rev':
 
            cs = other_repo.scm_instance.get_changeset(other_rev)
 
            other_ref_name = cs.raw_id[:12]
 
            other_ref = '%s:%s:%s' % (other_ref_type, other_ref_name, cs.raw_id)
 
        other_display = h.short_ref(other_ref_type, other_ref_name)
 

	
 
        cs_ranges, _cs_ranges_not, ancestor_revs = \
 
            CompareController._get_changesets(org_repo.scm_instance.alias,
 
                                              other_repo.scm_instance, other_rev, # org and other "swapped"
 
                                              org_repo.scm_instance, org_rev,
 
                                              )
 
        if not cs_ranges:
 
            raise self.Empty(_('Cannot create empty pull request'))
 

	
 
        if not ancestor_revs:
 
            ancestor_rev = org_repo.scm_instance.EMPTY_CHANGESET
 
        elif len(ancestor_revs) == 1:
 
            ancestor_rev = ancestor_revs[0]
 
        else:
 
            raise self.AmbiguousAncestor(
 
                _('Cannot create pull request - criss cross merge detected, please merge a later %s revision to %s')
 
                % (other_ref_name, org_ref_name))
 

	
 
        self.revisions = [cs_.raw_id for cs_ in cs_ranges]
 

	
 
        # hack: ancestor_rev is not an other_rev but we want to show the
 
        # requested destination and have the exact ancestor
 
        other_ref = '%s:%s:%s' % (other_ref_type, other_ref_name, ancestor_rev)
 

	
 
        if not title:
 
            if org_repo == other_repo:
 
                title = '%s to %s' % (org_display, other_display)
 
            else:
 
                title = '%s#%s to %s#%s' % (org_repo.repo_name, org_display,
 
                                            other_repo.repo_name, other_display)
 
        description = description or _('No description')
 

	
 
        self.org_repo = org_repo
 
        self.other_repo = other_repo
 
        self.org_ref = org_ref
 
        self.org_rev = org_rev
 
        self.other_ref = other_ref
 
        self.title = title
 
        self.description = description
 
        self.owner = owner
 
        self.reviewers = reviewers
 

	
 
        if not CreatePullRequestAction.is_user_authorized(self.org_repo, self.other_repo):
 
            raise self.Unauthorized(_('You are not authorized to create the pull request'))
 

	
 
    def execute(self):
 
        created_by = User.get(request.authuser.user_id)
 

	
 
        pr = PullRequest()
 
        pr.org_repo = self.org_repo
 
        pr.org_ref = self.org_ref
 
        pr.other_repo = self.other_repo
 
        pr.other_ref = self.other_ref
 
        pr.revisions = self.revisions
 
        pr.title = self.title
 
        pr.description = self.description
 
        pr.owner = self.owner
 
        Session().add(pr)
 
        Session().flush() # make database assign pull_request_id
 

	
 
        if self.org_repo.scm_instance.alias == 'git':
 
            # create a ref under refs/pull/ so that commits don't get garbage-collected
 
            self.org_repo.scm_instance._repo[b"refs/pull/%d/head" % pr.pull_request_id] = ascii_bytes(self.org_rev)
 

	
 
        # reset state to under-review
 
        from kallithea.model.changeset_status import ChangesetStatusModel
 
        from kallithea.model.comment import ChangesetCommentsModel
 
        comment = ChangesetCommentsModel().create(
 
            text='',
 
            repo=self.org_repo,
 
            author=created_by,
 
            pull_request=pr,
 
            send_email=False,
 
            status_change=ChangesetStatus.STATUS_UNDER_REVIEW,
 
        )
 
        ChangesetStatusModel().set_status(
 
            self.org_repo,
 
            ChangesetStatus.STATUS_UNDER_REVIEW,
 
            created_by,
 
            comment,
 
            pull_request=pr,
 
        )
 

	
 
        mention_recipients = extract_mentioned_users(self.description)
 
        PullRequestModel().add_reviewers(created_by, pr, self.reviewers, mention_recipients)
 

	
 
        return pr
 

	
 

	
 
class CreatePullRequestIterationAction(object):
 
    @staticmethod
 
    def is_user_authorized(old_pull_request):
 
        """Performs authorization check with only the minimum amount of
 
        information needed for such a check, rather than a full command
 
        object.
 
        """
 
        if h.HasPermissionAny('hg.admin')():
 
            return True
 

	
 
        # Authorized to edit the old PR?
 
        if request.authuser.user_id != old_pull_request.owner_id:
 
            return False
 

	
 
        # Authorized to create a new PR?
 
        if not CreatePullRequestAction.is_user_authorized(old_pull_request.org_repo, old_pull_request.other_repo):
 
            return False
 

	
 
        return True
 

	
 
    def __init__(self, old_pull_request, new_org_rev, new_other_rev, title, description, owner, reviewers):
 
        self.old_pull_request = old_pull_request
 

	
 
        org_repo = old_pull_request.org_repo
 
        org_ref_type, org_ref_name, org_rev = old_pull_request.org_ref.split(':')
 

	
 
        other_repo = old_pull_request.other_repo
 
        other_ref_type, other_ref_name, other_rev = old_pull_request.other_ref.split(':') # other_rev is ancestor
 
        #assert other_ref_type == 'branch', other_ref_type # TODO: what if not?
 

	
 
        new_org_ref = '%s:%s:%s' % (org_ref_type, org_ref_name, new_org_rev)
 
        new_other_ref = '%s:%s:%s' % (other_ref_type, other_ref_name, new_other_rev)
 

	
 
        self.create_action = CreatePullRequestAction(org_repo, other_repo, new_org_ref, new_other_ref, None, None, owner, reviewers)
 

	
 
        # Generate complete title/description
 

	
 
        old_revisions = set(old_pull_request.revisions)
 
        revisions = self.create_action.revisions
 
        new_revisions = [r for r in revisions if r not in old_revisions]
 
        lost = old_revisions.difference(revisions)
 

	
 
        infos = ['This is a new iteration of %s "%s".' %
 
                 (h.canonical_url('pullrequest_show', repo_name=old_pull_request.other_repo.repo_name,
 
                      pull_request_id=old_pull_request.pull_request_id),
 
                  old_pull_request.title)]
 

	
 
        if lost:
 
            infos.append(_('Missing changesets since the previous iteration:'))
 
            for r in old_pull_request.revisions:
 
                if r in lost:
 
                    rev_desc = org_repo.get_changeset(r).message.split('\n')[0]
 
                    infos.append('  %s %s' % (h.short_id(r), rev_desc))
 

	
 
        if new_revisions:
 
            infos.append(_('New changesets on %s %s since the previous iteration:') % (org_ref_type, org_ref_name))
 
            for r in reversed(revisions):
 
                if r in new_revisions:
 
                    rev_desc = org_repo.get_changeset(r).message.split('\n')[0]
 
                    infos.append('  %s %s' % (h.short_id(r), h.shorter(rev_desc, 80)))
 

	
 
            if self.create_action.other_ref == old_pull_request.other_ref:
 
                infos.append(_("Ancestor didn't change - diff since previous iteration:"))
 
                infos.append(h.canonical_url('compare_url',
 
                                 repo_name=org_repo.repo_name, # other_repo is always same as repo_name
 
                                 org_ref_type='rev', org_ref_name=h.short_id(org_rev), # use old org_rev as base
 
                                 other_ref_type='rev', other_ref_name=h.short_id(new_org_rev),
 
                                 )) # note: linear diff, merge or not doesn't matter
 
            else:
 
                infos.append(_('This iteration is based on another %s revision and there is no simple diff.') % other_ref_name)
 
        else:
 
            infos.append(_('No changes found on %s %s since previous iteration.') % (org_ref_type, org_ref_name))
 
            # TODO: fail?
 

	
 
        v = 2
 
        m = re.match(r'(.*)\(v(\d+)\)\s*$', title)
 
        if m is not None:
 
            title = m.group(1)
 
            v = int(m.group(2)) + 1
 
        self.create_action.title = '%s (v%s)' % (title.strip(), v)
 

	
 
        # using a mail-like separator, insert new iteration info in description with latest first
 
        descriptions = description.replace('\r\n', '\n').split('\n-- \n', 1)
 
        description = descriptions[0].strip() + '\n\n-- \n' + '\n'.join(infos)
 
        if len(descriptions) > 1:
 
            description += '\n\n' + descriptions[1].strip()
 
        self.create_action.description = description
 

	
 
        if not CreatePullRequestIterationAction.is_user_authorized(self.old_pull_request):
 
            raise CreatePullRequestAction.Unauthorized(_('You are not authorized to create the pull request'))
 

	
 
    def execute(self):
 
        pull_request = self.create_action.execute()
 

	
 
        # Close old iteration
 
        from kallithea.model.comment import ChangesetCommentsModel
 
        ChangesetCommentsModel().create(
 
            text=_('Closed, next iteration: %s .') % pull_request.url(canonical=True),
 
            repo=self.old_pull_request.other_repo_id,
 
            author=request.authuser.user_id,
 
            pull_request=self.old_pull_request.pull_request_id,
 
            closing_pr=True)
 
        PullRequestModel().close_pull_request(self.old_pull_request.pull_request_id)
 
        return pull_request
kallithea/templates/about.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<%inherit file="/base/base.html"/>
 
<%block name="title">
 
    ${_('About')}
 
</%block>
 
<%block name="header_menu">
 
    ${self.menu('about')}
 
</%block>
 
<%def name="main()">
 

	
 
<div class="panel panel-primary">
 
  <div class="panel-heading">
 
    <h5 class="panel-title">${_('About')} Kallithea</h5>
 
  </div>
 

	
 
  <div class="panel-body panel-about">
 
  <p><a href="https://kallithea-scm.org/">Kallithea</a> is a project of the
 
  <a href="http://sfconservancy.org/">Software Freedom Conservancy, Inc.</a>
 
  and is released under the terms of the
 
  <a href="http://www.gnu.org/copyleft/gpl.html">GNU General Public License,
 
  v 3.0 (GPLv3)</a>.</p>
 

	
 
  <p>Kallithea is copyrighted by various authors, including but not
 
  necessarily limited to the following:</p>
 
  <ul>
 

	
 
  <li>Copyright &copy; 2012&ndash;2020, Mads Kiilerich</li>
 
  <li>Copyright &copy; 2014&ndash;2020, Thomas De Schampheleire</li>
 
  <li>Copyright &copy; 2015&ndash;2017, 2019&ndash;2020, Étienne Gilli</li>
 
  <li>Copyright &copy; 2016&ndash;2017, 2020, Asterios Dimitriou</li>
 
  <li>Copyright &copy; 2017&ndash;2020, Allan Nordhøy</li>
 
  <li>Copyright &copy; 2017, 2020, Anton Schur</li>
 
  <li>Copyright &copy; 2018&ndash;2020, ssantos</li>
 
  <li>Copyright &copy; 2019&ndash;2020, Manuel Jacob</li>
 
  <li>Copyright &copy; 2019&ndash;2020, Private</li>
 
  <li>Copyright &copy; 2020, David Ignjić</li>
 
  <li>Copyright &copy; 2020, Dennis Fink</li>
 
  <li>Copyright &copy; 2020, J. Lavoie</li>
 
  <li>Copyright &copy; 2020, robertus</li>
 
  <li>Copyright &copy; 2020, Ross Thomas</li>
 
  <li>Copyright &copy; 2012, 2014&ndash;2017, 2019, Andrej Shadura</li>
 
  <li>Copyright &copy; 2017&ndash;2019, Allan Nordhøy</li>
 
  <li>Copyright &copy; 2018&ndash;2019, ssantos</li>
 
  <li>Copyright &copy; 2019, Adi Kriegisch</li>
 
  <li>Copyright &copy; 2019, Danni Randeris</li>
 
  <li>Copyright &copy; 2019, Edmund Wong</li>
 
  <li>Copyright &copy; 2019, Elizabeth Sherrock</li>
 
  <li>Copyright &copy; 2019, Hüseyin Tunç</li>
 
  <li>Copyright &copy; 2019, leela</li>
 
  <li>Copyright &copy; 2019, Manuel Jacob</li>
 
  <li>Copyright &copy; 2019, Mateusz Mendel</li>
 
  <li>Copyright &copy; 2019, Nathan</li>
 
  <li>Copyright &copy; 2019, Oleksandr Shtalinberg</li>
 
  <li>Copyright &copy; 2019, THANOS SIOURDAKIS</li>
 
  <li>Copyright &copy; 2019, Wolfgang Scherer</li>
 
  <li>Copyright &copy; 2019, Христо Станев</li>
 
  <li>Copyright &copy; 2012, 2014&ndash;2018, Dominik Ruf</li>
 
  <li>Copyright &copy; 2014&ndash;2015, 2018, Michal Čihař</li>
 
  <li>Copyright &copy; 2015, 2018, Branko Majic</li>
 
  <li>Copyright &copy; 2018, Chris Rule</li>
 
  <li>Copyright &copy; 2018, Jesús Sánchez</li>
 
  <li>Copyright &copy; 2018, Patrick Vane</li>
 
  <li>Copyright &copy; 2018, Pheng Heong Tan</li>
 
  <li>Copyright &copy; 2018, Максим Якимчук</li>
 
  <li>Copyright &copy; 2018, Марс Ямбар</li>
 
  <li>Copyright &copy; 2012&ndash;2017, Unity Technologies</li>
 
  <li>Copyright &copy; 2015&ndash;2017, Søren Løvborg</li>
 
  <li>Copyright &copy; 2015, 2017, Sam Jaques</li>
 
  <li>Copyright &copy; 2017, Alessandro Molina</li>
 
  <li>Copyright &copy; 2017, Anton Schur</li>
 
  <li>Copyright &copy; 2017, Ching-Chen Mao</li>
 
  <li>Copyright &copy; 2017, Eivind Tagseth</li>
 
  <li>Copyright &copy; 2017, FUJIWARA Katsunori</li>
 
  <li>Copyright &copy; 2017, Holger Schramm</li>
 
  <li>Copyright &copy; 2017, Karl Goetz</li>
 
  <li>Copyright &copy; 2017, Lars Kruse</li>
 
  <li>Copyright &copy; 2017, Marko Semet</li>
 
  <li>Copyright &copy; 2017, Viktar Vauchkevich</li>
 
  <li>Copyright &copy; 2012&ndash;2016, Takumi IINO</li>
 
  <li>Copyright &copy; 2015&ndash;2016, Jan Heylen</li>
 
  <li>Copyright &copy; 2015&ndash;2016, Robert Martinez</li>
 
  <li>Copyright &copy; 2015&ndash;2016, Robert Rauch</li>
 
  <li>Copyright &copy; 2016, Angel Ezquerra</li>
 
  <li>Copyright &copy; 2016, Anton Shestakov</li>
 
  <li>Copyright &copy; 2016, Brandon Jones</li>
 
  <li>Copyright &copy; 2016, Kateryna Musina</li>
 
  <li>Copyright &copy; 2016, Konstantin Veretennicov</li>
 
  <li>Copyright &copy; 2016, Oscar Curero</li>
 
  <li>Copyright &copy; 2016, Robert James Dennington</li>
 
  <li>Copyright &copy; 2016, timeless@gmail.com</li>
 
  <li>Copyright &copy; 2016, YFdyh000</li>
 
  <li>Copyright &copy; 2012&ndash;2013, 2015, Aras Pranckevičius</li>
 
  <li>Copyright &copy; 2014&ndash;2015, Bradley M. Kuhn</li>
 
  <li>Copyright &copy; 2014&ndash;2015, Christian Oyarzun</li>
 
  <li>Copyright &copy; 2014&ndash;2015, Joseph Rivera</li>
 
  <li>Copyright &copy; 2014&ndash;2015, Sean Farley</li>
 
  <li>Copyright &copy; 2015, Anatoly Bubenkov</li>
 
  <li>Copyright &copy; 2015, Andrew Bartlett</li>
 
  <li>Copyright &copy; 2015, Balázs Úr</li>
 
  <li>Copyright &copy; 2015, Ben Finney</li>
 
  <li>Copyright &copy; 2015, Daniel Hobley</li>
 
  <li>Copyright &copy; 2015, David Avigni</li>
 
  <li>Copyright &copy; 2015, Denis Blanchette</li>
 
  <li>Copyright &copy; 2015, duanhongyi</li>
 
  <li>Copyright &copy; 2015, EriCSN Chang</li>
 
  <li>Copyright &copy; 2015, Grzegorz Krason</li>
 
  <li>Copyright &copy; 2015, Jiří Suchan</li>
 
  <li>Copyright &copy; 2015, Kazunari Kobayashi</li>
 
  <li>Copyright &copy; 2015, Kevin Bullock</li>
 
  <li>Copyright &copy; 2015, kobanari</li>
 
  <li>Copyright &copy; 2015, Marc Abramowitz</li>
 
  <li>Copyright &copy; 2015, Marc Villetard</li>
 
  <li>Copyright &copy; 2015, Matthias Zilk</li>
 
  <li>Copyright &copy; 2015, Michael Pohl</li>
 
  <li>Copyright &copy; 2015, Michael V. DePalatis</li>
 
  <li>Copyright &copy; 2015, Morten Skaaning</li>
 
  <li>Copyright &copy; 2015, Nick High</li>
 
  <li>Copyright &copy; 2015, Niemand Jedermann</li>
 
  <li>Copyright &copy; 2015, Peter Vitt</li>
 
  <li>Copyright &copy; 2015, Ronny Pfannschmidt</li>
 
  <li>Copyright &copy; 2015, Tuux</li>
 
  <li>Copyright &copy; 2015, Viktar Palstsiuk</li>
 
  <li>Copyright &copy; 2014, Ante Ilic</li>
 
  <li>Copyright &copy; 2014, Calinou</li>
 
  <li>Copyright &copy; 2014, Daniel Anderson</li>
 
  <li>Copyright &copy; 2014, Henrik Stuart</li>
 
  <li>Copyright &copy; 2014, Ingo von Borstel</li>
 
  <li>Copyright &copy; 2014, invision70</li>
 
  <li>Copyright &copy; 2014, Jelmer Vernooij</li>
 
  <li>Copyright &copy; 2014, Jim Hague</li>
 
  <li>Copyright &copy; 2014, Matt Fellows</li>
 
  <li>Copyright &copy; 2014, Max Roman</li>
 
  <li>Copyright &copy; 2014, Na'Tosha Bard</li>
 
  <li>Copyright &copy; 2014, Rasmus Selsmark</li>
 
  <li>Copyright &copy; 2014, SkryabinD</li>
 
  <li>Copyright &copy; 2014, Tim Freund</li>
 
  <li>Copyright &copy; 2014, Travis Burtrum</li>
 
  <li>Copyright &copy; 2014, whosaysni</li>
 
  <li>Copyright &copy; 2014, Zoltan Gyarmati</li>
 
  <li>Copyright &copy; 2010&ndash;2013, Marcin Kuźmiński</li>
 
  <li>Copyright &copy; 2010&ndash;2013, RhodeCode GmbH</li>
 
  <li>Copyright &copy; 2011, 2013, Aparkar</li>
 
  <li>Copyright &copy; 2012&ndash;2013, Nemcio</li>
 
  <li>Copyright &copy; 2012&ndash;2013, xpol</li>
 
  <li>Copyright &copy; 2013, Andrey Mivrenik</li>
 
  <li>Copyright &copy; 2013, ArcheR</li>
 
  <li>Copyright &copy; 2013, Dennis Brakhane</li>
 
  <li>Copyright &copy; 2013, gnustavo</li>
 
  <li>Copyright &copy; 2013, Grzegorz Rożniecki</li>
 
  <li>Copyright &copy; 2013, Ilya Beda</li>
 
  <li>Copyright &copy; 2013, ivlevdenis</li>
 
  <li>Copyright &copy; 2013, Jonathan Sternberg</li>
 
  <li>Copyright &copy; 2013, Leonardo Carneiro</li>
 
  <li>Copyright &copy; 2013, Magnus Ericmats</li>
 
  <li>Copyright &copy; 2013, Martin Vium</li>
 
  <li>Copyright &copy; 2013, Mikhail Zholobov</li>
 
  <li>Copyright &copy; 2013, mokeev1995</li>
 
  <li>Copyright &copy; 2013, Ruslan Bekenev</li>
 
  <li>Copyright &copy; 2013, shirou - しろう</li>
 
  <li>Copyright &copy; 2013, Simon Lopez</li>
 
  <li>Copyright &copy; 2013, softforwinxp</li>
 
  <li>Copyright &copy; 2013, stephanj</li>
 
  <li>Copyright &copy; 2013, zhmylove</li>
 
  <li>Copyright &copy; 2013, こいんとす</li>
 
  <li>Copyright &copy; 2011&ndash;2012, Augusto Herrmann</li>
 
  <li>Copyright &copy; 2012, Dan Sheridan</li>
 
  <li>Copyright &copy; 2012, H Waldo G</li>
 
  <li>Copyright &copy; 2012, hppj</li>
 
  <li>Copyright &copy; 2012, Indra Talip</li>
 
  <li>Copyright &copy; 2012, mikespook</li>
 
  <li>Copyright &copy; 2012, nansenat16</li>
 
  <li>Copyright &copy; 2012, Philip Jameson</li>
 
  <li>Copyright &copy; 2012, Raoul Thill</li>
 
  <li>Copyright &copy; 2012, Tony Bussieres</li>
 
  <li>Copyright &copy; 2012, Vincent Duvert</li>
 
  <li>Copyright &copy; 2012, Vladislav Poluhin</li>
 
  <li>Copyright &copy; 2012, Zachary Auclair</li>
 
  <li>Copyright &copy; 2011, Ankit Solanki</li>
 
  <li>Copyright &copy; 2011, Dmitri Kuznetsov</li>
 
  <li>Copyright &copy; 2011, Jared Bunting</li>
 
  <li>Copyright &copy; 2011, Jason Harris</li>
 
  <li>Copyright &copy; 2011, Les Peabody</li>
 
  <li>Copyright &copy; 2011, Liad Shani</li>
 
  <li>Copyright &copy; 2011, Lorenzo M. Catucci</li>
 
  <li>Copyright &copy; 2011, Matt Zuba</li>
 
  <li>Copyright &copy; 2011, Nicolas VINOT</li>
 
  <li>Copyright &copy; 2011, Shawn K. O'Shea</li>
 
  <li>Copyright &copy; 2010, Łukasz Balcerzak</li>
 

	
 
## We did not list the following copyright holders, given that they appeared
 
## to use for-profit company affiliations in their contribution in the
 
## Mercurial log and therefore I didn't know if copyright was theirs or
 
## their company's.
 
## Copyright &copy; 2011 Thayne Harbaugh <thayne@fusionio.com>
 
## Copyright &copy; 2012 Dies Koper <diesk@fast.au.fujitsu.com>
 
## Copyright &copy; 2012 Erwin Kroon <e.kroon@smartmetersolutions.nl>
 
## Copyright &copy; 2012 Vincent Caron <vcaron@bearstech.com>
 
##
 
## These contributors' contributions may not be copyrightable:
 
## philip.j@hostdime.com in 2012
 
## Stefan Engel <mail@engel-stefan.de> in 2012
 
## Ton Plomp <tcplomp@gmail.com> in 2013
 
##
 
  </ul>
 

	
 
  <p>The above are the copyright holders who have submitted direct
 
  contributions to the Kallithea repository.</p>
 

	
 
  <p>In the <a href="https://kallithea-scm.org/repos/kallithea">Kallithea
 
  source code</a>, there is a
 
  <a href="https://kallithea-scm.org/repos/kallithea/files/tip/LICENSE.md">list
 
  of third-party libraries and code that Kallithea incorporates</a>.</p>
 

	
 
  <p>The front-end contains a <a href="${h.url('/LICENSES.txt')}">list of
 
  software that is used to build the front-end</a> but isn't distributed as a
 
  part of Kallithea.</p>
 

	
 
  </div>
 
</div>
 

	
 
</%def>
kallithea/tests/functional/test_pullrequests.py
Show inline comments
 
import re
 

	
 
import pytest
 

	
 
from kallithea.controllers.pullrequests import PullrequestsController
 
from kallithea.model.db import PullRequest, User
 
from kallithea.model.meta import Session
 
from kallithea.tests import base
 
from kallithea.tests.fixture import Fixture
 

	
 

	
 
fixture = Fixture()
 

	
 

	
 
class TestPullrequestsController(base.TestController):
 

	
 
    def test_index(self):
 
        self.log_user()
 
        response = self.app.get(base.url(controller='pullrequests', action='index',
 
                                    repo_name=base.HG_REPO))
 

	
 
    def test_create_trivial(self):
 
        self.log_user()
 
        response = self.app.post(base.url(controller='pullrequests', action='create',
 
                                     repo_name=base.HG_REPO),
 
                                 {'org_repo': base.HG_REPO,
 
                                  'org_ref': 'branch:stable:4f7e2131323e0749a740c0a56ab68ae9269c562a',
 
                                  'other_repo': base.HG_REPO,
 
                                  'other_ref': 'branch:default:96507bd11ecc815ebc6270fdf6db110928c09c1e',
 
                                  'pullrequest_title': 'title',
 
                                  'pullrequest_desc': 'description',
 
                                  '_session_csrf_secret_token': self.session_csrf_secret_token(),
 
                                 },
 
                                 status=302)
 
        # will redirect to URL like http://localhost/vcs_test_hg/pull-request/1/_/stable
 
        pull_request_id = int(response.location.split('/')[5])
 

	
 
        response = response.follow()
 
        assert response.status == '200 OK'
 
        response.mustcontain('Successfully opened new pull request')
 
        response.mustcontain('No additional changesets found for iterating on this pull request')
 
        response.mustcontain('href="/vcs_test_hg/changeset/4f7e2131323e0749a740c0a56ab68ae9269c562a"')
 

	
 
        response = self.app.post(base.url('pullrequest_delete',
 
                                 repo_name=base.HG_REPO, pull_request_id=pull_request_id),
 
                                 {
 
                                  '_session_csrf_secret_token': self.session_csrf_secret_token(),
 
                                 },
 
                                 status=302)
 
        response = response.follow()
 
        assert response.status == '200 OK'
 
        response.mustcontain('Successfully deleted pull request')
 

	
 
    def test_available(self):
 
        self.log_user()
 
        response = self.app.post(base.url(controller='pullrequests', action='create',
 
                                     repo_name=base.HG_REPO),
 
                                 {'org_repo': base.HG_REPO,
 
                                  'org_ref': 'rev:94f45ed825a1:94f45ed825a113e61af7e141f44ca578374abef0',
 
                                  'other_repo': base.HG_REPO,
 
                                  'other_ref': 'branch:default:96507bd11ecc815ebc6270fdf6db110928c09c1e',
 
                                  'pullrequest_title': 'title',
 
                                  'pullrequest_desc': 'description',
 
                                  '_session_csrf_secret_token': self.session_csrf_secret_token(),
 
                                 },
 
                                 status=302)
 
        response = response.follow()
 
        assert response.status == '200 OK'
 
        response.mustcontain(no='No additional changesets found for iterating on this pull request')
 
        response.mustcontain('The following additional changes are available on stable:')
 
        response.mustcontain('<input id="updaterev_4f7e2131323e0749a740c0a56ab68ae9269c562a" name="updaterev" type="radio" value="4f7e2131323e0749a740c0a56ab68ae9269c562a" />')
 
        response.mustcontain('href="/vcs_test_hg/changeset/4f7e2131323e0749a740c0a56ab68ae9269c562a"') # as update
 

	
 
    def test_range(self):
 
        self.log_user()
 
        response = self.app.post(base.url(controller='pullrequests', action='create',
 
                                     repo_name=base.HG_REPO),
 
                                 {'org_repo': base.HG_REPO,
 
                                  'org_ref': 'branch:stable:4f7e2131323e0749a740c0a56ab68ae9269c562a',
 
                                  'other_repo': base.HG_REPO,
 
                                  'other_ref': 'rev:94f45ed825a1:94f45ed825a113e61af7e141f44ca578374abef0',
 
                                  'pullrequest_title': 'title',
 
                                  'pullrequest_desc': 'description',
 
                                  '_session_csrf_secret_token': self.session_csrf_secret_token(),
 
                                 },
 
                                 status=302)
 
        response = response.follow()
 
        assert response.status == '200 OK'
 
        response.mustcontain('No additional changesets found for iterating on this pull request')
 
        response.mustcontain('href="/vcs_test_hg/changeset/4f7e2131323e0749a740c0a56ab68ae9269c562a"')
 

	
 
    def test_update_reviewers(self):
 
        self.log_user()
 
        regular_user = User.get_by_username(base.TEST_USER_REGULAR_LOGIN)
 
        regular_user2 = User.get_by_username(base.TEST_USER_REGULAR2_LOGIN)
 
        admin_user = User.get_by_username(base.TEST_USER_ADMIN_LOGIN)
 

	
 
        # create initial PR
 
        response = self.app.post(base.url(controller='pullrequests', action='create',
 
                                     repo_name=base.HG_REPO),
 
                                 {'org_repo': base.HG_REPO,
 
                                  'org_ref': 'rev:94f45ed825a1:94f45ed825a113e61af7e141f44ca578374abef0',
 
                                  'other_repo': base.HG_REPO,
 
                                  'other_ref': 'branch:default:96507bd11ecc815ebc6270fdf6db110928c09c1e',
 
                                  'pullrequest_title': 'title',
 
                                  'pullrequest_desc': 'description',
 
                                  '_session_csrf_secret_token': self.session_csrf_secret_token(),
 
                                 },
 
                                 status=302)
 
        pull_request1_id = re.search(r'/pull-request/(\d+)/', response.location).group(1)
 
        assert response.location == 'http://localhost/%s/pull-request/%s/_/stable' % (base.HG_REPO, pull_request1_id)
 

	
 
        # create new iteration
 
        response = self.app.post(base.url(controller='pullrequests', action='post',
 
                                     repo_name=base.HG_REPO, pull_request_id=pull_request1_id),
 
                                 {
 
                                  'updaterev': '4f7e2131323e0749a740c0a56ab68ae9269c562a',
 
                                  'pullrequest_title': 'title',
 
                                  'pullrequest_desc': 'description',
 
                                  'owner': base.TEST_USER_ADMIN_LOGIN,
 
                                  '_session_csrf_secret_token': self.session_csrf_secret_token(),
 
                                  'review_members': [regular_user.user_id],
 
                                 },
 
                                 status=302)
 
        pull_request2_id = re.search(r'/pull-request/(\d+)/', response.location).group(1)
 
        assert pull_request2_id != pull_request1_id
 
        assert response.location == 'http://localhost/%s/pull-request/%s/_/stable' % (base.HG_REPO, pull_request2_id)
 
        response = response.follow()
 
        # verify reviewer was added
 
        response.mustcontain('<input type="hidden" value="%s" name="review_members" />' % regular_user.user_id)
 

	
 
        # update without creating new iteration
 
        response = self.app.post(base.url(controller='pullrequests', action='post',
 
                                     repo_name=base.HG_REPO, pull_request_id=pull_request2_id),
 
                                 {
 
                                  'pullrequest_title': 'Title',
 
                                  'pullrequest_desc': 'description',
 
                                  'owner': base.TEST_USER_ADMIN_LOGIN,
 
                                  '_session_csrf_secret_token': self.session_csrf_secret_token(),
 
                                  'org_review_members': [admin_user.user_id], # fake - just to get some 'meanwhile' warning ... but it is also added ...
 
                                  'review_members': [regular_user2.user_id, admin_user.user_id],
 
                                 },
 
                                 status=302)
 
        assert response.location == 'http://localhost/%s/pull-request/%s/_/stable' % (base.HG_REPO, pull_request2_id)
 
        response = response.follow()
 
        # verify reviewers were added / removed
 
        response.mustcontain('Meanwhile, the following reviewers have been added: test_regular')
 
        response.mustcontain('Meanwhile, the following reviewers have been removed: test_admin')
 
        response.mustcontain('<input type="hidden" value="%s" name="review_members" />' % regular_user.user_id)
 
        response.mustcontain('<input type="hidden" value="%s" name="review_members" />' % regular_user2.user_id)
 
        response.mustcontain(no='<input type="hidden" value="%s" name="review_members" />' % admin_user.user_id)
 

	
 
    def test_update_with_invalid_reviewer(self):
 
        invalid_user_id = 99999
 
        self.log_user()
 
        # create a valid pull request
 
        response = self.app.post(base.url(controller='pullrequests', action='create',
 
                                     repo_name=base.HG_REPO),
 
                                 {
 
                                  'org_repo': base.HG_REPO,
 
                                  'org_ref': 'rev:94f45ed825a1:94f45ed825a113e61af7e141f44ca578374abef0',
 
                                  'other_repo': base.HG_REPO,
 
                                  'other_ref': 'branch:default:96507bd11ecc815ebc6270fdf6db110928c09c1e',
 
                                  'pullrequest_title': 'title',
 
                                  'pullrequest_desc': 'description',
 
                                  '_session_csrf_secret_token': self.session_csrf_secret_token(),
 
                                 },
 
                                status=302)
 
        # location is of the form:
 
        # http://localhost/vcs_test_hg/pull-request/54/_/title
 
        m = re.search(r'/pull-request/(\d+)/', response.location)
 
        assert m is not None
 
        pull_request_id = m.group(1)
 

	
 
        # update it
 
        response = self.app.post(base.url(controller='pullrequests', action='post',
 
                                     repo_name=base.HG_REPO, pull_request_id=pull_request_id),
 
                                 {
 
                                  'updaterev': '4f7e2131323e0749a740c0a56ab68ae9269c562a',
 
                                  'pullrequest_title': 'title',
 
                                  'pullrequest_desc': 'description',
 
                                  'owner': base.TEST_USER_ADMIN_LOGIN,
 
                                  '_session_csrf_secret_token': self.session_csrf_secret_token(),
 
                                  'review_members': [str(invalid_user_id)],
 
                                 },
 
                                 status=400)
 
        response.mustcontain('Invalid reviewer &quot;%s&quot; specified' % invalid_user_id)
 

	
 
    def test_edit_with_invalid_reviewer(self):
 
        invalid_user_id = 99999
 
        self.log_user()
 
        # create a valid pull request
 
        response = self.app.post(base.url(controller='pullrequests', action='create',
 
                                     repo_name=base.HG_REPO),
 
                                 {
 
                                  'org_repo': base.HG_REPO,
 
                                  'org_ref': 'branch:stable:4f7e2131323e0749a740c0a56ab68ae9269c562a',
 
                                  'other_repo': base.HG_REPO,
 
                                  'other_ref': 'branch:default:96507bd11ecc815ebc6270fdf6db110928c09c1e',
 
                                  'pullrequest_title': 'title',
 
                                  'pullrequest_desc': 'description',
 
                                  '_session_csrf_secret_token': self.session_csrf_secret_token(),
 
                                 },
 
                                status=302)
 
        # location is of the form:
 
        # http://localhost/vcs_test_hg/pull-request/54/_/title
 
        m = re.search(r'/pull-request/(\d+)/', response.location)
 
        assert m is not None
 
        pull_request_id = m.group(1)
 

	
 
        # edit it
 
        response = self.app.post(base.url(controller='pullrequests', action='post',
 
                                     repo_name=base.HG_REPO, pull_request_id=pull_request_id),
 
                                 {
 
                                  'pullrequest_title': 'title',
 
                                  'pullrequest_desc': 'description',
 
                                  'owner': base.TEST_USER_ADMIN_LOGIN,
 
                                  '_session_csrf_secret_token': self.session_csrf_secret_token(),
 
                                  'review_members': [str(invalid_user_id)],
 
                                 },
 
                                 status=400)
 
        response.mustcontain('Invalid reviewer &quot;%s&quot; specified' % invalid_user_id)
 

	
 
    def test_iteration_refs(self):
 
        # Repo graph excerpt:
 
        #   o   fb95b340e0d0 webvcs
 
        #  /:
 
        # o :   41d2568309a0 default
 
        # : :
 
        # : o   5ec21f21aafe webvcs
 
        # : :
 
        # : o   9e6119747791 webvcs
 
        # : :
 
        # o :   3d1091ee5a53 default
 
        # :/
 
        # o     948da46b29c1 default
 

	
 
        self.log_user()
 

	
 
        # create initial PR
 
        response = self.app.post(
 
            base.url(controller='pullrequests', action='create', repo_name=base.HG_REPO),
 
            {
 
                'org_repo': base.HG_REPO,
 
                'org_ref': 'rev:9e6119747791:9e6119747791ff886a5abe1193a730b6bf874e1c',
 
                'other_repo': base.HG_REPO,
 
                'other_ref': 'branch:default:3d1091ee5a533b1f4577ec7d8a226bb315fb1336',
 
                'pullrequest_title': 'title',
 
                'pullrequest_desc': 'description',
 
                '_session_csrf_secret_token': self.session_csrf_secret_token(),
 
            },
 
            status=302)
 
        pr1_id = int(re.search(r'/pull-request/(\d+)/', response.location).group(1))
 
        pr1 = PullRequest.get(pr1_id)
 

	
 
        assert pr1.org_ref == 'branch:webvcs:9e6119747791ff886a5abe1193a730b6bf874e1c'
 
        assert pr1.other_ref == 'branch:default:948da46b29c125838a717f6a8496eb409717078d'
 

	
 
        Session().rollback() # invalidate loaded PR objects before issuing next request.
 

	
 
        # create PR 2 (new iteration with same ancestor)
 
        response = self.app.post(
 
            base.url(controller='pullrequests', action='post', repo_name=base.HG_REPO, pull_request_id=pr1_id),
 
            {
 
                'updaterev': '5ec21f21aafe95220f1fc4843a4a57c378498b71',
 
                'pullrequest_title': 'title',
 
                'pullrequest_desc': 'description',
 
                'owner': base.TEST_USER_REGULAR_LOGIN,
 
                '_session_csrf_secret_token': self.session_csrf_secret_token(),
 
             },
 
             status=302)
 
        pr2_id = int(re.search(r'/pull-request/(\d+)/', response.location).group(1))
 
        pr1 = PullRequest.get(pr1_id)
 
        pr2 = PullRequest.get(pr2_id)
 

	
 
        assert pr2_id != pr1_id
 
        assert pr1.status == PullRequest.STATUS_CLOSED
 
        assert pr2.org_ref == 'branch:webvcs:5ec21f21aafe95220f1fc4843a4a57c378498b71'
 
        assert pr2.other_ref == pr1.other_ref
 

	
 
        Session().rollback() # invalidate loaded PR objects before issuing next request.
 

	
 
        # create PR 3 (new iteration with new ancestor)
 
        response = self.app.post(
 
            base.url(controller='pullrequests', action='post', repo_name=base.HG_REPO, pull_request_id=pr2_id),
 
            {
 
                'updaterev': 'fb95b340e0d03fa51f33c56c991c08077c99303e',
 
                'pullrequest_title': 'title',
 
                'pullrequest_desc': 'description',
 
                'owner': base.TEST_USER_REGULAR_LOGIN,
 
                '_session_csrf_secret_token': self.session_csrf_secret_token(),
 
             },
 
             status=302)
 
        pr3_id = int(re.search(r'/pull-request/(\d+)/', response.location).group(1))
 
        pr2 = PullRequest.get(pr2_id)
 
        pr3 = PullRequest.get(pr3_id)
 

	
 
        assert pr3_id != pr2_id
 
        assert pr2.status == PullRequest.STATUS_CLOSED
 
        assert pr3.org_ref == 'branch:webvcs:fb95b340e0d03fa51f33c56c991c08077c99303e'
 
        assert pr3.other_ref == 'branch:default:41d2568309a05f422cffb8008e599d385f8af439'
 

	
 

	
 
@pytest.mark.usefixtures("test_context_fixture") # apply fixture for all test methods
 
class TestPullrequestsGetRepoRefs(base.TestController):
 

	
 
    def setup_method(self, method):
 
        self.repo_name = 'main'
 
        repo = fixture.create_repo(self.repo_name, repo_type='hg')
 
        self.repo_scm_instance = repo.scm_instance
 
        Session().commit()
 
        self.c = PullrequestsController()
 

	
 
    def teardown_method(self, method):
 
        fixture.destroy_repo('main')
 
        Session().commit()
 
        Session.remove()
 

	
 
    def test_repo_refs_empty_repo(self):
 
        # empty repo with no commits, no branches, no bookmarks, just one tag
 
        refs, default = self.c._get_repo_refs(self.repo_scm_instance)
 
        assert default == 'tag:null:0000000000000000000000000000000000000000'
 

	
 
    def test_repo_refs_one_commit_no_hints(self):
 
        cs0 = fixture.commit_change(self.repo_name, filename='file1',
 
                content='line1\n', message='commit1', vcs_type='hg',
 
                parent=None, newfile=True)
 

	
 
        refs, default = self.c._get_repo_refs(self.repo_scm_instance)
 
        assert default == 'branch:default:%s' % cs0.raw_id
 
        assert ([('branch:default:%s' % cs0.raw_id, 'default (current tip)')],
 
                'Branches') in refs
 

	
 
    def test_repo_refs_one_commit_rev_hint(self):
 
        cs0 = fixture.commit_change(self.repo_name, filename='file1',
 
                content='line1\n', message='commit1', vcs_type='hg',
 
                parent=None, newfile=True)
 

	
 
        refs, default = self.c._get_repo_refs(self.repo_scm_instance, rev=cs0.raw_id)
 
        expected = 'branch:default:%s' % cs0.raw_id
 
        assert default == expected
 
        assert ([(expected, 'default (current tip)')], 'Branches') in refs
 

	
 
    def test_repo_refs_two_commits_no_hints(self):
 
        cs0 = fixture.commit_change(self.repo_name, filename='file1',
 
                content='line1\n', message='commit1', vcs_type='hg',
 
                parent=None, newfile=True)
 
        cs1 = fixture.commit_change(self.repo_name, filename='file2',
 
                content='line2\n', message='commit2', vcs_type='hg',
 
                parent=None, newfile=True)
 

	
 
        refs, default = self.c._get_repo_refs(self.repo_scm_instance)
 
        expected = 'branch:default:%s' % cs1.raw_id
 
        assert default == expected
 
        assert ([(expected, 'default (current tip)')], 'Branches') in refs
 

	
 
    def test_repo_refs_two_commits_rev_hints(self):
 
        cs0 = fixture.commit_change(self.repo_name, filename='file1',
 
                content='line1\n', message='commit1', vcs_type='hg',
 
                parent=None, newfile=True)
 
        cs1 = fixture.commit_change(self.repo_name, filename='file2',
 
                content='line2\n', message='commit2', vcs_type='hg',
 
                parent=None, newfile=True)
 

	
 
        refs, default = self.c._get_repo_refs(self.repo_scm_instance, rev=cs0.raw_id)
 
        expected = 'rev:%s:%s' % (cs0.raw_id, cs0.raw_id)
 
        assert default == expected
 
        assert ([(expected, 'Changeset: %s' % cs0.raw_id[0:12])], 'Special') in refs
 
        assert ([('branch:default:%s' % cs1.raw_id, 'default (current tip)')], 'Branches') in refs
 

	
 
        refs, default = self.c._get_repo_refs(self.repo_scm_instance, rev=cs1.raw_id)
 
        expected = 'branch:default:%s' % cs1.raw_id
 
        assert default == expected
 
        assert ([(expected, 'default (current tip)')], 'Branches') in refs
 

	
 
    def test_repo_refs_two_commits_branch_hint(self):
 
        cs0 = fixture.commit_change(self.repo_name, filename='file1',
 
                content='line1\n', message='commit1', vcs_type='hg',
 
                parent=None, newfile=True)
 
        cs1 = fixture.commit_change(self.repo_name, filename='file2',
 
                content='line2\n', message='commit2', vcs_type='hg',
 
                parent=None, newfile=True)
 

	
 
        refs, default = self.c._get_repo_refs(self.repo_scm_instance, branch='default')
 
        expected = 'branch:default:%s' % cs1.raw_id
 
        assert default == expected
 
        assert ([(expected, 'default (current tip)')], 'Branches') in refs
 

	
 
    def test_repo_refs_one_branch_no_hints(self):
 
        cs0 = fixture.commit_change(self.repo_name, filename='file1',
 
                content='line1\n', message='commit1', vcs_type='hg',
 
                parent=None, newfile=True)
 
        # TODO

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)