Compare commits

..

8 Commits

Author SHA1 Message Date
ca8c32cd7a sync: kill git fetch process before SSH control master process
If the SSH control master process is killed while an active git
fetch is using its network socket, the underlying SSH client may
not realize the connection was broken.  This can lead to both the
client and the server waiting indefinitely for network messages
which will never be sent.

Work around the problem by keeping track of any processes that use
the tunnels we establish.  If we are about to kill any of the SSH
control masters that we started, ensure the clients using them are
successfully killed first.

Change-Id: Ida6c124dcb0c6a26bf7dd69cba2fbdc2ecd5b2fc
Signed-off-by: Shawn O. Pearce <sop@google.com>
2010-05-11 18:31:47 -07:00
f0a9a1a30e upload: Move confirmation threshold from 3 to 5 commits
Change-Id: I7275d195cf04f02694206b9f838540b0228ff5e1
2010-05-05 09:20:51 -07:00
879a9a5cf0 upload: Confirm unusually large number of uploaded commit
Add a sentinel check to require a second explicit confirmation if the
user is attempting to upload (or upload --replace) an unusually large
number of commits.  This may help the user to catch an accidentally
incorrect rebase they had done previously.

Change-Id: I12c4d102f90a631d6ad193486a70ffd520ef6ae0
2010-05-04 17:15:37 -07:00
ff6929dde8 branches: Enable output of multiple projects
Fixes a bug introduced by 498a0e8a79
("Make 'repo branches -a' the default behavior").

Change-Id: Ib739f82f4647890c46d7c9fb2f2e63a16a0481de
2010-05-04 07:51:28 -07:00
1c85f4e43b Rename _ssh_sock() to fix code style issue.
Since _ssh_sock is imported out of the git_command module, the leading
underscore should be removed from the function name.
2010-04-27 14:35:27 -07:00
719965af35 Override manifest file only after it is fully written to disk.
We called "Override()" before closing the file passed in argument.

Change-Id: I15adb99deb14297ef72fcb1b0945eb246f172fb0
2010-04-26 11:20:22 -07:00
5732e47ebb Strip refs/heads in the branch sent to the manifest server.
The manifest server doesn't want to have refs/heads passed to it, so
we need to strip that when the branch contains it.

Change-Id: I044f8a9629220e886fd5e02e3c1ac4b4bb6020ba
2010-04-26 11:19:07 -07:00
f3fdf823cf sync: Safely skip already deleted projects
Do not error if a project is missing on the filesystem, is deleted
from manifest.xml, but still exists in project.list.

Change-Id: I1d13e435473c83091e27e4df571504ef493282dd
2010-04-14 14:21:50 -07:00
5 changed files with 107 additions and 37 deletions

View File

@ -17,6 +17,7 @@ import os
import sys import sys
import subprocess import subprocess
import tempfile import tempfile
from signal import SIGTERM
from error import GitError from error import GitError
from trace import REPO_TRACE, IsTrace, Trace from trace import REPO_TRACE, IsTrace, Trace
@ -29,8 +30,9 @@ LAST_CWD = None
_ssh_proxy_path = None _ssh_proxy_path = None
_ssh_sock_path = None _ssh_sock_path = None
_ssh_clients = []
def _ssh_sock(create=True): def ssh_sock(create=True):
global _ssh_sock_path global _ssh_sock_path
if _ssh_sock_path is None: if _ssh_sock_path is None:
if not create: if not create:
@ -51,6 +53,24 @@ def _ssh_proxy():
'git_ssh') 'git_ssh')
return _ssh_proxy_path return _ssh_proxy_path
def _add_ssh_client(p):
_ssh_clients.append(p)
def _remove_ssh_client(p):
try:
_ssh_clients.remove(p)
except ValueError:
pass
def terminate_ssh_clients():
global _ssh_clients
for p in _ssh_clients:
try:
os.kill(p.pid, SIGTERM)
p.wait()
except OSError:
pass
_ssh_clients = []
class _GitCall(object): class _GitCall(object):
def version(self): def version(self):
@ -119,7 +139,7 @@ class GitCommand(object):
if disable_editor: if disable_editor:
env['GIT_EDITOR'] = ':' env['GIT_EDITOR'] = ':'
if ssh_proxy: if ssh_proxy:
env['REPO_SSH_SOCK'] = _ssh_sock() env['REPO_SSH_SOCK'] = ssh_sock()
env['GIT_SSH'] = _ssh_proxy() env['GIT_SSH'] = _ssh_proxy()
if project: if project:
@ -188,6 +208,9 @@ class GitCommand(object):
except Exception, e: except Exception, e:
raise GitError('%s: %s' % (command[1], e)) raise GitError('%s: %s' % (command[1], e))
if ssh_proxy:
_add_ssh_client(p)
self.process = p self.process = p
self.stdin = p.stdin self.stdin = p.stdin
@ -210,4 +233,8 @@ class GitCommand(object):
else: else:
p.stderr = None p.stderr = None
return self.process.wait() try:
rc = p.wait()
finally:
_remove_ssh_client(p)
return rc

