浏览代码

Improved documentation.

Jonas Borgström 12 年之前
父节点
当前提交
f827b99ffe
共有 11 个文件被更改,包括 260 次插入58 次删除
  1. 0 0
      LICENSE
  2. 19 14
      README.rst
  3. 64 37
      darc/archiver.py
  4. 2 1
      docs/Makefile
  5. 11 0
      docs/detailedusage.rst
  6. 2 1
      docs/generalusage.rst
  7. 3 2
      docs/index.rst
  8. 2 2
      docs/terminology.rst
  9. 6 0
      docs/update_usage.sh
  10. 147 0
      docs/usage.rst
  11. 4 1
      setup.py

+ 0 - 0
COPYING → LICENSE


+ 19 - 14
README → README.rst

@@ -1,14 +1,19 @@
-.. -*- restructuredtext -*-
-
-
 What is darc?
 What is darc?
-=============
+-------------
 Darc is a Deduplicating ARChiver written in Python. The main goal of darc is
 Darc is a Deduplicating ARChiver written in Python. The main goal of darc is
 to provide an efficient and secure way to backup data. The data deduplication
 to provide an efficient and secure way to backup data. The data deduplication
-technique used makes darc suitable for taking daily backups.
+technique used makes darc suitable for daily backups since only actual changes
+are stored.
+
+Easy to use
+~~~~~~~~~~~
+Initialze backup repository and create a backup archive::
+
+    $ darc init /usbdrive/my-backup.darc
+    $ darc create -v /usbdrive/my-backup.darc::documents ~/Documents
 
 
 Main features
 Main features
--------------
+~~~~~~~~~~~~~
 Space efficient storage
 Space efficient storage
   Variable block size deduplication is used to reduce the number of bytes 
   Variable block size deduplication is used to reduce the number of bytes 
   stored by detecting redundant data. Each file is split into a number of
   stored by detecting redundant data. Each file is split into a number of
@@ -24,28 +29,28 @@ Off-site backups
     darc is installed.
     darc is installed.
 
 
 What do I need?
 What do I need?
-===============
+---------------
 Darc requires Python 3.2 or above to work. Besides Python darc also requires 
 Darc requires Python 3.2 or above to work. Besides Python darc also requires 
 msgpack-python and sufficiently recent OpenSSL (>= 1.0.0).
 msgpack-python and sufficiently recent OpenSSL (>= 1.0.0).
 
 
 How do I install it?
 How do I install it?
-====================
+--------------------
 ::
 ::
   $ python setup.py install
   $ python setup.py install
 
 
 Where are the docs?
 Where are the docs?
-===================
-Go to http://pythonhosted.org/darc/ for a prebuilt version of the docs. You
+-------------------
+Go to https://pythonhosted.org/darc/ for a prebuilt version of the docs. You
 can also build them yourself form the docs folder.
 can also build them yourself form the docs folder.
 
 
 Where are the tests?
 Where are the tests?
-====================
+--------------------
 The tests are in the darc/testsuite package. To run the test suite use the
 The tests are in the darc/testsuite package. To run the test suite use the
 following command::
 following command::
 
 
   $ python -m darc.testsuite.run
   $ python -m darc.testsuite.run
 
 
 Where can I get help?
 Where can I get help?
-=====================
-
-Send wishes, comments, patches, etc. to jonas@borgstrom.se
+---------------------
+Send questions, comments, patches, etc. to jonas@borgstrom.se. Issues and
+pull requests can also be created on https://github.com/jborg/darc

+ 64 - 37
darc/archiver.py

@@ -46,6 +46,8 @@ class Archiver:
         return RepositoryServer().serve()
         return RepositoryServer().serve()
 
 
     def do_init(self, args):
     def do_init(self, args):
+        """Initialize a new repository
+        """
         print('Initializing repository at "%s"' % args.repository.orig)
         print('Initializing repository at "%s"' % args.repository.orig)
         repository = self.open_repository(args.repository, create=True)
         repository = self.open_repository(args.repository, create=True)
         key = key_creator(repository, args)
         key = key_creator(repository, args)
