Chinaunix首页 | 论坛 | 博客
  • 博客访问: 24765
  • 博文数量: 8
  • 博客积分: 130
  • 博客等级: 入伍新兵
  • 技术积分: 75
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-13 15:31
文章分类

全部博文(8)

文章存档

2016年(2)

2011年(5)

2010年(1)

我的朋友
最近访客

分类: Python/Ruby

2016-01-20 15:54:11


点击(此处)折叠或打开

  1. '''
  2.  ====================================================================
  3.  Copyright (c) 2003-2009 Barry A Scott. All rights reserved.

  4.  This software is licensed as described in the file LICENSE.txt,
  5.  which you should have received as part of this distribution.

  6.  ====================================================================
  7. '''
  8. import pysvn
  9. import time
  10. import sys
  11. import os
  12. import parse_datetime
  13. import glob
  14. import locale
  15. import types

  16. try:
  17.     sorted( [] )
  18. except NameError:
  19.     def sorted( list_in ):
  20.         list_out = list( list_in )
  21.         list_out.sort()
  22.         return list_out

  23. if hasattr( types, 'StringTypes' ):
  24.     StringTypes = types.StringTypes
  25. else:
  26.     StringTypes = [type( '' )]

  27. if hasattr( types, 'DictType' ):
  28.     DictType = types.DictType
  29. else:
  30.     DictType = type( {} )

  31. class CommandError( Exception ):
  32.     def __init__( self, reason ):
  33.         Exception.__init__( self )
  34.         self._reason = reason

  35.     def reason( self ):
  36.         return self._reason

  37.     def __str__( self ):
  38.         return self._reason

  39. def main( args ):
  40.     progname = os.path.basename( args[0] )
  41.     pause = False
  42.     if args[1:2] == ['--pause']:
  43.         del args[1]
  44.         pause = True

  45.     # if the locale is not setup SVN can report errors handling non ascii file names
  46.     initLocale()

  47.     svn_cmd = SvnCommand( progname )
  48.     rc = svn_cmd.dispatch( args[1:] )
  49.     if pause:
  50.         sys.stdin.readline()
  51.     return rc

  52. def initLocale():
  53.     # init the locale
  54.     if sys.platform in ['win32','cygwin']:
  55.         locale.setlocale( locale.LC_ALL, '' )

  56.     else:
  57.         language_code, encoding = locale.getdefaultlocale()
  58.         if language_code is None:
  59.             language_code = 'en_GB'

  60.         if encoding is None:
  61.             encoding = 'UTF-8'
  62.         if encoding.lower() == 'utf':
  63.             encoding = 'UTF-8'

  64.         try:
  65.             # setlocale fails when params it does not understand are passed
  66.             locale.setlocale( locale.LC_ALL, '%s.%s' % (language_code, encoding) )
  67.         except locale.Error:
  68.             # force a locale that will work
  69.             locale.setlocale( locale.LC_ALL, 'en_GB.UTF-8' )

  70. def fmtDateTime( t ):
  71.     return time.strftime( '%d-%b-%Y %H:%M:%S', time.localtime( t ) )

  72. wc_status_kind_map = {
  73. pysvn.wc_status_kind.added: 'A',
  74. pysvn.wc_status_kind.conflicted: 'C',
  75. pysvn.wc_status_kind.deleted: 'D',
  76. pysvn.wc_status_kind.external: 'X',
  77. pysvn.wc_status_kind.ignored: 'I',
  78. pysvn.wc_status_kind.incomplete: '!',
  79. pysvn.wc_status_kind.missing: '!',
  80. pysvn.wc_status_kind.merged: 'G',
  81. pysvn.wc_status_kind.modified: 'M',
  82. pysvn.wc_status_kind.none: ' ',
  83. pysvn.wc_status_kind.normal: ' ',
  84. pysvn.wc_status_kind.obstructed: '~',
  85. pysvn.wc_status_kind.replaced: 'R',
  86. pysvn.wc_status_kind.unversioned: '?',
  87. }

  88. wc_notify_action_map = {
  89. pysvn.wc_notify_action.add: 'A',
  90. pysvn.wc_notify_action.commit_added: 'A',
  91. pysvn.wc_notify_action.commit_deleted: 'D',
  92. pysvn.wc_notify_action.commit_modified: 'M',
  93. pysvn.wc_notify_action.commit_postfix_txdelta: None,
  94. pysvn.wc_notify_action.commit_replaced: 'R',
  95. pysvn.wc_notify_action.copy: 'c',
  96. pysvn.wc_notify_action.delete: 'D',
  97. pysvn.wc_notify_action.failed_revert: 'F',
  98. pysvn.wc_notify_action.resolved: 'R',
  99. pysvn.wc_notify_action.restore: 'R',
  100. pysvn.wc_notify_action.revert: 'R',
  101. pysvn.wc_notify_action.skip: 'skip',
  102. pysvn.wc_notify_action.status_completed: None,
  103. pysvn.wc_notify_action.status_external: 'X',
  104. pysvn.wc_notify_action.update_add: 'A',
  105. pysvn.wc_notify_action.update_completed: None,
  106. pysvn.wc_notify_action.update_delete: 'D',
  107. pysvn.wc_notify_action.update_external: 'X',
  108. pysvn.wc_notify_action.update_update: 'U',
  109. pysvn.wc_notify_action.annotate_revision: 'A',
  110. }

  111. # new in svn 1.4?
  112. if hasattr( pysvn.wc_notify_action, 'locked' ):
  113.     wc_notify_action_map[ pysvn.wc_notify_action.locked ] = 'locked'
  114.     wc_notify_action_map[ pysvn.wc_notify_action.unlocked ] = 'unlocked'
  115.     wc_notify_action_map[ pysvn.wc_notify_action.failed_lock ] = 'failed_lock'
  116.     wc_notify_action_map[ pysvn.wc_notify_action.failed_unlock ] = 'failed_unlock'

  117. # new in svn 1.5
  118. if hasattr( pysvn.wc_notify_action, 'exists' ):
  119.     wc_notify_action_map[ pysvn.wc_notify_action.exists ] = 'exists'
  120.     wc_notify_action_map[ pysvn.wc_notify_action.changelist_set ] = 'changelist_set'
  121.     wc_notify_action_map[ pysvn.wc_notify_action.changelist_clear ] = 'changelist_clear'
  122.     wc_notify_action_map[ pysvn.wc_notify_action.changelist_moved ] = 'changelist_moved'
  123.     wc_notify_action_map[ pysvn.wc_notify_action.foreign_merge_begin ] = 'foreign_merge_begin'
  124.     wc_notify_action_map[ pysvn.wc_notify_action.merge_begin ] = 'merge_begin'
  125.     wc_notify_action_map[ pysvn.wc_notify_action.update_replace ] = 'update_replace'

  126. # new in svn 1.6
  127. if hasattr( pysvn.wc_notify_action, 'property_added' ):
  128.     wc_notify_action_map[ pysvn.wc_notify_action.property_added ] = 'property_added'
  129.     wc_notify_action_map[ pysvn.wc_notify_action.property_modified ] = 'property_modified'
  130.     wc_notify_action_map[ pysvn.wc_notify_action.property_deleted ] = 'property_deleted'
  131.     wc_notify_action_map[ pysvn.wc_notify_action.property_deleted_nonexistent ] = 'property_deleted_nonexistent'
  132.     wc_notify_action_map[ pysvn.wc_notify_action.revprop_set ] = 'revprop_set'
  133.     wc_notify_action_map[ pysvn.wc_notify_action.revprop_deleted ] = 'revprop_deleted'
  134.     wc_notify_action_map[ pysvn.wc_notify_action.merge_completed ] = 'merge_completed'
  135.     wc_notify_action_map[ pysvn.wc_notify_action.tree_conflict ] = 'tree_conflict'
  136.     wc_notify_action_map[ pysvn.wc_notify_action.failed_external ] = 'failed_external'

  137. # new in svn 1.7
  138. if hasattr( pysvn.wc_notify_action, 'update_started' ):
  139.     wc_notify_action_map[ pysvn.wc_notify_action.update_started ] = 'update_started'
  140.     wc_notify_action_map[ pysvn.wc_notify_action.update_skip_obstruction ] = 'update_skip_obstruction'
  141.     wc_notify_action_map[ pysvn.wc_notify_action.update_skip_working_only ] = 'update_skip_working_only'
  142.     wc_notify_action_map[ pysvn.wc_notify_action.update_external_removed ] = 'update_external_removed'
  143.     wc_notify_action_map[ pysvn.wc_notify_action.update_shadowed_add ] = 'update_shadowed_add'
  144.     wc_notify_action_map[ pysvn.wc_notify_action.update_shadowed_update ] = 'update_shadowed_update'
  145.     wc_notify_action_map[ pysvn.wc_notify_action.update_shadowed_delete ] = 'update_shadowed_delete'
  146.     wc_notify_action_map[ pysvn.wc_notify_action.merge_record_info ] = 'merge_record_info'
  147.     wc_notify_action_map[ pysvn.wc_notify_action.upgraded_path ] = 'upgraded_path'
  148.     wc_notify_action_map[ pysvn.wc_notify_action.merge_record_info_begin ] = 'merge_record_info_begin'
  149.     wc_notify_action_map[ pysvn.wc_notify_action.merge_elide_info ] = 'merge_elide_info'
  150.     wc_notify_action_map[ pysvn.wc_notify_action.patch ] = 'patch'
  151.     wc_notify_action_map[ pysvn.wc_notify_action.patch_applied_hunk ] = 'patch_applied_hunk'
  152.     wc_notify_action_map[ pysvn.wc_notify_action.patch_rejected_hunk ] = 'patch_rejected_hunk'
  153.     wc_notify_action_map[ pysvn.wc_notify_action.patch_hunk_already_applied ] = 'patch_hunk_already_applied'
  154.     wc_notify_action_map[ pysvn.wc_notify_action.commit_copied ] = 'commit_copied'
  155.     wc_notify_action_map[ pysvn.wc_notify_action.commit_copied_replaced ] = 'commit_copied_replaced'
  156.     wc_notify_action_map[ pysvn.wc_notify_action.url_redirect ] = 'url_redirect'
  157.     wc_notify_action_map[ pysvn.wc_notify_action.path_nonexistent ] = 'path_nonexistent'
  158.     wc_notify_action_map[ pysvn.wc_notify_action.exclude ] = 'exclude'
  159.     wc_notify_action_map[ pysvn.wc_notify_action.failed_conflict ] = 'failed_conflict'
  160.     wc_notify_action_map[ pysvn.wc_notify_action.failed_missing ] = 'failed_missing'
  161.     wc_notify_action_map[ pysvn.wc_notify_action.failed_out_of_date ] = 'failed_out_of_date'
  162.     wc_notify_action_map[ pysvn.wc_notify_action.failed_no_parent ] = 'failed_no_parent'

  163. # new in svn 1.7.1+?
  164. if hasattr( pysvn.wc_notify_action, 'failed_locked' ):
  165.     wc_notify_action_map[ pysvn.wc_notify_action.failed_locked ] = 'failed_locked'
  166.     wc_notify_action_map[ pysvn.wc_notify_action.failed_forbidden_by_server ] = 'failed_forbidden_by_server'
  167.     wc_notify_action_map[ pysvn.wc_notify_action.skip_conflicted ] = 'skip_conflicted'

  168. # new in svn 1.8
  169. if hasattr( pysvn.wc_notify_action, 'update_broken_lock' ):
  170.     wc_notify_action_map[ pysvn.wc_notify_action.update_broken_lock ] = 'update_broken_lock'
  171.     wc_notify_action_map[ pysvn.wc_notify_action.failed_obstruction ] = 'failed_obstruction'
  172.     wc_notify_action_map[ pysvn.wc_notify_action.conflict_resolver_starting ] = 'conflict_resolver_starting'
  173.     wc_notify_action_map[ pysvn.wc_notify_action.conflict_resolver_done ] = 'conflict_resolver_done'
  174.     wc_notify_action_map[ pysvn.wc_notify_action.left_local_modifications ] = 'left_local_modifications'
  175.     wc_notify_action_map[ pysvn.wc_notify_action.foreign_copy_begin ] = 'foreign_copy_begin'
  176.     wc_notify_action_map[ pysvn.wc_notify_action.move_broken ] = 'move_broken'

  177. class SvnCommand:
  178.     def __init__( self, progname ):
  179.         self.progname = progname
  180.         self.client = None
  181.         self.revision_update_complete = None
  182.         self.notify_message_list = []
  183.         self.pysvn_testing = False
  184.         self.debug_enabled = False

  185.     def debug( self, msg, args=() ):
  186.         if self.debug_enabled:
  187.             print( 'Debug: %s' % (msg % args) )

  188.     def initClient( self, config_dir ):
  189.         self.client = pysvn.Client( config_dir )
  190.         self.client.exception_style = 1
  191.         self.client.commit_info_style = 1
  192.         self.client.callback_get_login = self.callback_getLogin
  193.         self.client.callback_get_log_message = self.callback_getLogMessage
  194.         self.client.callback_notify = self.callback_notify
  195.         self.client.callback_cancel = self.callback_cancel
  196.         if hasattr( self.client, 'callback_conflict_resolver' ):
  197.             self.client.callback_conflict_resolver = self.callback_conflict_resolver
  198.         self.client.callback_cancel = self.callback_cancel
  199.         self.client.callback_ssl_client_cert_password_prompt = self.callback_ssl_client_cert_password_prompt
  200.         self.client.callback_ssl_client_cert_prompt = self.callback_ssl_client_cert_prompt
  201.         self.client.callback_ssl_server_prompt = self.callback_ssl_server_prompt
  202.         self.client.callback_ssl_server_trust_prompt = self.callback_ssl_server_trust_prompt

  203.     def callback_ssl_client_cert_password_prompt( self ):
  204.         print( 'callback_ssl_client_cert_password_prompt' )

  205.     def callback_ssl_client_cert_prompt( self ):
  206.         print( 'callback_ssl_client_cert_prompt' )

  207.     def callback_ssl_server_prompt( self ):
  208.         print( 'callback_ssl_server_prompt' )

  209.     def callback_ssl_server_trust_prompt( self, trust_data ):
  210.         for key,value in trust_data.items():
  211.             print( '%s: %s' % (key, value) )
  212.         print('')
  213.         answer = ''
  214.         while answer.lower() not in ['p','t','r']:
  215.             sys.stdout.write( '(P)ermanent accept, (T)emporary accept or (R)eject: ' )
  216.             answer = sys.stdin.readline().strip()
  217.         if answer.lower() == 'p':
  218.             return True, trust_data['failures'], True
  219.         if answer.lower() == 't':
  220.             return True, trust_data['failures'], False
  221.         return False, 0, False

  222.     def callback_cancel( self ):
  223.         return False

  224.     def callback_notify( self, arg_dict ):
  225.         if arg_dict['action'] == pysvn.wc_notify_action.update_completed:
  226.             self.revision_update_complete = arg_dict['revision']
  227.         elif arg_dict['path'] != '' and wc_notify_action_map[ arg_dict['action'] ] is not None:
  228.             msg = '%s %s' % (wc_notify_action_map[ arg_dict['action'] ], arg_dict['path'])
  229.             if self.pysvn_testing != '99.99.99':
  230.                 self.notify_message_list.append( msg )
  231.             else:
  232.                 print( msg )

  233.     def callback_conflict_resolver( self, arg_dict ):
  234.         print( 'callback_conflict_resolver' )
  235.         for key in sorted( arg_dict.keys() ):
  236.             value = arg_dict[ key ]

  237.             if type(value) == DictType:
  238.                 value = '{%s}' % (', '.join( ['%r: %r' % (key, value) for key, value in sorted( value.items() )] ),)

  239.             elif type(value) not in StringTypes:
  240.                 value = repr(value)

  241.             print( ' %s: %s' % (key, value) )

  242.         return pysvn.wc_conflict_choice.postpone, None, False

  243.     def callback_getLogin( self, realm, username, may_save ):
  244.         print( 'May save: %s ' % may_save )
  245.         print( 'Realm: %s ' % realm )
  246.         if username:
  247.             print( 'Username: %s' % username )
  248.         else:
  249.             sys.stdout.write( 'Username: ' )
  250.             username = sys.stdin.readline().strip()
  251.             if len(username) == 0:
  252.                 return 0, '', '', False

  253.         sys.stdout.write( 'Password: ' )
  254.         password = sys.stdin.readline().strip()

  255.         save_password = 'x'
  256.         while save_password.lower() not in ['y','ye','yes','n', 'no','']:
  257.             sys.stdout.write( 'Save password? [y/n] ' )
  258.             save_password = sys.stdin.readline().strip()
  259.         
  260.         return 1, username, password, save_password in ['y','ye','yes']

  261.     def getLogMessage( self ):
  262.         sys.stdout.write( 'Log message\n' )
  263.         sys.stdout.write( '--- -------\n' )
  264.         message = sys.stdin.read()
  265.         return message

  266.     def callback_getLogMessage( self ):
  267.         return True, self.getLogMessage()

  268.     def dispatch( self, argv ):
  269.         try:
  270.             args = SvnArguments( argv )
  271.             cmd_name = 'cmd_%s' % args.getCommandName( 'help' )

  272.             self.initClient( args.getOptionalValue( '--config-dir', '' ) )
  273.             self.client.set_auth_cache( args.getBooleanOption( '--no-auth-cache', False ) )
  274.             self.pysvn_testing = args.getOptionalValue( '--pysvn-testing', '99.99.99' )
  275.             self.debug_enabled = args.getBooleanOption( '--debug', True )

  276.             getattr( self, cmd_name, self.cmd_help )( args )

  277.             self.printNotifyMessages()

  278.         except pysvn.ClientError as e:
  279.             self.printNotifyMessages()
  280.             print( e.args[0] )
  281.             return 1

  282.         except CommandError as e:
  283.             self.printNotifyMessages()
  284.             print( e.reason() )
  285.             return 1

  286.         return 0

  287.     def printNotifyMessages( self ):
  288.             # different versions of SVN notify messages in different orders
  289.             # by sorting before printing we hope to have one set of regression
  290.             # test data for multiple versions of SVN
  291.             self.notify_message_list.sort()
  292.             for msg in self.notify_message_list:
  293.                 print( msg )
  294.             self.notify_message_list = []

  295.     def cmd_version( self, args ):
  296.         print( 'PYSVN Version: %r' % (pysvn.version,) )
  297.         print( 'SVN Version: %r' % (pysvn.svn_version,) )
  298.         if hasattr( pysvn, 'svn_api_version' ):
  299.             print( 'SVN API Version: %r' % (pysvn.svn_api_version,) )
  300.         print( 'pysvn._pysvn %r' % (pysvn._pysvn,) )


  301.     def cmd_is_url( self, args ):
  302.         path = args.getPositionalArgs( 1 )[0]
  303.         is_url = self.client.is_url( path )
  304.         if is_url:
  305.             print( 'url %s' % path )
  306.         else:
  307.             print( 'path %s' % path )

  308.     def cmd_add( self, args ):
  309.         recurse = args.getBooleanOption( '--non-recursive', False )
  310.         force = args.getBooleanOption( '--force', False )
  311.         
  312.         self.client.add( args.getPositionalArgs( 1 ), recurse=recurse, force=force )

  313.     def cmd_add_to_changelist( self, args ):
  314.         if not hasattr( self.client, add_to_changelist ):
  315.             print( 'Error: add_to_changelist is not supported by this version of Subversion' )
  316.             return

  317.         path, changelist = args.getPositionalArgs( 2, 2 )
  318.         self.client.add_to_changelist( path, changelist )

  319.     def cmd_annotate( self, args ):
  320.         start_revision, end_revision = args.getOptionalRevisionPair( '--revision', '0', 'head' )
  321.         positional_args = args.getPositionalArgs( 1, 1 )
  322.         all_lines = self.client.annotate( positional_args[0],
  323.                     revision_start=start_revision,
  324.                     revision_end=end_revision )
  325.         self.printNotifyMessages()

  326.         for line in all_lines:
  327.             print( '%d| r%d | %s | %s | %s' %
  328.                 (line['number']
  329.                 ,line['revision'].number
  330.                 ,line['author']
  331.                 ,line['date']
  332.                 ,line['line']) )
  333.     cmd_ann = cmd_annotate

  334.     def cmd_cat( self, args ):
  335.         revision = args.getOptionalRevision( '--revision', 'head' )
  336.         text = self.client.cat( args.getPositionalArgs( 1, 1 )[0], revision=revision )
  337.         print( text.decode( 'utf-8' ).replace( '\r\n', '\n' ) )

  338.     def cmd_checkout( self, args ):
  339.         recurse = args.getBooleanOption( '--non-recursive', False )
  340.         positional_args = args.getPositionalArgs( 1, 2 )
  341.         if len(positional_args) == 1:
  342.             positional_args.append( os.path.basename( positional_args[0] ) )

  343.         self.revision_update_complete = None
  344.         self.client.checkout( positional_args[0], positional_args[1], recurse=recurse )
  345.         self.printNotifyMessages()

  346.         if self.revision_update_complete is not None:
  347.             print( 'Checked out revision %s' % self.revision_update_complete.number )
  348.         else:
  349.             print( 'Checked out unknown revision - checkout failed?' )

  350.     cmd_co = cmd_checkout

  351.     def cmd_cleanup( self, args ):
  352.         positional_args = args.getPositionalArgs( 0, 1 )
  353.         if len(positional_args) == 0:
  354.             positional_args.append( '.' )

  355.         self.client.cleanup( positional_args[0] )

  356.     def cmd_checkin( self, args ):
  357.         msg = args.getOptionalValue( '--message', '' )

  358.         recurse = args.getBooleanOption( '--non-recursive', False )
  359.         positional_args = args.getPositionalArgs( 0 )
  360.         if len(positional_args) == 0:
  361.             positional_args.append( '.' )
  362.         if msg == '':
  363.             msg = self.getLogMessage()
  364.             
  365.         commit_info = self.client.checkin( positional_args, msg, recurse=recurse )
  366.         rev = commit_info["revision"]
  367.         self.printNotifyMessages()

  368.         if commit_info['post_commit_err'] is not None:
  369.             print( commit_info['post_commit_err'] )

  370.         if rev is None:
  371.             print( 'Nothing to commit' )
  372.         elif rev.number > 0:
  373.             print( 'Revision %s' % rev.number )
  374.         else:
  375.             print( 'Commit failed' )


  376.     cmd_commit = cmd_checkin
  377.     cmd_ci = cmd_checkin

  378.     def cmd_copy( self, args ):
  379.         positional_args = args.getPositionalArgs( 2, 2 )
  380.         self.client.copy( positional_args[0], positional_args[1] )
  381.     cmd_cp = cmd_copy

  382.     def cmd_diff( self, args ):
  383.         recurse = args.getBooleanOption( '--non-recursive', False )
  384.         revision1, revision2 = args.getOptionalRevisionPair( '--revision', 'base', 'working' )
  385.         positional_args = args.getPositionalArgs( 0, 1 )
  386.         if len(positional_args) == 0:
  387.             positional_args.append( '.' )

  388.         if 'TEMP' in os.environ:
  389.             tmpdir = os.environ['TEMP']
  390.         elif 'TMPDIR' in os.environ:
  391.             tmpdir = os.environ['TMPDIR']
  392.         elif 'TMP' in os.environ:
  393.             tmpdir = os.environ['TMP']
  394.         elif os.path.exists( '/usr/tmp' ):
  395.             tmpdir = '/usr/tmp'
  396.         elif os.path.exists( '/tmp' ):
  397.             tmpdir = '/tmp'
  398.         else:
  399.             print( 'No tmp dir!' )
  400.             return

  401.         self.debug( 'cmd_diff %r, %r, %r, %r, %r' % (tmpdir, positional_args[0], recurse, revision1, revision2) )
  402.         diff_text = self.client.diff( tmpdir, positional_args[0], recurse=recurse,
  403.                                             revision1=revision1, revision2=revision2,
  404.                                             diff_options=['-u'] )
  405.         print( diff_text.replace( '\r\n', '\n' ) )

  406.     def cmd_export( self, args ):
  407.         force = args.getBooleanOption( '--force', False )
  408.         revision_url = args.getOptionalRevision( '--revision', 'head' )
  409.         revision_wc = args.getOptionalRevision( '--revision', 'working' )
  410.         native_eol = args.getOptionalValue( '--native-eol', None )
  411.         positional_args = args.getPositionalArgs( 2, 2 )
  412.         if self.client.is_url( positional_args[0] ):
  413.             revision = revision_url
  414.         else:
  415.             revision = revision_wc

  416.         self.client.export( positional_args[0], positional_args[1], revision=revision, force=force, native_eol=native_eol )

  417.     def cmd_info( self, args ):
  418.         positional_args = args.getPositionalArgs( 0, 1 )
  419.         if len(positional_args) == 0:
  420.             positional_args.append( '.' )

  421.         path = positional_args[0]
  422.     
  423.         entry = self.client.info( path )

  424.         print( 'Path: %s' % path )
  425.         if entry.name and entry.name != 'svn:this_dir':
  426.             print( 'Name: %s' % entry.name )
  427.         if entry.url:
  428.             print( 'Url: %s' % entry.url )
  429.         if entry.repos and self.pysvn_testing >= '01.03.00':
  430.             print( 'Repository: %s' % entry.repos )
  431.         if entry.uuid:
  432.             print( 'Repository UUID: %s' % entry.uuid )
  433.         if entry.revision.kind == pysvn.opt_revision_kind.number:
  434.             print( 'Revision: %s' % entry.revision.number )
  435.         if entry.kind == pysvn.node_kind.file:
  436.             print( 'Node kind: file' )
  437.         elif entry.kind == pysvn.node_kind.dir:
  438.             print( 'Node kind: directory' )
  439.         elif entry.kind == pysvn.node_kind.none:
  440.             print( 'Node kind: none' )
  441.         else:
  442.             print( 'Node kind: unknown' )

  443.         if entry.schedule == pysvn.wc_schedule.normal:
  444.             print( "Schedule: normal" )
  445.         elif entry.schedule == pysvn.wc_schedule.add:
  446.             print( "Schedule: add" )
  447.         elif entry.schedule == pysvn.wc_schedule.delete:
  448.             print( "Schedule: delete" )
  449.         elif entry.schedule == pysvn.wc_schedule.replace:
  450.             print( "Schedule: replace" )
  451.         if entry.is_copied:
  452.             if entry.copyfrom_url:
  453.                 print( 'Copied From URL: %s' % entry.copyfrom_url )
  454.             if entry.copyfrom_rev.number:
  455.                 print( 'Copied From Rev: %s' % entry.copyfrom_rev.number )
  456.         if entry.commit_author:
  457.             print( 'Last Changed Author: %s' % entry.commit_author )
  458.         if entry.commit_revision.number:
  459.             print( 'Last Changed Rev: %s' % entry.commit_revision.number )
  460.         if entry.commit_time:
  461.             print( 'Last Changed Date: %s' % fmtDateTime( entry.commit_time ) )
  462.         if entry.text_time:
  463.             print( 'Text Last Updated: %s' % fmtDateTime( entry.text_time ) )
  464.         if entry.properties_time and self.pysvn_testing == '99.99.99':
  465.             print( 'Properties Last Updated: %s' % fmtDateTime( entry.properties_time ) )
  466.         if entry.checksum:
  467.             print( 'Checksum: %s' % entry.checksum )

  468.     def cmd_info2( self, args ):
  469.         recurse = args.getBooleanOption( '--recursive', True )
  470.         revision_url = args.getOptionalRevision( '--revision', 'head' )
  471.         revision_path = args.getOptionalRevision( '--revision', 'unspecified' )

  472.         positional_args = args.getPositionalArgs( 0, 1 )
  473.         if len(positional_args) == 0:
  474.             positional_args.append( '.' )

  475.         path = positional_args[0]

  476.         if self.client.is_url( path ):
  477.             revision = revision_url
  478.         else:
  479.             revision = revision_path

  480.         all_entries = self.client.info2( path, revision=revision, recurse=recurse )

  481.         for path, info in all_entries:
  482.             print('')
  483.             print( 'Path: %s' % path )

  484.             if info.URL:
  485.                 print( 'Url: %s' % info.URL )
  486.             if info.rev:
  487.                 print( 'Revision: %s' % info.rev.number )
  488.             if info.repos_root_URL and self.pysvn_testing >= '01.03.00':
  489.                 print( 'Repository root_URL: %s' % info.repos_root_URL )
  490.             if info.repos_UUID:
  491.                 print( 'Repository UUID: %s' % info.repos_UUID )
  492.             if info.last_changed_author:
  493.                 print( 'Last changed author: %s' % info.last_changed_author )
  494.             if info.last_changed_date:
  495.                 print( 'Last Changed Date: %s' % fmtDateTime( info.last_changed_date ) )
  496.             if info.last_changed_rev.kind == pysvn.opt_revision_kind.number:
  497.                 print( 'Last changed revision: %s' % info.last_changed_rev.number )
  498.             if info.kind == pysvn.node_kind.file:
  499.                 print( 'Node kind: file' )
  500.             elif info.kind == pysvn.node_kind.dir:
  501.                 print( 'Node kind: directory' )
  502.             elif info.kind == pysvn.node_kind.none:
  503.                 print( 'Node kind: none' )
  504.             else:
  505.                 print( 'Node kind: unknown' )
  506.             if info.lock:
  507.                 print( 'Lock Owner: %s' % info.lock.owner )
  508.                 print( 'Lock Creation Date: %s' % fmtDateTime( info.lock.creation_date ) )
  509.                 if info.lock.expiration_date is not None:
  510.                     print( 'Lock Expiration Date: %s' % fmtDateTime( info.lock.expiration_date ) )
  511.                 print( 'Lock Token: %s' % info.lock.token )
  512.                 print( 'Lock Comment:' )
  513.                 if info.lock.comment not in ['', None]:
  514.                     print( info.lock.comment )
  515.             if info.wc_info:
  516.                 wc_info = info.wc_info
  517.                 if wc_info.schedule == pysvn.wc_schedule.normal:
  518.                     print( "Schedule: normal" )
  519.                 elif wc_info.schedule == pysvn.wc_schedule.add:
  520.                     print( "Schedule: add" )
  521.                 elif wc_info.schedule == pysvn.wc_schedule.delete:
  522.                     print( "Schedule: delete" )
  523.                 elif wc_info.schedule == pysvn.wc_schedule.replace:
  524.                     print( "Schedule: replace" )
  525.                 if wc_info.copyfrom_url:
  526.                     print( 'Copied From URL: %s' % wc_info.copyfrom_url )
  527.                     print( 'Copied From Rev: %s' % wc_info.copyfrom_rev.number )
  528.                 if wc_info.text_time:
  529.                     print( 'Text Last Updated: %s' % fmtDateTime( wc_info.text_time ) )
  530.                 if wc_info.prop_time and self.pysvn_testing == '99.99.99':
  531.                     print( 'Properties Last Updated: %s' % fmtDateTime( wc_info.prop_time ) )
  532.                 if wc_info.checksum:
  533.                     print( 'Checksum: %s' % wc_info.checksum )


  534.     def cmd_import( self, args ):
  535.         msg = self.getOptionalValue( '--message', '' )
  536.         recurse = args.getBooleanOption( '--non-recursive', False )
  537.         positional_args = args.getPositionalArgs( 2, 2 )
  538.         self.client.import_( positional_args[0], positional_args[1], recurse=recurse )

  539.     def cmd_lock( self, args ):
  540.         msg = args.getOptionalValue( '--message', '' )
  541.         force = args.getBooleanOption( '--force', True )
  542.         positional_args = args.getPositionalArgs( 1, 1 )
  543.         self.client.lock( positional_args[0], msg, force );

  544.     def cmd_log( self, args ):
  545.         start_revision, end_revision = args.getOptionalRevisionPair( '--revision', 'head', '0' )
  546.         verbose = args.getBooleanOption( '--verbose', True )
  547.         positional_args = args.getPositionalArgs( 1, 1 )
  548.         all_logs = self.client.log( positional_args[0],
  549.                                     revision_start=start_revision,
  550.                                     revision_end=end_revision,
  551.                                     discover_changed_paths=verbose )

  552.         for log in all_logs:
  553.             print( '-'*60 )
  554.             print( 'rev %d: %s | %s | %d lines' %
  555.                 (log.revision.number
  556.                 ,log.author
  557.                 ,fmtDateTime( log.date )
  558.                 ,len( log.message.split('\n') )) )

  559.             if len( log.changed_paths ) > 0:
  560.                 print( 'Changed paths:' )
  561.                 for change_info in log.changed_paths:
  562.                     if change_info.copyfrom_path is None:
  563.                         print( ' %s %s' % (change_info.action, change_info.path) )
  564.                     else:
  565.                         print( ' %s %s (from %s:%d)' %
  566.                             (change_info.action
  567.                             ,change_info.path
  568.                             ,change_info.copyfrom_path
  569.                             ,change_info.copyfrom_revision.number) )

  570.             print( log.message )

  571.         print( '-'*60 )

  572.     def cmd_ls( self, args ):
  573.         recurse = args.getBooleanOption( '--recursive', True )
  574.         revision = args.getOptionalRevision( '--revision', 'head' )
  575.         verbose = args.getBooleanOption( '--verbose', True )
  576.         positional_args = args.getPositionalArgs( 0 )
  577.         if len(positional_args) == 0:
  578.             positional_args.append( '.' )

  579.         for arg in positional_args:
  580.             all_files = self.client.ls( arg, revision=revision, recurse=recurse )
  581.             if verbose:
  582.                 for file in all_files:
  583.                     args = {}
  584.                     args.update( file )
  585.                     args['name'] = args['name']
  586.                     args['last_author'] = args['last_author']
  587.                     args['time_str'] = fmtDateTime( file.time )
  588.                     args['created_rev_num'] = file.created_rev.number
  589.                     print( '%(created_rev_num)7d %(last_author)-10s %(size)6d %(time_str)s %(name)s' % args )
  590.             else:
  591.                 for file in all_files:
  592.                     print( '%(name)s' % file )

  593.     def cmd_list( self, args ):
  594.         recurse = args.getBooleanOption( '--recursive', True )
  595.         revision = args.getOptionalRevision( '--revision', 'head' )
  596.         verbose = args.getBooleanOption( '--verbose', True )
  597.         positional_args = args.getPositionalArgs( 0 )
  598.         if len(positional_args) == 0:
  599.             positional_args.append( '.' )

  600.         for arg in positional_args:
  601.             all_files = self.client.list( arg, revision=revision, recurse=recurse )
  602.             if verbose:
  603.                 for file, Q in all_files:
  604.                     args = {}
  605.                     args.update( file )
  606.                     args['time_str'] = fmtDateTime( file.time )
  607.                     args['created_rev_num'] = file.created_rev.number
  608.                     print( '%(created_rev_num)7d %(last_author)-10s %(size)6d %(time_str)s %(path)s' % args )
  609.             else:
  610.                 for file, Q in all_files:
  611.                     print( '%(path)s' % file )

  612.     def cmd_merge( self, args ):
  613.         recurse = args.getBooleanOption( '--recursive', True )
  614.         dry_run = args.getBooleanOption( '--dry-run', False )
  615.         notice_ancestry = args.getBooleanOption( '--notice-ancestry', False )

  616.         # need to figure out which variaty of the merge command this is
  617.         if args.haveOption( '--revision' ):
  618.             # its merge -r N:M SOURCE [WCPATH]
  619.             revision1, revision2 = args.getMandatoryRevisionPair( '--revision' )
  620.             positional_args = args.getPositionalArgs( 1, 2 )
  621.             if len(positional_args) == 1:
  622.                 positional_args.append( '.' )
  623.             path1 = positional_args[0]
  624.             path2 = positional_args[0]
  625.             wcpath = positional_args[1]
  626.         else:
  627.             # its merge sourceURL1[@N] sourceURL2[@M] [WCPATH]
  628.             positional_args = args.getPositionalArgs( 2, 3 )
  629.             if len(positional_args) == 2:
  630.                 positional_args.append( '.' )

  631.             path1, rev1 = self.parsePathWithRevision( positional_args[0] )
  632.             path2, rev2 = self.parsePathWithRevision( positional_args[1] )
  633.             wcpath = positional_args[2]

  634.             
  635.         self.client.merge( path1, revision1, path2, revision2, wcpath,
  636.                 recurse=recurse, dry_run=dry_run, notice_ancestry=notice_ancestry )

  637.     def cmd_mkdir( self, args ):
  638.         if args.haveOption( '--message' ):
  639.             msg = args.getOptionalValue( '--message', '' )
  640.             if msg == '':
  641.                 msg = self.getLogMessage()

  642.         else:
  643.             msg = ''

  644.         self.client.mkdir( args.getPositionalArgs( 1, 1 )[0], msg )

  645.     def cmd_move( self, args ):
  646.         positional_args = args.getPositionalArgs( 2, 2 )
  647.         self.client.move( positional_args[0], positional_args[1] )
  648.     cmd_mv = cmd_move

  649.     def cmd_patch( self, args ):
  650.         dry_run = args.getBooleanOption( '--dry-run', True )
  651.         reverse = args.getBooleanOption( '--reverse', True )
  652.         ignore_whitespace = args.getBooleanOption( '--ignore-whitespace', True )
  653.         remove_tempfiles = not args.getBooleanOption( '--no-remove-tempfiles', True )

  654.         patch_path, wc_dir_path = args.getPositionalArgs( 2, 2 )
  655.         abs_patch_path = os.path.abspath( patch_path )
  656.         abs_wc_dir_path = os.path.abspath( wc_dir_path )

  657.         self.client.patch( abs_patch_path, abs_wc_dir_path,
  658.                             dry_run=dry_run,
  659.                             reverse=reverse,
  660.                             ignore_whitespace=ignore_whitespace,
  661.                             remove_tempfiles=remove_tempfiles )

  662.     def key_props_by_path( self, a ):
  663.         return a[0]

  664.     def cmd_proplist( self, args ):
  665.         recurse = args.getBooleanOption( '--recursive', True )
  666.         revision = args.getOptionalRevision( '--revision', 'working' )
  667.         verbose = args.getBooleanOption( '--verbose', True )
  668.         positional_args = args.getPositionalArgs( 0, 0 )
  669.         if len(positional_args) == 0:
  670.             positional_args.append( '.' )

  671.         for arg in positional_args:

  672.             if self.client.is_url( arg ):
  673.                 revision = args.getOptionalRevision( '--revision', 'head' )

  674.             all_props = self.client.proplist( arg, revision=revision, recurse=recurse )
  675.             all_props.sort( key=self.key_props_by_path )

  676.             for path, props in all_props:
  677.                 print( "Properties on '%s':" % path )
  678.                 prop_names = sorted( props.keys() )
  679.                 for name in prop_names:
  680.                     if verbose:
  681.                         print( ' %s: %s' % (name, props[name]) )
  682.                     else:
  683.                         print( ' %s' % name )
  684.             
  685.     cmd_pl = cmd_proplist

  686.     def cmd_propget( self, args ):
  687.         recurse = args.getBooleanOption( '--recursive', True )
  688.         revision = args.getOptionalRevision( '--revision', 'working' )
  689.         positional_args = args.getPositionalArgs( 1, 2 )
  690.         if len(positional_args) == 1:
  691.             positional_args.append( '.' )
  692.         if self.client.is_url( positional_args[0] ):
  693.             revision = args.getOptionalRevision( '--revision', 'head' )

  694.         props = self.client.propget( positional_args[0], positional_args[1], revision=revision, recurse=recurse )
  695.         prop_names = sorted( props.keys() )
  696.         for name in prop_names:
  697.             print( '%s: %s' % (name, props[name]) )

  698.     cmd_pg = cmd_propget

  699.     def cmd_propset( self, args ):
  700.         recurse = args.getBooleanOption( '--recursive', True )
  701.         revision = args.getOptionalRevision( '--revision', 'working' )
  702.         verbose = args.getBooleanOption( '--verbose', True )
  703.         positional_args = args.getPositionalArgs( 2, 3 )
  704.         if len(positional_args) == 2:
  705.             positional_args.append( '.' )
  706.         if self.client.is_url( positional_args[0] ):
  707.             revision = args.getOptionalRevision( '--revision', 'head' )

  708.         self.client.propset( positional_args[0], positional_args[1], positional_args[2], revision=revision, recurse=recurse )
  709.     cmd_ps = cmd_propset

  710.     def cmd_propdel( self, args ):
  711.         recurse = args.getBooleanOption( '--recursive', True )
  712.         revision = args.getOptionalRevision( '--revision', 'working' )
  713.         verbose = args.getBooleanOption( '--verbose', True )
  714.         positional_args = args.getPositionalArgs( 1, 2 )
  715.         if len(positional_args) == 1:
  716.             positional_args.append( '.' )
  717.         if self.client.is_url( positional_args[0] ):
  718.             revision = args.getOptionalRevision( '--revision', 'head' )

  719.         self.client.propdel( positional_args[0], positional_args[1], revision=revision, recurse=recurse )
  720.     cmd_pd = cmd_propdel

  721.     def cmd_revproplist( self, args ):
  722.         revision = args.getOptionalRevision( '--revision', 'head' )
  723.         verbose = args.getBooleanOption( '--verbose', False )
  724.         positional_args = args.getPositionalArgs( 0, 1 )
  725.         if len(positional_args) == 0:
  726.             positional_args.append( '.' )

  727.         rev, prop_dict = self.client.revproplist( positional_args[0], revision=revision )
  728.         print( 'Revision: %s' % rev.number )
  729.         prop_keys = prop_dict.keys()
  730.         for key in sorted( prop_keys ):
  731.             print( '%s: %s' % (key, prop_dict[ key ]) )

  732.     cmd_rpl = cmd_revproplist

  733.     def cmd_revpropget( self, args ):
  734.         revision = args.getOptionalRevision( '--revision', 'head' )
  735.         positional_args = args.getPositionalArgs( 1, 2 )
  736.         if len(positional_args) == 1:
  737.             positional_args.append( '.' )

  738.         rev, value = self.client.revpropget( positional_args[0], positional_args[1], revision=revision )
  739.         print( 'Revision: %s' % rev.number )
  740.         print( '%s: %s' % (positional_args[0], value) )

  741.     cmd_rpg = cmd_revpropget

  742.     def cmd_revpropset( self, args ):
  743.         force = args.getBooleanOption( '--force', False )
  744.         revision = args.getOptionalRevision( '--revision', 'head' )
  745.         positional_args = args.getPositionalArgs( 2, 3 )
  746.         if len(positional_args) == 2:
  747.             positional_args.append( '.' )

  748.         rev = self.client.revpropset( positional_args[0], positional_args[1], positional_args[2], revision=revision, force=force )
  749.     cmd_rps = cmd_revpropset

  750.     def cmd_revpropdel( self, args ):
  751.         force = args.getBooleanOption( '--force', False )
  752.         revision = args.getOptionalRevision( '--revision', 'head' )
  753.         positional_args = args.getPositionalArgs( 1, 2 )
  754.         if len(positional_args) == 1:
  755.             positional_args.append( '.' )

  756.         self.client.revpropdel( positional_args[0], positional_args[1], revision=revision, force=force )
  757.     cmd_rpd = cmd_revpropdel

  758.     def cmd_remove( self, args ):
  759.         force = args.getBooleanOption( '--force', True )
  760.         positional_args = args.getPositionalArgs( 1, 0 )
  761.         self.client.remove( positional_args, force=force )
  762.     cmd_rm = cmd_remove

  763.     def cmd_remove_from_changelists( self, args ):
  764.         if not hasattr( self.client, remove_from_changelists ):
  765.             print( 'Error: remove_from_changelists is not supported by this version of Subversion' )
  766.             return

  767.         path = args.getPositionalArgs( 1, 1 )[0]
  768.         self.client.remove_from_changelists( path )

  769.     def cmd_resolved( self, args ):
  770.         recurse = args.getBooleanOption( '--recursive', True )
  771.         positional_args = args.getPositionalArgs( 1, 1 )
  772.         self.client.resolved( positional_args[0], recurse=recurse )

  773.     def cmd_revert( self, args ):
  774.         recurse = args.getBooleanOption( '--recursive', True )
  775.         positional_args = args.getPositionalArgs( 1, 1 )
  776.         self.client.revert( positional_args[0], recurse=recurse )

  777.     def key_by_path( self, a ):
  778.         return a.path

  779.     def cmd_status( self, args ):
  780.         recurse = args.getBooleanOption( '--non-recursive', False )
  781.         verbose = args.getBooleanOption( '--verbose', True )
  782.         quiet = args.getBooleanOption( '--quiet', True )
  783.         ignore = args.getBooleanOption( '--no-ignore', False )
  784.         update = args.getBooleanOption( '--show-updates', True )

  785.         positional_args = args.getPositionalArgs( 0 )
  786.         if len(positional_args) == 0:
  787.             all_files = self.client.status( '', recurse=recurse, get_all=verbose, ignore=ignore, update=update )
  788.             self._cmd_status_print( all_files, verbose, update, ignore, quiet )
  789.         else:
  790.             for arg in positional_args:
  791.                 all_files = self.client.status( arg, recurse=recurse, get_all=verbose, ignore=ignore, update=update )
  792.                 self._cmd_status_print( all_files, verbose, update, ignore, quiet )

  793.     def _cmd_status_print( self, all_files, detailed, update, ignore, quiet ):
  794.         all_files.sort( key=self.key_by_path )
  795.         for file in all_files:
  796.             if file.text_status == pysvn.wc_status_kind.ignored and ignore:
  797.                 continue

  798.             if file.text_status == pysvn.wc_status_kind.unversioned and quiet:
  799.                 continue

  800.             state = '%s%s%s%s%s' % (wc_status_kind_map[ file.text_status ],
  801.                     wc_status_kind_map[ file.prop_status ],
  802.                     ' L'[ file.is_locked ],
  803.                     ' +'[ file.is_copied ],
  804.                     ' S'[ file.is_switched ])

  805.             if( file.repos_text_status != pysvn.wc_status_kind.none
  806.             or file.repos_prop_status != pysvn.wc_status_kind.none ):
  807.                 odd_status = '%s%s' % (wc_status_kind_map[ file.repos_text_status ],
  808.                     wc_status_kind_map[ file.repos_prop_status ])
  809.             else:
  810.                 odd_status = ' '

  811.             lock_state = ' '
  812.             if file.entry is not None and hasattr( file.entry, 'lock_token' ):
  813.                 if file.entry.lock_token is not None:
  814.                     lock_state = 'K'

  815.             if hasattr( file, 'repos_lock' ) and file.repos_lock is not None:
  816.                 lock_state = 'O'

  817.             if file.entry is not None and detailed:
  818.                 print( '%s%s %s %6d %6d %-14s %s' %
  819.                     (state,
  820.                     lock_state,
  821.                     odd_status,
  822.                     file.entry.revision.number,
  823.                     file.entry.commit_revision.number,
  824.                     file.entry.commit_author,
  825.                     file.path) )

  826.             elif detailed:
  827.                 print( '%s%s %s %6s %6s %-14s %s' %
  828.                     (state,
  829.                     lock_state,
  830.                     odd_status,
  831.                     '',
  832.                     '',
  833.                     '',
  834.                     file.path) )

  835.             elif update:
  836.                 print( '%s%s %s %s' %
  837.                     (state,
  838.                     lock_state,
  839.                     odd_status,
  840.                     file.path) )

  841.             else:
  842.                 if( file.text_status != pysvn.wc_status_kind.normal
  843.                 or file.prop_status != pysvn.wc_status_kind.normal
  844.                 or lock_state.strip() != ''):
  845.                     print( '%s%s %s' % (state, lock_state, file.path) )

  846.     cmd_st = cmd_status
  847.     cmd_stat = cmd_status

  848.     def cmd_switch( self, args ):
  849.         recurse = args.getBooleanOption( '--non-recursive', False )
  850.         revision = args.getOptionalRevision( '--revision', 'head' )
  851.         positional_args = args.getPositionalArgs( 1, 2 )
  852.         if len(positional_args) == 1:
  853.             positional_args.append( '.' )
  854.         self.client.switch( positional_args[0], positional_args[1],
  855.                 recurse=recurse, revision=revision )

  856.     def cmd_relocate( self, args ):
  857.         recurse = args.getBooleanOption( '--non-recursive', False )
  858.         positional_args = args.getPositionalArgs( 2, 3 )
  859.         if len(positional_args) == 2:
  860.             positional_args.append( '.' )
  861.         self.client.relocate( positional_args[0], positional_args[1],
  862.                 positional_args[2], recurse=recurse )

  863.     def cmd_unlock( self, args ):
  864.         force = args.getBooleanOption( '--force', False )
  865.         positional_args = args.getPositionalArgs( 1, 1 )
  866.         self.client.unlock( positional_args[0], force );

  867.     def cmd_update( self, args ):
  868.         recurse = args.getBooleanOption( '--non-recursive', False )
  869.         positional_args = args.getPositionalArgs( 0 )
  870.         if len(positional_args) == 0:
  871.             positional_args.append( '.' )

  872.         rev_list = self.client.update( positional_args[0], recurse=recurse )
  873.         self.printNotifyMessages()
  874.         if type(rev_list) == type([]) and len(rev_list) != 1:
  875.             print( 'rev_list = %r' % [rev.number for rev in rev_list] )

  876.         if self.revision_update_complete is not None:
  877.             print( 'Updated to revision %s' % self.revision_update_complete.number )
  878.         else:
  879.             print( 'Updated to unknown revision - update failed?' )

  880.     cmd_up = cmd_update

  881.     def cmd_help( self, args ):
  882.         print( 'Version: pysvn %d.%d.%d-%d' % pysvn.version,'svn %d.%d.%d-%s' % pysvn.svn_version )
  883.         valid_cmd_names = [name for name in SvnCommand.__dict__.keys() if name.find('cmd_') == 0]
  884.         valid_cmd_names.sort()
  885.         print( 'Available subcommands:' )
  886.         index = 0
  887.         line = ''
  888.         for name in valid_cmd_names:
  889.             line = line + (' %-12s' % name[4:])
  890.             if index % 4 == 3:
  891.                 print( line )
  892.                 line = ''
  893.             index += 1

  894. # key is long option name, value is 1 if need next arg as value
  895. long_opt_info = {
  896.     '--pause': 0,

  897.     '--auto-props': 0, # enable automatic properties
  898.     '--config-dir': 1, # read user configuration files from directory ARG
  899.     '--diff-cmd': 1, # use ARG as diff command
  900.     '--diff3-cmd': 1, # use ARG as merge command
  901.     '--dry-run': 0, # try operation but make no changes
  902.     '--editor-cmd': 1, # use ARG as external editor
  903.     '--encoding': 1, # treat value as being in charset encoding ARG
  904.     '--force': 0, # force operation to run
  905.     '--force-log': 0, # force validity of log message source
  906.     '--incremental': 0, # give output suitable for concatenation
  907.     '--new': 1, # use ARG as the newer target
  908.     '--no-auth-cache': 0, # do not cache authentication tokens
  909.     '--no-auto-props': 0, # disable automatic properties
  910.     '--no-diff-deleted': 0, # do not print differences for deleted files
  911.     '--no-ignore': 0, # disregard default and svn:ignore property ignores
  912.     '--no-remove-tempfiles': 0, # do not remove temp files
  913.     '--non-interactive': 0, # do no interactive prompting
  914.     '--notice-ancestry': 0, # notice ancestry when calculating differences
  915.     '--old': 1, # use ARG as the older target
  916.     '--password': 1, # specify a password ARG
  917.     '--relocate': 0, # relocate via URL-rewriting
  918.     '--revprop': 0, # operate on a revision property (use with -r)
  919.     '--strict': 0, # use strict semantics
  920.     '--targets': 1, # pass contents of file ARG as additional args
  921.     '--username': 1, # specify a username ARG
  922.     '--version': 0, # print client version info
  923.     '--xml': 0, # output in xml
  924.     '--file': 1, # read data from file ARG
  925.     '--native-eol': 1, # native eol ARG
  926.     '--non-recursive': 0, # operate on single directory only
  927.     '--recursive': 0, # descend recursively
  928.     '--message': 1, # specify commit message ARG
  929.     '--quiet': 0, # print as little as possible
  930.     '--revision': 1, # revision X or X:Y range. X or Y can be one of:
  931.     '--show-updates': 0, # display update information
  932.     '--verbose': 0, # print extra information
  933.     '--extensions': 1, # pass ARG as bundled options to GNU diff
  934.     '--pysvn-testing': 1, # modify behaviour to assist testing pysvn
  935.     '--debug': 0, # do debug stuff
  936. }

  937. # map short name to long
  938. short_opt_info = {
  939.     '-F': '--file',
  940.     '-N': '--non-recursive',
  941.     '-R': '--recursive',
  942.     '-m': '--message',
  943.     '-q': '--quiet',
  944.     '-r': '--revision',
  945.     '-u': '--show-updates',
  946.     '-v': '--verbose',
  947.     '-x': '--extensions',
  948. }


  949. #
  950. # Usage:
  951. # Construct with a commnad list
  952. # call getCommandName()
  953. # call getBooleanOption() and getOptionalValue() as needed
  954. # finally call getPositionalArgs()
  955. #
  956. #
  957. class SvnArguments:
  958.     def __init__( self, all_args ):
  959.         self.positional_args = []
  960.         self.named_options = {}
  961.         self.used_named_options = {}

  962.         need_next_arg = 0
  963.         name = ''

  964.         for arg in all_args:
  965.             if need_next_arg:
  966.                 self.named_options[ name ] = arg
  967.                 need_next_arg = 0

  968.             elif self._isOption( arg ):
  969.                 name, need_next_arg = self._optionInfo( arg )
  970.                 if not need_next_arg:
  971.                     self.named_options[ name ] = None

  972.             else:
  973.                 expanded_arg = glob.glob( arg )
  974.                 if len(expanded_arg) > 0:
  975.                     self.positional_args.extend( expanded_arg )
  976.                 else:
  977.                     self.positional_args.append( arg )
  978.         if need_next_arg:
  979.             raise CommandError( 'Missing arg to option %s' % name )

  980.     def _isOption( self, arg ):
  981.         return arg[0] == '-'
  982.             
  983.     def _optionInfo( self, opt ):
  984.         # return long_name, arg_needed
  985.         long_opt = short_opt_info.get( opt, opt )
  986.         if long_opt in long_opt_info:
  987.             return long_opt, long_opt_info[ long_opt ]
  988.         raise CommandError( 'unknown option %s' % opt )

  989.     def _checkOptionsUsed( self ):
  990.         # check all options have been used
  991.         for opt_name in self.named_options.keys():
  992.             if opt_name not in self.used_named_options:
  993.                 raise CommandError( 'unused option %s' % opt_name )

  994.     def parsePathWithRevision( self, path_rev, default_rev ):
  995.         if '@' in path_rev:
  996.             path = path_rev[:path_rev.find('@')]
  997.             rev = self._parseRevisionArg( path_rev[path_rev.find('@')+1:] )
  998.         else:
  999.             path = path_rev
  1000.             rev = self._parseRevisionArg( default_rev )
  1001.         return path, rev

  1002.     def _parseRevisionArg( self, rev_string ):
  1003.         if rev_string.lower() == 'base':
  1004.             return pysvn.Revision( pysvn.opt_revision_kind.base )
  1005.         if rev_string.lower() == 'head':
  1006.             return pysvn.Revision( pysvn.opt_revision_kind.head )
  1007.         if rev_string.lower() == 'working':
  1008.             return pysvn.Revision( pysvn.opt_revision_kind.working )
  1009.         if rev_string.lower() == 'committed':
  1010.             return pysvn.Revision( pysvn.opt_revision_kind.committed )
  1011.         if rev_string.lower() == 'prev':
  1012.             return pysvn.Revision( pysvn.opt_revision_kind.prev )
  1013.         if rev_string.lower() == 'unspecified':
  1014.             return pysvn.Revision( pysvn.opt_revision_kind.unspecified )
  1015.         if rev_string[0] == '{' and rev_string[-1] == '}':
  1016.             try:
  1017.                 date = parse_datetime.parse_time( rev_string[1:-2] )
  1018.                 return pysvn.Revision( pysvn.opt_revision_kind.date, date )
  1019.             except parse_datetime.DateTimeSyntaxError as e:
  1020.                 raise CommandError( e.reason() )
  1021.         # either a rev number or a date
  1022.         try:
  1023.             return pysvn.Revision( pysvn.opt_revision_kind.number, int(rev_string) )
  1024.         except ValueError:
  1025.             pass
  1026.         raise CommandError( 'Cannot parse %s as a revision value' % rev_string )


  1027.     def _splitRevisionString( self, rev_string ):
  1028.         # split the string at the first : that is not inside a {} pair
  1029.         if rev_string[0] == '{':
  1030.             # the : may be after the closing }
  1031.             close_paren_index = rev_string.find( '}' )
  1032.             if close_paren_index == -1:
  1033.                 # error leave to others to report
  1034.                 return [rev_string]

  1035.             if close_paren_index == len(rev_string ):
  1036.                 # its just one revision
  1037.                 return [rev_string]

  1038.             if rev_string[close_paren_index+1] == ':':
  1039.                 return [rev_string[:close_paren_index+1], rev_string[close_paren_index+2:]]

  1040.             # another error case
  1041.             return [rev_string]
  1042.         else:
  1043.             return rev_string.split(':',1)

  1044.     def getCommandName( self, default_command ):
  1045.         if len(self.positional_args) > 0:
  1046.             return self.positional_args.pop( 0 )
  1047.         else:
  1048.             return default_command

  1049.     def haveOption( self, opt_name ):
  1050.         return opt_name in self.named_options

  1051.     def getBooleanOption( self, opt_name, present_value=True ):
  1052.         if opt_name in self.named_options:
  1053.             self.used_named_options[ opt_name ] = None
  1054.             return present_value
  1055.         else:
  1056.             return not present_value

  1057.     def getOptionalValue( self, opt_name, default ):
  1058.         if opt_name in self.named_options:
  1059.             self.used_named_options[ opt_name ] = None
  1060.             return self.named_options[ opt_name ]
  1061.         else:
  1062.             return default

  1063.     def getOptionalRevision( self, opt_name, start_default ):
  1064.         if opt_name in self.named_options:
  1065.             self.used_named_options[ opt_name ] = None
  1066.             rev_string = self.named_options[ opt_name ]

  1067.             return self._parseRevisionArg( rev_string )
  1068.         else:
  1069.             return self._parseRevisionArg( start_default )

  1070.     def getMandatoryRevisionPair( self, opt_name ):
  1071.         # parse a M:N or M as revision pair
  1072.         if opt_name not in self.named_options:
  1073.             raise CommandError( 'mandatory %s required' % opt_name )

  1074.         self.used_named_options[ opt_name ] = None

  1075.         rev_strings = self._splitRevisionString( self.named_options[ opt_name ] )
  1076.         if len(rev_strings) == 1:
  1077.             raise CommandError( 'mandatory %s requires a pair of revisions' % opt_name )

  1078.         return [self._parseRevisionArg( rev_strings[0] ),
  1079.             self._parseRevisionArg( rev_strings[1] )]

  1080.     def getOptionalRevisionPair( self, opt_name, start_default, end_default=None ):
  1081.         # parse a M:N or M as revision pair
  1082.         if opt_name in self.named_options:
  1083.             self.used_named_options[ opt_name ] = None
  1084.             rev_strings = self._splitRevisionString( self.named_options[ opt_name ] )
  1085.             if len(rev_strings) == 1:
  1086.                 if end_default is None:
  1087.                     # M means M:M
  1088.                     rev_strings.append( rev_strings[0] )
  1089.                 else:
  1090.                     # M means M:end_default
  1091.                     rev_strings.append( end_default )

  1092.             return [self._parseRevisionArg( rev_strings[0] ),
  1093.                 self._parseRevisionArg( rev_strings[1] )]
  1094.         else:
  1095.             return (self._parseRevisionArg( start_default ),
  1096.                 self._parseRevisionArg( end_default ))

  1097.     def getPositionalArgs( self, min_args, max_args=0 ):
  1098.         # check min and max then return the list
  1099.         if len(self.positional_args) < min_args:
  1100.             raise CommandError( 'too few arguments - need at least %d' % min_args )
  1101.         if max_args != 0 and len(self.positional_args) > max_args:
  1102.             raise CommandError( 'too many arguments - need no more then %d' % max_args )

  1103.         # as this is the last call on the args object we check the option where all used
  1104.         self._checkOptionsUsed()

  1105.         return self.positional_args

  1106. if __name__ == '__main__':
  1107.     sys.exit( main( sys.argv ) )

阅读(966) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~