Merge changes from topic "windows-support"
* changes:
Port os.rename calls to work on Windows
Workaround shutil.rmtree limitation on Windows
Add support for creating symbolic links on Windows
Make "git command" and "forall" work on Windows
diff --git a/git_config.py b/git_config.py
index e00f6be..8c24739 100644
--- a/git_config.py
+++ b/git_config.py
@@ -50,16 +50,24 @@
from git_command import GitCommand
from git_command import ssh_sock
from git_command import terminate_ssh_clients
+from git_refs import R_CHANGES, R_HEADS, R_TAGS
-R_HEADS = 'refs/heads/'
-R_TAGS = 'refs/tags/'
ID_RE = re.compile(r'^[0-9a-f]{40}$')
REVIEW_CACHE = dict()
+def IsChange(rev):
+ return rev.startswith(R_CHANGES)
+
def IsId(rev):
return ID_RE.match(rev)
+def IsTag(rev):
+ return rev.startswith(R_TAGS)
+
+def IsImmutable(rev):
+ return IsChange(rev) or IsId(rev) or IsTag(rev)
+
def _key(name):
parts = name.split('.')
if len(parts) < 2:
diff --git a/git_refs.py b/git_refs.py
index 3c26606..58c838a 100644
--- a/git_refs.py
+++ b/git_refs.py
@@ -16,11 +16,12 @@
import os
from trace import Trace
-HEAD = 'HEAD'
-R_HEADS = 'refs/heads/'
-R_TAGS = 'refs/tags/'
-R_PUB = 'refs/published/'
-R_M = 'refs/remotes/m/'
+HEAD = 'HEAD'
+R_CHANGES = 'refs/changes/'
+R_HEADS = 'refs/heads/'
+R_TAGS = 'refs/tags/'
+R_PUB = 'refs/published/'
+R_M = 'refs/remotes/m/'
class GitRefs(object):
diff --git a/progress.py b/progress.py
index d948654..0dd5d1a 100644
--- a/progress.py
+++ b/progress.py
@@ -21,7 +21,8 @@
_NOT_TTY = not os.isatty(2)
class Progress(object):
- def __init__(self, title, total=0, units=''):
+ def __init__(self, title, total=0, units='', print_newline=False,
+ always_print_percentage=False):
self._title = title
self._total = total
self._done = 0
@@ -29,6 +30,8 @@
self._start = time()
self._show = False
self._units = units
+ self._print_newline = print_newline
+ self._always_print_percentage = always_print_percentage
def update(self, inc=1):
self._done += inc
@@ -50,13 +53,14 @@
else:
p = (100 * self._done) / self._total
- if self._lastp != p:
+ if self._lastp != p or self._always_print_percentage:
self._lastp = p
- sys.stderr.write('\r%s: %3d%% (%d%s/%d%s) ' % (
+ sys.stderr.write('\r%s: %3d%% (%d%s/%d%s)%s' % (
self._title,
p,
self._done, self._units,
- self._total, self._units))
+ self._total, self._units,
+ "\n" if self._print_newline else ""))
sys.stderr.flush()
def end(self):
diff --git a/project.py b/project.py
index e8de484..e700d16 100644
--- a/project.py
+++ b/project.py
@@ -177,11 +177,15 @@
def UploadForReview(self, people,
auto_topic=False,
draft=False,
+ private=False,
+ wip=False,
dest_branch=None):
self.project.UploadForReview(self.name,
people,
auto_topic=auto_topic,
draft=draft,
+ private=private,
+ wip=wip,
dest_branch=dest_branch)
def GetPublishedRefs(self):
@@ -1108,6 +1112,8 @@
people=([], []),
auto_topic=False,
draft=False,
+ private=False,
+ wip=False,
dest_branch=None):
"""Uploads the named branch for code review.
"""
@@ -1159,9 +1165,14 @@
dest_branch)
if auto_topic:
ref_spec = ref_spec + '/' + branch.name
+
if not url.startswith('ssh://'):
rp = ['r=%s' % p for p in people[0]] + \
['cc=%s' % p for p in people[1]]
+ if private:
+ rp = rp + ['private']
+ if wip:
+ rp = rp + ['wip']
if rp:
ref_spec = ref_spec + '%' + ','.join(rp)
cmd.append(ref_spec)
@@ -1275,7 +1286,7 @@
need_to_fetch = not (optimized_fetch and
(ID_RE.match(self.revisionExpr) and
- self._CheckForSha1()))
+ self._CheckForImmutableRevision()))
if (need_to_fetch and
not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
current_branch_only=current_branch_only,
@@ -1885,7 +1896,7 @@
# Direct Git Commands ##
- def _CheckForSha1(self):
+ def _CheckForImmutableRevision(self):
try:
# if revision (sha or tag) is not present then following function
# throws an error.
@@ -1939,7 +1950,9 @@
tag_name = self.revisionExpr[len(R_TAGS):]
if is_sha1 or tag_name is not None:
- if self._CheckForSha1():
+ if self._CheckForImmutableRevision():
+ print('Skipped fetching project %s (already have persistent ref)'
+ % self.name)
return True
if is_sha1 and not depth:
# When syncing a specific commit and --depth is not set:
@@ -2095,7 +2108,7 @@
# We just synced the upstream given branch; verify we
# got what we wanted, else trigger a second run of all
# refs.
- if not self._CheckForSha1():
+ if not self._CheckForImmutableRevision():
if current_branch_only and depth:
# Sync the current branch only with depth set to None
return self._RemoteFetch(name=name,
@@ -2961,14 +2974,14 @@
self.revisionExpr = base
self.revisionId = None
- def MetaBranchSwitch(self):
+ def MetaBranchSwitch(self, submodules=False):
""" Prepare MetaProject for manifest branch switch
"""
# detach and delete manifest branch, allowing a new
# branch to take over
syncbuf = SyncBuffer(self.config, detach_head=True)
- self.Sync_LocalHalf(syncbuf)
+ self.Sync_LocalHalf(syncbuf, submodules=submodules)
syncbuf.Finish()
return GitCommand(self,
diff --git a/subcmds/download.py b/subcmds/download.py
index a029462..e1010aa 100644
--- a/subcmds/download.py
+++ b/subcmds/download.py
@@ -26,11 +26,12 @@
common = True
helpSummary = "Download and checkout a change"
helpUsage = """
-%prog {project change[/patchset]}...
+%prog {[project] change[/patchset]}...
"""
helpDescription = """
The '%prog' command downloads a change from the review system and
makes it available in your project's local working directory.
+If no project is specified try to use current directory as a project.
"""
def _Options(self, p):
@@ -55,7 +56,7 @@
m = CHANGE_RE.match(a)
if m:
if not project:
- self.Usage()
+ project = self.GetProjects(".")[0]
chg_id = int(m.group(1))
if m.group(2):
ps_id = int(m.group(2))
diff --git a/subcmds/init.py b/subcmds/init.py
index e647091..eeddca0 100644
--- a/subcmds/init.py
+++ b/subcmds/init.py
@@ -256,7 +256,7 @@
sys.exit(1)
if opt.manifest_branch:
- m.MetaBranchSwitch()
+ m.MetaBranchSwitch(submodules=opt.submodules)
syncbuf = SyncBuffer(m.config)
m.Sync_LocalHalf(syncbuf, submodules=opt.submodules)
diff --git a/subcmds/stage.py b/subcmds/stage.py
index 2884976..9d35426 100644
--- a/subcmds/stage.py
+++ b/subcmds/stage.py
@@ -60,8 +60,8 @@
out.nl()
for i in range(len(all_projects)):
- p = all_projects[i]
- out.write('%3d: %s', i + 1, p.relpath + '/')
+ project = all_projects[i]
+ out.write('%3d: %s', i + 1, project.relpath + '/')
out.nl()
out.nl()
diff --git a/subcmds/start.py b/subcmds/start.py
index 290b689..c3ec303 100644
--- a/subcmds/start.py
+++ b/subcmds/start.py
@@ -18,7 +18,7 @@
import sys
from command import Command
-from git_config import IsId
+from git_config import IsImmutable
from git_command import git
import gitc_utils
from progress import Progress
@@ -96,11 +96,11 @@
project.Sync_LocalHalf(sync_buf)
project.revisionId = gitc_project.old_revision
- # If the current revision is a specific SHA1 then we can't push back
- # to it; so substitute with dest_branch if defined, or with manifest
- # default revision instead.
+ # If the current revision is immutable, such as a SHA1, a tag or
+ # a change, then we can't push back to it. Substitute with
+ # dest_branch, if defined; or with manifest default revision instead.
branch_merge = ''
- if IsId(project.revisionExpr):
+ if IsImmutable(project.revisionExpr):
if project.dest_branch:
branch_merge = project.dest_branch
else:
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 797fc40..b88c596 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -356,7 +356,9 @@
def _Fetch(self, projects, opt):
fetched = set()
lock = _threading.Lock()
- pm = Progress('Fetching projects', len(projects))
+ pm = Progress('Fetching projects', len(projects),
+ print_newline=not(opt.quiet),
+ always_print_percentage=opt.quiet)
objdir_project_map = dict()
for project in projects:
@@ -393,7 +395,7 @@
t.join()
# If we saw an error, exit with code 1 so that other scripts can check.
- if err_event.isSet():
+ if err_event.isSet() and not opt.force_broken:
print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
sys.exit(1)
@@ -779,8 +781,8 @@
# generate a new args list to represent the opened projects.
# TODO: make this more reliable -- if there's a project name/path overlap,
# this may choose the wrong project.
- args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
- for p in opened_projects]
+ args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
+ for path in opened_projects]
if not args:
return
all_projects = self.GetProjects(args,
diff --git a/subcmds/upload.py b/subcmds/upload.py
index 1172dad..61b18bc 100644
--- a/subcmds/upload.py
+++ b/subcmds/upload.py
@@ -154,6 +154,12 @@
p.add_option('-d', '--draft',
action='store_true', dest='draft', default=False,
help='If specified, upload as a draft.')
+ p.add_option('-p', '--private',
+ action='store_true', dest='private', default=False,
+ help='If specified, upload as a private change.')
+ p.add_option('-w', '--wip',
+ action='store_true', dest='wip', default=False,
+ help='If specified, upload as a work-in-progress change.')
p.add_option('-D', '--destination', '--dest',
type='string', action='store', dest='dest_branch',
metavar='BRANCH',
@@ -198,7 +204,8 @@
commit_list = branch.commits
destination = opt.dest_branch or project.dest_branch or project.revisionExpr
- print('Upload project %s/ to remote branch %s:' % (project.relpath, destination))
+ print('Upload project %s/ to remote branch %s%s:' %
+ (project.relpath, destination, ' (draft)' if opt.draft else ''))
print(' branch %s (%2d commit%s, %s):' % (
name,
len(commit_list),
@@ -377,7 +384,12 @@
branch.uploaded = False
continue
- branch.UploadForReview(people, auto_topic=opt.auto_topic, draft=opt.draft, dest_branch=destination)
+ branch.UploadForReview(people,
+ auto_topic=opt.auto_topic,
+ draft=opt.draft,
+ private=opt.private,
+ wip=opt.wip,
+ dest_branch=destination)
branch.uploaded = True
except UploadError as e:
branch.error = e
@@ -463,8 +475,8 @@
self.manifest.topdir,
self.manifest.manifestProject.GetRemote('origin').url,
abort_if_user_denies=True)
- pending_proj_names = [project.name for (project, avail) in pending]
- pending_worktrees = [project.worktree for (project, avail) in pending]
+ pending_proj_names = [project.name for (project, available) in pending]
+ pending_worktrees = [project.worktree for (project, available) in pending]
try:
hook.Run(opt.allow_all_hooks, project_list=pending_proj_names,
worktree_list=pending_worktrees)