@@ -57,12 +59,16 @@ class Archiver:
         return self.exit_code
         return self.exit_code
 
 
     def do_change_passphrase(self, args):
     def do_change_passphrase(self, args):
+        """Change passphrase on repository key file
+        """
         repository = self.open_repository(Location(args.repository))
         repository = self.open_repository(Location(args.repository))
         manifest, key = Manifest.load(repository)
         manifest, key = Manifest.load(repository)
         key.change_passphrase()
         key.change_passphrase()
         return self.exit_code
         return self.exit_code
 
 
     def do_create(self, args):
     def do_create(self, args):
+        """Create new archive
+        """
         t0 = datetime.now()
         t0 = datetime.now()
         repository = self.open_repository(args.archive)
         repository = self.open_repository(args.archive)
         manifest, key = Manifest.load(repository)
         manifest, key = Manifest.load(repository)
@@ -150,6 +156,8 @@ class Archiver:
             self.print_error('Unknown file type: %s', path)
             self.print_error('Unknown file type: %s', path)
 
 
     def do_extract(self, args):
     def do_extract(self, args):
+        """Extract archive contents
+        """
         repository = self.open_repository(args.archive)
         repository = self.open_repository(args.archive)
         manifest, key = Manifest.load(repository)
         manifest, key = Manifest.load(repository)
         archive = Archive(repository, key, manifest, args.archive.archive,
         archive = Archive(repository, key, manifest, args.archive.archive,
@@ -174,6 +182,8 @@ class Archiver:
         return self.exit_code
         return self.exit_code
 
 
     def do_delete(self, args):
     def do_delete(self, args):
+        """Delete archive
+        """
         repository = self.open_repository(args.archive)
         repository = self.open_repository(args.archive)
         manifest, key = Manifest.load(repository)
         manifest, key = Manifest.load(repository)
         cache = Cache(repository, key, manifest)
         cache = Cache(repository, key, manifest)
@@ -182,6 +192,8 @@ class Archiver:
         return self.exit_code
         return self.exit_code
 
 
     def do_list(self, args):
     def do_list(self, args):
+        """List archive or repository contents
+        """
         repository = self.open_repository(args.src)
         repository = self.open_repository(args.src)
         manifest, key = Manifest.load(repository)
         manifest, key = Manifest.load(repository)
         if args.src.archive:
         if args.src.archive:
@@ -214,6 +226,8 @@ class Archiver:
         return self.exit_code
         return self.exit_code
 
 
     def do_verify(self, args):
     def do_verify(self, args):
+        """Verify archive consistency
+        """
         repository = self.open_repository(args.archive)
         repository = self.open_repository(args.archive)
         manifest, key = Manifest.load(repository)
         manifest, key = Manifest.load(repository)
         archive = Archive(repository, key, manifest, args.archive.archive)
         archive = Archive(repository, key, manifest, args.archive.archive)
@@ -234,6 +248,8 @@ class Archiver:
         return self.exit_code
         return self.exit_code
 
 
     def do_info(self, args):
     def do_info(self, args):
+        """Show archive details such as disk space used
+        """
         repository = self.open_repository(args.archive)
         repository = self.open_repository(args.archive)
         manifest, key = Manifest.load(repository)
         manifest, key = Manifest.load(repository)
         cache = Cache(repository, key, manifest)
         cache = Cache(repository, key, manifest)
@@ -249,6 +265,8 @@ class Archiver:
         return self.exit_code
         return self.exit_code
 
 
     def do_prune(self, args):
     def do_prune(self, args):
+        """Prune repository archives according to specified rules
+        """
         repository = self.open_repository(args.repository)
         repository = self.open_repository(args.repository)
         manifest, key = Manifest.load(repository)
         manifest, key = Manifest.load(repository)
         cache = Cache(repository, key, manifest)
         cache = Cache(repository, key, manifest)
@@ -294,7 +312,7 @@ class Archiver:
         common_parser = argparse.ArgumentParser(add_help=False)
         common_parser = argparse.ArgumentParser(add_help=False)
         common_parser.add_argument('-v', '--verbose', dest='verbose', action='store_true',
         common_parser.add_argument('-v', '--verbose', dest='verbose', action='store_true',
                             default=False,
                             default=False,
-                            help='Verbose output')
+                            help='verbose output')
 
 
         parser = argparse.ArgumentParser(description='Darc - Deduplicating Archiver')
         parser = argparse.ArgumentParser(description='Darc - Deduplicating Archiver')
         subparsers = parser.add_subparsers(title='Available subcommands')
         subparsers = parser.add_subparsers(title='Available subcommands')
@@ -302,104 +320,113 @@ class Archiver:
         subparser = subparsers.add_parser('serve', parents=[common_parser])
         subparser = subparsers.add_parser('serve', parents=[common_parser])
         subparser.set_defaults(func=self.do_serve)
         subparser.set_defaults(func=self.do_serve)
 
 
-        subparser = subparsers.add_parser('init', parents=[common_parser])
+        subparser = subparsers.add_parser('init', parents=[common_parser],
+                                          description=self.do_init.__doc__)
         subparser.set_defaults(func=self.do_init)
         subparser.set_defaults(func=self.do_init)
         subparser.add_argument('repository',
         subparser.add_argument('repository',
                                type=location_validator(archive=False),
                                type=location_validator(archive=False),
-                               help='Repository to create')
+                               help='repository to create')
         subparser.add_argument('--key-file', dest='keyfile',
         subparser.add_argument('--key-file', dest='keyfile',
                                action='store_true', default=False,
                                action='store_true', default=False,
-                               help='Encrypt data using key file')
+                               help='enable key file based encryption')
         subparser.add_argument('--passphrase', dest='passphrase',
         subparser.add_argument('--passphrase', dest='passphrase',
                                action='store_true', default=False,
                                action='store_true', default=False,
-                               help='Encrypt data using passphrase derived keys')
+                               help='enable passphrase based encryption')
 
 
-        subparser = subparsers.add_parser('change-passphrase', parents=[common_parser])
+        subparser = subparsers.add_parser('change-passphrase', parents=[common_parser],
+                                          description=self.do_change_passphrase.__doc__)
         subparser.set_defaults(func=self.do_change_passphrase)
         subparser.set_defaults(func=self.do_change_passphrase)
         subparser.add_argument('repository', type=location_validator(archive=False))
         subparser.add_argument('repository', type=location_validator(archive=False))
 
 
-        subparser = subparsers.add_parser('create', parents=[common_parser])
+        subparser = subparsers.add_parser('create', parents=[common_parser],
+                                          description=self.do_create.__doc__)
         subparser.set_defaults(func=self.do_create)
         subparser.set_defaults(func=self.do_create)
         subparser.add_argument('-s', '--stats', dest='stats',
         subparser.add_argument('-s', '--stats', dest='stats',
                                action='store_true', default=False,
                                action='store_true', default=False,
-                               help='Print statistics for the created archive')
+                               help='print statistics for the created archive')
         subparser.add_argument('-e', '--exclude', dest='excludes',
         subparser.add_argument('-e', '--exclude', dest='excludes',
                                type=ExcludePattern, action='append',
                                type=ExcludePattern, action='append',
-                               help='Exclude condition')
+                               metavar="PATTERN", help='exclude paths matching PATTERN')
         subparser.add_argument('-c', '--checkpoint-interval', dest='checkpoint_interval',
         subparser.add_argument('-c', '--checkpoint-interval', dest='checkpoint_interval',
                                type=int, default=300, metavar='SECONDS',
                                type=int, default=300, metavar='SECONDS',
-                               help='Write checkpointe ever SECONDS seconds (Default: 300)')
+                               help='write checkpointe ever SECONDS seconds (Default: 300)')
         subparser.add_argument('--do-not-cross-mountpoints', dest='dontcross',
         subparser.add_argument('--do-not-cross-mountpoints', dest='dontcross',
                                action='store_true', default=False,
                                action='store_true', default=False,
-                               help='Do not cross mount points')
+                               help='do not cross mount points')
         subparser.add_argument('--numeric-owner', dest='numeric_owner',
         subparser.add_argument('--numeric-owner', dest='numeric_owner',
                                action='store_true', default=False,
                                action='store_true', default=False,
-                               help='Only store numeric user and group identifiers')
+                               help='only store numeric user and group identifiers')
         subparser.add_argument('archive', metavar='ARCHIVE',
         subparser.add_argument('archive', metavar='ARCHIVE',
                                type=location_validator(archive=True),
                                type=location_validator(archive=True),
-                               help='Archive to create')
+                               help='archive to create')
         subparser.add_argument('paths', metavar='PATH', nargs='+', type=str,
         subparser.add_argument('paths', metavar='PATH', nargs='+', type=str,
-                               help='Paths to archive')
+                               help='paths to archive')
 
 
-        subparser = subparsers.add_parser('extract', parents=[common_parser])
+        subparser = subparsers.add_parser('extract', parents=[common_parser],
+                                          description=self.do_extract.__doc__)
         subparser.set_defaults(func=self.do_extract)
         subparser.set_defaults(func=self.do_extract)
         subparser.add_argument('-e', '--exclude', dest='excludes',
         subparser.add_argument('-e', '--exclude', dest='excludes',
                                type=ExcludePattern, action='append',
                                type=ExcludePattern, action='append',
-                               help='Exclude condition')
+                               metavar="PATTERN", help='exclude paths matching PATTERN')
         subparser.add_argument('--numeric-owner', dest='numeric_owner',
         subparser.add_argument('--numeric-owner', dest='numeric_owner',
                                action='store_true', default=False,
                                action='store_true', default=False,
-                               help='Only obey numeric user and group identifiers')
+                               help='only obey numeric user and group identifiers')
         subparser.add_argument('archive', metavar='ARCHIVE',
         subparser.add_argument('archive', metavar='ARCHIVE',
                                type=location_validator(archive=True),
                                type=location_validator(archive=True),
-                               help='Archive to extract')
+                               help='archive to extract')
         subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
         subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
-                               help='Paths to extract')
+                               help='paths to extract')
 
 
-        subparser = subparsers.add_parser('delete', parents=[common_parser])
+        subparser = subparsers.add_parser('delete', parents=[common_parser],
+                                          description=self.do_delete.__doc__)
         subparser.set_defaults(func=self.do_delete)
         subparser.set_defaults(func=self.do_delete)
         subparser.add_argument('archive', metavar='ARCHIVE',
         subparser.add_argument('archive', metavar='ARCHIVE',
                                type=location_validator(archive=True),
                                type=location_validator(archive=True),
-                               help='Archive to delete')
+                               help='archive to delete')
 
 
-        subparser = subparsers.add_parser('list', parents=[common_parser])
+        subparser = subparsers.add_parser('list', parents=[common_parser],
+                                          description=self.do_list.__doc__)
         subparser.set_defaults(func=self.do_list)
         subparser.set_defaults(func=self.do_list)
         subparser.add_argument('src', metavar='SRC', type=location_validator(),
         subparser.add_argument('src', metavar='SRC', type=location_validator(),
-                               help='Repository/Archive to list contents of')
+                               help='repository/Archive to list contents of')
 
 
-        subparser = subparsers.add_parser('verify', parents=[common_parser])
+        subparser = subparsers.add_parser('verify', parents=[common_parser],
+                                          description=self.do_verify.__doc__)
         subparser.set_defaults(func=self.do_verify)
         subparser.set_defaults(func=self.do_verify)
         subparser.add_argument('-e', '--exclude', dest='excludes',
         subparser.add_argument('-e', '--exclude', dest='excludes',
                                type=ExcludePattern, action='append',
                                type=ExcludePattern, action='append',
-                               help='Include condition')
+                               metavar="PATTERN", help='exclude paths matching PATTERN')
         subparser.add_argument('archive', metavar='ARCHIVE',
         subparser.add_argument('archive', metavar='ARCHIVE',
                                type=location_validator(archive=True),
                                type=location_validator(archive=True),
-                               help='Archive to verity integrity of')
+                               help='archive to verity integrity of')
         subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
         subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
