mirror of
https://github.com/google/adb-sync.git
synced 2026-01-03 01:48:02 +00:00
pyformat.
This commit is contained in:
309
adb-sync
309
adb-sync
@@ -13,7 +13,6 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
"""Sync files from/to an Android device."""
|
"""Sync files from/to an Android device."""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
@@ -30,13 +29,13 @@ import time
|
|||||||
|
|
||||||
|
|
||||||
def _sprintf(s, *args):
|
def _sprintf(s, *args):
|
||||||
# To be able to use string formatting, we first have to covert to
|
# To be able to use string formatting, we first have to covert to
|
||||||
# unicode strings; however, we must do so in a way that preserves all
|
# unicode strings; however, we must do so in a way that preserves all
|
||||||
# bytes, and convert back at the end. An encoding that maps all byte
|
# bytes, and convert back at the end. An encoding that maps all byte
|
||||||
# values to different Unicode codepoints is cp437.
|
# values to different Unicode codepoints is cp437.
|
||||||
return (s.decode('cp437') % tuple([
|
return (s.decode('cp437') % tuple([
|
||||||
(x.decode('cp437') if type(x) == bytes else x) for x in args
|
(x.decode('cp437') if type(x) == bytes else x) for x in args
|
||||||
])).encode('cp437')
|
])).encode('cp437')
|
||||||
|
|
||||||
|
|
||||||
class AdbFileSystem(object):
|
class AdbFileSystem(object):
|
||||||
@@ -52,7 +51,8 @@ class AdbFileSystem(object):
|
|||||||
# - st_mode (but only about S_ISDIR and S_ISREG properties)
|
# - st_mode (but only about S_ISDIR and S_ISREG properties)
|
||||||
# Therefore, we only capture parts of 'ls -l' output that we actually use.
|
# Therefore, we only capture parts of 'ls -l' output that we actually use.
|
||||||
# The other fields will be filled with dummy values.
|
# The other fields will be filled with dummy values.
|
||||||
LS_TO_STAT_RE = re.compile(br'''^
|
LS_TO_STAT_RE = re.compile(
|
||||||
|
br"""^
|
||||||
(?:
|
(?:
|
||||||
(?P<S_IFREG> -) |
|
(?P<S_IFREG> -) |
|
||||||
(?P<S_IFBLK> b) |
|
(?P<S_IFBLK> b) |
|
||||||
@@ -86,7 +86,8 @@ class AdbFileSystem(object):
|
|||||||
[ ]
|
[ ]
|
||||||
# Don't capture filename for symlinks (ambiguous).
|
# Don't capture filename for symlinks (ambiguous).
|
||||||
(?(S_IFLNK) .* | (?P<filename> .*))
|
(?(S_IFLNK) .* | (?P<filename> .*))
|
||||||
$''', re.DOTALL | re.VERBOSE)
|
$""", re.DOTALL | re.VERBOSE)
|
||||||
|
|
||||||
def LsToStat(self, line):
|
def LsToStat(self, line):
|
||||||
"""Convert a line from 'ls -l' output to a stat result.
|
"""Convert a line from 'ls -l' output to a stat result.
|
||||||
|
|
||||||
@@ -109,19 +110,28 @@ class AdbFileSystem(object):
|
|||||||
|
|
||||||
# Get the values we're interested in.
|
# Get the values we're interested in.
|
||||||
st_mode = ( # 0755
|
st_mode = ( # 0755
|
||||||
stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
|
stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH
|
||||||
if groups['S_IFREG']: st_mode |= stat.S_IFREG
|
| stat.S_IXOTH)
|
||||||
if groups['S_IFBLK']: st_mode |= stat.S_IFBLK
|
if groups['S_IFREG']:
|
||||||
if groups['S_IFCHR']: st_mode |= stat.S_IFCHR
|
st_mode |= stat.S_IFREG
|
||||||
if groups['S_IFDIR']: st_mode |= stat.S_IFDIR
|
if groups['S_IFBLK']:
|
||||||
if groups['S_IFIFO']: st_mode |= stat.S_IFIFO
|
st_mode |= stat.S_IFBLK
|
||||||
if groups['S_IFLNK']: st_mode |= stat.S_IFLNK
|
if groups['S_IFCHR']:
|
||||||
if groups['S_IFSOCK']: st_mode |= stat.S_IFSOCK
|
st_mode |= stat.S_IFCHR
|
||||||
|
if groups['S_IFDIR']:
|
||||||
|
st_mode |= stat.S_IFDIR
|
||||||
|
if groups['S_IFIFO']:
|
||||||
|
st_mode |= stat.S_IFIFO
|
||||||
|
if groups['S_IFLNK']:
|
||||||
|
st_mode |= stat.S_IFLNK
|
||||||
|
if groups['S_IFSOCK']:
|
||||||
|
st_mode |= stat.S_IFSOCK
|
||||||
st_size = groups['st_size']
|
st_size = groups['st_size']
|
||||||
if st_size is not None:
|
if st_size is not None:
|
||||||
st_size = int(st_size)
|
st_size = int(st_size)
|
||||||
st_mtime = time.mktime(time.strptime(match.group('st_mtime').decode('utf-8'),
|
st_mtime = time.mktime(
|
||||||
'%Y-%m-%d %H:%M'))
|
time.strptime(
|
||||||
|
match.group('st_mtime').decode('utf-8'), '%Y-%m-%d %H:%M'))
|
||||||
|
|
||||||
# Fill the rest with dummy values.
|
# Fill the rest with dummy values.
|
||||||
st_ino = 1
|
st_ino = 1
|
||||||
@@ -145,13 +155,14 @@ class AdbFileSystem(object):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
popen_args: Arguments for subprocess.Popen; stdout=PIPE is implicitly
|
popen_args: Arguments for subprocess.Popen; stdout=PIPE is implicitly
|
||||||
added.
|
added.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
An object for use by 'with'.
|
An object for use by 'with'.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Stdout(object):
|
class Stdout(object):
|
||||||
|
|
||||||
def __init__(self, popen):
|
def __init__(self, popen):
|
||||||
self.popen = popen
|
self.popen = popen
|
||||||
|
|
||||||
@@ -184,13 +195,14 @@ class AdbFileSystem(object):
|
|||||||
# while echo does its own backslash escape handling additionally to the
|
# while echo does its own backslash escape handling additionally to the
|
||||||
# shell's. Too bad printf "%s\n" is not available.
|
# shell's. Too bad printf "%s\n" is not available.
|
||||||
test_strings = [
|
test_strings = [
|
||||||
b'(',
|
b'(', b'(; #`ls`$PATH\'"(\\\\\\\\){};!\xc0\xaf\xff\xc2\xbf'
|
||||||
b'(; #`ls`$PATH\'"(\\\\\\\\){};!\xc0\xaf\xff\xc2\xbf'
|
|
||||||
]
|
]
|
||||||
for test_string in test_strings:
|
for test_string in test_strings:
|
||||||
good = False
|
good = False
|
||||||
with self.Stdout(self.adb + [b'shell', _sprintf(b'date +%s',
|
with self.Stdout(
|
||||||
self.QuoteArgument(test_string))]) as stdout:
|
self.adb +
|
||||||
|
[b'shell',
|
||||||
|
_sprintf(b'date +%s', self.QuoteArgument(test_string))]) as stdout:
|
||||||
for line in stdout:
|
for line in stdout:
|
||||||
line = line.rstrip(b'\r\n')
|
line = line.rstrip(b'\r\n')
|
||||||
if line == test_string:
|
if line == test_string:
|
||||||
@@ -201,11 +213,13 @@ class AdbFileSystem(object):
|
|||||||
|
|
||||||
def listdir(self, path): # os's name, so pylint: disable=g-bad-name
|
def listdir(self, path): # os's name, so pylint: disable=g-bad-name
|
||||||
"""List the contents of a directory, caching them for later lstat calls."""
|
"""List the contents of a directory, caching them for later lstat calls."""
|
||||||
with self.Stdout(self.adb + [b'shell', _sprintf(b'ls -al %s',
|
with self.Stdout(
|
||||||
self.QuoteArgument(path + b'/'))]) as stdout:
|
self.adb +
|
||||||
|
[b'shell',
|
||||||
|
_sprintf(b'ls -al %s', self.QuoteArgument(path + b'/'))]) as stdout:
|
||||||
for line in stdout:
|
for line in stdout:
|
||||||
if line.startswith(b'total '):
|
if line.startswith(b'total '):
|
||||||
continue
|
continue
|
||||||
line = line.rstrip(b'\r\n')
|
line = line.rstrip(b'\r\n')
|
||||||
try:
|
try:
|
||||||
statdata, filename = self.LsToStat(line)
|
statdata, filename = self.LsToStat(line)
|
||||||
@@ -221,11 +235,13 @@ class AdbFileSystem(object):
|
|||||||
"""Stat a file."""
|
"""Stat a file."""
|
||||||
if path in self.stat_cache:
|
if path in self.stat_cache:
|
||||||
return self.stat_cache[path]
|
return self.stat_cache[path]
|
||||||
with self.Stdout(self.adb + [b'shell', _sprintf(b'ls -ald %s',
|
with self.Stdout(
|
||||||
self.QuoteArgument(path))]) as stdout:
|
self.adb +
|
||||||
|
[b'shell', _sprintf(b'ls -ald %s', self.QuoteArgument(path))]
|
||||||
|
) as stdout:
|
||||||
for line in stdout:
|
for line in stdout:
|
||||||
if line.startswith(b'total '):
|
if line.startswith(b'total '):
|
||||||
continue
|
continue
|
||||||
line = line.rstrip(b'\r\n')
|
line = line.rstrip(b'\r\n')
|
||||||
statdata, filename = self.LsToStat(line)
|
statdata, filename = self.LsToStat(line)
|
||||||
self.stat_cache[path] = statdata
|
self.stat_cache[path] = statdata
|
||||||
@@ -234,20 +250,23 @@ class AdbFileSystem(object):
|
|||||||
|
|
||||||
def unlink(self, path): # os's name, so pylint: disable=g-bad-name
|
def unlink(self, path): # os's name, so pylint: disable=g-bad-name
|
||||||
"""Delete a file."""
|
"""Delete a file."""
|
||||||
if subprocess.call(self.adb + [b'shell', _sprintf(b'rm %s',
|
if subprocess.call(
|
||||||
self.QuoteArgument(path))]) != 0:
|
self.adb +
|
||||||
|
[b'shell', _sprintf(b'rm %s', self.QuoteArgument(path))]) != 0:
|
||||||
raise OSError('unlink failed')
|
raise OSError('unlink failed')
|
||||||
|
|
||||||
def rmdir(self, path): # os's name, so pylint: disable=g-bad-name
|
def rmdir(self, path): # os's name, so pylint: disable=g-bad-name
|
||||||
"""Delete a directory."""
|
"""Delete a directory."""
|
||||||
if subprocess.call(self.adb + [b'shell', _sprintf(b'rmdir %s',
|
if subprocess.call(
|
||||||
self.QuoteArgument(path))]) != 0:
|
self.adb +
|
||||||
|
[b'shell', _sprintf(b'rmdir %s', self.QuoteArgument(path))]) != 0:
|
||||||
raise OSError('rmdir failed')
|
raise OSError('rmdir failed')
|
||||||
|
|
||||||
def makedirs(self, path): # os's name, so pylint: disable=g-bad-name
|
def makedirs(self, path): # os's name, so pylint: disable=g-bad-name
|
||||||
"""Create a directory."""
|
"""Create a directory."""
|
||||||
if subprocess.call(self.adb + [b'shell', _sprintf(b'mkdir -p %s',
|
if subprocess.call(
|
||||||
self.QuoteArgument(path))]) != 0:
|
self.adb +
|
||||||
|
[b'shell', _sprintf(b'mkdir -p %s', self.QuoteArgument(path))]) != 0:
|
||||||
raise OSError('mkdir failed')
|
raise OSError('mkdir failed')
|
||||||
|
|
||||||
def utime(self, path, times):
|
def utime(self, path, times):
|
||||||
@@ -255,18 +274,23 @@ class AdbFileSystem(object):
|
|||||||
"""Set the time of a file to a specified unix time."""
|
"""Set the time of a file to a specified unix time."""
|
||||||
atime, mtime = times
|
atime, mtime = times
|
||||||
timestr = time.strftime(b'%Y%m%d.%H%M%S', time.localtime(mtime))
|
timestr = time.strftime(b'%Y%m%d.%H%M%S', time.localtime(mtime))
|
||||||
if subprocess.call(self.adb + [b'shell', _sprintf(b'touch -mt %s %s',
|
if subprocess.call(self.adb + [
|
||||||
timestr, self.QuoteArgument(path))]) != 0:
|
b'shell',
|
||||||
|
_sprintf(b'touch -mt %s %s', timestr, self.QuoteArgument(path))
|
||||||
|
]) != 0:
|
||||||
raise OSError('touch failed')
|
raise OSError('touch failed')
|
||||||
timestr = time.strftime(b'%Y%m%d.%H%M%S', time.localtime(atime))
|
timestr = time.strftime(b'%Y%m%d.%H%M%S', time.localtime(atime))
|
||||||
if subprocess.call(self.adb + [b'shell',_sprintf( b'touch -at %s %s',
|
if subprocess.call(self.adb + [
|
||||||
timestr, self.QuoteArgument(path))]) != 0:
|
b'shell',
|
||||||
|
_sprintf(b'touch -at %s %s', timestr, self.QuoteArgument(path))
|
||||||
|
]) != 0:
|
||||||
raise OSError('touch failed')
|
raise OSError('touch failed')
|
||||||
|
|
||||||
def glob(self, path):
|
def glob(self, path):
|
||||||
with self.Stdout(self.adb + [b'shell',
|
with self.Stdout(
|
||||||
_sprintf(b'for p in %s; do echo "$p"; done',
|
self.adb +
|
||||||
path)]) as stdout:
|
[b'shell', _sprintf(b'for p in %s; do echo "$p"; done', path)]
|
||||||
|
) as stdout:
|
||||||
for line in stdout:
|
for line in stdout:
|
||||||
yield line.rstrip(b'\r\n')
|
yield line.rstrip(b'\r\n')
|
||||||
|
|
||||||
@@ -413,8 +437,8 @@ class FileSyncer(object):
|
|||||||
logging.info('Scanning and diffing...')
|
logging.info('Scanning and diffing...')
|
||||||
locallist = BuildFileList(os, self.local)
|
locallist = BuildFileList(os, self.local)
|
||||||
remotelist = BuildFileList(self.adb, self.remote)
|
remotelist = BuildFileList(self.adb, self.remote)
|
||||||
self.local_only, self.both, self.remote_only = DiffLists(locallist,
|
self.local_only, self.both, self.remote_only = DiffLists(
|
||||||
remotelist)
|
locallist, remotelist)
|
||||||
if not self.local_only and not self.both and not self.remote_only:
|
if not self.local_only and not self.both and not self.remote_only:
|
||||||
logging.warning('No files seen. User error?')
|
logging.warning('No files seen. User error?')
|
||||||
self.src_to_dst = (self.local_to_remote, self.remote_to_local)
|
self.src_to_dst = (self.local_to_remote, self.remote_to_local)
|
||||||
@@ -448,13 +472,14 @@ class FileSyncer(object):
|
|||||||
dry_run = self.dry_run
|
dry_run = self.dry_run
|
||||||
|
|
||||||
class DeleteInterruptedFile(object):
|
class DeleteInterruptedFile(object):
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, traceback):
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
if exc_type is not None:
|
if exc_type is not None:
|
||||||
logging.info(b'Interrupted-%s-Delete: %r',
|
logging.info(b'Interrupted-%s-Delete: %r',
|
||||||
'Pull' if fs == os else 'Push', name)
|
'Pull' if fs == os else 'Push', name)
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
fs.unlink(name)
|
fs.unlink(name)
|
||||||
return False
|
return False
|
||||||
@@ -522,17 +547,19 @@ class FileSyncer(object):
|
|||||||
if stat.S_ISDIR(localstat.st_mode) or stat.S_ISDIR(remotestat.st_mode):
|
if stat.S_ISDIR(localstat.st_mode) or stat.S_ISDIR(remotestat.st_mode):
|
||||||
if not self.allow_replace:
|
if not self.allow_replace:
|
||||||
logging.info('Would have to replace to do this. '
|
logging.info('Would have to replace to do this. '
|
||||||
'Use --force to allow this.')
|
'Use --force to allow this.')
|
||||||
continue
|
continue
|
||||||
if not self.allow_overwrite:
|
if not self.allow_overwrite:
|
||||||
logging.info('Would have to overwrite to do this, '
|
logging.info('Would have to overwrite to do this, '
|
||||||
'which --no-clobber forbids.')
|
'which --no-clobber forbids.')
|
||||||
continue
|
continue
|
||||||
if stat.S_ISDIR(dst_stat.st_mode):
|
if stat.S_ISDIR(dst_stat.st_mode):
|
||||||
kill_files = [x for x in self.dst_only[i]
|
kill_files = [
|
||||||
if x[0][:len(name) + 1] == name + b'/']
|
x for x in self.dst_only[i] if x[0][:len(name) + 1] == name + b'/'
|
||||||
self.dst_only[i][:] = [x for x in self.dst_only[i]
|
]
|
||||||
if x[0][:len(name) + 1] != name + b'/']
|
self.dst_only[i][:] = [
|
||||||
|
x for x in self.dst_only[i] if x[0][:len(name) + 1] != name + b'/'
|
||||||
|
]
|
||||||
for l, s in reversed(kill_files):
|
for l, s in reversed(kill_files):
|
||||||
if stat.S_ISDIR(s.st_mode):
|
if stat.S_ISDIR(s.st_mode):
|
||||||
if not self.dry_run:
|
if not self.dry_run:
|
||||||
@@ -571,10 +598,9 @@ class FileSyncer(object):
|
|||||||
self.num_bytes += s.st_size
|
self.num_bytes += s.st_size
|
||||||
if not self.dry_run:
|
if not self.dry_run:
|
||||||
if self.preserve_times:
|
if self.preserve_times:
|
||||||
logging.info('%s-Times: accessed %s, modified %s',
|
logging.info('%s-Times: accessed %s, modified %s', self.push[i],
|
||||||
self.push[i],
|
time.asctime(time.localtime(s.st_atime)),
|
||||||
time.asctime(time.localtime(s.st_atime)),
|
time.asctime(time.localtime(s.st_mtime)))
|
||||||
time.asctime(time.localtime(s.st_mtime)))
|
|
||||||
self.dst_fs[i].utime(dst_name, (s.st_atime, s.st_mtime))
|
self.dst_fs[i].utime(dst_name, (s.st_atime, s.st_mtime))
|
||||||
|
|
||||||
def TimeReport(self):
|
def TimeReport(self):
|
||||||
@@ -585,7 +611,8 @@ class FileSyncer(object):
|
|||||||
end_time = time.time()
|
end_time = time.time()
|
||||||
dt = end_time - self.start_time
|
dt = end_time - self.start_time
|
||||||
rate = self.num_bytes / 1024.0 / dt
|
rate = self.num_bytes / 1024.0 / dt
|
||||||
logging.info('Total: %d KB/s (%d bytes in %.3fs)', rate, self.num_bytes, dt)
|
logging.info('Total: %d KB/s (%d bytes in %.3fs)', rate, self.num_bytes,
|
||||||
|
dt)
|
||||||
|
|
||||||
|
|
||||||
def ExpandWildcards(globber, path):
|
def ExpandWildcards(globber, path):
|
||||||
@@ -617,64 +644,106 @@ def FixPath(src, dst):
|
|||||||
|
|
||||||
def main(*args):
|
def main(*args):
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='Synchronize a directory between an Android device and the '+
|
description='Synchronize a directory between an Android device and the ' +
|
||||||
'local file system')
|
'local file system')
|
||||||
parser.add_argument('source', metavar='SRC', type=str, nargs='+',
|
parser.add_argument(
|
||||||
help='The directory to read files/directories from. '+
|
'source',
|
||||||
'This must be a local path if -R is not specified, '+
|
metavar='SRC',
|
||||||
'and an Android path if -R is specified. If SRC does '+
|
type=str,
|
||||||
'not end with a final slash, its last path component '+
|
nargs='+',
|
||||||
'is appended to DST (like rsync does).')
|
help='The directory to read files/directories from. ' +
|
||||||
parser.add_argument('destination', metavar='DST', type=str,
|
'This must be a local path if -R is not specified, ' +
|
||||||
help='The directory to write files/directories to. '+
|
'and an Android path if -R is specified. If SRC does ' +
|
||||||
'This must be an Android path if -R is not specified, '+
|
'not end with a final slash, its last path component ' +
|
||||||
'and a local path if -R is specified.')
|
'is appended to DST (like rsync does).')
|
||||||
parser.add_argument('-e', '--adb', metavar='COMMAND', default='adb', type=str,
|
parser.add_argument(
|
||||||
help='Use the given adb binary and arguments.')
|
'destination',
|
||||||
parser.add_argument('--device', action='store_true',
|
metavar='DST',
|
||||||
help='Directs command to the only connected USB device; '+
|
type=str,
|
||||||
'returns an error if more than one USB device is '+
|
help='The directory to write files/directories to. ' +
|
||||||
'present. '+
|
'This must be an Android path if -R is not specified, ' +
|
||||||
'Corresponds to the "-d" option of adb.')
|
'and a local path if -R is specified.')
|
||||||
parser.add_argument('--emulator', action='store_true',
|
parser.add_argument(
|
||||||
help='Directs command to the only running emulator; '+
|
'-e',
|
||||||
'returns an error if more than one emulator is running. '+
|
'--adb',
|
||||||
'Corresponds to the "-e" option of adb.')
|
metavar='COMMAND',
|
||||||
parser.add_argument('-s', '--serial', metavar='DEVICE', type=str,
|
default='adb',
|
||||||
help='Directs command to the device or emulator with '+
|
type=str,
|
||||||
'the given serial number or qualifier. Overrides '+
|
help='Use the given adb binary and arguments.')
|
||||||
'ANDROID_SERIAL environment variable. Use "adb devices" '+
|
parser.add_argument(
|
||||||
'to list all connected devices with their respective '+
|
'--device',
|
||||||
'serial number. '+
|
action='store_true',
|
||||||
'Corresponds to the "-s" option of adb.')
|
help='Directs command to the only connected USB device; ' +
|
||||||
parser.add_argument('-H', '--host', metavar='HOST', type=str,
|
'returns an error if more than one USB device is ' + 'present. ' +
|
||||||
help='Name of adb server host (default: localhost). '+
|
'Corresponds to the "-d" option of adb.')
|
||||||
'Corresponds to the "-H" option of adb.')
|
parser.add_argument(
|
||||||
parser.add_argument('-P', '--port', metavar='PORT', type=str,
|
'--emulator',
|
||||||
help='Port of adb server (default: 5037). '+
|
action='store_true',
|
||||||
'Corresponds to the "-P" option of adb.')
|
help='Directs command to the only running emulator; ' +
|
||||||
parser.add_argument('-R', '--reverse', action='store_true',
|
'returns an error if more than one emulator is running. ' +
|
||||||
help='Reverse sync (pull, not push).')
|
'Corresponds to the "-e" option of adb.')
|
||||||
parser.add_argument('-2', '--two-way', action='store_true',
|
parser.add_argument(
|
||||||
help='Two-way sync (compare modification time; after '+
|
'-s',
|
||||||
'the sync, both sides will have all files in the '+
|
'--serial',
|
||||||
'respective newest version. This relies on the clocks '+
|
metavar='DEVICE',
|
||||||
'of your system and the device to match.')
|
type=str,
|
||||||
|
help='Directs command to the device or emulator with ' +
|
||||||
|
'the given serial number or qualifier. Overrides ' +
|
||||||
|
'ANDROID_SERIAL environment variable. Use "adb devices" ' +
|
||||||
|
'to list all connected devices with their respective ' + 'serial number. '
|
||||||
|
+ 'Corresponds to the "-s" option of adb.')
|
||||||
|
parser.add_argument(
|
||||||
|
'-H',
|
||||||
|
'--host',
|
||||||
|
metavar='HOST',
|
||||||
|
type=str,
|
||||||
|
help='Name of adb server host (default: localhost). ' +
|
||||||
|
'Corresponds to the "-H" option of adb.')
|
||||||
|
parser.add_argument(
|
||||||
|
'-P',
|
||||||
|
'--port',
|
||||||
|
metavar='PORT',
|
||||||
|
type=str,
|
||||||
|
help='Port of adb server (default: 5037). ' +
|
||||||
|
'Corresponds to the "-P" option of adb.')
|
||||||
|
parser.add_argument(
|
||||||
|
'-R',
|
||||||
|
'--reverse',
|
||||||
|
action='store_true',
|
||||||
|
help='Reverse sync (pull, not push).')
|
||||||
|
parser.add_argument(
|
||||||
|
'-2',
|
||||||
|
'--two-way',
|
||||||
|
action='store_true',
|
||||||
|
help='Two-way sync (compare modification time; after ' +
|
||||||
|
'the sync, both sides will have all files in the ' +
|
||||||
|
'respective newest version. This relies on the clocks ' +
|
||||||
|
'of your system and the device to match.')
|
||||||
#parser.add_argument('-t', '--times', action='store_true',
|
#parser.add_argument('-t', '--times', action='store_true',
|
||||||
# help='Preserve modification times when copying.')
|
# help='Preserve modification times when copying.')
|
||||||
parser.add_argument('-d', '--delete', action='store_true',
|
parser.add_argument(
|
||||||
help='Delete files from DST that are not present on '+
|
'-d',
|
||||||
'SRC. Mutually exclusive with -2.')
|
'--delete',
|
||||||
parser.add_argument('-f', '--force', action='store_true',
|
action='store_true',
|
||||||
help='Allow deleting files/directories when having to '+
|
help='Delete files from DST that are not present on ' +
|
||||||
'replace a file by a directory or vice versa. This is '+
|
'SRC. Mutually exclusive with -2.')
|
||||||
'disabled by default to prevent large scale accidents.')
|
parser.add_argument(
|
||||||
parser.add_argument('-n', '--no-clobber', action='store_true',
|
'-f',
|
||||||
help='Do not ever overwrite any '+
|
'--force',
|
||||||
'existing files. Mutually exclusive with -f.')
|
action='store_true',
|
||||||
parser.add_argument('--dry-run',action='store_true',
|
help='Allow deleting files/directories when having to ' +
|
||||||
help='Do not do anything - just show what would '+
|
'replace a file by a directory or vice versa. This is ' +
|
||||||
'be done.')
|
'disabled by default to prevent large scale accidents.')
|
||||||
|
parser.add_argument(
|
||||||
|
'-n',
|
||||||
|
'--no-clobber',
|
||||||
|
action='store_true',
|
||||||
|
help='Do not ever overwrite any ' +
|
||||||
|
'existing files. Mutually exclusive with -f.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--dry-run',
|
||||||
|
action='store_true',
|
||||||
|
help='Do not do anything - just show what would ' + 'be done.')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
args_encoding = locale.getdefaultlocale()[1]
|
args_encoding = locale.getdefaultlocale()[1]
|
||||||
|
|
||||||
@@ -734,17 +803,18 @@ def main(*args):
|
|||||||
if (remote_to_local and local_to_remote) or delete_missing:
|
if (remote_to_local and local_to_remote) or delete_missing:
|
||||||
if ((remote_to_local and len(localpaths) != len(set(localpaths))) or
|
if ((remote_to_local and len(localpaths) != len(set(localpaths))) or
|
||||||
(local_to_remote and len(remotepaths) != len(set(remotepaths)))):
|
(local_to_remote and len(remotepaths) != len(set(remotepaths)))):
|
||||||
logging.error('--two-way and --delete are only supported for disjoint sets of '
|
logging.error(
|
||||||
'source and destination paths (in other words, all SRC must '
|
'--two-way and --delete are only supported for disjoint sets of '
|
||||||
'differ in basename).')
|
'source and destination paths (in other words, all SRC must '
|
||||||
|
'differ in basename).')
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
return
|
return
|
||||||
|
|
||||||
for i in range(len(localpaths)):
|
for i in range(len(localpaths)):
|
||||||
logging.info('Sync: local %r, remote %r', localpaths[i], remotepaths[i])
|
logging.info('Sync: local %r, remote %r', localpaths[i], remotepaths[i])
|
||||||
syncer = FileSyncer(adb, localpaths[i], remotepaths[i],
|
syncer = FileSyncer(adb, localpaths[i], remotepaths[i], local_to_remote,
|
||||||
local_to_remote, remote_to_local, preserve_times,
|
remote_to_local, preserve_times, delete_missing,
|
||||||
delete_missing, allow_overwrite, allow_replace, dry_run)
|
allow_overwrite, allow_replace, dry_run)
|
||||||
if not syncer.IsWorking():
|
if not syncer.IsWorking():
|
||||||
logging.error('Device not connected or not working.')
|
logging.error('Device not connected or not working.')
|
||||||
return
|
return
|
||||||
@@ -756,5 +826,6 @@ def main(*args):
|
|||||||
finally:
|
finally:
|
||||||
syncer.TimeReport()
|
syncer.TimeReport()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main(*sys.argv)
|
main(*sys.argv)
|
||||||
|
|||||||
Reference in New Issue
Block a user