Changeset - a47f8f57b347
[Not reviewed]
.hgtags
Show inline comments
 
@@ -33,48 +33,49 @@ c252049af24cd98eef5f4143fa3abbff3c912e29
 
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
README.rst
Show inline comments
 
@@ -4,182 +4,179 @@ 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 ``$``.
 
@@ -267,71 +250,59 @@ rarely needed:
 
  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
 
@@ -36,54 +36,53 @@ Administrator guide
 
**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
 
@@ -83,84 +83,67 @@ def api_call(apikey, apihost, method=Non
 
        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
 
@@ -116,98 +116,100 @@ class PullrequestsController(BaseRepoCon
 
                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')
 

	
kallithea/i18n/fr/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -784,107 +784,110 @@ 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"
 
@@ -1669,108 +1672,114 @@ 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 ""
kallithea/i18n/nb_NO/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -244,96 +244,99 @@ 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"
 

	
 
@@ -1119,48 +1122,51 @@ 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"
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
 
@@ -94,97 +94,97 @@ class PullRequestModel(object):
 
            '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)
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>
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,
kallithea/tests/functional/test_pullrequests_git.py
Show inline comments
 
file copied from kallithea/tests/functional/test_pullrequests.py to kallithea/tests/functional/test_pullrequests_git.py
 
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))
 
                                    repo_name=base.GIT_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',
 
                                     repo_name=base.GIT_REPO),
 
                                 {'org_repo': base.GIT_REPO,
 
                                  'org_ref': 'branch:master:5f2c6ee195929b0be80749243c18121c9864a3b3',
 
                                  'other_repo': base.GIT_REPO,
 
                                  'other_ref': 'tag:v0.2.2:137fea89f304a42321d40488091ee2ed419a3686',
 
                                  '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_git/pull-request/1/_/master
 
        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.mustcontain('Git pull requests don&#39;t support iterating yet.')
 

	
 
    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',
 
        response = self.app.post(base.url('pullrequest_delete',
 
                                 repo_name=base.GIT_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('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)
 
        response.mustcontain('Successfully deleted pull request')
 

	
 
        # 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):
 
    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),
 
                                     repo_name=base.GIT_REPO),
 
                                 {
 
                                  'org_repo': base.HG_REPO,
 
                                  'org_ref': 'rev:94f45ed825a1:94f45ed825a113e61af7e141f44ca578374abef0',
 
                                  'other_repo': base.HG_REPO,
 
                                  'other_ref': 'branch:default:96507bd11ecc815ebc6270fdf6db110928c09c1e',
 
                                  'org_repo': base.GIT_REPO,
 
                                  'org_ref': 'branch:master:5f2c6ee195929b0be80749243c18121c9864a3b3',
 
                                  'other_repo': base.GIT_REPO,
 
                                  'other_ref': 'tag:v0.2.2:137fea89f304a42321d40488091ee2ed419a3686',
 
                                  '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
 
        # http://localhost/vcs_test_git/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),
 
                                     repo_name=base.GIT_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')
 
        repo = fixture.create_repo(self.repo_name, repo_type='git')
 
        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'
 
        assert default == ''  # doesn't make sense, but better than nothing
 

	
 
    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',
 
                content='line1\n', message='commit1', vcs_type='git',
 
                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
 
        assert default == 'branch:master:%s' % cs0.raw_id
 
        assert ([('branch:master:%s' % cs0.raw_id, 'master')], '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',
 
                content='line1\n', message='commit1', vcs_type='git',
 
                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
 
        expected = 'branch:master:%s' % cs0.raw_id
 
        assert default == expected
 
        assert ([(expected, 'default (current tip)')], 'Branches') in refs
 
        assert ([(expected, 'master')], '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',
 
                content='line1\n', message='commit1', vcs_type='git',
 
                parent=None, newfile=True)
 
        cs1 = fixture.commit_change(self.repo_name, filename='file2',
 
                content='line2\n', message='commit2', vcs_type='hg',
 
                content='line2\n', message='commit2', vcs_type='git',
 
                parent=None, newfile=True)
 

	
 
        refs, default = self.c._get_repo_refs(self.repo_scm_instance)
 
        expected = 'branch:default:%s' % cs1.raw_id
 
        expected = 'branch:master:%s' % cs1.raw_id
 
        assert default == expected
 
        assert ([(expected, 'default (current tip)')], 'Branches') in refs
 
        assert ([(expected, 'master')], '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',
 
                content='line1\n', message='commit1', vcs_type='git',
 
                parent=None, newfile=True)
 
        cs1 = fixture.commit_change(self.repo_name, filename='file2',
 
                content='line2\n', message='commit2', vcs_type='hg',
 
                content='line2\n', message='commit2', vcs_type='git',
 
                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
 
        assert ([('branch:master:%s' % cs1.raw_id, 'master')], '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
 
        expected = 'branch:master:%s' % cs1.raw_id
 
        assert default == expected
 
        assert ([(expected, 'default (current tip)')], 'Branches') in refs
 
        assert ([(expected, 'master')], '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',
 
                content='line1\n', message='commit1', vcs_type='git',
 
                parent=None, newfile=True)
 
        cs1 = fixture.commit_change(self.repo_name, filename='file2',
 
                content='line2\n', message='commit2', vcs_type='hg',
 
                content='line2\n', message='commit2', vcs_type='git',
 
                parent=None, newfile=True)
 

	
 
        refs, default = self.c._get_repo_refs(self.repo_scm_instance, branch='default')
 
        expected = 'branch:default:%s' % cs1.raw_id
 
        refs, default = self.c._get_repo_refs(self.repo_scm_instance, branch='master')
 
        expected = 'branch:master:%s' % cs1.raw_id
 
        assert default == expected
 
        assert ([(expected, 'default (current tip)')], 'Branches') in refs
 
        assert ([(expected, 'master')], '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',
 
                content='line1\n', message='commit1', vcs_type='git',
 
                parent=None, newfile=True)
 
        # TODO
0 comments (0 inline, 0 general)