-                               help='Paths to verify')
+                               help='paths to verify')
 
 
-        subparser = subparsers.add_parser('info', parents=[common_parser])
+        subparser = subparsers.add_parser('info', parents=[common_parser],
+                                          description=self.do_info.__doc__)
         subparser.set_defaults(func=self.do_info)
         subparser.set_defaults(func=self.do_info)
         subparser.add_argument('archive', metavar='ARCHIVE',
         subparser.add_argument('archive', metavar='ARCHIVE',
                                type=location_validator(archive=True),
                                type=location_validator(archive=True),
-                               help='Archive to display information about')
+                               help='archive to display information about')
 
 
-        subparser = subparsers.add_parser('prune', parents=[common_parser])
+        subparser = subparsers.add_parser('prune', parents=[common_parser],
+                                          description=self.do_prune.__doc__)
         subparser.set_defaults(func=self.do_prune)
         subparser.set_defaults(func=self.do_prune)
         subparser.add_argument('-H', '--hourly', dest='hourly', type=int, default=0,
         subparser.add_argument('-H', '--hourly', dest='hourly', type=int, default=0,
-                               help='Number of hourly archives to keep')
+                               help='number of hourly archives to keep')
         subparser.add_argument('-d', '--daily', dest='daily', type=int, default=0,
         subparser.add_argument('-d', '--daily', dest='daily', type=int, default=0,
-                               help='Number of daily archives to keep')
+                               help='number of daily archives to keep')
         subparser.add_argument('-w', '--weekly', dest='weekly', type=int, default=0,
         subparser.add_argument('-w', '--weekly', dest='weekly', type=int, default=0,
-                               help='Number of daily archives to keep')
+                               help='number of daily archives to keep')
         subparser.add_argument('-m', '--monthly', dest='monthly', type=int, default=0,
         subparser.add_argument('-m', '--monthly', dest='monthly', type=int, default=0,
-                               help='Number of monthly archives to keep')
+                               help='number of monthly archives to keep')
         subparser.add_argument('-y', '--yearly', dest='yearly', type=int, default=0,
         subparser.add_argument('-y', '--yearly', dest='yearly', type=int, default=0,
-                               help='Number of yearly archives to keep')
+                               help='number of yearly archives to keep')
         subparser.add_argument('-p', '--prefix', dest='prefix', type=str,
         subparser.add_argument('-p', '--prefix', dest='prefix', type=str,
-                               help='Only consider archive names starting with this prefix')
+                               help='only consider archive names starting with this prefix')
         subparser.add_argument('repository', metavar='REPOSITORY',
         subparser.add_argument('repository', metavar='REPOSITORY',
                                type=location_validator(archive=False),
                                type=location_validator(archive=False),
-                               help='Repository to prune')
+                               help='repository to prune')
         args = parser.parse_args(args or ['-h'])
         args = parser.parse_args(args or ['-h'])
         self.verbose = args.verbose
         self.verbose = args.verbose
         return args.func(args)
         return args.func(args)

