1# -*- coding: utf-8 -*-
2# Copyright 2011 Google Inc. All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15"""Implementation of Unix-like mv command for cloud storage providers."""
16
17from __future__ import absolute_import
18
19from gslib.command import Command
20from gslib.command_argument import CommandArgument
21from gslib.commands.cp import CP_SUB_ARGS
22from gslib.cs_api_map import ApiSelector
23from gslib.exception import CommandException
24from gslib.storage_url import StorageUrlFromString
25from gslib.util import NO_MAX
26
27
28_SYNOPSIS = """
29  gsutil mv [-p] src_url dst_url
30  gsutil mv [-p] src_url... dst_url
31  gsutil mv [-p] -I dst_url
32"""
33
34_DETAILED_HELP_TEXT = ("""
35<B>SYNOPSIS</B>
36""" + _SYNOPSIS + """
37
38
39<B>DESCRIPTION</B>
40  The gsutil mv command allows you to move data between your local file
41  system and the cloud, move data within the cloud, and move data between
42  cloud storage providers. For example, to move all objects from a
43  bucket to a local directory you could use:
44
45    gsutil mv gs://my_bucket dir
46
47  Similarly, to move all objects from a local directory to a bucket you could
48  use:
49
50    gsutil mv ./dir gs://my_bucket
51
52
53<B>RENAMING BUCKET SUBDIRECTORIES</B>
54  You can use the gsutil mv command to rename subdirectories. For example,
55  the command:
56
57    gsutil mv gs://my_bucket/olddir gs://my_bucket/newdir
58
59  would rename all objects and subdirectories under gs://my_bucket/olddir to be
60  under gs://my_bucket/newdir, otherwise preserving the subdirectory structure.
61
62  If you do a rename as specified above and you want to preserve ACLs, you
63  should use the -p option (see OPTIONS).
64
65  Note that when using mv to rename bucket subdirectories you cannot specify
66  the source URL using wildcards. You need to spell out the complete name:
67
68    gsutil mv gs://my_bucket/olddir gs://my_bucket/newdir
69
70  If you have a large number of files to move you might want to use the
71  gsutil -m option, to perform a multi-threaded/multi-processing move:
72
73    gsutil -m mv gs://my_bucket/olddir gs://my_bucket/newdir
74
75
76<B>NON-ATOMIC OPERATION</B>
77  Unlike the case with many file systems, the gsutil mv command does not
78  perform a single atomic operation. Rather, it performs a copy from source
79  to destination followed by removing the source for each object.
80
81
82<B>OPTIONS</B>
83  All options that are available for the gsutil cp command are also available
84  for the gsutil mv command (except for the -R flag, which is implied by the
85  gsutil mv command). Please see the OPTIONS sections of "gsutil help cp"
86  for more information.
87
88""")
89
90
91class MvCommand(Command):
92  """Implementation of gsutil mv command.
93
94     Note that there is no atomic rename operation - this command is simply
95     a shorthand for 'cp' followed by 'rm'.
96  """
97
98  # Command specification. See base class for documentation.
99  command_spec = Command.CreateCommandSpec(
100      'mv',
101      command_name_aliases=['move', 'ren', 'rename'],
102      usage_synopsis=_SYNOPSIS,
103      min_args=1,
104      max_args=NO_MAX,
105      # Flags for mv are passed through to cp.
106      supported_sub_args=CP_SUB_ARGS,
107      file_url_ok=True,
108      provider_url_ok=False,
109      urls_start_arg=0,
110      gs_api_support=[ApiSelector.XML, ApiSelector.JSON],
111      gs_default_api=ApiSelector.JSON,
112      argparse_arguments=[
113          CommandArgument.MakeZeroOrMoreCloudOrFileURLsArgument()
114      ]
115  )
116  # Help specification. See help_provider.py for documentation.
117  help_spec = Command.HelpSpec(
118      help_name='mv',
119      help_name_aliases=['move', 'rename'],
120      help_type='command_help',
121      help_one_line_summary='Move/rename objects and/or subdirectories',
122      help_text=_DETAILED_HELP_TEXT,
123      subcommand_help_text={},
124  )
125
126  def RunCommand(self):
127    """Command entry point for the mv command."""
128    # Check each source arg up, refusing to delete a bucket src URL (force users
129    # to explicitly do that as a separate operation).
130    for arg_to_check in self.args[0:-1]:
131      url = StorageUrlFromString(arg_to_check)
132      if url.IsCloudUrl() and (url.IsBucket() or url.IsProvider()):
133        raise CommandException('You cannot move a source bucket using the mv '
134                               'command. If you meant to move\nall objects in '
135                               'the bucket, you can use a command like:\n'
136                               '\tgsutil mv %s/* %s' %
137                               (arg_to_check, self.args[-1]))
138
139    # Insert command-line opts in front of args so they'll be picked up by cp
140    # and rm commands (e.g., for -p option). Use undocumented (internal
141    # use-only) cp -M option, which causes each original object to be deleted
142    # after successfully copying to its destination, and also causes naming
143    # behavior consistent with Unix mv naming behavior (see comments in
144    # ConstructDstUrl).
145    unparsed_args = ['-M']
146    if self.recursion_requested:
147      unparsed_args.append('-R')
148    unparsed_args.extend(self.unparsed_args)
149    self.command_runner.RunNamedCommand('cp', unparsed_args, self.headers,
150                                        self.debug, self.parallel_operations)
151
152    return 0
153