Repository: pypa/pip

Issue:
Rework pip command line look & feel
This is more of a _request for comments_ with code attached, rather than an actual pull request (I have my doubts about it too).

This patch changes the look of the command line interface from [this](https://raw.github.com/gist/2701636/f5757e6fdd6a056483516b832384a95ac8c6597a/old%20pip%20help) to [this](https://raw.github.com/gist/2701636/7581f57849fcd29241f8a4ca6a53018b1127ccb4/new%20pip%20help), assuming a terminal width of 100 characters. It's much more compact and somewhat more consistent.

This patch also introduces a distinction between long and short help messages. If you pass a tuple as the help argument to `add_option`, the first element is what the help formatter will use. The second argument could be a more detailed paragraph of text that can be used in a man page or some other kind of reference.

This patch is based on top of #463.



Discussion:

Resolving patch:
diff --git a/pip/vcs/git.py b/pip/vcs/git.py
--- a/pip/vcs/git.py
+++ b/pip/vcs/git.py
@@ -1,5 +1,6 @@
 import tempfile
 import re
+import os.path
 from pip.util import call_subprocess
 from pip.util import display_path, rmtree
 from pip.vcs import vcs, VersionControl
@@ -28,8 +29,8 @@ def __init__(self, url=None, *args, **kwargs):
                 initial_slashes = path[:-len(path.lstrip('/'))]
                 newpath = initial_slashes + url2pathname(path).replace('\\', '/').lstrip('/')
                 url = urlunsplit((scheme, netloc, newpath, query, fragment))
-                after_plus = scheme.find('+')+1
-                url = scheme[:after_plus]+ urlunsplit((scheme[after_plus:], netloc, newpath, query, fragment))
+                after_plus = scheme.find('+') + 1
+                url = scheme[:after_plus] + urlunsplit((scheme[after_plus:], netloc, newpath, query, fragment))
 
         super(Git, self).__init__(url, *args, **kwargs)
 
@@ -86,6 +87,8 @@ def switch(self, dest, url, rev_options):
         call_subprocess(
             [self.cmd, 'checkout', '-q'] + rev_options, cwd=dest)
 
+        self.update_submodules(dest)
+
     def update(self, dest, rev_options):
         # First fetch changes from the default remote
         call_subprocess([self.cmd, 'fetch', '-q'], cwd=dest)
@@ -93,6 +96,8 @@ def update(self, dest, rev_options):
         if rev_options:
             rev_options = self.check_rev_options(rev_options[0], dest, rev_options)
         call_subprocess([self.cmd, 'reset', '--hard', '-q'] + rev_options, cwd=dest)
+        #: update submodules
+        self.update_submodules(dest)
 
     def obtain(self, dest):
         url, rev = self.get_url_rev()
@@ -105,6 +110,8 @@ def obtain(self, dest):
         if self.check_destination(dest, url, rev_options, rev_display):
             logger.notify('Cloning %s%s to %s' % (url, rev_display, display_path(dest)))
             call_subprocess([self.cmd, 'clone', '-q', url, dest])
+            #: repo may contain submodules
+            self.update_submodules(dest)
             if rev:
                 rev_options = self.check_rev_options(rev, dest, rev_options)
                 # Only do a checkout if rev_options differs from HEAD
@@ -161,8 +168,10 @@ def get_src_requirement(self, dist, location, find_tags):
         elif (current_rev in branch_revs and
               branch_revs[current_rev] != 'origin/master'):
             # It's the head of a branch
-            full_egg_name = '%s-%s' % (egg_project_name,
-                                       branch_revs[current_rev].replace('origin/', ''))
+            full_egg_name = '%s-%s' % (
+                egg_project_name,
+                branch_revs[current_rev].replace('origin/', '')
+            )
         else:
             full_egg_name = '%s-dev' % egg_project_name
 
@@ -202,5 +211,11 @@ def _get_revision_from_rev_parse(self, name, location):
         return call_subprocess([self.cmd, 'rev-parse', name],
                                show_stdout=False, cwd=location)
 
+    def update_submodules(self, location):
+        if not os.path.exists(os.path.join(location, '.gitmodules')):
+            return
+        call_subprocess([self.cmd, 'submodule', 'init', '-q'], cwd=location)
+        call_subprocess([self.cmd, 'submodule', 'update', '--recursive', '-q'],
+                        cwd=location)
 
 vcs.register(Git)


Test patch:
diff --git a/tests/git_submodule_helpers.py b/tests/git_submodule_helpers.py
new file mode 100644
--- /dev/null
+++ b/tests/git_submodule_helpers.py
@@ -0,0 +1,62 @@
+import textwrap
+from tests.test_pip import (mkdir, write_file,)
+
+def _create_test_package_submodule(env):
+    mkdir('version_pkg_submodule')
+    submodule_path = env.scratch_path/'version_pkg_submodule'
+    env.run('touch', 'testfile', cwd=submodule_path)
+    env.run('git', 'init', cwd=submodule_path)
+    env.run('git', 'add', '.', cwd=submodule_path)
+    env.run('git', 'commit', '-q',
+            '--author', 'Pip <python-virtualenv@googlegroups.com>',
+            '-am', 'initial version / submodule', cwd=submodule_path)
+    return submodule_path
+
+def _change_test_package_submodule(env, submodule_path):
+    write_file(submodule_path/'testfile', 'this is a changed file')
+    write_file(submodule_path/'testfile2', 'this is an added file')
+    env.run('git', 'add', '.', cwd=submodule_path)
+    env.run('git', 'commit', '-q',
+            '--author', 'Pip <python-virtualenv@googlegroups.com>',
+            '-am', 'submodule change', cwd=submodule_path)
+
+def _pull_in_submodule_changes_to_module(env, module_path):
+    env.run(cwd=module_path/'testpkg/static/', *('git pull -q origin master'.split(' ')))
+    env.run('git', 'commit', '-q',
+            '--author', 'Pip <python-virtualenv@googlegroups.com>',
+            '-am', 'submodule change', cwd=module_path)
+
+def _create_test_package_with_submodule(env):
+    mkdir('version_pkg')
+    version_pkg_path = env.scratch_path/'version_pkg'
+    mkdir(version_pkg_path/'testpkg')
+    pkg_path = version_pkg_path/'testpkg'
+
+    write_file('__init__.py', '# hello there', pkg_path)
+    write_file('version_pkg.py', textwrap.dedent('''\
+                                def main():
+                                    print('0.1')
+                                '''), pkg_path)
+    write_file('setup.py', textwrap.dedent('''\
+                        from setuptools import setup, find_packages
+                        setup(name='version_pkg',
+                              version='0.1',
+                              packages=find_packages(),
+                             )
+                        '''), version_pkg_path)
+    env.run('git', 'init', cwd=version_pkg_path)
+    env.run('git', 'add', '.', cwd=version_pkg_path)
+    env.run('git', 'commit', '-q',
+            '--author', 'Pip <python-virtualenv@googlegroups.com>',
+            '-am', 'initial version', cwd=version_pkg_path)
+
+
+    submodule_path = _create_test_package_submodule(env)
+
+    env.run('git', 'submodule', 'add', submodule_path, 'testpkg/static', cwd=version_pkg_path)
+    env.run('git', 'commit', '-q',
+            '--author', 'Pip <python-virtualenv@googlegroups.com>',
+            '-am', 'initial version w submodule', cwd=version_pkg_path)
+
+
+    return version_pkg_path, submodule_path
diff --git a/tests/test_vcs_git.py b/tests/test_vcs_git.py
--- a/tests/test_vcs_git.py
+++ b/tests/test_vcs_git.py
@@ -1,7 +1,8 @@
 from mock import patch
 from pip.vcs.git import Git
 from tests.test_pip import (reset_env, run_pip,
-                            _create_test_package)
+                            _create_test_package,)
+from tests.git_submodule_helpers import _create_test_package_submodule, _change_test_package_submodule, _pull_in_submodule_changes_to_module, _create_test_package_with_submodule
 
 
 def test_get_tag_revs_should_return_tag_name_and_commit_pair():
@@ -75,3 +76,26 @@ def test_check_rev_options_should_handle_ambiguous_commit(branches_revs_mock,
 
     result = git.check_rev_options('0.1', '.', [])
     assert result == ['123456'], result
+
+
+
+
+
+
+def test_check_submodule_addition():
+    """
+        Test if submodules will be pulled in on install, and updated on upgrade
+    """
+    env = reset_env()
+    module_path, submodule_path = _create_test_package_with_submodule(env)
+
+    install_result = run_pip('install', '-e', 'git+'+module_path+'#egg=version_pkg')
+    assert '.virtualenv/src/version-pkg/testpkg/static/testfile' in install_result.files_created
+
+    _change_test_package_submodule(env, submodule_path)
+    _pull_in_submodule_changes_to_module(env, module_path)
+
+    update_result = run_pip('install', '-e', 'git+'+module_path+'#egg=version_pkg', '--upgrade', expect_error=True) # expect error because git writes to stdout in some weird cases
+
+    assert env.venv/'src/version-pkg/testpkg/static/testfile2' in update_result.files_created
+