checkoutmanager 4.0.1
Gives you overview and control over your git/hg/svn checkouts/clones
Requires Python: >=3.10

Checkoutmanager
===============

Makes bzr/hg/git/svn checkouts in several places according to a
``.checkoutmanager.cfg`` config file (in your home directory).

The advantage: you've got one command with which you can update all your
checkouts.  And with which you can ask for a list of uncommitted changes.  And
you can rebuild your entire checkout structure on a new machine just by
copying the config file (this was actually the purpose I build it for: I had
to change laptops when I switched jobs...).

Checkoutmanager works on linux, osx and windows.


Starting to use it
------------------

Starting is easy. Just ``pip install checkoutmanager`` (or ``uv tool install
checkoutmanager``) and run ``checkoutmanager``.

- The first time, you'll get a sample configuration you can copy to
  ``.checkoutmanager.cfg`` in your home directory.

- The second time, you'll get a usage message.  (You'll want to do
  ``checkoutmanager co`` to grab your initial checkouts).


Generic usage
-------------

What I normally do every morning when I get to work is ``checkoutmanager
up``.  This grabs the latest versions of all my checkouts from the server(s).
So an ``svn up`` for my subversion checkouts, a ``hg pull -u`` for mercurial
and so on.

From time to time, I'll do a ``checkoutmanager st`` to show if I've got some
uncommitted files lying around somewhere.  Very handy if you've worked in
several directories throughout the day: it prevents you from forgetting to
check in that one bugfix for a whole week.

A new project means I add a single line to my config file and run
``checkoutmanager co``.

Checkoutmanager allows you to spread your checkouts over multiple
directories.  It cannot mix version control systems per directory, however.
As an example, I've got a ``~/buildout/`` directory with my big svn website
projects checked out there.  And a directory with my svn work python
libraries.  And a ``~/hg/`` dir with my mercurial projects.  And I've made
checkouts of several config directories in my home dir, such as
``~/.emacs.d``, ``~/.subversion`` and so on.  Works just fine.


Commands
--------

Available commands:

exists
  Print whether checkouts are present or missing

up
  Grab latest version from the server.

st
  Print status of files in the checkouts

co
  Grab missing checkouts from the server

missing
  Print directories that are missing from the config file

out
  Show changesets you haven't pushed to the server yet

in
  Show incoming changesets that would be pulled in with 'up'. For some
  version control systems, this depends on the English output of the
  respective commands and is therefore inherently fragile.


Output directory naming
-----------------------

If you don't specify an output directory name for your checkout url, it just
takes the last part.  To make life easier, we do have some adjustments we
make:

- ``https://xxx/yyy/product/trunk`` becomes ``product`` instead of
  ``trunk``. (Handy for subversion).

- ``https://xxx/yyy/product/branches/experiment`` becomes
  ``product_experiment`` instead of ``experiment`` (Handy for subversion).

- ``https://xxx/customername/buildout/trunk`` becomes ``customername``
  instead of "trunk" or "buildout". (Old convention we still support).

- Bzr checkouts that start with "lp:" (special launchpad urls) get their "lp:"
  stripped.

- Git checkouts lose the ".git" at the end of the url.

- If you want to preserve the directory configuration of your version control
  system, add the ``preserve_tree`` option to a group. It should contain one
  or more base checkout urls (one per line). If the checkout url starts with
  one of the ``preserve_tree`` urls, the folder structure after it is
  preserved.

  With a ``preserve_tree`` of ``https://github.com``,
  ``https://github.com/reinout/checkoutmanager`` becomes
  ``reinout/checkoutmanager`` instead of ``checkoutmanager``. Also handy for
  subversion, which often has nested directories.

  If the ``preserve_tree`` base url isn't found, the standard rules are used,
  so you won't get an error.

If you want different behaviour from the defaults above, just specify a
directory name (separated by a space) in the configuration file after the
url. So ``https://github.com/reinout/checkoutmanager bla_bla`` becomes
``bla_bla`` instead of ``checkoutmanager``


Custom commands
---------------

You can write your own custom commands. To do that you need to create a Python
package and register an entry point in your ``setup.py`` for the
``checkoutmanager.custom_actions`` target.

A ``test`` command is included with ``checkoutmanager`` and can serve as an
example. It is registered like this in checkoutmanager's own ``setup.py``:

.. code:: python

   entry_points={
       'checkoutmanager.custom_actions': [
           'test = checkoutmanager.tests.custom_actions:test_action'
        ]
   }

The entry point function must take one positional argument which will be the
``checkoutmanager.dirinfo.DirInfo`` instance associated with the directroy
for which the command is being executed. The function can also take optional
keyword arguments. See ``checkoutmanager.tests.custom_actions.test_action`` for
an example.

Arguments are passed to the custom command using the following syntax:

.. code:: bash

   checkoutmanager action:arg1=val1,arg2=val2


Config file
-----------

.. Comment: copy/paste the sample config file from src/checkoutmanager/sample.cfg!

Sample configuration file::

    # Sample config file.  Should be placed as .checkoutmanager.cfg in your home
    # directory.
    #
    # There are different sections per base location and version control
    # system.
    #
    # ``checkoutmanager co`` checks them all out (or clones them).
    # ``checkoutmanager up`` updates them all.
    # ``checkoutmanager st`` to see if there are uncommitted changes.
    # ``checkoutmanager out`` to see if there are unpushed git/hg commits.


    [git-example]
    vcs = git
    basedir = ~/example/git
    checkouts =
        https://github.com/reinout/checkoutmanager
        git@github.com:django/django.git


    [recipes]
    # Buildout recipes I work on.
    vcs = svn
    basedir = ~/example/svn
    checkouts =
        http://svn.zope.org/repos/main/z3c.recipe.usercrontab/trunk


    [hg-example]
    vcs = hg
    basedir = ~/example/utilities
    checkouts =
        https://bitbucket.org/reinout/eolfixer
        https://bitbucket.org/reinout/createcoverage


    # [dotfolders]
    # # Advanced usage!
    # # Folders that end up as dotted configfolders in my home dir.
    # # Note that there's a directory name behind the repository
    # # location, separated by a space.
    # vcs = bzr
    # basedir = ~
    # checkouts =
    #     lp:emacsconfig/trunk .emacs.d
    #     sftp://somewhere/subversion/trunk .subversion
    # # By ignoring everything, we do not find missing import files but also
    # # don't get warnings for every subdirectory in our home dir
    # ignore =
    #     *
    #     .*



Developing on this project itself
---------------------------------

Developing is pretty straightforward::

  $ uv sync               # Install the project
  $ pre-commit run --all  # Syntax checks and formatting
  $ uv run pytest         # (Or activate the virtualenv and just run pytest)