Retire Packaging Deb project repos
This commit is part of a series to retire the Packaging Deb project. Step 2 is to remove all content from the project repos, replacing it with a README notification where to find ongoing work, and how to recover the repo if needed at some future point (as in https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project). Change-Id: Idb6639333fd4d976be89a151d8ef32cf1e9def89
This commit is contained in:
15
.gitignore
vendored
15
.gitignore
vendored
@@ -1,15 +0,0 @@
|
||||
build
|
||||
dist
|
||||
git_review.egg-info
|
||||
MANIFEST
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
.eggs
|
||||
.gerrit
|
||||
.testrepository
|
||||
.tox
|
||||
.venv
|
||||
*.egg
|
||||
*.egg-info
|
||||
*.pyc
|
||||
doc/build
|
@@ -1,4 +0,0 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack-infra/git-review.git
|
9
.mailmap
9
.mailmap
@@ -1,9 +0,0 @@
|
||||
# Format is:
|
||||
# <preferred e-mail> <other e-mail 1>
|
||||
# <preferred e-mail> <other e-mail 2>
|
||||
|
||||
David Ostrovsky <david@ostrovsky.org> <David.Ostrovsky@gmx.de>
|
||||
David Ostrovsky <david@ostrovsky.org> <david.ostrovsky@gmx.de>
|
||||
James E. Blair <james.blair@rackspace.com> <jeblair@openstack.org>
|
||||
James E. Blair <james.blair@rackspace.com> <corvus@inaugust.com>
|
||||
James E. Blair <james.blair@rackspace.com> <jeblair@hp.com>
|
@@ -1,8 +0,0 @@
|
||||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
|
||||
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
|
||||
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
|
||||
${PYTHON:-python} -m subunit.run discover -t ./ ./git_review/tests $LISTOPT $IDOPTION
|
||||
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
@@ -1,22 +0,0 @@
|
||||
============================
|
||||
Contributing to git-review
|
||||
============================
|
||||
|
||||
To get the latest code, see: https://git.openstack.org/cgit/openstack-infra/git-review
|
||||
|
||||
Bugs are handled at: https://storyboard.openstack.org/#!/project/719
|
||||
|
||||
There is a mailing list at: http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-infra
|
||||
|
||||
Code reviews, as you might expect, are handled by gerrit at:
|
||||
https://review.openstack.org
|
||||
|
||||
See http://wiki.openstack.org/GerritWorkflow for details. Pull
|
||||
requests submitted through GitHub will be ignored.
|
||||
|
||||
Use ``git review`` to submit patches (after creating a gerrit account
|
||||
that links to your launchpad account). Example::
|
||||
|
||||
# Do your commits
|
||||
git review
|
||||
# Enter your username if prompted
|
311
HACKING.rst
311
HACKING.rst
@@ -1,311 +0,0 @@
|
||||
Hacking git-review
|
||||
==================
|
||||
|
||||
Development of git-review is managed by OpenStack's Gerrit, which can be
|
||||
found at https://review.openstack.org/
|
||||
|
||||
Instructions on submitting patches can be found at
|
||||
http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
git-review should, in general, not depend on a huge number of external
|
||||
libraries, so that installing it is a lightweight operation.
|
||||
|
||||
OpenStack Style Commandments
|
||||
============================
|
||||
|
||||
- Step 1: Read http://www.python.org/dev/peps/pep-0008/
|
||||
- Step 2: Read http://www.python.org/dev/peps/pep-0008/ again
|
||||
- Step 3: Read on
|
||||
|
||||
|
||||
General
|
||||
-------
|
||||
- Put two newlines between top-level code (funcs, classes, etc)
|
||||
- Use only UNIX style newlines ("\n"), not Windows style ("\r\n")
|
||||
- Put one newline between methods in classes and anywhere else
|
||||
- Long lines should be wrapped in parentheses
|
||||
in preference to using a backslash for line continuation.
|
||||
- Do not write "except:", use "except Exception:" at the very least
|
||||
- Include your name with TODOs as in "#TODO(termie)"
|
||||
- Do not shadow a built-in or reserved word. Example::
|
||||
|
||||
def list():
|
||||
return [1, 2, 3]
|
||||
|
||||
mylist = list() # BAD, shadows `list` built-in
|
||||
|
||||
class Foo(object):
|
||||
def list(self):
|
||||
return [1, 2, 3]
|
||||
|
||||
mylist = Foo().list() # OKAY, does not shadow built-in
|
||||
|
||||
- Use the "is not" operator when testing for unequal identities. Example::
|
||||
|
||||
if not X is Y: # BAD, intended behavior is ambiguous
|
||||
pass
|
||||
|
||||
if X is not Y: # OKAY, intuitive
|
||||
pass
|
||||
|
||||
- Use the "not in" operator for evaluating membership in a collection. Example::
|
||||
|
||||
if not X in Y: # BAD, intended behavior is ambiguous
|
||||
pass
|
||||
|
||||
if X not in Y: # OKAY, intuitive
|
||||
pass
|
||||
|
||||
if not (X in Y or X in Z): # OKAY, still better than all those 'not's
|
||||
pass
|
||||
|
||||
|
||||
Imports
|
||||
-------
|
||||
- Do not import objects, only modules (*)
|
||||
- Do not import more than one module per line (*)
|
||||
- Do not use wildcard ``*`` import (*)
|
||||
- Do not make relative imports
|
||||
- Do not make new nova.db imports in nova/virt/*
|
||||
- Order your imports by the full module path
|
||||
- Organize your imports according to the following template
|
||||
|
||||
(*) exceptions are:
|
||||
|
||||
- imports from ``migrate`` package
|
||||
- imports from ``sqlalchemy`` package
|
||||
- imports from ``nova.db.sqlalchemy.session`` module
|
||||
- imports from ``nova.db.sqlalchemy.migration.versioning_api`` package
|
||||
|
||||
Example::
|
||||
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
{{stdlib imports in human alphabetical order}}
|
||||
\n
|
||||
{{third-party lib imports in human alphabetical order}}
|
||||
\n
|
||||
{{nova imports in human alphabetical order}}
|
||||
\n
|
||||
\n
|
||||
{{begin your code}}
|
||||
|
||||
|
||||
Human Alphabetical Order Examples
|
||||
---------------------------------
|
||||
Example::
|
||||
|
||||
import httplib
|
||||
import logging
|
||||
import random
|
||||
import StringIO
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import eventlet
|
||||
import webob.exc
|
||||
|
||||
import nova.api.ec2
|
||||
from nova.api import openstack
|
||||
from nova.auth import users
|
||||
from nova.endpoint import cloud
|
||||
import nova.flags
|
||||
from nova import test
|
||||
|
||||
|
||||
Docstrings
|
||||
----------
|
||||
Example::
|
||||
|
||||
"""A one line docstring looks like this and ends in a period."""
|
||||
|
||||
|
||||
"""A multi line docstring has a one-line summary, less than 80 characters.
|
||||
|
||||
Then a new paragraph after a newline that explains in more detail any
|
||||
general information about the function, class or method. Example usages
|
||||
are also great to have here if it is a complex class or function.
|
||||
|
||||
When writing the docstring for a class, an extra line should be placed
|
||||
after the closing quotations. For more in-depth explanations for these
|
||||
decisions see http://www.python.org/dev/peps/pep-0257/
|
||||
|
||||
If you are going to describe parameters and return values, use Sphinx, the
|
||||
appropriate syntax is as follows.
|
||||
|
||||
:param foo: the foo parameter
|
||||
:param bar: the bar parameter
|
||||
:returns: return_type -- description of the return value
|
||||
:returns: description of the return value
|
||||
:raises: AttributeError, KeyError
|
||||
"""
|
||||
|
||||
|
||||
Dictionaries/Lists
|
||||
------------------
|
||||
If a dictionary (dict) or list object is longer than 80 characters, its items
|
||||
should be split with newlines. Embedded iterables should have their items
|
||||
indented. Additionally, the last item in the dictionary should have a trailing
|
||||
comma. This increases readability and simplifies future diffs.
|
||||
|
||||
Example::
|
||||
|
||||
my_dictionary = {
|
||||
"image": {
|
||||
"name": "Just a Snapshot",
|
||||
"size": 2749573,
|
||||
"properties": {
|
||||
"user_id": 12,
|
||||
"arch": "x86_64",
|
||||
},
|
||||
"things": [
|
||||
"thing_one",
|
||||
"thing_two",
|
||||
],
|
||||
"status": "ACTIVE",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Calling Methods
|
||||
---------------
|
||||
Calls to methods 80 characters or longer should format each argument with
|
||||
newlines. This is not a requirement, but a guideline::
|
||||
|
||||
unnecessarily_long_function_name('string one',
|
||||
'string two',
|
||||
kwarg1=constants.ACTIVE,
|
||||
kwarg2=['a', 'b', 'c'])
|
||||
|
||||
|
||||
Rather than constructing parameters inline, it is better to break things up::
|
||||
|
||||
list_of_strings = [
|
||||
'what_a_long_string',
|
||||
'not as long',
|
||||
]
|
||||
|
||||
dict_of_numbers = {
|
||||
'one': 1,
|
||||
'two': 2,
|
||||
'twenty four': 24,
|
||||
}
|
||||
|
||||
object_one.call_a_method('string three',
|
||||
'string four',
|
||||
kwarg1=list_of_strings,
|
||||
kwarg2=dict_of_numbers)
|
||||
|
||||
|
||||
Internationalization (i18n) Strings
|
||||
-----------------------------------
|
||||
In order to support multiple languages, we have a mechanism to support
|
||||
automatic translations of exception and log strings.
|
||||
|
||||
Example::
|
||||
|
||||
msg = _("An error occurred")
|
||||
raise HTTPBadRequest(explanation=msg)
|
||||
|
||||
If you have a variable to place within the string, first internationalize the
|
||||
template string then do the replacement.
|
||||
|
||||
Example::
|
||||
|
||||
msg = _("Missing parameter: %s") % ("flavor",)
|
||||
LOG.error(msg)
|
||||
|
||||
If you have multiple variables to place in the string, use keyword parameters.
|
||||
This helps our translators reorder parameters when needed.
|
||||
|
||||
Example::
|
||||
|
||||
msg = _("The server with id %(s_id)s has no key %(m_key)s")
|
||||
LOG.error(msg % {"s_id": "1234", "m_key": "imageId"})
|
||||
|
||||
|
||||
Creating Unit Tests
|
||||
-------------------
|
||||
For every new feature, unit tests should be created that both test and
|
||||
(implicitly) document the usage of said feature. If submitting a patch for a
|
||||
bug that had no unit test, a new passing unit test should be added. If a
|
||||
submitted bug fix does have a unit test, be sure to add a new one that fails
|
||||
without the patch and passes with the patch.
|
||||
|
||||
For more information on creating unit tests and utilizing the testing
|
||||
infrastructure in OpenStack Nova, please read nova/tests/README.rst.
|
||||
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
The testing system is based on a combination of tox and testr. The canonical
|
||||
approach to running tests is to simply run the command `tox`. This will
|
||||
create virtual environments, populate them with dependencies and run all of
|
||||
the tests that OpenStack CI systems run. Behind the scenes, tox is running
|
||||
`testr run --parallel`, but is set up such that you can supply any additional
|
||||
testr arguments that are needed to tox. For example, you can run:
|
||||
`tox -- --analyze-isolation` to cause tox to tell testr to add
|
||||
--analyze-isolation to its argument list.
|
||||
|
||||
It is also possible to run the tests inside of a virtual environment
|
||||
you have created, or it is possible that you have all of the dependencies
|
||||
installed locally already. In this case, you can interact with the testr
|
||||
command directly. Running `testr run` will run the entire test suite. `testr
|
||||
run --parallel` will run it in parallel (this is the default incantation tox
|
||||
uses.) More information about testr can be found at:
|
||||
http://wiki.openstack.org/testr
|
||||
|
||||
|
||||
openstack-common
|
||||
----------------
|
||||
|
||||
A number of modules from openstack-common are imported into the project.
|
||||
|
||||
These modules are "incubating" in openstack-common and are kept in sync
|
||||
with the help of openstack-common's update.py script. See:
|
||||
|
||||
http://wiki.openstack.org/CommonLibrary#Incubation
|
||||
|
||||
The copy of the code should never be directly modified here. Please
|
||||
always update openstack-common first and then run the script to copy
|
||||
the changes across.
|
||||
|
||||
OpenStack Trademark
|
||||
-------------------
|
||||
|
||||
OpenStack is a registered trademark of the OpenStack Foundation, and uses the
|
||||
following capitalization:
|
||||
|
||||
OpenStack
|
||||
|
||||
|
||||
Commit Messages
|
||||
---------------
|
||||
Using a common format for commit messages will help keep our git history
|
||||
readable. Follow these guidelines:
|
||||
|
||||
First, provide a brief summary of 50 characters or less. Summaries
|
||||
of greater then 72 characters will be rejected by the gate.
|
||||
|
||||
The first line of the commit message should provide an accurate
|
||||
description of the change, not just a reference to a bug or
|
||||
blueprint. It must be followed by a single blank line.
|
||||
|
||||
If the change relates to a specific driver (libvirt, xenapi, qpid, etc...),
|
||||
begin the first line of the commit message with the driver name, lowercased,
|
||||
followed by a colon.
|
||||
|
||||
Following your brief summary, provide a more detailed description of
|
||||
the patch, manually wrapping the text at 72 characters. This
|
||||
description should provide enough detail that one does not have to
|
||||
refer to external resources to determine its high-level functionality.
|
||||
|
||||
Once you use 'git review', two lines will be appended to the commit
|
||||
message: a blank line followed by a 'Change-Id'. This is important
|
||||
to correlate this commit with a specific review in Gerrit, and it
|
||||
should not be modified.
|
||||
|
||||
For further information on constructing high quality commit messages,
|
||||
and how to split up commits into a series of changes, consult the
|
||||
project wiki:
|
||||
|
||||
http://wiki.openstack.org/GitCommitMessages
|
202
LICENSE
202
LICENSE
@@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@@ -1,7 +0,0 @@
|
||||
include README.rst
|
||||
include LICENSE
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
include HACKING.rst
|
||||
include git-review.1
|
||||
include tox.ini
|
14
README
Normal file
14
README
Normal file
@@ -0,0 +1,14 @@
|
||||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
For ongoing work on maintaining OpenStack packages in the Debian
|
||||
distribution, please see the Debian OpenStack packaging team at
|
||||
https://wiki.debian.org/OpenStack/.
|
||||
|
||||
For any further questions, please email
|
||||
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||
Freenode.
|
12
README.rst
12
README.rst
@@ -1,12 +0,0 @@
|
||||
git-review
|
||||
==========
|
||||
|
||||
A git command for submitting branches to Gerrit
|
||||
|
||||
git-review is a tool that helps submitting git branches to gerrit for
|
||||
review.
|
||||
|
||||
* Free software: Apache license
|
||||
* Documentation: http://docs.openstack.org/infra/git-review/
|
||||
* Source: https://git.openstack.org/cgit/openstack-infra/git-review
|
||||
* Bugs: https://storyboard.openstack.org/#!/project/719
|
177
doc/Makefile
177
doc/Makefile
@@ -1,177 +0,0 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
|
||||
# User-friendly check for sphinx-build
|
||||
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
|
||||
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
|
||||
endif
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " xml to make Docutils-native XML files"
|
||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/git-review.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/git-review.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/git-review"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/git-review"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
latexpdfja:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through platex and dvipdfmx..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
xml:
|
||||
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
||||
@echo
|
||||
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
||||
|
||||
pseudoxml:
|
||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
||||
@echo
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
@@ -1,252 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# git-review documentation build configuration file, created by
|
||||
# sphinx-quickstart on Mon Dec 1 14:06:22 2014.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'oslosphinx',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'git-review'
|
||||
copyright = u'2014, OpenStack Contributors'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
#keep_warnings = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
# html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
#html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'git-reviewdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'git-review.tex', u'git-review Documentation',
|
||||
u'OpenStack Contributors', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'git-review', u'git-review Documentation',
|
||||
[u'OpenStack Contributors'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'git-review', u'git-review Documentation',
|
||||
u'OpenStack Contributors', 'git-review', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#texinfo_no_detailmenu = False
|
@@ -1,23 +0,0 @@
|
||||
.. include:: ../../CONTRIBUTING.rst
|
||||
|
||||
Running tests
|
||||
=============
|
||||
|
||||
Running tests for git-review means running a local copy of Gerrit to
|
||||
check that git-review interacts correctly with it. This requires the
|
||||
following:
|
||||
|
||||
* a Java Runtime Environment on the machine to run tests on
|
||||
|
||||
* Internet access to download the gerrit.war file, or a locally
|
||||
cached copy (it needs to be located in a .gerrit directory at the
|
||||
top level of the git-review project)
|
||||
|
||||
To run git-review integration tests the following commands may by run::
|
||||
|
||||
tox -e py27
|
||||
tox -e py26
|
||||
tox -e py32
|
||||
tox -e py33
|
||||
|
||||
depending on what Python interpreter would you like to use.
|
@@ -1,22 +0,0 @@
|
||||
============
|
||||
git-review
|
||||
============
|
||||
|
||||
``git-review`` is a tool that helps submitting git branches to gerrit
|
||||
for review.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
installation
|
||||
usage
|
||||
developing
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
@@ -1,93 +0,0 @@
|
||||
================================
|
||||
Installation and Configuration
|
||||
================================
|
||||
|
||||
Installing git-review
|
||||
=====================
|
||||
|
||||
``git-review`` can be often be installed via system packages, ``pypi``
|
||||
releases or other platform-specific methods. See
|
||||
`<https://www.mediawiki.org/wiki/Gerrit/git-review>`__ for platform
|
||||
information.
|
||||
|
||||
For assistance installing pacakges from ``pypi`` on your OS check out
|
||||
`get-pip.py <https://pip.pypa.io/en/stable/installing/>`__.
|
||||
|
||||
For installation from source simply add ``git-review`` to your $PATH
|
||||
after installing the dependencies listed in requirements.txt
|
||||
|
||||
Setup
|
||||
=====
|
||||
|
||||
By default, git-review will look for a remote named 'gerrit' for working
|
||||
with Gerrit. If the remote exists, git-review will submit the current
|
||||
branch to HEAD:refs/for/master at that remote.
|
||||
|
||||
If the Gerrit remote does not exist, git-review looks for a file
|
||||
called .gitreview at the root of the repository with information about
|
||||
the gerrit remote. Assuming that file is present, git-review should
|
||||
be able to automatically configure your repository the first time it
|
||||
is run.
|
||||
|
||||
The name of the Gerrit remote is configurable; see the configuration
|
||||
section below.
|
||||
|
||||
.gitreview file format
|
||||
======================
|
||||
|
||||
Example .gitreview file (used to upload for git-review itself)::
|
||||
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack-infra/git-review.git
|
||||
defaultbranch=master
|
||||
|
||||
Required values: host, project
|
||||
|
||||
Optional values: port (default: 29418), defaultbranch (default: master),
|
||||
defaultremote (default: gerrit).
|
||||
|
||||
**Notes**
|
||||
|
||||
* Username is not required because it is requested on first run
|
||||
|
||||
* Unlike git config files, there cannot be any whitespace before the name
|
||||
of the variable.
|
||||
|
||||
* Upon first run, git-review will create a remote for working with Gerrit,
|
||||
if it does not already exist. By default, the remote name is 'gerrit',
|
||||
but this can be overridden with the 'defaultremote' configuration
|
||||
option.
|
||||
|
||||
* You can specify different values to be used as defaults in
|
||||
~/.config/git-review/git-review.conf or /etc/git-review/git-review.conf.
|
||||
|
||||
* Git-review will query git credential system for gerrit user/password when
|
||||
authentication failed over http(s). Unlike git, git-review does not persist
|
||||
gerrit user/password in git credential system for security purposes and git
|
||||
credential system configuration stays under user responsibility.
|
||||
|
||||
Hooks
|
||||
=====
|
||||
|
||||
git-review has a custom hook mechanism to run a script before certain
|
||||
actions. This is done in the same spirit as the classic hooks in git.
|
||||
|
||||
There are two types of hooks, a global one which is stored in
|
||||
~/.config/git-review/hooks/ and one local to the repository stored in
|
||||
.git/hooks/ with the other git hook scripts.
|
||||
|
||||
**The script needs be executable before getting executed**
|
||||
|
||||
The name of the script is $action-review where action can be
|
||||
:
|
||||
|
||||
* pre - run at first before doing anything.
|
||||
|
||||
* post - run at the end after the review was sent.
|
||||
|
||||
* draft - run when in draft mode.
|
||||
|
||||
if the script returns with an exit status different than zero,
|
||||
git-review will exit with the a custom shell exit code 71.
|
@@ -1,55 +0,0 @@
|
||||
=======
|
||||
Usage
|
||||
=======
|
||||
|
||||
Hack on some code, then::
|
||||
|
||||
git review
|
||||
|
||||
If you want to submit that code to a branch other than "master", then::
|
||||
|
||||
git review branchname
|
||||
|
||||
If you want to submit to a different remote::
|
||||
|
||||
git review -r my-remote
|
||||
|
||||
If you want to supply a review topic::
|
||||
|
||||
git review -t topic/awesome-feature
|
||||
|
||||
If you want to subscribe some reviewers::
|
||||
|
||||
git review --reviewers a@example.com b@example.com
|
||||
|
||||
If you want to disable autogenerated topic::
|
||||
|
||||
git review -T
|
||||
|
||||
If you want to submit a branch for review and then remove the local branch::
|
||||
|
||||
git review -f
|
||||
|
||||
If you want to skip the automatic "git rebase -i" step::
|
||||
|
||||
git review -R
|
||||
|
||||
If you want to download change 781 from gerrit to review it::
|
||||
|
||||
git review -d 781
|
||||
|
||||
If you want to download patchset 4 for change 781 from gerrit to review it::
|
||||
|
||||
git review -d 781,4
|
||||
|
||||
If you want to compare patchset 4 with patchset 10 of change 781 from gerrit::
|
||||
|
||||
git review -m 781,4-10
|
||||
|
||||
If you want to see a list of open reviews::
|
||||
|
||||
git review -l
|
||||
|
||||
If you just want to do the commit message and remote setup steps::
|
||||
|
||||
git review -s
|
477
git-review.1
477
git-review.1
@@ -1,477 +0,0 @@
|
||||
.\" Uses mdoc(7). See `man 7 mdoc` for details about the syntax used here
|
||||
.\"
|
||||
.Dd June 12th, 2015
|
||||
.Dt GIT\-REVIEW 1
|
||||
.Sh NAME
|
||||
.Nm git\-review
|
||||
.Nd Submit changes to Gerrit for review
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl r Ar remote
|
||||
.Op Fl uv
|
||||
.Fl d Ar change
|
||||
.Op Ar branch
|
||||
.Nm
|
||||
.Op Fl r Ar remote
|
||||
.Op Fl uv
|
||||
.Fl x Ar change
|
||||
.Op Ar branch
|
||||
.Nm
|
||||
.Op Fl r Ar remote
|
||||
.Op Fl uv
|
||||
.Fl N Ar change
|
||||
.Op Ar branch
|
||||
.Nm
|
||||
.Op Fl r Ar remote
|
||||
.Op Fl uv
|
||||
.Fl X Ar change
|
||||
.Op Ar branch
|
||||
.Nm
|
||||
.Op Fl r Ar remote
|
||||
.Op Fl uv
|
||||
.Fl m
|
||||
.Ar change\-ps\-range
|
||||
.Op Ar branch
|
||||
.Nm
|
||||
.Op Fl r Ar remote
|
||||
.Op Fl fnuv
|
||||
.Fl s
|
||||
.Op Ar branch
|
||||
.Nm
|
||||
.Op Fl fnuvDRT
|
||||
.Op Fl r Ar remote
|
||||
.Op Fl t Ar topic
|
||||
.Op Fl \-reviewers Ar reviewer ...
|
||||
.Op Ar branch
|
||||
.Nm
|
||||
.Fl l
|
||||
.Nm
|
||||
.Fl \-version
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
automates and streamlines some of the tasks involved with
|
||||
submitting local changes to a Gerrit server for review. It is
|
||||
designed to make it easier to comprehend Gerrit, especially for
|
||||
users that have recently switched to Git from another version
|
||||
control system.
|
||||
.Pp
|
||||
.Ar change
|
||||
can be
|
||||
.Ar changeNumber
|
||||
as obtained using
|
||||
.Fl \-list
|
||||
option, or it can be
|
||||
.Ar changeNumber,patchsetNumber
|
||||
for fetching exact patchset from the change.
|
||||
In that case local branch name will have a \-patch[patchsetNumber] suffix.
|
||||
.Pp
|
||||
The following options are available:
|
||||
.Bl -tag -width indent
|
||||
.It Fl c , Fl \-compatible
|
||||
Push changes to refs compatible with Gerrit of versions before 2.3.
|
||||
.It Fl d Ar change , Fl \-download= Ns Ar change
|
||||
Download
|
||||
.Ar change
|
||||
from Gerrit
|
||||
into a local branch. The branch will be named after the patch author and the name of a topic.
|
||||
If the local branch already exists, it will attempt to update with the latest patchset for this change.
|
||||
.It Fl x Ar change , Fl \-cherrypick= Ns Ar change
|
||||
Apply
|
||||
.Ar change
|
||||
from Gerrit and commit into the current local branch ("cherry pick").
|
||||
No additional branch is created.
|
||||
.Pp
|
||||
This makes it possible to review a change without creating a local branch for
|
||||
it. On the other hand, be aware: if you are not careful, this can easily result
|
||||
in additional patch sets for dependent changes. Also, if the current branch is
|
||||
different enough, the change may not apply at all or produce merge conflicts
|
||||
that need to be resolved by hand.
|
||||
.It Fl N Ar change , Fl \-cherrypickonly= Ns Ar change
|
||||
Apply
|
||||
.Ar change
|
||||
from Gerrit
|
||||
into the current working directory, add it to the staging area ("git index"), but do not commit it.
|
||||
.Pp
|
||||
This makes it possible to review a change without creating a local commit for
|
||||
it. Useful if you want to merge several commits into one that will be submitted for review.
|
||||
.Pp
|
||||
If the current branch is different enough, the change may not apply at all
|
||||
or produce merge conflicts that need to be resolved by hand.
|
||||
.It Fl X Ar change , Fl \-cherrypickindicate= Ns Ar change
|
||||
Apply
|
||||
.Ar change
|
||||
from Gerrit and commit into the current local branch ("cherry pick"),
|
||||
indicating which commit this change was cherry\-picked from.
|
||||
.Pp
|
||||
This makes it possible to re\-review a change for a different branch without
|
||||
creating a local branch for it.
|
||||
.Pp
|
||||
If the current branch is different enough, the change may not apply at all
|
||||
or produce merge conflicts that need to be resolved by hand.
|
||||
.It Fl i , Fl \-new\-changeid
|
||||
Force the git-review to generate a new Change-Id, even if one already exists
|
||||
in the changelog.
|
||||
.It Fl m Ar change\-ps\-range , Fl \-compare= Ns Ar change\-ps\-range
|
||||
Download the specified patchsets for
|
||||
.Ar change
|
||||
from Gerrit, rebase both on master and display differences (git\-diff).
|
||||
.Pp
|
||||
.Ar change\-ps\-range
|
||||
can be specified as
|
||||
.Ar changeNumber, Ns Ar oldPatchSetNumber Ns Op Ns Ar \-newPatchSetNumber
|
||||
.Pp
|
||||
.Ar oldPatchSetNumber
|
||||
is mandatory, and if
|
||||
.Ar newPatchSetNumber
|
||||
is not specified, the latest patchset will be used.
|
||||
.Pp
|
||||
This makes it possible to easily compare what has changed from last time you
|
||||
reviewed the proposed change.
|
||||
.Pp
|
||||
If the master branch is different enough, the rebase can produce merge conflicts.
|
||||
If that happens rebasing will be aborted and diff displayed for not\-rebased branches.
|
||||
You can also use
|
||||
.Ar \-\-no\-rebase ( Ar \-R )
|
||||
to always skip rebasing.
|
||||
.It Fl f , Fl \-finish
|
||||
Close down the local branch and switch back to the target branch on
|
||||
successful submission.
|
||||
.It Fl F , Fl \-force\-rebase
|
||||
Force a rebase before doing anything else, even if not otherwise needed.
|
||||
.It Fl n , Fl \-dry\-run
|
||||
Don\(aqt actually perform any commands that have direct effects. Print them
|
||||
instead.
|
||||
.It Fl r Ar remote , Fl \-remote= Ns Ar remote
|
||||
Git remote to use for Gerrit.
|
||||
.It Fl s , Fl \-setup
|
||||
Just run the repo setup commands but don\(aqt submit anything.
|
||||
.It Fl t Ar topic , Fl \-topic= Ns Ar topic
|
||||
Sets the target topic for this change on the Gerrit server.
|
||||
If not specified, a bug number from the commit summary will be used. Alternatively, the local branch name will be used if different from remote branch.
|
||||
.It Fl T , Fl \-no\-topic
|
||||
Submit review without topic.
|
||||
.It Fl \-reviewers Ar reviewer ...
|
||||
Subscribe one or more reviewers to the uploaded patch sets. Reviewers should be identifiable by Gerrit (usually use their Gerrit username or email address).
|
||||
.It Fl u , Fl \-update
|
||||
Skip cached local copies and force updates from network resources.
|
||||
.It Fl l , Fl \-list
|
||||
List the available reviews on the Gerrit server for this project.
|
||||
.It Fl y , Fl \-yes
|
||||
Indicate that you do, in fact, understand if you are submitting more than
|
||||
one patch.
|
||||
.It Fl v , Fl \-verbose
|
||||
Turns on more verbose output.
|
||||
.It Fl D , Fl \-draft
|
||||
Submit review as a draft. Requires Gerrit 2.3 or newer.
|
||||
.It Fl R , Fl \-no\-rebase
|
||||
Do not automatically perform a rebase before submitting the change to
|
||||
Gerrit.
|
||||
.Pp
|
||||
When submitting a change for review, you will usually want it to be based on the tip of upstream branch in order to avoid possible conflicts. When amending a change and rebasing the new patchset, the Gerrit web interface will show a difference between the two patchsets which contains all commits in between. This may confuse many reviewers that would expect to see a much simpler difference.
|
||||
.Pp
|
||||
Also can be used for
|
||||
.Fl \-compare
|
||||
to skip automatic rebase of fetched reviews.
|
||||
.It Fl \-color Ar always|never|auto
|
||||
Enable or disable a color output. Default is "auto".
|
||||
.It Fl \-no\-color
|
||||
Same thing as \-\-color=never.
|
||||
.It Fl \-no\-custom\-script
|
||||
Do not run scripts, installed as hooks/{action}-review, where action
|
||||
is one of "pre", "draft", or "post".
|
||||
.It Fl \-track
|
||||
Choose the branch to submit the change against (and, if
|
||||
rebasing, to rebase against) from the branch being tracked
|
||||
(if a branch is being tracked), and set the tracking branch
|
||||
when downloading a change to point to the remote and branch
|
||||
against which patches should be submitted.
|
||||
See gitreview.track configuration.
|
||||
.It Fl \-no\-track
|
||||
Ignore any branch being tracked by the current branch,
|
||||
overriding gitreview.track.
|
||||
This option is implied by providing a specific branch name
|
||||
on the command line.
|
||||
.It Fl \-use-pushurl
|
||||
Use the pushurl option for the origin remote rather than conventional
|
||||
separate Gerrit remotes.
|
||||
.It Fl \-version
|
||||
Print the version number and exit.
|
||||
.El
|
||||
.Sh CONFIGURATION
|
||||
This utility can be configured by adding entries to Git configuration.
|
||||
.Pp
|
||||
The following configuration keys are supported:
|
||||
.Bl -tag
|
||||
.It gitreview.username
|
||||
Default username used to access the repository. If not specified
|
||||
in the Git configuration, Git remote or
|
||||
.Pa .gitreview
|
||||
file, the user will be prompted to specify the username.
|
||||
.Pp
|
||||
Example entry in the
|
||||
.Pa .gitconfig
|
||||
file:
|
||||
.Bd -literal -offset indent
|
||||
[gitreview]
|
||||
username=\fImygerrituser\fP
|
||||
.Ed
|
||||
.It gitreview.scheme
|
||||
This setting determines the default scheme (ssh/http/https) of gerrit remote
|
||||
.It gitreview.host
|
||||
This setting determines the default hostname of gerrit remote
|
||||
.It gitreview.port
|
||||
This setting determines the default port of gerrit remote
|
||||
.It gitreview.project
|
||||
This setting determines the default name of gerrit git repo
|
||||
.It gitreview.remote
|
||||
This setting determines the default name to use for gerrit remote
|
||||
.It gitreview.branch
|
||||
This setting determines the default branch
|
||||
.It gitreview.track
|
||||
Determines whether to prefer the currently-tracked branch (if any)
|
||||
and the branch against which the changeset was submitted to Gerrit
|
||||
(if there is exactly one such branch) to the defaultremote and
|
||||
defaultbranch for submitting and rebasing against.
|
||||
If the local topic branch is tracking a remote branch, the remote
|
||||
and branch that the local topic branch is tracking should be used
|
||||
for submit and rebase operations, rather than the defaultremote
|
||||
and defaultbranch.
|
||||
.Pp
|
||||
When downloading a patch, creates the local branch to track the
|
||||
appropriate remote and branch in order to choose that branch by
|
||||
default when submitting modifications to that changeset.
|
||||
.Pp
|
||||
A value of 'true' or 'false' should be specified.
|
||||
.Bl -tag
|
||||
.It true
|
||||
Do prefer the currently-tracked branch (if any) \- equivalent
|
||||
to setting
|
||||
.Fl \-track
|
||||
when submitting changes.
|
||||
.It false
|
||||
Ignore tracking branches \- equivalent to setting
|
||||
.Fl \-no\-track
|
||||
(the default) or providing an explicit branch name when submitting
|
||||
changes. This is the default value unless overridden by
|
||||
.Pa .gitreview
|
||||
file, and is implied by providing a specific branch name on the
|
||||
command line.
|
||||
.El
|
||||
.It gitreview.rebase
|
||||
This setting determines whether changes submitted will
|
||||
be rebased to the newest state of the branch.
|
||||
.Pp
|
||||
A value of 'true' or 'false' should be specified.
|
||||
.Bl -tag
|
||||
.It false
|
||||
Do not rebase changes on submit \- equivalent to setting
|
||||
.Fl R
|
||||
when submitting changes.
|
||||
.It true
|
||||
Do rebase changes on submit. This is the default value unless
|
||||
overridden by
|
||||
.Pa .gitreview
|
||||
file.
|
||||
.El
|
||||
.Pp
|
||||
This setting takes precedence over repository\-specific configuration
|
||||
in the
|
||||
.Pa .gitreview
|
||||
file.
|
||||
.El
|
||||
.Bl -tag
|
||||
.It color.review
|
||||
Whether to use ANSI escape sequences to add color to the output displayed by
|
||||
this command. Default value is determined by color.ui.
|
||||
.Bl -tag
|
||||
.It auto or true
|
||||
If you want output to use color when written to the terminal (default with Git
|
||||
1.8.4 and newer).
|
||||
.It always
|
||||
If you want all output to use color
|
||||
.It never or false
|
||||
If you wish not to use color for any output. (default with Git older than 1.8.4)
|
||||
.El
|
||||
.El
|
||||
.Pp
|
||||
.Nm
|
||||
will query git credential system for Gerrit user/password when
|
||||
authentication failed over http(s). Unlike git,
|
||||
.Nm
|
||||
does not persist Gerrit user/password in git credential system for security
|
||||
purposes and git credential system configuration stays under user responsibility.
|
||||
.Sh FILES
|
||||
To use
|
||||
.Nm
|
||||
with your project, it is recommended that you create
|
||||
a file at the root of the repository named
|
||||
.Pa .gitreview
|
||||
and place information about your Gerrit installation in it. The format is similar to the Windows .ini file format:
|
||||
.Bd -literal -offset indent
|
||||
[gerrit]
|
||||
host=\fIhostname\fP
|
||||
port=\fITCP port number of gerrit\fP
|
||||
project=\fIproject name\fP
|
||||
defaultbranch=\fIbranch to work on\fP
|
||||
.Ed
|
||||
.Pp
|
||||
It is also possible to specify optional default name for
|
||||
the Git remote using the
|
||||
.Cm defaultremote
|
||||
configuration parameter.
|
||||
.Pp
|
||||
Setting
|
||||
.Cm defaultrebase
|
||||
to zero will make
|
||||
.Nm
|
||||
not to rebase changes by default (same as the
|
||||
.Fl R
|
||||
command line option)
|
||||
.Bd -literal -offset indent
|
||||
[gerrit]
|
||||
scheme=ssh
|
||||
host=review.example.com
|
||||
port=29418
|
||||
project=department/project.git
|
||||
defaultbranch=master
|
||||
defaultremote=review
|
||||
defaultrebase=0
|
||||
track=0
|
||||
.Ed
|
||||
.Pp
|
||||
When the same option is provided through FILES and CONFIGURATION, the
|
||||
CONFIGURATION value wins.
|
||||
.Pp
|
||||
.Sh DIAGNOSTICS
|
||||
.Pp
|
||||
Normally, exit status is 0 if executed successfully.
|
||||
Exit status 1 indicates general error, sometimes more
|
||||
specific error codes are available:
|
||||
.Bl -tag -width 999
|
||||
.It 2
|
||||
Gerrit
|
||||
.Ar commit\-msg
|
||||
hook could not be successfully installed.
|
||||
.It 3
|
||||
Could not parse malformed argument value or user input.
|
||||
.It 32
|
||||
Cannot fetch list of open changesets from Gerrit.
|
||||
.It 33
|
||||
Cannot parse list of open changesets received from Gerrit.
|
||||
.It 34
|
||||
Cannot query information about changesets.
|
||||
.It 35
|
||||
Cannot fetch information about the changeset to be downloaded.
|
||||
.It 36
|
||||
Changeset not found.
|
||||
.It 37
|
||||
Particular patchset cannot be fetched from the remote git repository.
|
||||
.It 38
|
||||
Specified patchset number not found in the changeset.
|
||||
.It 39
|
||||
Invalid patchsets for comparison.
|
||||
.It 40
|
||||
Connection to Gerrit was closed.
|
||||
.It 64
|
||||
Cannot checkout downloaded patchset into the new branch.
|
||||
.It 65
|
||||
Cannot checkout downloaded patchset into existing branch.
|
||||
.It 66
|
||||
Cannot hard reset working directory and git index after download.
|
||||
.It 67
|
||||
Cannot switch to some other branch when trying to finish
|
||||
the current branch.
|
||||
.It 68
|
||||
Cannot delete current branch.
|
||||
.It 69
|
||||
Requested patchset cannot be fully applied to the current branch. This exit
|
||||
status will be returned when there are merge conflicts with the current branch.
|
||||
Possible reasons include an attempt to apply patchset from the different branch
|
||||
or code. This exit status will also be returned if the patchset is already
|
||||
applied to the current branch.
|
||||
.It 70
|
||||
Cannot determine top level Git directory or .git subdirectory path.
|
||||
.It 101
|
||||
Unauthorized (401) http request done by git-review.
|
||||
.It 104
|
||||
Not Found (404) http request done by git-review.
|
||||
.El
|
||||
.Pp
|
||||
Exit status larger than 31 indicates problem with
|
||||
communication with Gerrit or remote Git repository,
|
||||
exit status larger than 63 means there was a problem with
|
||||
a local repository or a working copy.
|
||||
.Pp
|
||||
Exit status larger than or equal to 128 means internal
|
||||
error in running the "git" command.
|
||||
.Pp
|
||||
.Sh EXAMPLES
|
||||
To fetch a remote change number 3004:
|
||||
.Pp
|
||||
.Bd -literal -offset indent
|
||||
$ git\-review \-d 3004
|
||||
Downloading refs/changes/04/3004/1 from gerrit into
|
||||
review/someone/topic_name
|
||||
Switched to branch 'review/someone/topic_name
|
||||
$ git branch
|
||||
master
|
||||
* review/author/topic_name
|
||||
.Ed
|
||||
.Pp
|
||||
Gerrit looks up both name of the author and the topic name from Gerrit
|
||||
to name a local branch. This facilitates easier identification of changes.
|
||||
.Pp
|
||||
To fetch a remote patchset number 5 from change number 3004:
|
||||
.Pp
|
||||
.Bd -literal -offset indent
|
||||
$ git\-review \-d 3004,5
|
||||
Downloading refs/changes/04/3004/5 from gerrit into
|
||||
review/someone/topic_name\-patch5
|
||||
Switched to branch 'review/someone/topic_name\-patch5
|
||||
$ git branch
|
||||
master
|
||||
* review/author/topic_name\-patch5
|
||||
.Ed
|
||||
.Pp
|
||||
To send a change for review and delete local branch afterwards:
|
||||
.Bd -literal -offset indent
|
||||
$ git\-review \-f
|
||||
remote: Resolving deltas: 0% (0/8)
|
||||
To ssh://username@review.example.com/department/project.git
|
||||
* [new branch] HEAD \-> refs/for/master/topic_name
|
||||
Switched to branch 'master'
|
||||
Deleted branch 'review/someone/topic_name'
|
||||
$ git branch
|
||||
* master
|
||||
.Ed
|
||||
.Pp
|
||||
An example
|
||||
.Pa .gitreview
|
||||
configuration file for a project
|
||||
.Pa department/project
|
||||
hosted on
|
||||
.Cm review.example.com
|
||||
port
|
||||
.Cm 29418
|
||||
in the branch
|
||||
.Cm master
|
||||
:
|
||||
.Bd -literal -offset indent
|
||||
[gerrit]
|
||||
host=review.example.com
|
||||
port=29418
|
||||
project=department/project.git
|
||||
defaultbranch=master
|
||||
.Ed
|
||||
.Sh BUGS
|
||||
Bug reports can be submitted to
|
||||
.Lk https://launchpad.net/git\-review
|
||||
.Sh AUTHORS
|
||||
.Nm
|
||||
is maintained by
|
||||
.An "OpenStack, LLC"
|
||||
.Pp
|
||||
This manpage has been enhanced by:
|
||||
.An "Antoine Musso" Aq hashar@free.fr
|
||||
.An "Marcin Cieslak" Aq saper@saper.info
|
||||
.An "Pavel Sedlák" Aq psedlak@redhat.com
|
1658
git_review/cmd.py
1658
git_review/cmd.py
File diff suppressed because it is too large
Load Diff
@@ -1,408 +0,0 @@
|
||||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import shutil
|
||||
import stat
|
||||
import struct
|
||||
import sys
|
||||
|
||||
if sys.version < '3':
|
||||
import urllib
|
||||
import urlparse
|
||||
urlparse = urlparse.urlparse
|
||||
else:
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
urlparse = urllib.parse.urlparse
|
||||
|
||||
import fixtures
|
||||
import requests
|
||||
import testtools
|
||||
from testtools import content
|
||||
|
||||
from git_review.tests import utils
|
||||
|
||||
WAR_URL = 'http://tarballs.openstack.org/' \
|
||||
'ci/gerrit/gerrit-v2.11.4.13.cb9800e.war'
|
||||
# Update GOLDEN_SITE_VER for every change altering golden site, including
|
||||
# WAR_URL changes. Set new value to something unique (just +1 it for example)
|
||||
GOLDEN_SITE_VER = '4'
|
||||
|
||||
|
||||
# NOTE(yorik-sar): This function needs to be a perfect hash function for
|
||||
# existing test IDs. This is verified by running check_test_id_hashes script
|
||||
# prior to running tests. Note that this doesn't imply any cryptographic
|
||||
# requirements for underlying algorithm, so we can use weak hash here.
|
||||
# Range of results for this function is limited by port numbers selection
|
||||
# in _pick_gerrit_port_and_dir method (it can't be greater than 10000 now).
|
||||
def _hash_test_id(test_id):
|
||||
if not isinstance(test_id, bytes):
|
||||
test_id = test_id.encode('utf-8')
|
||||
hash_ = hashlib.md5(test_id).digest()
|
||||
num = struct.unpack("=I", hash_[:4])[0]
|
||||
return num % 10000
|
||||
|
||||
|
||||
class DirHelpers(object):
|
||||
def _dir(self, base, *args):
|
||||
"""Creates directory name from base name and other parameters."""
|
||||
return os.path.join(getattr(self, base + '_dir'), *args)
|
||||
|
||||
|
||||
class IsoEnvDir(DirHelpers, fixtures.Fixture):
|
||||
"""Created isolated env and associated directories"""
|
||||
|
||||
def __init__(self, base_dir=None):
|
||||
super(IsoEnvDir, self).setUp()
|
||||
|
||||
# set up directories for isolation
|
||||
if base_dir:
|
||||
self.base_dir = base_dir
|
||||
self.temp_dir = self._dir('base', 'tmp')
|
||||
if not os.path.exists(self.temp_dir):
|
||||
os.mkdir(self.temp_dir)
|
||||
self.addCleanup(shutil.rmtree, self.temp_dir)
|
||||
else:
|
||||
self.temp_dir = self.useFixture(fixtures.TempDir()).path
|
||||
|
||||
self.work_dir = self._dir('temp', 'work')
|
||||
self.home_dir = self._dir('temp', 'home')
|
||||
self.xdg_config_dir = self._dir('home', '.xdgconfig')
|
||||
for path in [self.home_dir, self.xdg_config_dir, self.work_dir]:
|
||||
if not os.path.exists(path):
|
||||
os.mkdir(path)
|
||||
|
||||
# ensure user proxy conf doesn't interfere with tests
|
||||
self.useFixture(fixtures.EnvironmentVariable('no_proxy', '*'))
|
||||
self.useFixture(fixtures.EnvironmentVariable('NO_PROXY', '*'))
|
||||
|
||||
# isolate tests from user and system git configuration
|
||||
self.useFixture(fixtures.EnvironmentVariable('HOME', self.home_dir))
|
||||
self.useFixture(
|
||||
fixtures.EnvironmentVariable('XDG_CONFIG_HOME',
|
||||
self.xdg_config_dir))
|
||||
self.useFixture(
|
||||
fixtures.EnvironmentVariable('GIT_CONFIG_NOSYSTEM', "1"))
|
||||
self.useFixture(
|
||||
fixtures.EnvironmentVariable('EMAIL', "you@example.com"))
|
||||
self.useFixture(
|
||||
fixtures.EnvironmentVariable('GIT_AUTHOR_NAME',
|
||||
"gitreview tester"))
|
||||
self.useFixture(
|
||||
fixtures.EnvironmentVariable('GIT_COMMITTER_NAME',
|
||||
"gitreview tester"))
|
||||
|
||||
|
||||
class GerritHelpers(DirHelpers):
|
||||
|
||||
def init_dirs(self):
|
||||
self.primary_dir = os.path.abspath(os.path.curdir)
|
||||
self.gerrit_dir = self._dir('primary', '.gerrit')
|
||||
self.gsite_dir = self._dir('gerrit', 'golden_site')
|
||||
self.gerrit_war = self._dir('gerrit', WAR_URL.split('/')[-1])
|
||||
|
||||
def ensure_gerrit_war(self):
|
||||
# check if gerrit.war file exists in .gerrit directory
|
||||
if not os.path.exists(self.gerrit_dir):
|
||||
os.mkdir(self.gerrit_dir)
|
||||
|
||||
if not os.path.exists(self.gerrit_war):
|
||||
print("Downloading Gerrit binary from %s..." % WAR_URL)
|
||||
resp = requests.get(WAR_URL)
|
||||
if resp.status_code != 200:
|
||||
raise RuntimeError("Problem requesting Gerrit war")
|
||||
utils.write_to_file(self.gerrit_war, resp.content)
|
||||
print("Saved to %s" % self.gerrit_war)
|
||||
|
||||
def init_gerrit(self):
|
||||
"""Run Gerrit from the war file and configure it."""
|
||||
golden_ver_file = self._dir('gsite', 'golden_ver')
|
||||
if os.path.exists(self.gsite_dir):
|
||||
if not os.path.exists(golden_ver_file):
|
||||
golden_ver = '0'
|
||||
else:
|
||||
with open(golden_ver_file) as f:
|
||||
golden_ver = f.read().strip()
|
||||
if GOLDEN_SITE_VER != golden_ver:
|
||||
print("Existing golden site has version %s, removing..." %
|
||||
golden_ver)
|
||||
shutil.rmtree(self.gsite_dir)
|
||||
else:
|
||||
print("Golden site of version %s already exists" %
|
||||
GOLDEN_SITE_VER)
|
||||
return
|
||||
|
||||
# We write out the ssh host key for gerrit's ssh server which
|
||||
# for undocumented reasons forces gerrit init to download the
|
||||
# bouncy castle libs which we need for ssh that works on
|
||||
# newer distros like ubuntu xenial.
|
||||
os.makedirs(self._dir('gsite', 'etc'))
|
||||
# create SSH host key
|
||||
host_key_file = self._dir('gsite', 'etc', 'ssh_host_rsa_key')
|
||||
utils.run_cmd('ssh-keygen', '-t', 'rsa', '-b', '4096',
|
||||
'-f', host_key_file, '-N', '')
|
||||
|
||||
print("Creating a new golden site of version " + GOLDEN_SITE_VER)
|
||||
|
||||
# initialize Gerrit
|
||||
utils.run_cmd('java', '-jar', self.gerrit_war,
|
||||
'init', '-d', self.gsite_dir,
|
||||
'--batch', '--no-auto-start', '--install-plugin',
|
||||
'download-commands')
|
||||
utils.run_cmd('java', '-jar', self.gerrit_war, 'reindex',
|
||||
'-d', self.gsite_dir)
|
||||
|
||||
with open(golden_ver_file, 'w') as f:
|
||||
f.write(GOLDEN_SITE_VER)
|
||||
|
||||
# create SSH public key
|
||||
key_file = self._dir('gsite', 'test_ssh_key')
|
||||
utils.run_cmd('ssh-keygen', '-t', 'rsa', '-b', '4096',
|
||||
'-f', key_file, '-N', '')
|
||||
with open(key_file + '.pub', 'rb') as pub_key_file:
|
||||
pub_key = pub_key_file.read()
|
||||
|
||||
# create admin user in Gerrit database
|
||||
sql_query = """INSERT INTO ACCOUNTS (REGISTERED_ON) VALUES (NOW());
|
||||
INSERT INTO ACCOUNT_GROUP_MEMBERS (ACCOUNT_ID, GROUP_ID) \
|
||||
VALUES (0, 1);
|
||||
INSERT INTO ACCOUNT_EXTERNAL_IDS (ACCOUNT_ID, EXTERNAL_ID, PASSWORD) \
|
||||
VALUES (0, 'username:test_user', 'test_pass');
|
||||
INSERT INTO ACCOUNT_SSH_KEYS (SSH_PUBLIC_KEY, VALID) \
|
||||
VALUES ('%s', 'Y')""" % pub_key.decode()
|
||||
|
||||
utils.run_cmd('java', '-jar',
|
||||
self._dir('gsite', 'bin', 'gerrit.war'),
|
||||
'gsql', '-d', self.gsite_dir, '-c', sql_query)
|
||||
|
||||
def _run_gerrit_cli(self, command, *args):
|
||||
"""SSH to gerrit Gerrit server and run command there."""
|
||||
return utils.run_cmd('ssh', '-p', str(self.gerrit_port),
|
||||
'test_user@' + self.gerrit_host, 'gerrit',
|
||||
command, *args)
|
||||
|
||||
def _run_git_review(self, *args, **kwargs):
|
||||
"""Run git-review utility from source."""
|
||||
git_review = utils.run_cmd('which', 'git-review')
|
||||
kwargs.setdefault('chdir', self.test_dir)
|
||||
return utils.run_cmd(git_review, *args, **kwargs)
|
||||
|
||||
|
||||
class BaseGitReviewTestCase(testtools.TestCase, GerritHelpers):
|
||||
"""Base class for the git-review tests."""
|
||||
|
||||
_remote = 'gerrit'
|
||||
|
||||
@property
|
||||
def project_uri(self):
|
||||
return self.project_ssh_uri
|
||||
|
||||
def setUp(self):
|
||||
"""Configure testing environment.
|
||||
|
||||
Prepare directory for the testing and clone test Git repository.
|
||||
Require Gerrit war file in the .gerrit directory to run Gerrit local.
|
||||
"""
|
||||
super(BaseGitReviewTestCase, self).setUp()
|
||||
self.useFixture(fixtures.Timeout(2 * 60, True))
|
||||
|
||||
# ensures git-review command runs in local mode (for functional tests)
|
||||
self.useFixture(
|
||||
fixtures.EnvironmentVariable('GITREVIEW_LOCAL_MODE', ''))
|
||||
|
||||
self.init_dirs()
|
||||
ssh_addr, ssh_port, http_addr, http_port, self.site_dir = \
|
||||
self._pick_gerrit_port_and_dir()
|
||||
self.gerrit_host, self.gerrit_port = ssh_addr, ssh_port
|
||||
|
||||
self.test_dir = self._dir('site', 'tmp', 'test_project')
|
||||
self.ssh_dir = self._dir('site', 'tmp', 'ssh')
|
||||
self.project_ssh_uri = (
|
||||
'ssh://test_user@%s:%s/test/test_project.git' % (
|
||||
ssh_addr, ssh_port))
|
||||
self.project_http_uri = (
|
||||
'http://test_user:test_pass@%s:%s/test/test_project.git' % (
|
||||
http_addr, http_port))
|
||||
|
||||
self._run_gerrit(ssh_addr, ssh_port, http_addr, http_port)
|
||||
self._configure_ssh(ssh_addr, ssh_port)
|
||||
|
||||
# create Gerrit empty project
|
||||
self._run_gerrit_cli('create-project', '--empty-commit',
|
||||
'--name', 'test/test_project')
|
||||
|
||||
# setup isolated area to work under
|
||||
self.useFixture(IsoEnvDir(self.site_dir))
|
||||
|
||||
# prepare repository for the testing
|
||||
self._run_git('clone', self.project_uri)
|
||||
utils.write_to_file(self._dir('test', 'test_file.txt'),
|
||||
'test file created'.encode())
|
||||
self._create_gitreview_file()
|
||||
|
||||
# push changes to the Gerrit
|
||||
self._run_git('add', '--all')
|
||||
self._run_git('commit', '-m', 'Test file and .gitreview added.')
|
||||
self._run_git('push', 'origin', 'master')
|
||||
# push a branch to gerrit
|
||||
self._run_git('checkout', '-b', 'testbranch')
|
||||
utils.write_to_file(self._dir('test', 'test_file.txt'),
|
||||
'test file branched'.encode())
|
||||
self._create_gitreview_file(defaultbranch='testbranch')
|
||||
self._run_git('add', '--all')
|
||||
self._run_git('commit', '-m', 'Branched.')
|
||||
self._run_git('push', 'origin', 'testbranch')
|
||||
# cleanup
|
||||
shutil.rmtree(self.test_dir)
|
||||
|
||||
# go to the just cloned test Git repository
|
||||
self._run_git('clone', self.project_uri)
|
||||
self.configure_gerrit_remote()
|
||||
self.addCleanup(shutil.rmtree, self.test_dir)
|
||||
|
||||
# ensure user is configured for all tests
|
||||
self._configure_gitreview_username()
|
||||
|
||||
def set_remote(self, uri):
|
||||
self._run_git('remote', 'set-url', self._remote, uri)
|
||||
|
||||
def reset_remote(self):
|
||||
self._run_git('remote', 'rm', self._remote)
|
||||
|
||||
def attach_on_exception(self, filename):
|
||||
@self.addOnException
|
||||
def attach_file(exc_info):
|
||||
if os.path.exists(filename):
|
||||
content.attach_file(self, filename)
|
||||
else:
|
||||
self.addDetail(os.path.basename(filename),
|
||||
content.text_content('Not found'))
|
||||
|
||||
def _run_git(self, command, *args):
|
||||
"""Run git command using test git directory."""
|
||||
if command == 'clone':
|
||||
return utils.run_git(command, args[0], self._dir('test'))
|
||||
return utils.run_git('--git-dir=' + self._dir('test', '.git'),
|
||||
'--work-tree=' + self._dir('test'),
|
||||
command, *args)
|
||||
|
||||
def _run_gerrit(self, ssh_addr, ssh_port, http_addr, http_port):
|
||||
# create a copy of site dir
|
||||
shutil.copytree(self.gsite_dir, self.site_dir)
|
||||
self.addCleanup(shutil.rmtree, self.site_dir)
|
||||
# write config
|
||||
with open(self._dir('site', 'etc', 'gerrit.config'), 'w') as _conf:
|
||||
new_conf = utils.get_gerrit_conf(
|
||||
ssh_addr, ssh_port, http_addr, http_port)
|
||||
_conf.write(new_conf)
|
||||
|
||||
# If test fails, attach Gerrit config and logs to the result
|
||||
self.attach_on_exception(self._dir('site', 'etc', 'gerrit.config'))
|
||||
for name in ['error_log', 'sshd_log', 'httpd_log']:
|
||||
self.attach_on_exception(self._dir('site', 'logs', name))
|
||||
|
||||
# start Gerrit
|
||||
gerrit_sh = self._dir('site', 'bin', 'gerrit.sh')
|
||||
utils.run_cmd(gerrit_sh, 'start')
|
||||
self.addCleanup(utils.run_cmd, gerrit_sh, 'stop')
|
||||
|
||||
def _simple_change(self, change_text, commit_message,
|
||||
file_=None):
|
||||
"""Helper method to create small changes and commit them."""
|
||||
if file_ is None:
|
||||
file_ = self._dir('test', 'test_file.txt')
|
||||
utils.write_to_file(file_, change_text.encode())
|
||||
self._run_git('add', file_)
|
||||
self._run_git('commit', '-m', commit_message)
|
||||
|
||||
def _simple_amend(self, change_text, file_=None):
|
||||
"""Helper method to amend existing commit with change."""
|
||||
if file_ is None:
|
||||
file_ = self._dir('test', 'test_file_new.txt')
|
||||
utils.write_to_file(file_, change_text.encode())
|
||||
self._run_git('add', file_)
|
||||
# cannot use --no-edit because it does not exist in older git
|
||||
message = self._run_git('log', '-1', '--format=%s\n\n%b')
|
||||
self._run_git('commit', '--amend', '-m', message)
|
||||
|
||||
def _configure_ssh(self, ssh_addr, ssh_port):
|
||||
"""Setup ssh and scp to run with special options."""
|
||||
|
||||
os.mkdir(self.ssh_dir)
|
||||
|
||||
ssh_key = utils.run_cmd('ssh-keyscan', '-p', str(ssh_port), ssh_addr)
|
||||
utils.write_to_file(self._dir('ssh', 'known_hosts'), ssh_key.encode())
|
||||
self.addCleanup(os.remove, self._dir('ssh', 'known_hosts'))
|
||||
|
||||
# Attach known_hosts to test results if anything fails
|
||||
self.attach_on_exception(self._dir('ssh', 'known_hosts'))
|
||||
|
||||
for cmd in ('ssh', 'scp'):
|
||||
cmd_file = self._dir('ssh', cmd)
|
||||
s = '#!/bin/sh\n' \
|
||||
'/usr/bin/%s -i %s -o UserKnownHostsFile=%s ' \
|
||||
'-o IdentitiesOnly=yes ' \
|
||||
'-o PasswordAuthentication=no $@' % \
|
||||
(cmd,
|
||||
self._dir('gsite', 'test_ssh_key'),
|
||||
self._dir('ssh', 'known_hosts'))
|
||||
utils.write_to_file(cmd_file, s.encode())
|
||||
os.chmod(cmd_file, os.stat(cmd_file).st_mode | stat.S_IEXEC)
|
||||
|
||||
os.environ['PATH'] = self.ssh_dir + os.pathsep + os.environ['PATH']
|
||||
os.environ['GIT_SSH'] = self._dir('ssh', 'ssh')
|
||||
|
||||
def configure_gerrit_remote(self):
|
||||
self._run_git('remote', 'add', self._remote, self.project_uri)
|
||||
|
||||
def _configure_gitreview_username(self):
|
||||
self._run_git('config', 'gitreview.username', 'test_user')
|
||||
|
||||
def _pick_gerrit_port_and_dir(self):
|
||||
hash_ = _hash_test_id(self.id())
|
||||
host = '127.0.0.1'
|
||||
return (
|
||||
host, 12000 + hash_, # avoid 11211 that is memcached port on CI
|
||||
host, 22000 + hash_, # avoid ephemeral ports at 32678+
|
||||
self._dir('gerrit', 'site-' + str(hash_)),
|
||||
)
|
||||
|
||||
def _create_gitreview_file(self, **kwargs):
|
||||
cfg = ('[gerrit]\n'
|
||||
'scheme=%s\n'
|
||||
'host=%s\n'
|
||||
'port=%s\n'
|
||||
'project=test/test_project.git\n'
|
||||
'%s')
|
||||
parsed = urlparse(self.project_uri)
|
||||
host_port = parsed.netloc.rpartition('@')[-1]
|
||||
host, __, port = host_port.partition(':')
|
||||
extra = '\n'.join('%s=%s' % kv for kv in kwargs.items())
|
||||
cfg %= parsed.scheme, host, port, extra
|
||||
utils.write_to_file(self._dir('test', '.gitreview'), cfg.encode())
|
||||
|
||||
|
||||
class HttpMixin(object):
|
||||
"""HTTP remote_url mixin."""
|
||||
|
||||
@property
|
||||
def project_uri(self):
|
||||
return self.project_http_uri
|
||||
|
||||
def _configure_gitreview_username(self):
|
||||
# trick to set http password
|
||||
self._run_git('config', 'gitreview.username', 'test_user:test_pass')
|
@@ -1,57 +0,0 @@
|
||||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
|
||||
from git_review import tests
|
||||
from git_review.tests import utils
|
||||
|
||||
|
||||
def list_test_ids(argv):
|
||||
res = utils.run_cmd(sys.executable, '-m', 'testtools.run', *argv[1:])
|
||||
return res.split('\n')
|
||||
|
||||
|
||||
def find_collisions(test_ids):
|
||||
hashes = {}
|
||||
for test_id in test_ids:
|
||||
hash_ = tests._hash_test_id(test_id)
|
||||
if hash_ in hashes:
|
||||
return (hashes[hash_], test_id)
|
||||
hashes[hash_] = test_id
|
||||
return None
|
||||
|
||||
|
||||
def main(argv):
|
||||
test_ids = list_test_ids(argv)
|
||||
if not test_ids:
|
||||
print("No tests found, check command line arguments", file=sys.stderr)
|
||||
return 1
|
||||
collision = find_collisions(test_ids)
|
||||
if collision is None:
|
||||
return 0
|
||||
print(
|
||||
"Found a collision for test ids hash function: %s and %s\n"
|
||||
"You should change _hash_test_id function in"
|
||||
" git_review/tests/__init__.py module to fit new set of test ids."
|
||||
% collision,
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 2
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
@@ -1,26 +0,0 @@
|
||||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from git_review import tests
|
||||
|
||||
|
||||
def main():
|
||||
helpers = tests.GerritHelpers()
|
||||
helpers.init_dirs()
|
||||
helpers.ensure_gerrit_war()
|
||||
helpers.init_gerrit()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@@ -1,567 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from git_review import tests
|
||||
from git_review.tests import utils
|
||||
|
||||
|
||||
class ConfigTestCase(tests.BaseGitReviewTestCase):
|
||||
"""Class for config tests."""
|
||||
|
||||
def test_get_config_from_cli(self):
|
||||
self.reset_remote()
|
||||
self._run_git('remote', 'rm', 'origin')
|
||||
self._create_gitreview_file(defaultremote='remote-file')
|
||||
self._run_git('config', 'gitreview.remote', 'remote-gitconfig')
|
||||
self._run_git_review('-s', '-r', 'remote-cli')
|
||||
|
||||
remote = self._run_git('remote').strip()
|
||||
self.assertEqual('remote-cli', remote)
|
||||
|
||||
def test_get_config_from_gitconfig(self):
|
||||
self.reset_remote()
|
||||
self._run_git('remote', 'rm', 'origin')
|
||||
self._create_gitreview_file(defaultremote='remote-file')
|
||||
self._run_git('config', 'gitreview.remote', 'remote-gitconfig')
|
||||
self._run_git_review('-s')
|
||||
|
||||
remote = self._run_git('remote').strip()
|
||||
self.assertEqual('remote-gitconfig', remote)
|
||||
|
||||
def test_get_config_from_file(self):
|
||||
self.reset_remote()
|
||||
self._run_git('remote', 'rm', 'origin')
|
||||
self._create_gitreview_file(defaultremote='remote-file')
|
||||
self._run_git_review('-s')
|
||||
|
||||
remote = self._run_git('remote').strip()
|
||||
self.assertEqual('remote-file', remote)
|
||||
|
||||
|
||||
class GitReviewTestCase(tests.BaseGitReviewTestCase):
|
||||
"""Class for the git-review tests."""
|
||||
|
||||
def test_cloned_repo(self):
|
||||
"""Test git-review on the just cloned repository."""
|
||||
self._simple_change('test file modified', 'test commit message')
|
||||
self.assertNotIn('Change-Id:', self._run_git('log', '-1'))
|
||||
self.assertIn('remote: New Changes:', self._run_git_review())
|
||||
self.assertIn('Change-Id:', self._run_git('log', '-1'))
|
||||
|
||||
def test_git_review_s(self):
|
||||
"""Test git-review -s."""
|
||||
self.reset_remote()
|
||||
self._run_git_review('-s')
|
||||
self._simple_change('test file modified', 'test commit message')
|
||||
self.assertIn('Change-Id:', self._run_git('log', '-1'))
|
||||
|
||||
def test_git_review_s_in_detached_head(self):
|
||||
"""Test git-review -s in detached HEAD state."""
|
||||
self.reset_remote()
|
||||
master_sha1 = self._run_git('rev-parse', 'master')
|
||||
self._run_git('checkout', master_sha1)
|
||||
self._run_git_review('-s')
|
||||
self._simple_change('test file modified', 'test commit message')
|
||||
self.assertIn('Change-Id:', self._run_git('log', '-1'))
|
||||
|
||||
def test_git_review_s_with_outdated_repo(self):
|
||||
"""Test git-review -s with a outdated repo."""
|
||||
self._simple_change('test file to outdate', 'test commit message 1')
|
||||
self._run_git('push', 'origin', 'master')
|
||||
self._run_git('reset', '--hard', 'HEAD^')
|
||||
|
||||
# Review setup with an outdated repo
|
||||
self.reset_remote()
|
||||
self._run_git_review('-s')
|
||||
self._simple_change('test file modified', 'test commit message 2')
|
||||
self.assertIn('Change-Id:', self._run_git('log', '-1'))
|
||||
|
||||
def test_git_review_s_from_subdirectory(self):
|
||||
"""Test git-review -s from subdirectory."""
|
||||
self.reset_remote()
|
||||
utils.run_cmd('mkdir', 'subdirectory', chdir=self.test_dir)
|
||||
self._run_git_review(
|
||||
'-s', chdir=os.path.join(self.test_dir, 'subdirectory'))
|
||||
|
||||
def test_git_review_d(self):
|
||||
"""Test git-review -d."""
|
||||
self._run_git_review('-s')
|
||||
|
||||
# create new review to be downloaded
|
||||
self._simple_change('test file modified', 'test commit message')
|
||||
self._run_git_review()
|
||||
change_id = self._run_git('log', '-1').split()[-1]
|
||||
|
||||
shutil.rmtree(self.test_dir)
|
||||
|
||||
# download clean Git repository and fresh change from Gerrit to it
|
||||
self._run_git('clone', self.project_uri)
|
||||
self.configure_gerrit_remote()
|
||||
self._run_git_review('-d', change_id)
|
||||
self.assertIn('test commit message', self._run_git('log', '-1'))
|
||||
|
||||
# test backport branch
|
||||
self._run_git('checkout', '-b', 'mybackport',
|
||||
self._remote + '/' + 'testbranch')
|
||||
self._simple_change('test file modified in branch',
|
||||
'test branch commit message\n\nChange-Id: %s' %
|
||||
change_id)
|
||||
self._run_git_review('testbranch')
|
||||
self._run_git('checkout', 'master')
|
||||
self._run_git_review('-d', change_id, 'testbranch')
|
||||
self.assertIn('test branch commit message',
|
||||
self._run_git('log', '-1'))
|
||||
|
||||
# second download should also work correctly
|
||||
self._run_git('checkout', 'master')
|
||||
self._run_git_review('-d', change_id)
|
||||
self.assertIn('test commit message', self._run_git('show', 'HEAD'))
|
||||
self.assertNotIn('test commit message',
|
||||
self._run_git('show', 'HEAD^1'))
|
||||
|
||||
# and branch is tracking
|
||||
head = self._run_git('symbolic-ref', '-q', 'HEAD')
|
||||
self.assertIn(
|
||||
'refs/remotes/%s/master' % self._remote,
|
||||
self._run_git("for-each-ref", "--format='%(upstream)'", head))
|
||||
|
||||
def test_multiple_changes(self):
|
||||
"""Test git-review asks about multiple changes.
|
||||
|
||||
Should register user's wish to send two change requests by interactive
|
||||
'yes' message and by the -y option.
|
||||
"""
|
||||
self._run_git_review('-s')
|
||||
|
||||
# 'yes' message
|
||||
self._simple_change('test file modified 1st time',
|
||||
'test commit message 1')
|
||||
self._simple_change('test file modified 2nd time',
|
||||
'test commit message 2')
|
||||
|
||||
review_res = self._run_git_review(confirm=True)
|
||||
self.assertIn("Type 'yes' to confirm", review_res)
|
||||
self.assertIn("Processing changes: new: 2", review_res)
|
||||
|
||||
# abandon changes sent to the Gerrit
|
||||
head = self._run_git('rev-parse', 'HEAD')
|
||||
head_1 = self._run_git('rev-parse', 'HEAD^1')
|
||||
self._run_gerrit_cli('review', '--abandon', head)
|
||||
self._run_gerrit_cli('review', '--abandon', head_1)
|
||||
|
||||
# -y option
|
||||
self._simple_change('test file modified 3rd time',
|
||||
'test commit message 3')
|
||||
self._simple_change('test file modified 4th time',
|
||||
'test commit message 4')
|
||||
review_res = self._run_git_review('-y')
|
||||
self.assertIn("Processing changes: new: 2", review_res)
|
||||
|
||||
def test_git_review_re(self):
|
||||
"""Test git-review adding reviewers to changes."""
|
||||
self._run_git_review('-s')
|
||||
|
||||
# Create users to add as reviewers
|
||||
self._run_gerrit_cli('create-account', '--email',
|
||||
'reviewer1@example.com', 'reviewer1')
|
||||
self._run_gerrit_cli('create-account', '--email',
|
||||
'reviewer2@example.com', 'reviewer2')
|
||||
|
||||
self._simple_change('test file', 'test commit message')
|
||||
|
||||
review_res = self._run_git_review('--reviewers', 'reviewer1',
|
||||
'reviewer2')
|
||||
self.assertIn("Processing changes: new: 1", review_res)
|
||||
|
||||
# verify both reviewers are on patch set
|
||||
head = self._run_git('rev-parse', 'HEAD')
|
||||
change = self._run_gerrit_cli('query', '--format=JSON',
|
||||
'--all-reviewers', head)
|
||||
# The first result should be the one we want
|
||||
change = json.loads(change.split('\n')[0])
|
||||
|
||||
self.assertEqual(2, len(change['allReviewers']))
|
||||
|
||||
reviewers = set()
|
||||
for reviewer in change['allReviewers']:
|
||||
reviewers.add(reviewer['username'])
|
||||
|
||||
self.assertEqual(set(['reviewer1', 'reviewer2']), reviewers)
|
||||
|
||||
def test_rebase_no_remote_branch_msg(self):
|
||||
"""Test message displayed where no remote branch exists."""
|
||||
self._run_git_review('-s')
|
||||
self._run_git('checkout', '-b', 'new_branch')
|
||||
self._simple_change('simple message',
|
||||
'need to avoid noop message')
|
||||
exc = self.assertRaises(Exception, self._run_git_review, 'new_branch')
|
||||
self.assertIn("The branch 'new_branch' does not exist on the given "
|
||||
"remote '%s'" % self._remote, exc.args[0])
|
||||
|
||||
def test_need_rebase_no_upload(self):
|
||||
"""Test change needing a rebase does not upload."""
|
||||
self._run_git_review('-s')
|
||||
head_1 = self._run_git('rev-parse', 'HEAD^1')
|
||||
|
||||
self._run_git('checkout', '-b', 'test_branch', head_1)
|
||||
|
||||
self._simple_change('some other message',
|
||||
'create conflict with master')
|
||||
|
||||
exc = self.assertRaises(Exception, self._run_git_review)
|
||||
self.assertIn(
|
||||
"Errors running git rebase -p -i remotes/%s/master" % self._remote,
|
||||
exc.args[0])
|
||||
self.assertIn("It is likely that your change has a merge conflict.",
|
||||
exc.args[0])
|
||||
|
||||
def test_upload_without_rebase(self):
|
||||
"""Test change not needing a rebase can upload without rebasing."""
|
||||
self._run_git_review('-s')
|
||||
head_1 = self._run_git('rev-parse', 'HEAD^1')
|
||||
|
||||
self._run_git('checkout', '-b', 'test_branch', head_1)
|
||||
|
||||
self._simple_change('some new message',
|
||||
'just another file (no conflict)',
|
||||
self._dir('test', 'new_test_file.txt'))
|
||||
|
||||
review_res = self._run_git_review('-v')
|
||||
self.assertIn(
|
||||
"Running: git rebase -p -i remotes/%s/master" % self._remote,
|
||||
review_res)
|
||||
self.assertEqual(self._run_git('rev-parse', 'HEAD^1'), head_1)
|
||||
|
||||
def test_uploads_with_nondefault_rebase(self):
|
||||
"""Test changes rebase against correct branches."""
|
||||
# prepare maintenance branch that is behind master
|
||||
self._create_gitreview_file(track='true',
|
||||
defaultremote='origin')
|
||||
self._run_git('add', '.gitreview')
|
||||
self._run_git('commit', '-m', 'track=true.')
|
||||
self._simple_change('diverge master from maint',
|
||||
'no conflict',
|
||||
self._dir('test', 'test_file_to_diverge.txt'))
|
||||
self._run_git('push', 'origin', 'master')
|
||||
self._run_git('push', 'origin', 'master', 'master:other')
|
||||
self._run_git_review('-s')
|
||||
head_1 = self._run_git('rev-parse', 'HEAD^1')
|
||||
self._run_gerrit_cli('create-branch',
|
||||
'test/test_project',
|
||||
'maint', head_1)
|
||||
self._run_git('fetch')
|
||||
|
||||
br_out = self._run_git('checkout',
|
||||
'-b', 'test_branch', 'origin/maint')
|
||||
expected_track = 'Branch test_branch set up to track remote' + \
|
||||
' branch maint from origin.'
|
||||
self.assertIn(expected_track, br_out)
|
||||
branches = self._run_git('branch', '-a')
|
||||
expected_branch = '* test_branch'
|
||||
observed = branches.split('\n')
|
||||
self.assertIn(expected_branch, observed)
|
||||
|
||||
self._simple_change('some new message',
|
||||
'just another file (no conflict)',
|
||||
self._dir('test', 'new_tracked_test_file.txt'))
|
||||
change_id = self._run_git('log', '-1').split()[-1]
|
||||
|
||||
review_res = self._run_git_review('-v')
|
||||
# no rebase needed; if it breaks it would try to rebase to master
|
||||
self.assertNotIn("Running: git rebase -p -i remotes/origin/master",
|
||||
review_res)
|
||||
# Don't need to query gerrit for the branch as the second half
|
||||
# of this test will work only if the branch was correctly
|
||||
# stored in gerrit
|
||||
|
||||
# delete branch locally
|
||||
self._run_git('checkout', 'master')
|
||||
self._run_git('branch', '-D', 'test_branch')
|
||||
|
||||
# download, amend, submit
|
||||
self._run_git_review('-d', change_id)
|
||||
self._simple_amend('just another file (no conflict)',
|
||||
self._dir('test', 'new_tracked_test_file_2.txt'))
|
||||
new_change_id = self._run_git('log', '-1').split()[-1]
|
||||
self.assertEqual(change_id, new_change_id)
|
||||
review_res = self._run_git_review('-v')
|
||||
# caused the right thing to happen
|
||||
self.assertIn("Running: git rebase -p -i remotes/origin/maint",
|
||||
review_res)
|
||||
|
||||
# track different branch than expected in changeset
|
||||
branch = self._run_git('rev-parse', '--abbrev-ref', 'HEAD')
|
||||
self._run_git('branch',
|
||||
'--set-upstream',
|
||||
branch,
|
||||
'remotes/origin/other')
|
||||
self.assertRaises(
|
||||
Exception, # cmd.BranchTrackingMismatch inside
|
||||
self._run_git_review, '-d', change_id)
|
||||
|
||||
def test_no_rebase_check(self):
|
||||
"""Test -R causes a change to be uploaded without rebase checking."""
|
||||
self._run_git_review('-s')
|
||||
head_1 = self._run_git('rev-parse', 'HEAD^1')
|
||||
|
||||
self._run_git('checkout', '-b', 'test_branch', head_1)
|
||||
self._simple_change('some new message', 'just another file',
|
||||
self._dir('test', 'new_test_file.txt'))
|
||||
|
||||
review_res = self._run_git_review('-v', '-R')
|
||||
self.assertNotIn('rebase', review_res)
|
||||
self.assertEqual(self._run_git('rev-parse', 'HEAD^1'), head_1)
|
||||
|
||||
def test_rebase_anyway(self):
|
||||
"""Test -F causes a change to be rebased regardless."""
|
||||
self._run_git_review('-s')
|
||||
head = self._run_git('rev-parse', 'HEAD')
|
||||
head_1 = self._run_git('rev-parse', 'HEAD^1')
|
||||
|
||||
self._run_git('checkout', '-b', 'test_branch', head_1)
|
||||
self._simple_change('some new message', 'just another file',
|
||||
self._dir('test', 'new_test_file.txt'))
|
||||
review_res = self._run_git_review('-v', '-F')
|
||||
self.assertIn('rebase', review_res)
|
||||
self.assertEqual(self._run_git('rev-parse', 'HEAD^1'), head)
|
||||
|
||||
def _assert_branch_would_be(self, branch, extra_args=None):
|
||||
extra_args = extra_args or []
|
||||
output = self._run_git_review('-n', *extra_args)
|
||||
# last non-empty line should be:
|
||||
# git push gerrit HEAD:refs/publish/master
|
||||
last_line = output.strip().split('\n')[-1]
|
||||
branch_was = last_line.rsplit(' ', 1)[-1].split('/', 2)[-1]
|
||||
self.assertEqual(branch, branch_was)
|
||||
|
||||
def test_detached_head(self):
|
||||
"""Test on a detached state: we shouldn't have '(detached' as topic."""
|
||||
self._run_git_review('-s')
|
||||
curr_branch = self._run_git('rev-parse', '--abbrev-ref', 'HEAD')
|
||||
# Note: git checkout --detach has been introduced in git 1.7.5 (2011)
|
||||
self._run_git('checkout', curr_branch + '^0')
|
||||
self._simple_change('some new message', 'just another file',
|
||||
self._dir('test', 'new_test_file.txt'))
|
||||
# switch to French, 'git branch' should return '(détaché du HEAD)'
|
||||
lang_env = os.getenv('LANG', 'C')
|
||||
os.environ.update(LANG='fr_FR.UTF-8')
|
||||
try:
|
||||
self._assert_branch_would_be(curr_branch)
|
||||
finally:
|
||||
os.environ.update(LANG=lang_env)
|
||||
|
||||
def test_git_review_t(self):
|
||||
self._run_git_review('-s')
|
||||
self._simple_change('test file modified', 'commit message for bug 654')
|
||||
self._assert_branch_would_be('master/zat', extra_args=['-t', 'zat'])
|
||||
|
||||
def test_bug_topic(self):
|
||||
self._run_git_review('-s')
|
||||
self._simple_change('a change', 'new change for bug 123')
|
||||
self._assert_branch_would_be('master/bug/123')
|
||||
|
||||
def test_bug_topic_newline(self):
|
||||
self._run_git_review('-s')
|
||||
self._simple_change('a change', 'new change not for bug\n\n123')
|
||||
self._assert_branch_would_be('master')
|
||||
|
||||
def test_bp_topic(self):
|
||||
self._run_git_review('-s')
|
||||
self._simple_change('a change', 'new change for blueprint asdf')
|
||||
self._assert_branch_would_be('master/bp/asdf')
|
||||
|
||||
def test_bp_topic_newline(self):
|
||||
self._run_git_review('-s')
|
||||
self._simple_change('a change', 'new change not for blueprint\n\nasdf')
|
||||
self._assert_branch_would_be('master')
|
||||
|
||||
def test_author_name_topic_bp(self):
|
||||
old_author = None
|
||||
if 'GIT_AUTHOR_NAME' in os.environ:
|
||||
old_author = os.environ['GIT_AUTHOR_NAME']
|
||||
try:
|
||||
os.environ['GIT_AUTHOR_NAME'] = 'BPNAME'
|
||||
self._run_git_review('-s')
|
||||
self._simple_change('a change',
|
||||
'new change 1 with name but no topic')
|
||||
self._assert_branch_would_be('master')
|
||||
finally:
|
||||
if old_author:
|
||||
os.environ['GIT_AUTHOR_NAME'] = old_author
|
||||
else:
|
||||
del os.environ['GIT_AUTHOR_NAME']
|
||||
|
||||
def test_author_email_topic_bp(self):
|
||||
old_author = None
|
||||
if 'GIT_AUTHOR_EMAIL' in os.environ:
|
||||
old_author = os.environ['GIT_AUTHOR_EMAIL']
|
||||
try:
|
||||
os.environ['GIT_AUTHOR_EMAIL'] = 'bpemail@example.com'
|
||||
self._run_git_review('-s')
|
||||
self._simple_change('a change',
|
||||
'new change 1 with email but no topic')
|
||||
self._assert_branch_would_be('master')
|
||||
finally:
|
||||
if old_author:
|
||||
os.environ['GIT_AUTHOR_EMAIL'] = old_author
|
||||
else:
|
||||
del os.environ['GIT_AUTHOR_EMAIL']
|
||||
|
||||
def test_author_name_topic_bug(self):
|
||||
old_author = None
|
||||
if 'GIT_AUTHOR_NAME' in os.environ:
|
||||
old_author = os.environ['GIT_AUTHOR_NAME']
|
||||
try:
|
||||
os.environ['GIT_AUTHOR_NAME'] = 'Bug: #1234'
|
||||
self._run_git_review('-s')
|
||||
self._simple_change('a change',
|
||||
'new change 2 with name but no topic')
|
||||
self._assert_branch_would_be('master')
|
||||
finally:
|
||||
if old_author:
|
||||
os.environ['GIT_AUTHOR_NAME'] = old_author
|
||||
else:
|
||||
del os.environ['GIT_AUTHOR_NAME']
|
||||
|
||||
def test_author_email_topic_bug(self):
|
||||
old_author = None
|
||||
if 'GIT_AUTHOR_EMAIL' in os.environ:
|
||||
old_author = os.environ['GIT_AUTHOR_EMAIL']
|
||||
try:
|
||||
os.environ['GIT_AUTHOR_EMAIL'] = 'bug5678@example.com'
|
||||
self._run_git_review('-s')
|
||||
self._simple_change('a change',
|
||||
'new change 2 with email but no topic')
|
||||
self._assert_branch_would_be('master')
|
||||
finally:
|
||||
if old_author:
|
||||
os.environ['GIT_AUTHOR_EMAIL'] = old_author
|
||||
else:
|
||||
del os.environ['GIT_AUTHOR_EMAIL']
|
||||
|
||||
def test_git_review_T(self):
|
||||
self._run_git_review('-s')
|
||||
self._simple_change('test file modified', 'commit message for bug 456')
|
||||
self._assert_branch_would_be('master/bug/456')
|
||||
self._assert_branch_would_be('master', extra_args=['-T'])
|
||||
|
||||
def test_git_review_T_t(self):
|
||||
self.assertRaises(Exception, self._run_git_review, '-T', '-t', 'taz')
|
||||
|
||||
def test_git_review_l(self):
|
||||
self._run_git_review('-s')
|
||||
|
||||
# Populate "project" repo
|
||||
self._simple_change('project: test1', 'project: change1, merged')
|
||||
self._simple_change('project: test2', 'project: change2, open')
|
||||
self._simple_change('project: test3', 'project: change3, abandoned')
|
||||
self._run_git_review('-y')
|
||||
head = self._run_git('rev-parse', 'HEAD')
|
||||
head_2 = self._run_git('rev-parse', 'HEAD^^')
|
||||
self._run_gerrit_cli('review', head_2, '--code-review=+2', '--submit')
|
||||
self._run_gerrit_cli('review', head, '--abandon')
|
||||
|
||||
# Populate "project2" repo
|
||||
self._run_gerrit_cli('create-project', '--empty-commit', '--name',
|
||||
'test/test_project2')
|
||||
project2_uri = self.project_uri.replace('test/test_project',
|
||||
'test/test_project2')
|
||||
self._run_git('fetch', project2_uri, 'HEAD')
|
||||
self._run_git('checkout', 'FETCH_HEAD')
|
||||
self._simple_change('project2: test1', 'project2: change1, open')
|
||||
self._run_git('push', project2_uri, 'HEAD:refs/for/master')
|
||||
|
||||
# Only project1 open changes
|
||||
result = self._run_git_review('-l')
|
||||
self.assertNotIn('project: change1, merged', result)
|
||||
self.assertIn('project: change2, open', result)
|
||||
self.assertNotIn('project: change3, abandoned', result)
|
||||
self.assertNotIn('project2:', result)
|
||||
|
||||
def _test_git_review_F(self, rebase):
|
||||
self._run_git_review('-s')
|
||||
|
||||
# Populate repo
|
||||
self._simple_change('create file', 'test commit message')
|
||||
change1 = self._run_git('rev-parse', 'HEAD')
|
||||
self._run_git_review()
|
||||
self._run_gerrit_cli('review', change1, '--code-review=+2', '--submit')
|
||||
self._run_git('reset', '--hard', 'HEAD^')
|
||||
|
||||
# Review with force_rebase
|
||||
self._run_git('config', 'gitreview.rebase', rebase)
|
||||
self._simple_change('create file2', 'test commit message 2',
|
||||
self._dir('test', 'test_file2.txt'))
|
||||
self._run_git_review('-F')
|
||||
head_1 = self._run_git('rev-parse', 'HEAD^')
|
||||
self.assertEqual(change1, head_1)
|
||||
|
||||
def test_git_review_F(self):
|
||||
self._test_git_review_F('1')
|
||||
|
||||
def test_git_review_F_norebase(self):
|
||||
self._test_git_review_F('0')
|
||||
|
||||
def test_git_review_F_R(self):
|
||||
self.assertRaises(Exception, self._run_git_review, '-F', '-R')
|
||||
|
||||
def test_config_instead_of_honored(self):
|
||||
self.set_remote('test_project_url')
|
||||
|
||||
self.assertRaises(Exception, self._run_git_review, '-l')
|
||||
|
||||
self._run_git('config', '--add', 'url.%s.insteadof' % self.project_uri,
|
||||
'test_project_url')
|
||||
self._run_git_review('-l')
|
||||
|
||||
def test_config_pushinsteadof_honored(self):
|
||||
self.set_remote('test_project_url')
|
||||
|
||||
self.assertRaises(Exception, self._run_git_review, '-l')
|
||||
|
||||
self._run_git('config', '--add',
|
||||
'url.%s.pushinsteadof' % self.project_uri,
|
||||
'test_project_url')
|
||||
self._run_git_review('-l')
|
||||
|
||||
|
||||
class PushUrlTestCase(GitReviewTestCase):
|
||||
"""Class for the git-review tests using origin push-url."""
|
||||
|
||||
_remote = 'origin'
|
||||
|
||||
def set_remote(self, uri):
|
||||
self._run_git('remote', 'set-url', '--push', self._remote, uri)
|
||||
|
||||
def reset_remote(self):
|
||||
self._run_git('config', '--unset', 'remote.%s.pushurl' % self._remote)
|
||||
|
||||
def configure_gerrit_remote(self):
|
||||
self.set_remote(self.project_uri)
|
||||
self._run_git('config', 'gitreview.usepushurl', '1')
|
||||
|
||||
def test_config_pushinsteadof_honored(self):
|
||||
self.skipTest("pushinsteadof doesn't rewrite pushurls")
|
||||
|
||||
|
||||
class HttpGitReviewTestCase(tests.HttpMixin, GitReviewTestCase):
|
||||
"""Class for the git-review tests over HTTP(S)."""
|
||||
pass
|
@@ -1,348 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import functools
|
||||
import os
|
||||
import textwrap
|
||||
|
||||
import fixtures
|
||||
import mock
|
||||
import testtools
|
||||
|
||||
from git_review import cmd
|
||||
from git_review.tests import IsoEnvDir
|
||||
from git_review.tests import utils
|
||||
|
||||
# Use of io.StringIO in python =< 2.7 requires all strings handled to be
|
||||
# unicode. See if StringIO.StringIO is available first
|
||||
try:
|
||||
import StringIO as io
|
||||
except ImportError:
|
||||
import io
|
||||
|
||||
|
||||
class ConfigTestCase(testtools.TestCase):
|
||||
"""Class testing config behavior."""
|
||||
|
||||
@mock.patch('git_review.cmd.LOCAL_MODE',
|
||||
mock.PropertyMock(return_value=True))
|
||||
@mock.patch('git_review.cmd.git_directories', return_value=['', 'fake'])
|
||||
@mock.patch('git_review.cmd.run_command_exc')
|
||||
def test_git_local_mode(self, run_mock, dir_mock):
|
||||
cmd.git_config_get_value('abc', 'def')
|
||||
run_mock.assert_called_once_with(
|
||||
cmd.GitConfigException,
|
||||
'git', 'config', '-f', 'fake/config', '--get', 'abc.def')
|
||||
|
||||
@mock.patch('git_review.cmd.LOCAL_MODE',
|
||||
mock.PropertyMock(return_value=True))
|
||||
@mock.patch('os.path.exists', return_value=False)
|
||||
def test_gitreview_local_mode(self, exists_mock):
|
||||
cmd.Config()
|
||||
self.assertFalse(exists_mock.called)
|
||||
|
||||
|
||||
class GitReviewConsole(testtools.TestCase, fixtures.TestWithFixtures):
|
||||
"""Class for testing the console output of git-review."""
|
||||
|
||||
reviews = [
|
||||
{
|
||||
'number': '1010101',
|
||||
'branch': 'master',
|
||||
'subject': 'A simple short subject',
|
||||
'topic': 'simple-topic'
|
||||
}, {
|
||||
'number': '9877',
|
||||
'branch': 'stable/codeword',
|
||||
'subject': 'A longer and slightly more wordy subject'
|
||||
}, {
|
||||
'number': '12345',
|
||||
'branch': 'master',
|
||||
'subject': 'A ridiculously long subject that can exceed the '
|
||||
'normal console width, just need to ensure the '
|
||||
'max width is short enough'
|
||||
}]
|
||||
|
||||
def setUp(self):
|
||||
# will set up isolated env dir
|
||||
super(GitReviewConsole, self).setUp()
|
||||
|
||||
# ensure all tests get a separate git dir to work in to avoid
|
||||
# local git config from interfering
|
||||
iso_env = self.useFixture(IsoEnvDir())
|
||||
|
||||
self._run_git = functools.partial(utils.run_git,
|
||||
chdir=iso_env.work_dir)
|
||||
|
||||
self.run_cmd_patcher = mock.patch('git_review.cmd.run_command_status')
|
||||
run_cmd_partial = functools.partial(
|
||||
cmd.run_command_status, GIT_WORK_TREE=iso_env.work_dir,
|
||||
GIT_DIR=os.path.join(iso_env.work_dir, '.git'))
|
||||
self.run_cmd_mock = self.run_cmd_patcher.start()
|
||||
self.run_cmd_mock.side_effect = run_cmd_partial
|
||||
|
||||
self._run_git('init')
|
||||
self._run_git('commit', '--allow-empty', '-m "initial commit"')
|
||||
self._run_git('commit', '--allow-empty', '-m "2nd commit"')
|
||||
|
||||
def tearDown(self):
|
||||
self.run_cmd_patcher.stop()
|
||||
super(GitReviewConsole, self).tearDown()
|
||||
|
||||
@mock.patch('git_review.cmd.query_reviews')
|
||||
@mock.patch('git_review.cmd.get_remote_url', mock.MagicMock)
|
||||
@mock.patch('git_review.cmd._has_color', False)
|
||||
def test_list_reviews_output(self, mock_query):
|
||||
|
||||
mock_query.return_value = self.reviews
|
||||
with mock.patch('sys.stdout', new_callable=io.StringIO) as output:
|
||||
cmd.list_reviews(None)
|
||||
console_output = output.getvalue().split('\n')
|
||||
|
||||
self.assertEqual(
|
||||
['1010101 master A simple short subject',
|
||||
' 9877 stable/codeword A longer and slightly more wordy '
|
||||
'subject'],
|
||||
console_output[:2])
|
||||
|
||||
@mock.patch('git_review.cmd.query_reviews')
|
||||
@mock.patch('git_review.cmd.get_remote_url', mock.MagicMock)
|
||||
@mock.patch('git_review.cmd._has_color', False)
|
||||
def test_list_reviews_output_with_topic(self, mock_query):
|
||||
|
||||
mock_query.return_value = self.reviews
|
||||
with mock.patch('sys.stdout', new_callable=io.StringIO) as output:
|
||||
cmd.list_reviews(None, with_topic=True)
|
||||
console_output = output.getvalue().split('\n')
|
||||
|
||||
self.assertEqual(
|
||||
['1010101 master simple-topic A simple short subject',
|
||||
' 9877 stable/codeword - A longer and slightly '
|
||||
'more wordy subject'],
|
||||
console_output[:2])
|
||||
|
||||
@mock.patch('git_review.cmd.query_reviews')
|
||||
@mock.patch('git_review.cmd.get_remote_url', mock.MagicMock)
|
||||
@mock.patch('git_review.cmd._has_color', False)
|
||||
def test_list_reviews_no_blanks(self, mock_query):
|
||||
|
||||
mock_query.return_value = self.reviews
|
||||
with mock.patch('sys.stdout', new_callable=io.StringIO) as output:
|
||||
cmd.list_reviews(None)
|
||||
console_output = output.getvalue().split('\n')
|
||||
|
||||
wrapper = textwrap.TextWrapper(replace_whitespace=False,
|
||||
drop_whitespace=False)
|
||||
for text in console_output:
|
||||
for line in wrapper.wrap(text):
|
||||
self.assertEqual(line.isspace(), False,
|
||||
"Extra blank lines appearing between reviews"
|
||||
"in console output")
|
||||
|
||||
@mock.patch('git_review.cmd._use_color', None)
|
||||
def test_color_output_disabled(self):
|
||||
"""Test disabling of colour output color.ui defaults to enabled
|
||||
"""
|
||||
|
||||
# git versions < 1.8.4 default to 'color.ui' being false
|
||||
# so must be set to auto to correctly test
|
||||
self._run_git("config", "color.ui", "auto")
|
||||
|
||||
self._run_git("config", "color.review", "never")
|
||||
self.assertFalse(cmd.check_use_color_output(),
|
||||
"Failed to detect color output disabled")
|
||||
|
||||
@mock.patch('git_review.cmd._use_color', None)
|
||||
def test_color_output_forced(self):
|
||||
"""Test force enable of colour output when color.ui
|
||||
is defaulted to false
|
||||
"""
|
||||
|
||||
self._run_git("config", "color.ui", "never")
|
||||
|
||||
self._run_git("config", "color.review", "always")
|
||||
self.assertTrue(cmd.check_use_color_output(),
|
||||
"Failed to detect color output forcefully "
|
||||
"enabled")
|
||||
|
||||
@mock.patch('git_review.cmd._use_color', None)
|
||||
def test_color_output_fallback(self):
|
||||
"""Test fallback to using color.ui when color.review is not
|
||||
set
|
||||
"""
|
||||
|
||||
self._run_git("config", "color.ui", "always")
|
||||
self.assertTrue(cmd.check_use_color_output(),
|
||||
"Failed to use fallback to color.ui when "
|
||||
"color.review not present")
|
||||
|
||||
|
||||
class FakeResponse(object):
|
||||
|
||||
def __init__(self, code, text=""):
|
||||
self.status_code = code
|
||||
self.text = text
|
||||
|
||||
|
||||
class FakeException(Exception):
|
||||
|
||||
def __init__(self, code, *args, **kwargs):
|
||||
super(FakeException, self).__init__(*args, **kwargs)
|
||||
self.code = code
|
||||
|
||||
|
||||
FAKE_GIT_CREDENTIAL_FILL = """\
|
||||
protocol=http
|
||||
host=gerrit.example.com
|
||||
username=user
|
||||
password=pass
|
||||
"""
|
||||
|
||||
|
||||
class ResolveTrackingUnitTest(testtools.TestCase):
|
||||
"""Class for testing resolve_tracking."""
|
||||
def setUp(self):
|
||||
testtools.TestCase.setUp(self)
|
||||
patcher = mock.patch('git_review.cmd.run_command_exc')
|
||||
self.addCleanup(patcher.stop)
|
||||
self.run_command_exc = patcher.start()
|
||||
|
||||
def test_track_local_branch(self):
|
||||
'Test that local tracked branch is not followed.'
|
||||
self.run_command_exc.side_effect = [
|
||||
'',
|
||||
'refs/heads/other/branch',
|
||||
]
|
||||
self.assertEqual(cmd.resolve_tracking(u'remote', u'rbranch'),
|
||||
(u'remote', u'rbranch'))
|
||||
|
||||
def test_track_untracked_branch(self):
|
||||
'Test that local untracked branch is not followed.'
|
||||
self.run_command_exc.side_effect = [
|
||||
'',
|
||||
'',
|
||||
]
|
||||
self.assertEqual(cmd.resolve_tracking(u'remote', u'rbranch'),
|
||||
(u'remote', u'rbranch'))
|
||||
|
||||
def test_track_remote_branch(self):
|
||||
'Test that remote tracked branch is followed.'
|
||||
self.run_command_exc.side_effect = [
|
||||
'',
|
||||
'refs/remotes/other/branch',
|
||||
]
|
||||
self.assertEqual(cmd.resolve_tracking(u'remote', u'rbranch'),
|
||||
(u'other', u'branch'))
|
||||
|
||||
def test_track_git_error(self):
|
||||
'Test that local tracked branch is not followed.'
|
||||
self.run_command_exc.side_effect = [cmd.CommandFailed(1, '', [], {})]
|
||||
self.assertRaises(cmd.CommandFailed,
|
||||
cmd.resolve_tracking, u'remote', u'rbranch')
|
||||
|
||||
|
||||
class GitReviewUnitTest(testtools.TestCase):
|
||||
"""Class for misc unit tests."""
|
||||
|
||||
@mock.patch('requests.get', return_value=FakeResponse(404))
|
||||
def test_run_http_exc_raise_http_error(self, mock_get):
|
||||
url = 'http://gerrit.example.com'
|
||||
try:
|
||||
cmd.run_http_exc(FakeException, url)
|
||||
self.fails('Exception expected')
|
||||
except FakeException as err:
|
||||
self.assertEqual(cmd.http_code_2_return_code(404), err.code)
|
||||
mock_get.assert_called_once_with(url)
|
||||
|
||||
@mock.patch('requests.get', side_effect=Exception())
|
||||
def test_run_http_exc_raise_unknown_error(self, mock_get):
|
||||
url = 'http://gerrit.example.com'
|
||||
try:
|
||||
cmd.run_http_exc(FakeException, url)
|
||||
self.fails('Exception expected')
|
||||
except FakeException as err:
|
||||
self.assertEqual(255, err.code)
|
||||
mock_get.assert_called_once_with(url)
|
||||
|
||||
@mock.patch('git_review.cmd.run_command_status')
|
||||
@mock.patch('requests.get', return_value=FakeResponse(200))
|
||||
def test_run_http_exc_without_auth(self, mock_get, mock_run):
|
||||
url = 'http://user@gerrit.example.com'
|
||||
|
||||
cmd.run_http_exc(FakeException, url)
|
||||
self.assertFalse(mock_run.called)
|
||||
mock_get.assert_called_once_with(url)
|
||||
|
||||
@mock.patch('git_review.cmd.run_command_status',
|
||||
return_value=(0, FAKE_GIT_CREDENTIAL_FILL))
|
||||
@mock.patch('requests.get',
|
||||
side_effect=[FakeResponse(401), FakeResponse(200)])
|
||||
def test_run_http_exc_with_auth(self, mock_get, mock_run):
|
||||
url = 'http://user@gerrit.example.com'
|
||||
|
||||
cmd.run_http_exc(FakeException, url)
|
||||
mock_run.assert_called_once_with('git', 'credential', 'fill',
|
||||
stdin='url=%s' % url)
|
||||
calls = [mock.call(url), mock.call(url, auth=('user', 'pass'))]
|
||||
mock_get.assert_has_calls(calls)
|
||||
|
||||
@mock.patch('git_review.cmd.run_command_status',
|
||||
return_value=(0, FAKE_GIT_CREDENTIAL_FILL))
|
||||
@mock.patch('requests.get', return_value=FakeResponse(401))
|
||||
def test_run_http_exc_with_failing_auth(self, mock_get, mock_run):
|
||||
url = 'http://user@gerrit.example.com'
|
||||
|
||||
try:
|
||||
cmd.run_http_exc(FakeException, url)
|
||||
self.fails('Exception expected')
|
||||
except FakeException as err:
|
||||
self.assertEqual(cmd.http_code_2_return_code(401), err.code)
|
||||
mock_run.assert_called_once_with('git', 'credential', 'fill',
|
||||
stdin='url=%s' % url)
|
||||
calls = [mock.call(url), mock.call(url, auth=('user', 'pass'))]
|
||||
mock_get.assert_has_calls(calls)
|
||||
|
||||
@mock.patch('git_review.cmd.run_command_status',
|
||||
return_value=(1, ''))
|
||||
@mock.patch('requests.get', return_value=FakeResponse(401))
|
||||
def test_run_http_exc_with_failing_git_creds(self, mock_get, mock_run):
|
||||
url = 'http://user@gerrit.example.com'
|
||||
|
||||
try:
|
||||
cmd.run_http_exc(FakeException, url)
|
||||
self.fails('Exception expected')
|
||||
except FakeException as err:
|
||||
self.assertEqual(cmd.http_code_2_return_code(401), err.code)
|
||||
mock_run.assert_called_once_with('git', 'credential', 'fill',
|
||||
stdin='url=%s' % url)
|
||||
mock_get.assert_called_once_with(url)
|
||||
|
||||
@mock.patch('sys.argv', ['argv0', '--track', 'branch'])
|
||||
@mock.patch('git_review.cmd.check_remote')
|
||||
@mock.patch('git_review.cmd.resolve_tracking')
|
||||
def test_command_line_no_track(self, resolve_tracking, check_remote):
|
||||
check_remote.side_effect = Exception()
|
||||
self.assertRaises(Exception, cmd._main)
|
||||
self.assertFalse(resolve_tracking.called)
|
||||
|
||||
@mock.patch('sys.argv', ['argv0', '--track'])
|
||||
@mock.patch('git_review.cmd.check_remote')
|
||||
@mock.patch('git_review.cmd.resolve_tracking')
|
||||
def test_track(self, resolve_tracking, check_remote):
|
||||
check_remote.side_effect = Exception()
|
||||
self.assertRaises(Exception, cmd._main)
|
||||
self.assertTrue(resolve_tracking.called)
|
@@ -1,84 +0,0 @@
|
||||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
|
||||
def run_cmd(*args, **kwargs):
|
||||
"""Run command and check the return code."""
|
||||
preexec_fn = None
|
||||
|
||||
if 'chdir' in kwargs:
|
||||
def preexec_fn():
|
||||
return os.chdir(kwargs['chdir'])
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(args, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, env=os.environ,
|
||||
preexec_fn=preexec_fn)
|
||||
|
||||
if 'confirm' in kwargs and kwargs['confirm']:
|
||||
proc.stdin.write('yes'.encode())
|
||||
proc.stdin.flush()
|
||||
|
||||
out, err = proc.communicate()
|
||||
out = out.decode('utf-8')
|
||||
except Exception:
|
||||
raise Exception(
|
||||
"Exception while processing the command:\n%s.\n%s" %
|
||||
(' '.join(args), traceback.format_exc())
|
||||
)
|
||||
|
||||
if proc.returncode != 0:
|
||||
raise Exception(
|
||||
"Error occurred while processing the command:\n%s.\n"
|
||||
"Stdout: %s\nStderr: %s" %
|
||||
(' '.join(args), out.strip(), err)
|
||||
)
|
||||
|
||||
return out.strip()
|
||||
|
||||
|
||||
def run_git(command, *args, **kwargs):
|
||||
"""Run git command with the specified args."""
|
||||
return run_cmd("git", command, *args, **kwargs)
|
||||
|
||||
|
||||
def write_to_file(path, content):
|
||||
"""Create (if does not exist) and write to the file."""
|
||||
with open(path, 'wb') as file_:
|
||||
file_.write(content)
|
||||
|
||||
GERRIT_CONF_TMPL = """
|
||||
[gerrit]
|
||||
basePath = git
|
||||
canonicalWebUrl = http://nonexistent/
|
||||
[database]
|
||||
type = h2
|
||||
database = db/ReviewDB
|
||||
[auth]
|
||||
type = DEVELOPMENT_BECOME_ANY_ACCOUNT
|
||||
[sshd]
|
||||
listenAddress = %s:%s
|
||||
[httpd]
|
||||
listenUrl = http://%s:%s/
|
||||
"""
|
||||
|
||||
|
||||
def get_gerrit_conf(ssh_addr, ssh_port, http_addr, http_port):
|
||||
return GERRIT_CONF_TMPL % (ssh_addr, ssh_port, http_addr, http_port)
|
@@ -1 +0,0 @@
|
||||
requests>=1.1
|
42
setup.cfg
42
setup.cfg
@@ -1,42 +0,0 @@
|
||||
[metadata]
|
||||
name = git-review
|
||||
summary = Tool to submit code to Gerrit
|
||||
description-file = README.rst
|
||||
license = Apache License (2.0)
|
||||
classifiers =
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python
|
||||
Development Status :: 5 - Production/Stable
|
||||
Environment :: Console
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Developers
|
||||
Intended Audience :: Information Technology
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: OS Independent
|
||||
keywords = git gerrit review
|
||||
author = OpenStack
|
||||
author-email = openstack-infra@lists.openstack.org
|
||||
home-page = http://docs.openstack.org/infra/git-review/
|
||||
project-url = http://docs.openstack.org/infra/
|
||||
|
||||
[files]
|
||||
packages =
|
||||
git_review
|
||||
|
||||
[entry_points]
|
||||
console_scripts =
|
||||
git-review = git_review.cmd:main
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = doc/source
|
||||
build-dir = doc/build
|
||||
all_files = 1
|
||||
|
||||
[pbr]
|
||||
manpages =
|
||||
git-review.1
|
||||
warnerrors = True
|
20
setup.py
20
setup.py
@@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import setuptools
|
||||
|
||||
|
||||
setuptools.setup(setup_requires=['pbr'], pbr=True)
|
@@ -1,7 +0,0 @@
|
||||
hacking>=0.10.0,<0.11
|
||||
mock
|
||||
fixtures>=0.3.14
|
||||
testrepository>=0.0.18
|
||||
testtools>=0.9.34
|
||||
oslosphinx
|
||||
sphinx>=1.1.2,!=1.2.0,<1.3
|
33
tox.ini
33
tox.ini
@@ -1,33 +0,0 @@
|
||||
[tox]
|
||||
envlist = py26,py27,py32,py33,py34,pep8
|
||||
|
||||
[testenv]
|
||||
install_command = pip install -U {opts} {packages}
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
|
||||
commands =
|
||||
python -m git_review.tests.check_test_id_hashes discover --list
|
||||
python -m git_review.tests.prepare
|
||||
python setup.py testr --slowest --testr-args='--concurrency=2 {posargs}'
|
||||
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8
|
||||
|
||||
[testenv:sdist]
|
||||
commands = python setup.py sdist {posargs}
|
||||
|
||||
[testenv:docs]
|
||||
commands = python setup.py build_sphinx
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[flake8]
|
||||
ignore = E125,H202,H405,H904
|
||||
show-source = True
|
||||
exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build
|
Reference in New Issue
Block a user