View File

@ -23,7 +23,10 @@ from signal import SIGTERM
from urllib2 import urlopen, HTTPError from urllib2 import urlopen, HTTPError
from error import GitError, UploadError from error import GitError, UploadError
from trace import Trace from trace import Trace
from git_command import GitCommand, _ssh_sock
from git_command import GitCommand
from git_command import ssh_sock
from git_command import terminate_ssh_clients
R_HEADS = 'refs/heads/' R_HEADS = 'refs/heads/'
R_TAGS = 'refs/tags/' R_TAGS = 'refs/tags/'
@ -371,7 +374,7 @@ def _open_ssh(host, port):
return False return False
command = ['ssh', command = ['ssh',
'-o','ControlPath %s' % _ssh_sock(), '-o','ControlPath %s' % ssh_sock(),
'-p',str(port), '-p',str(port),
'-M', '-M',
'-N', '-N',
@ -391,6 +394,8 @@ def _open_ssh(host, port):
return True return True
def close_ssh(): def close_ssh():
terminate_ssh_clients()
for key,p in _ssh_cache.iteritems(): for key,p in _ssh_cache.iteritems():
try: try:
os.kill(p.pid, SIGTERM) os.kill(p.pid, SIGTERM)
@ -399,7 +404,7 @@ def close_ssh():
pass pass
_ssh_cache.clear() _ssh_cache.clear()
d = _ssh_sock(create=False) d = ssh_sock(create=False)
if d: if d:
try: try:
os.rmdir(os.path.dirname(d)) os.rmdir(os.path.dirname(d))

View File

@ -136,7 +136,7 @@ is shown, then the branch appears in all projects.
hdr('%c%c %-*s' % (current, published, width, name)) hdr('%c%c %-*s' % (current, published, width, name))
out.write(' |') out.write(' |')
if in_cnt < project_cnt and (in_cnt == 1): if in_cnt < project_cnt:
fmt = out.write fmt = out.write
paths = [] paths = []
if in_cnt < project_cnt - in_cnt: if in_cnt < project_cnt - in_cnt:
@ -150,15 +150,17 @@ is shown, then the branch appears in all projects.
for b in i.projects: for b in i.projects:
have.add(b.project) have.add(b.project)
for p in projects: for p in projects:
paths.append(p.relpath) if not p in have:
paths.append(p.relpath)
s = ' %s %s' % (type, ', '.join(paths)) s = ' %s %s' % (type, ', '.join(paths))
if width + 7 + len(s) < 80: if width + 7 + len(s) < 80:
fmt(s) fmt(s)
else: else:
out.nl() fmt(' %s:' % type)
fmt(' %s:' % type)
for p in paths: for p in paths:
out.nl() out.nl()
fmt(' %s' % p) fmt(width*' ' + ' %s' % p)
else:
out.write(' in all projects')
out.nl() out.nl()

View File

@ -24,6 +24,7 @@ import time
import xmlrpclib import xmlrpclib
from git_command import GIT from git_command import GIT
from git_refs import R_HEADS
from project import HEAD from project import HEAD
from project import Project from project import Project
from project import RemoteSpec from project import RemoteSpec
@ -147,32 +148,36 @@ later is required to fix a server side protocol bug.
if not path: if not path:
continue continue
if path not in new_project_paths: if path not in new_project_paths:
project = Project( """If the path has already been deleted, we don't need to do it
manifest = self.manifest, """
name = path, if os.path.exists(self.manifest.topdir + '/' + path):
remote = RemoteSpec('origin'), project = Project(
gitdir = os.path.join(self.manifest.topdir, manifest = self.manifest,
path, '.git'), name = path,
worktree = os.path.join(self.manifest.topdir, path), remote = RemoteSpec('origin'),
relpath = path, gitdir = os.path.join(self.manifest.topdir,
revisionExpr = 'HEAD', path, '.git'),
revisionId = None) worktree = os.path.join(self.manifest.topdir, path),
if project.IsDirty(): relpath = path,
print >>sys.stderr, 'error: Cannot remove project "%s": \ revisionExpr = 'HEAD',
revisionId = None)
if project.IsDirty():
print >>sys.stderr, 'error: Cannot remove project "%s": \
uncommitted changes are present' % project.relpath uncommitted changes are present' % project.relpath
print >>sys.stderr, ' commit changes, then run sync again' print >>sys.stderr, ' commit changes, then run sync again'
return -1 return -1
else: else:
print >>sys.stderr, 'Deleting obsolete path %s' % project.worktree print >>sys.stderr, 'Deleting obsolete path %s' % project.worktree
shutil.rmtree(project.worktree) shutil.rmtree(project.worktree)
# Try deleting parent subdirs if they are empty # Try deleting parent subdirs if they are empty
dir = os.path.dirname(project.worktree) dir = os.path.dirname(project.worktree)
while dir != self.manifest.topdir: while dir != self.manifest.topdir:
try: try:
os.rmdir(dir) os.rmdir(dir)
except OSError: except OSError:
break break
dir = os.path.dirname(dir) dir = os.path.dirname(dir)
new_project_paths.sort() new_project_paths.sort()
fd = open(file_path, 'w') fd = open(file_path, 'w')
@ -201,6 +206,8 @@ uncommitted changes are present' % project.relpath
p = self.manifest.manifestProject p = self.manifest.manifestProject
b = p.GetBranch(p.CurrentBranch) b = p.GetBranch(p.CurrentBranch)
branch = b.merge branch = b.merge
if branch.startswith(R_HEADS):
branch = branch[len(R_HEADS):]
env = dict(os.environ) env = dict(os.environ)
if (env.has_key('TARGET_PRODUCT') and if (env.has_key('TARGET_PRODUCT') and
@ -219,13 +226,13 @@ uncommitted changes are present' % project.relpath
f = open(manifest_path, 'w') f = open(manifest_path, 'w')
try: try:
f.write(manifest_str) f.write(manifest_str)
self.manifest.Override(manifest_name)
finally: finally:
f.close() f.close()
except IOError: except IOError:
print >>sys.stderr, 'error: cannot write manifest to %s' % \ print >>sys.stderr, 'error: cannot write manifest to %s' % \
manifest_path manifest_path
sys.exit(1) sys.exit(1)
self.manifest.Override(manifest_name)
else: else:
print >>sys.stderr, 'error: %s' % manifest_str print >>sys.stderr, 'error: %s' % manifest_str
sys.exit(1) sys.exit(1)

View File

@ -20,6 +20,17 @@ from command import InteractiveCommand
from editor import Editor from editor import Editor
from error import UploadError from error import UploadError
UNUSUAL_COMMIT_THRESHOLD = 5
def _ConfirmManyUploads(multiple_branches=False):
if multiple_branches:
print "ATTENTION: One or more branches has an unusually high number of commits."
else:
print "ATTENTION: You are uploading an unusually high number of commits."
print "YOU PROBABLY DO NOT MEAN TO DO THIS. (Did you rebase across branches?)"
answer = raw_input("If you are sure you intend to do this, type 'yes': ").strip()
return answer == "yes"
def _die(fmt, *args): def _die(fmt, *args):
msg = fmt % args msg = fmt % args
print >>sys.stderr, 'error: %s' % msg print >>sys.stderr, 'error: %s' % msg
@ -128,6 +139,10 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
answer = sys.stdin.readline().strip() answer = sys.stdin.readline().strip()
answer = answer in ('y', 'Y', 'yes', '1', 'true', 't') answer = answer in ('y', 'Y', 'yes', '1', 'true', 't')
if answer:
if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD:
answer = _ConfirmManyUploads()
if answer: if answer:
self._UploadAndReport([branch], people) self._UploadAndReport([branch], people)
else: else:
@ -192,6 +207,16 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
todo.append(branch) todo.append(branch)
if not todo: if not todo:
_die("nothing uncommented for upload") _die("nothing uncommented for upload")
many_commits = False
for branch in todo:
if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD:
many_commits = True
break
if many_commits:
if not _ConfirmManyUploads(multiple_branches=True):
_die("upload aborted by user")
self._UploadAndReport(todo, people) self._UploadAndReport(todo, people)
def _FindGerritChange(self, branch): def _FindGerritChange(self, branch):
@ -258,6 +283,10 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
print >>sys.stderr, " use 'repo upload' without --replace" print >>sys.stderr, " use 'repo upload' without --replace"
sys.exit(1) sys.exit(1)
if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD:
if not _ConfirmManyUploads(multiple_branches=True):
_die("upload aborted by user")
branch.replace_changes = to_replace branch.replace_changes = to_replace
self._UploadAndReport([branch], people) self._UploadAndReport([branch], people)