# Copyright (C) 2009 Aaron Bentley
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA


"""Manage a series of branches as a pipeline.

Here is a summary of the commands added.

====================    =======================================================
Command                 Description
====================    =======================================================
reconfigure-pipeline    Reconfigure a tree with branch into a lightweight
                        checkout of a pipe.
add-pipe                Add a pipe to the pipeline
remove-pipe             Remove a pipe from the pipeline
switch-pipe             Switch from one pipe to another
show-pipeline           Show the current pipeline
pump                    From this pipe onward, merge all pipes into their next
                        pipe and commit
sync-pipeline           Synchronise the contents of this pipeline with another
                        copy
pipe-patches            Export the pipeline as a collection of patches,
                        one per pipe
lp-submit               Submit the specified pipe to Launchpad
====================    =======================================================

It also extends `merge --uncommitted` to work with pipes.

To get started, create a branch and lightweight checkout::

  bzr init pipe1
  bzr checkout --lightweight pipe1 tree
  cd tree
  commit -m "first commit"
  bzr add-pipe pipe2
  bzr show-pipeline

If you have an existing tree that you wish to start using as a pipeline,
use `reconfigure-pipeline` to convert it.

The switch-pipe command, in addition to the normal switch behavour, stores
any uncommitted changes to the source pipe, and restores any uncommitted
changes in the target pipe.  This supports a common need to switch between
several related tasks without committing.

The pump command merges and commits changes along the pipeline, starting
with the current pipe.  For example::

  bzr add-pipe next
  echo "hello" > new
  bzr add
  bzr commit -m "Added file new"
  bzr pump

This will commit a revision to the pipe named "next" that adds the file "new".

Each pipe is identified by its branch nick.  The location of each pipe is
provided as a location alias, which consists of ":pipe:" followed by its nick.
So, to switch to pipe named "my-new" using the standard switch command::

  bzr switch :pipe:my-new

The aliases :prev and :next refer to the pipe before and after the current
pipe, respectively.  So to see the changes added in this pipe::

  bzr diff -rancestor::prev

These aliases work virtually everywhere you can specify a branch.  Since pipes
are branches, you can do anything with them that you could do with a branch.

You might find the following command aliases useful::

  # Abbreviate switching to next pipe
  next = switch-pipe :next

  # Abbreviate switching to previous pipe
  prev = switch-pipe :prev

  # Show diff of changes originated in this pipe
  pdiff = diff -r ancestor::prev

  # Show status for changes originated in this pipe
  pstatus = status --short -r branch::prev

  # Submit the changes originated in this pipe for merging
  psend = send -r ancestor::prev..

  # Show commits which have not been pumped into the next pipe yet.
  unpumped = missing --mine :next

For more information on bzr-pipeline, see the home page:
http://bazaar-vcs.org/BzrPipeline.
"""


import bzrlib
from bzrlib import api
from bzrlib.commands import plugin_cmds
from bzrlib.directory_service import AliasDirectory, directories


supported_bzrlib_version = [(2, x, 0) for x in range(1, 2)]
api.require_any_api(bzrlib, supported_bzrlib_version)


def register(name, aliases=None):
    if aliases is None:
        aliases = []
    plugin_cmds.register_lazy(name, aliases,
                              'bzrlib.plugins.pipeline.commands')

register('cmd_add_pipe')
register('cmd_remove_pipe')
register('cmd_switch_pipe', ['swp'])
register('cmd_show_pipeline', ['pipes'])
register('cmd_pipe_patches')
register('cmd_pump')
register('cmd_merge')
register('cmd_reconfigure_pipeline')
register('cmd_sync_pipeline')
register('cmd_lp_submit')
plugin_cmds.register_lazy('cmd_import_loom', [],
                          'bzrlib.plugins.pipeline.loom')


def is_pipe_alias(name):
    if name in ('first', 'last', 'next', 'prev'):
        return True
    if name.startswith('pipe:'):
        return True
    else:
        return False


class PipeAliasDirectory(AliasDirectory):

    def look_up(self, name, url):
        if not is_pipe_alias(name):
            return AliasDirectory.look_up(self, name, url)
        from bzrlib.plugins.pipeline import pipeline
        return pipeline.look_up(self, name, url)

directories.remove(':')
directories.register(':', PipeAliasDirectory,
                     'Easy access to remembered branch locations')


def test_suite():
    from bzrlib.tests.TestUtil import TestLoader, TestSuite
    loader = TestLoader()
    from bzrlib.plugins.pipeline.tests import test_pipeline, test_blackbox
    result = TestSuite()
    result.addTest(loader.loadTestsFromModule(test_pipeline))
    result.addTest(loader.loadTestsFromModule(test_blackbox))
    return result
