@@ -986,6 +986,20 @@ def add(self, items, force=True, fprogress=lambda *args: None):
986986
987987return entries_added
988988
989+ def _items_to_rela_paths (self ,items ):
990+ """Returns a list of repo-relative paths from the given items which
991+ may be absolute or relative paths, entries or blobs"""
992+ paths = list ()
993+ for item in items :
994+ if isinstance (item , (BaseIndexEntry ,Blob )):
995+ paths .append (self ._to_relative_path (item .path ))
996+ elif isinstance (item ,basestring ):
997+ paths .append (self ._to_relative_path (item ))
998+ else :
999+ raise TypeError ("Invalid item type: %r" % item )
1000+ # END for each item
1001+ return paths
1002+
9891003@clear_cache
9901004@default_index
9911005def remove (self ,items ,working_tree = False ,** kwargs ):
@@ -1021,7 +1035,8 @@ def remove(self, items, working_tree=False, **kwargs):
10211035as 'r' to allow recurive removal of
10221036
10231037Returns
1024- List(path_string, ...) list of paths that have been removed effectively.
1038+ List(path_string, ...) list of repository relative paths that have
1039+ been removed effectively.
10251040This is interesting to know in case you have provided a directory or
10261041globs. Paths are relative to the repository.
10271042"""
@@ -1031,22 +1046,84 @@ def remove(self, items, working_tree=False, **kwargs):
10311046args .append ("--" )
10321047
10331048# preprocess paths
1034- paths = list ()
1035- for item in items :
1036- if isinstance (item , (BaseIndexEntry ,Blob )):
1037- paths .append (self ._to_relative_path (item .path ))
1038- elif isinstance (item ,basestring ):
1039- paths .append (self ._to_relative_path (item ))
1040- else :
1041- raise TypeError ("Invalid item type: %r" % item )
1042- # END for each item
1043-
1049+ paths = self ._items_to_rela_paths (items )
10441050removed_paths = self .repo .git .rm (args ,paths ,** kwargs ).splitlines ()
10451051
10461052# process output to gain proper paths
10471053# rm 'path'
10481054return [p [4 :- 1 ]for p in removed_paths ]
1055+
1056+ @clear_cache
1057+ @default_index
1058+ def move (self ,items ,skip_errors = False ,** kwargs ):
1059+ """
1060+ Rename/move the items, whereas the last item is considered the destination of
1061+ the move operation. If the destination is a file, the first item ( of two )
1062+ must be a file as well. If the destination is a directory, it may be preceeded
1063+ by one or more directories or files.
1064+
1065+ The working tree will be affected in non-bare repositories.
1066+
1067+ ``items``
1068+ Multiple types of items are supported, please see the 'remove' method
1069+ for reference.
1070+ ``skip_errors``
1071+ If True, errors such as ones resulting from missing source files will
1072+ be skpped.
1073+ ``**kwargs``
1074+ Additional arguments you would like to pass to git-mv, such as dry_run
1075+ or force.
1076+
1077+ Returns
1078+ List(tuple(source_path_string, destination_path_string), ...)
1079+ A list of pairs, containing the source file moved as well as its
1080+ actual destination. Relative to the repository root.
1081+
1082+ Raises
1083+ ValueErorr: If only one item was given
1084+ GitCommandError: If git could not handle your request
1085+ """
1086+ args = list ()
1087+ if skip_errors :
1088+ args .append ('-k' )
1089+
1090+ paths = self ._items_to_rela_paths (items )
1091+ if len (paths )< 2 :
1092+ raise ValueError ("Please provide at least one source and one destination of the move operation" )
1093+
1094+ was_dry_run = kwargs .pop ('dry_run' ,kwargs .pop ('n' ,None ))
1095+ kwargs ['dry_run' ]= True
1096+
1097+ # first execute rename in dryrun so the command tells us what it actually does
1098+ # ( for later output )
1099+ out = list ()
1100+ mvlines = self .repo .git .mv (args ,paths ,** kwargs ).splitlines ()
1101+
1102+ # parse result - first 0:n/2 lines are 'checking ', the remaining ones
1103+ # are the 'renaming' ones which we parse
1104+ for ln in xrange (len (mvlines )/ 2 ,len (mvlines )):
1105+ tokens = mvlines [ln ].split (' to ' )
1106+ assert len (tokens )== 2 ,"Too many tokens in %s" % mvlines [ln ]
1107+
1108+ # [0] = Renaming x
1109+ # [1] = y
1110+ out .append ((tokens [0 ][9 :],tokens [1 ]))
1111+ # END for each line to parse
1112+
1113+ # either prepare for the real run, or output the dry-run result
1114+ if was_dry_run :
1115+ return out
1116+ # END handle dryrun
10491117
1118+
1119+ # now apply the actual operation
1120+ kwargs .pop ('dry_run' )
1121+ self .repo .git .mv (args ,paths ,** kwargs )
1122+
1123+ return out
1124+
1125+
1126+
10501127@default_index
10511128def commit (self ,message ,parent_commits = None ,head = True ):
10521129"""