+ 2 - 1
docs/Makefile

@@ -37,6 +37,7 @@ clean:
 	-rm -rf $(BUILDDIR)/*
 	-rm -rf $(BUILDDIR)/*
 
 
 html:
 html:
+	./update_usage.sh
 	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
 	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
 	@echo
 	@echo
 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
@@ -138,4 +139,4 @@ gh-pages: html
 	rm -rf $$GH_PAGES_CLONE
 	rm -rf $$GH_PAGES_CLONE
 
 
 inotify:
 inotify:
-	while inotifywait -r .; do make html ; done
+	while inotifywait -r . --exclude usage.rst --exclude '_build/*' ; do make html ; done

+ 11 - 0
docs/detailedusage.rst

@@ -0,0 +1,11 @@
+.. include:: global.rst.inc
+.. _detailed_usage:
+
+Detailed Usage
+--------------
+
+|project_name| consists of a number of subcommands. Each subcommand accepts
+a number of arguments and options. The following sections will describe each
+subcommand in detail.
+
+.. include:: usage.rst

+ 2 - 1
docs/generalusage.rst

@@ -5,7 +5,8 @@ General Usage
 =============
 =============
 
 
 The following examples showcases how to use |project_name| to backup some
 The following examples showcases how to use |project_name| to backup some
-important files from a users home directory.
+important files from a users home directory (for more detailed information
+about each subcommand see the :ref:`detailed_usage` section).
 
 
 Initialize a local :ref:`repository <repository_def>` to store backup
 Initialize a local :ref:`repository <repository_def>` to store backup
 :ref:`archives <archive_def>` in (See :ref:`encrypted_repos` and
 :ref:`archives <archive_def>` in (See :ref:`encrypted_repos` and

+ 3 - 2
docs/index.rst

@@ -5,7 +5,7 @@ Darc
 |project_name| is a Deduplicating ARChiver written in Python.
 |project_name| is a Deduplicating ARChiver written in Python.
 The main goal of |project_name| is to provide an efficient and secure way
 The main goal of |project_name| is to provide an efficient and secure way
 to backup data. The data deduplication technique used makes |project_name|
 to backup data. The data deduplication technique used makes |project_name|
-suitable for taking daily backups.
+suitable for daily backups since only actual changes are stored.
 
 
 Main Features
 Main Features
 -------------
 -------------
@@ -49,8 +49,9 @@ User's Guide
 
 
    installation
    installation
    generalusage
    generalusage
+   detailedusage
    faq
    faq
-   definitions
+   terminology
 
 
 Contribute
 Contribute
 ==========
 ==========

+ 2 - 2
docs/definitions.rst → docs/terminology.rst

@@ -1,7 +1,7 @@
-.. _definitions:
+.. _terminology:
 .. include:: global.rst.inc
 .. include:: global.rst.inc
 
 
-Definitions
+Terminology
 ===========
 ===========
 
 
 .. _deduplication_def:
 .. _deduplication_def:

+ 6 - 0
docs/update_usage.sh

@@ -0,0 +1,6 @@
+#!/usr/bin/bash
+echo -n > usage.rst 
+for cmd in init create extract delete prune verify change-passphrase; do
+  echo -e ".. _usage_darc_$cmd:\n\ndarc $cmd\n~~~~~~\n::\n" >> usage.rst
+  darc $cmd -h >> usage.rst
+done

+ 147 - 0
docs/usage.rst

@@ -0,0 +1,147 @@
+.. _usage_darc_init:
+
+darc init
+~~~~~~
+::
+
+usage: darc init [-h] [-v] [--key-file] [--passphrase] repository
+
+Initialize a new repository
+
+positional arguments:
+  repository     repository to create
+
+optional arguments:
+  -h, --help     show this help message and exit
+  -v, --verbose  verbose output
+  --key-file     enable key file based encryption
+  --passphrase   enable passphrase based encryption
+.. _usage_darc_create:
+
+darc create
+~~~~~~
+::
+
+usage: darc create [-h] [-v] [-s] [-e PATTERN] [-c SECONDS]
+                   [--do-not-cross-mountpoints] [--numeric-owner]
+                   ARCHIVE PATH [PATH ...]
+
+Create new archive
+
+positional arguments:
+  ARCHIVE               archive to create
+  PATH                  paths to archive
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -v, --verbose         verbose output
+  -s, --stats           print statistics for the created archive
+  -e PATTERN, --exclude PATTERN
+                        exclude paths matching PATTERN
+  -c SECONDS, --checkpoint-interval SECONDS
+                        write checkpointe ever SECONDS seconds (Default: 300)
+  --do-not-cross-mountpoints
+                        do not cross mount points
+  --numeric-owner       only store numeric user and group identifiers
+.. _usage_darc_extract:
+
+darc extract
+~~~~~~
+::
+
+usage: darc extract [-h] [-v] [-e PATTERN] [--numeric-owner]
+                    ARCHIVE [PATH [PATH ...]]
+
+Extract archive contents
+
+positional arguments:
+  ARCHIVE               archive to extract
+  PATH                  paths to extract
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -v, --verbose         verbose output
+  -e PATTERN, --exclude PATTERN
+                        exclude paths matching PATTERN
+  --numeric-owner       only obey numeric user and group identifiers
+.. _usage_darc_delete:
+
+darc delete
+~~~~~~
+::
+
+usage: darc delete [-h] [-v] ARCHIVE
+
+Delete archive
+
+positional arguments:
+  ARCHIVE        archive to delete
+
+optional arguments:
+  -h, --help     show this help message and exit
+  -v, --verbose  verbose output
+.. _usage_darc_prune:
+
+darc prune
+~~~~~~
+::
+
+usage: darc prune [-h] [-v] [-H HOURLY] [-d DAILY] [-w WEEKLY] [-m MONTHLY]
+                  [-y YEARLY] [-p PREFIX]
+                  REPOSITORY
+
+Prune repository archives according to specified rules
+
+positional arguments:
+  REPOSITORY            repository to prune
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -v, --verbose         verbose output
+  -H HOURLY, --hourly HOURLY
+                        number of hourly archives to keep
+  -d DAILY, --daily DAILY
+                        number of daily archives to keep
+  -w WEEKLY, --weekly WEEKLY
+                        number of daily archives to keep
+  -m MONTHLY, --monthly MONTHLY
+                        number of monthly archives to keep
+  -y YEARLY, --yearly YEARLY
+                        number of yearly archives to keep
+  -p PREFIX, --prefix PREFIX
+                        only consider archive names starting with this prefix
+.. _usage_darc_verify:
+
+darc verify
+~~~~~~
+::
+
+usage: darc verify [-h] [-v] [-e PATTERN] ARCHIVE [PATH [PATH ...]]
+
+Verify archive consistency
+
+positional arguments:
+  ARCHIVE               archive to verity integrity of
+  PATH                  paths to verify
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -v, --verbose         verbose output
+  -e PATTERN, --exclude PATTERN
+                        exclude paths matching PATTERN
+.. _usage_darc_change-passphrase:
+
+darc change-passphrase
+~~~~~~
+::
+
+usage: darc change-passphrase [-h] [-v] repository
+
+Change passphrase on repository key file
+
+positional arguments:
+  repository
+
+optional arguments:
+  -h, --help     show this help message and exit
+  -v, --verbose  verbose output

+ 4 - 1
setup.py

@@ -1,5 +1,4 @@
 # -*- encoding: utf-8 *-*
 # -*- encoding: utf-8 *-*
-#!/usr/bin/env python
 import os
 import os
 import sys
 import sys
 from glob import glob
 from glob import glob
@@ -45,6 +44,9 @@ except ImportError:
     if not os.path.exists(chunker_source) or not os.path.exists(hashindex_source):
     if not os.path.exists(chunker_source) or not os.path.exists(hashindex_source):
         raise ImportError('The GIT version of darc needs Cython. Install Cython or use a released version')
         raise ImportError('The GIT version of darc needs Cython. Install Cython or use a released version')
 
 
+with open('README.rst', 'r') as fd:
+    long_description = fd.read()
+
 setup(
 setup(
     name='darc',
     name='darc',
     version=darc.__release__,
     version=darc.__release__,
@@ -52,6 +54,7 @@ setup(
     author_email='jonas@borgstrom.se',
     author_email='jonas@borgstrom.se',
     url='http://github.com/jborg/darc/',
     url='http://github.com/jborg/darc/',
     description='Deduplicating ARChiver written in Python',
     description='Deduplicating ARChiver written in Python',
+    long_description=long_description,
     license='BSD',
     license='BSD',
     platforms=['Linux', 'MacOS X'],
     platforms=['Linux', 'MacOS X'],
     classifiers=[
     classifiers=[