summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.pylintrc2
-rw-r--r--color.py62
-rw-r--r--git_config.py8
-rwxr-xr-xmain.py22
-rw-r--r--manifest_xml.py44
-rw-r--r--project.py36
-rwxr-xr-xrepo11
-rw-r--r--subcmds/download.py6
-rw-r--r--subcmds/grep.py14
-rw-r--r--subcmds/help.py2
-rw-r--r--subcmds/info.py195
-rw-r--r--subcmds/init.py4
-rw-r--r--subcmds/overview.py3
-rw-r--r--subcmds/sync.py180
-rw-r--r--subcmds/upload.py42
-rw-r--r--tests/test_git_config.py82
16 files changed, 456 insertions, 257 deletions
diff --git a/.pylintrc b/.pylintrc
index 9f81ee15..9e8882ee 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -53,7 +53,7 @@ load-plugins=
53enable=RP0004 53enable=RP0004
54 54
55# Disable the message(s) with the given id(s). 55# Disable the message(s) with the given id(s).
56disable=R0903,R0912,R0913,R0914,R0915,W0141,C0111,C0103,C0323,C0322,C0324,W0603,W0703,R0911,C0301,C0302,R0902,R0904,W0142,W0212,E1101,E1103,R0201,W0201,W0122,W0232,W0311,RP0001,RP0003,RP0101,RP0002,RP0401,RP0701,RP0801 56disable=R0903,R0912,R0913,R0914,R0915,W0141,C0111,C0103,W0603,W0703,R0911,C0301,C0302,R0902,R0904,W0142,W0212,E1101,E1103,R0201,W0201,W0122,W0232,RP0001,RP0003,RP0101,RP0002,RP0401,RP0701,RP0801
57 57
58[REPORTS] 58[REPORTS]
59 59
diff --git a/color.py b/color.py
index 33bc8779..d856313f 100644
--- a/color.py
+++ b/color.py
@@ -40,47 +40,47 @@ RESET = "\033[m" # pylint: disable=W1401
40 # backslash is not anomalous 40 # backslash is not anomalous
41 41
42def is_color(s): 42def is_color(s):
43 return s in COLORS 43 return s in COLORS
44 44
45def is_attr(s): 45def is_attr(s):
46 return s in ATTRS 46 return s in ATTRS
47 47
48def _Color(fg = None, bg = None, attr = None): 48def _Color(fg = None, bg = None, attr = None):
49 fg = COLORS[fg] 49 fg = COLORS[fg]
50 bg = COLORS[bg] 50 bg = COLORS[bg]
51 attr = ATTRS[attr] 51 attr = ATTRS[attr]
52 52
53 if attr >= 0 or fg >= 0 or bg >= 0: 53 if attr >= 0 or fg >= 0 or bg >= 0:
54 need_sep = False 54 need_sep = False
55 code = "\033[" #pylint: disable=W1401 55 code = "\033[" #pylint: disable=W1401
56 56
57 if attr >= 0: 57 if attr >= 0:
58 code += chr(ord('0') + attr) 58 code += chr(ord('0') + attr)
59 need_sep = True 59 need_sep = True
60 60
61 if fg >= 0: 61 if fg >= 0:
62 if need_sep: 62 if need_sep:
63 code += ';' 63 code += ';'
64 need_sep = True 64 need_sep = True
65 65
66 if fg < 8: 66 if fg < 8:
67 code += '3%c' % (ord('0') + fg) 67 code += '3%c' % (ord('0') + fg)
68 else: 68 else:
69 code += '38;5;%d' % fg 69 code += '38;5;%d' % fg
70 70
71 if bg >= 0: 71 if bg >= 0:
72 if need_sep: 72 if need_sep:
73 code += ';' 73 code += ';'
74 need_sep = True 74 need_sep = True
75 75
76 if bg < 8: 76 if bg < 8:
77 code += '4%c' % (ord('0') + bg) 77 code += '4%c' % (ord('0') + bg)
78 else: 78 else:
79 code += '48;5;%d' % bg 79 code += '48;5;%d' % bg
80 code += 'm' 80 code += 'm'
81 else: 81 else:
82 code = '' 82 code = ''
83 return code 83 return code
84 84
85 85
86class Coloring(object): 86class Coloring(object):
diff --git a/git_config.py b/git_config.py
index 6bb92003..56cc6a24 100644
--- a/git_config.py
+++ b/git_config.py
@@ -303,10 +303,10 @@ class GitConfig(object):
303 for line in d.rstrip('\0').split('\0'): # pylint: disable=W1401 303 for line in d.rstrip('\0').split('\0'): # pylint: disable=W1401
304 # Backslash is not anomalous 304 # Backslash is not anomalous
305 if '\n' in line: 305 if '\n' in line:
306 key, val = line.split('\n', 1) 306 key, val = line.split('\n', 1)
307 else: 307 else:
308 key = line 308 key = line
309 val = None 309 val = None
310 310
311 if key in c: 311 if key in c:
312 c[key].append(val) 312 c[key].append(val)
@@ -431,7 +431,7 @@ def _open_ssh(host, port=None):
431 '-o','ControlPath %s' % ssh_sock(), 431 '-o','ControlPath %s' % ssh_sock(),
432 host] 432 host]
433 if port is not None: 433 if port is not None:
434 command_base[1:1] = ['-p',str(port)] 434 command_base[1:1] = ['-p', str(port)]
435 435
436 # Since the key wasn't in _master_keys, we think that master isn't running. 436 # Since the key wasn't in _master_keys, we think that master isn't running.
437 # ...but before actually starting a master, we'll double-check. This can 437 # ...but before actually starting a master, we'll double-check. This can
diff --git a/main.py b/main.py
index 35622ce9..95944546 100755
--- a/main.py
+++ b/main.py
@@ -274,17 +274,17 @@ class _UserAgentHandler(urllib.request.BaseHandler):
274 return req 274 return req
275 275
276def _AddPasswordFromUserInput(handler, msg, req): 276def _AddPasswordFromUserInput(handler, msg, req):
277 # If repo could not find auth info from netrc, try to get it from user input 277 # If repo could not find auth info from netrc, try to get it from user input
278 url = req.get_full_url() 278 url = req.get_full_url()
279 user, password = handler.passwd.find_user_password(None, url) 279 user, password = handler.passwd.find_user_password(None, url)
280 if user is None: 280 if user is None:
281 print(msg) 281 print(msg)
282 try: 282 try:
283 user = raw_input('User: ') 283 user = raw_input('User: ')
284 password = getpass.getpass() 284 password = getpass.getpass()
285 except KeyboardInterrupt: 285 except KeyboardInterrupt:
286 return 286 return
287 handler.passwd.add_password(None, url, user, password) 287 handler.passwd.add_password(None, url, user, password)
288 288
289class _BasicAuthHandler(urllib.request.HTTPBasicAuthHandler): 289class _BasicAuthHandler(urllib.request.HTTPBasicAuthHandler):
290 def http_error_401(self, req, fp, code, msg, headers): 290 def http_error_401(self, req, fp, code, msg, headers):
diff --git a/manifest_xml.py b/manifest_xml.py
index bb93bca3..1b954561 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -67,7 +67,7 @@ class _XmlRemote(object):
67 # urljoin will get confused if there is no scheme in the base url 67 # urljoin will get confused if there is no scheme in the base url
68 # ie, if manifestUrl is of the form <hostname:port> 68 # ie, if manifestUrl is of the form <hostname:port>
69 if manifestUrl.find(':') != manifestUrl.find('/') - 1: 69 if manifestUrl.find(':') != manifestUrl.find('/') - 1:
70 manifestUrl = 'gopher://' + manifestUrl 70 manifestUrl = 'gopher://' + manifestUrl
71 url = urlparse.urljoin(manifestUrl, url) 71 url = urlparse.urljoin(manifestUrl, url)
72 return re.sub(r'^gopher://', '', url) 72 return re.sub(r'^gopher://', '', url)
73 73
@@ -349,24 +349,24 @@ class XmlManifest(object):
349 nodes = [] 349 nodes = []
350 for node in manifest.childNodes: # pylint:disable=W0631 350 for node in manifest.childNodes: # pylint:disable=W0631
351 # We only get here if manifest is initialised 351 # We only get here if manifest is initialised
352 if node.nodeName == 'include': 352 if node.nodeName == 'include':
353 name = self._reqatt(node, 'name') 353 name = self._reqatt(node, 'name')
354 fp = os.path.join(include_root, name) 354 fp = os.path.join(include_root, name)
355 if not os.path.isfile(fp): 355 if not os.path.isfile(fp):
356 raise ManifestParseError, \ 356 raise ManifestParseError, \
357 "include %s doesn't exist or isn't a file" % \ 357 "include %s doesn't exist or isn't a file" % \
358 (name,) 358 (name,)
359 try: 359 try:
360 nodes.extend(self._ParseManifestXml(fp, include_root)) 360 nodes.extend(self._ParseManifestXml(fp, include_root))
361 # should isolate this to the exact exception, but that's 361 # should isolate this to the exact exception, but that's
362 # tricky. actual parsing implementation may vary. 362 # tricky. actual parsing implementation may vary.
363 except (KeyboardInterrupt, RuntimeError, SystemExit): 363 except (KeyboardInterrupt, RuntimeError, SystemExit):
364 raise 364 raise
365 except Exception as e: 365 except Exception as e:
366 raise ManifestParseError( 366 raise ManifestParseError(
367 "failed parsing included manifest %s: %s", (name, e)) 367 "failed parsing included manifest %s: %s", (name, e))
368 else: 368 else:
369 nodes.append(node) 369 nodes.append(node)
370 return nodes 370 return nodes
371 371
372 def _ParseManifest(self, node_list): 372 def _ParseManifest(self, node_list):
@@ -404,9 +404,9 @@ class XmlManifest(object):
404 if node.nodeName == 'manifest-server': 404 if node.nodeName == 'manifest-server':
405 url = self._reqatt(node, 'url') 405 url = self._reqatt(node, 'url')
406 if self._manifest_server is not None: 406 if self._manifest_server is not None:
407 raise ManifestParseError( 407 raise ManifestParseError(
408 'duplicate manifest-server in %s' % 408 'duplicate manifest-server in %s' %
409 (self.manifestFile)) 409 (self.manifestFile))
410 self._manifest_server = url 410 self._manifest_server = url
411 411
412 for node in itertools.chain(*node_list): 412 for node in itertools.chain(*node_list):
diff --git a/project.py b/project.py
index 22ac9efd..75c5e5e8 100644
--- a/project.py
+++ b/project.py
@@ -556,7 +556,7 @@ class Project(object):
556 '--unmerged', 556 '--unmerged',
557 '--ignore-missing', 557 '--ignore-missing',
558 '--refresh') 558 '--refresh')
559 if self.work_git.DiffZ('diff-index','-M','--cached',HEAD): 559 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
560 return True 560 return True
561 if self.work_git.DiffZ('diff-files'): 561 if self.work_git.DiffZ('diff-files'):
562 return True 562 return True
@@ -585,14 +585,14 @@ class Project(object):
585 return self._userident_email 585 return self._userident_email
586 586
587 def _LoadUserIdentity(self): 587 def _LoadUserIdentity(self):
588 u = self.bare_git.var('GIT_COMMITTER_IDENT') 588 u = self.bare_git.var('GIT_COMMITTER_IDENT')
589 m = re.compile("^(.*) <([^>]*)> ").match(u) 589 m = re.compile("^(.*) <([^>]*)> ").match(u)
590 if m: 590 if m:
591 self._userident_name = m.group(1) 591 self._userident_name = m.group(1)
592 self._userident_email = m.group(2) 592 self._userident_email = m.group(2)
593 else: 593 else:
594 self._userident_name = '' 594 self._userident_name = ''
595 self._userident_email = '' 595 self._userident_email = ''
596 596
597 def GetRemote(self, name): 597 def GetRemote(self, name):
598 """Get the configuration for a single remote. 598 """Get the configuration for a single remote.
@@ -1381,14 +1381,14 @@ class Project(object):
1381 tag_name = None 1381 tag_name = None
1382 1382
1383 def CheckForSha1(): 1383 def CheckForSha1():
1384 try: 1384 try:
1385 # if revision (sha or tag) is not present then following function 1385 # if revision (sha or tag) is not present then following function
1386 # throws an error. 1386 # throws an error.
1387 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr) 1387 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1388 return True 1388 return True
1389 except GitError: 1389 except GitError:
1390 # There is no such persistent revision. We have to fetch it. 1390 # There is no such persistent revision. We have to fetch it.
1391 return False 1391 return False
1392 1392
1393 if current_branch_only: 1393 if current_branch_only:
1394 if ID_RE.match(self.revisionExpr) is not None: 1394 if ID_RE.match(self.revisionExpr) is not None:
@@ -1880,7 +1880,7 @@ class Project(object):
1880 self.level = self.level[1:] 1880 self.level = self.level[1:]
1881 1881
1882 info = info[1:].split(' ') 1882 info = info[1:].split(' ')
1883 info =_Info(path, *info) 1883 info = _Info(path, *info)
1884 if info.status in ('R', 'C'): 1884 if info.status in ('R', 'C'):
1885 info.src_path = info.path 1885 info.src_path = info.path
1886 info.path = out.next() 1886 info.path = out.next()
diff --git a/repo b/repo
index 4d8e8dca..ac1eca8f 100755
--- a/repo
+++ b/repo
@@ -3,8 +3,8 @@
3## repo default configuration 3## repo default configuration
4## 4##
5from __future__ import print_function 5from __future__ import print_function
6REPO_URL='https://gerrit.googlesource.com/git-repo' 6REPO_URL = 'https://gerrit.googlesource.com/git-repo'
7REPO_REV='stable' 7REPO_REV = 'stable'
8 8
9# Copyright (C) 2008 Google Inc. 9# Copyright (C) 2008 Google Inc.
10# 10#
@@ -24,7 +24,7 @@ REPO_REV='stable'
24VERSION = (1, 19) 24VERSION = (1, 19)
25 25
26# increment this if the MAINTAINER_KEYS block is modified 26# increment this if the MAINTAINER_KEYS block is modified
27KEYRING_VERSION = (1,1) 27KEYRING_VERSION = (1, 1)
28MAINTAINER_KEYS = """ 28MAINTAINER_KEYS = """
29 29
30 Repo Maintainer <repo@android.kernel.org> 30 Repo Maintainer <repo@android.kernel.org>
@@ -154,7 +154,8 @@ group.add_option('-m', '--manifest-name',
154 help='initial manifest file', metavar='NAME.xml') 154 help='initial manifest file', metavar='NAME.xml')
155group.add_option('--mirror', 155group.add_option('--mirror',
156 dest='mirror', action='store_true', 156 dest='mirror', action='store_true',
157 help='mirror the forrest') 157 help='create a replica of the remote repositories '
158 'rather than a client working directory')
158group.add_option('--reference', 159group.add_option('--reference',
159 dest='reference', 160 dest='reference',
160 help='location of mirror directory', metavar='DIR') 161 help='location of mirror directory', metavar='DIR')
@@ -225,7 +226,7 @@ def _Init(args):
225 except OSError as e: 226 except OSError as e:
226 print('fatal: cannot make %s directory: %s' 227 print('fatal: cannot make %s directory: %s'
227 % (repodir, e.strerror), file=sys.stderr) 228 % (repodir, e.strerror), file=sys.stderr)
228 # Don't faise CloneFailure; that would delete the 229 # Don't raise CloneFailure; that would delete the
229 # name. Instead exit immediately. 230 # name. Instead exit immediately.
230 # 231 #
231 sys.exit(1) 232 sys.exit(1)
diff --git a/subcmds/download.py b/subcmds/download.py
index 6aa54afa..471e88b5 100644
--- a/subcmds/download.py
+++ b/subcmds/download.py
@@ -33,13 +33,13 @@ makes it available in your project's local working directory.
33""" 33"""
34 34
35 def _Options(self, p): 35 def _Options(self, p):
36 p.add_option('-c','--cherry-pick', 36 p.add_option('-c', '--cherry-pick',
37 dest='cherrypick', action='store_true', 37 dest='cherrypick', action='store_true',
38 help="cherry-pick instead of checkout") 38 help="cherry-pick instead of checkout")
39 p.add_option('-r','--revert', 39 p.add_option('-r', '--revert',
40 dest='revert', action='store_true', 40 dest='revert', action='store_true',
41 help="revert instead of checkout") 41 help="revert instead of checkout")
42 p.add_option('-f','--ff-only', 42 p.add_option('-f', '--ff-only',
43 dest='ffonly', action='store_true', 43 dest='ffonly', action='store_true',
44 help="force fast-forward merge") 44 help="force fast-forward merge")
45 45
diff --git a/subcmds/grep.py b/subcmds/grep.py
index fa5f8765..dd391cfa 100644
--- a/subcmds/grep.py
+++ b/subcmds/grep.py
@@ -85,7 +85,7 @@ contain a line that matches both expressions:
85 g.add_option('--cached', 85 g.add_option('--cached',
86 action='callback', callback=carry, 86 action='callback', callback=carry,
87 help='Search the index, instead of the work tree') 87 help='Search the index, instead of the work tree')
88 g.add_option('-r','--revision', 88 g.add_option('-r', '--revision',
89 dest='revision', action='append', metavar='TREEish', 89 dest='revision', action='append', metavar='TREEish',
90 help='Search TREEish, instead of the work tree') 90 help='Search TREEish, instead of the work tree')
91 91
@@ -97,7 +97,7 @@ contain a line that matches both expressions:
97 g.add_option('-i', '--ignore-case', 97 g.add_option('-i', '--ignore-case',
98 action='callback', callback=carry, 98 action='callback', callback=carry,
99 help='Ignore case differences') 99 help='Ignore case differences')
100 g.add_option('-a','--text', 100 g.add_option('-a', '--text',
101 action='callback', callback=carry, 101 action='callback', callback=carry,
102 help="Process binary files as if they were text") 102 help="Process binary files as if they were text")
103 g.add_option('-I', 103 g.add_option('-I',
@@ -126,7 +126,7 @@ contain a line that matches both expressions:
126 g.add_option('--and', '--or', '--not', 126 g.add_option('--and', '--or', '--not',
127 action='callback', callback=carry, 127 action='callback', callback=carry,
128 help='Boolean operators to combine patterns') 128 help='Boolean operators to combine patterns')
129 g.add_option('-(','-)', 129 g.add_option('-(', '-)',
130 action='callback', callback=carry, 130 action='callback', callback=carry,
131 help='Boolean operator grouping') 131 help='Boolean operator grouping')
132 132
@@ -146,10 +146,10 @@ contain a line that matches both expressions:
146 action='callback', callback=carry, 146 action='callback', callback=carry,
147 metavar='CONTEXT', type='str', 147 metavar='CONTEXT', type='str',
148 help='Show CONTEXT lines after match') 148 help='Show CONTEXT lines after match')
149 g.add_option('-l','--name-only','--files-with-matches', 149 g.add_option('-l', '--name-only', '--files-with-matches',
150 action='callback', callback=carry, 150 action='callback', callback=carry,
151 help='Show only file names containing matching lines') 151 help='Show only file names containing matching lines')
152 g.add_option('-L','--files-without-match', 152 g.add_option('-L', '--files-without-match',
153 action='callback', callback=carry, 153 action='callback', callback=carry,
154 help='Show only file names not containing matching lines') 154 help='Show only file names not containing matching lines')
155 155
@@ -158,9 +158,9 @@ contain a line that matches both expressions:
158 out = GrepColoring(self.manifest.manifestProject.config) 158 out = GrepColoring(self.manifest.manifestProject.config)
159 159
160 cmd_argv = ['grep'] 160 cmd_argv = ['grep']
161 if out.is_on and git_require((1,6,3)): 161 if out.is_on and git_require((1, 6, 3)):
162 cmd_argv.append('--color') 162 cmd_argv.append('--color')
163 cmd_argv.extend(getattr(opt,'cmd_argv',[])) 163 cmd_argv.extend(getattr(opt, 'cmd_argv', []))
164 164
165 if '-e' not in cmd_argv: 165 if '-e' not in cmd_argv:
166 if not args: 166 if not args:
diff --git a/subcmds/help.py b/subcmds/help.py
index 57fb3cc2..15aab7f9 100644
--- a/subcmds/help.py
+++ b/subcmds/help.py
@@ -126,7 +126,7 @@ Displays detailed usage information about a command.
126 126
127 p('%s', title) 127 p('%s', title)
128 self.nl() 128 self.nl()
129 p('%s', ''.ljust(len(title),section_type[0])) 129 p('%s', ''.ljust(len(title), section_type[0]))
130 self.nl() 130 self.nl()
131 continue 131 continue
132 132
diff --git a/subcmds/info.py b/subcmds/info.py
new file mode 100644
index 00000000..3a25e3b5
--- /dev/null
+++ b/subcmds/info.py
@@ -0,0 +1,195 @@
1#
2# Copyright (C) 2012 The Android Open Source Project
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
16from command import PagedCommand
17from color import Coloring
18from error import NoSuchProjectError
19from git_refs import R_M
20
21class _Coloring(Coloring):
22 def __init__(self, config):
23 Coloring.__init__(self, config, "status")
24
25class Info(PagedCommand):
26 common = True
27 helpSummary = "Get info on the manifest branch, current branch or unmerged branches"
28 helpUsage = "%prog [-dl] [-o [-b]] [<project>...]"
29
30 def _Options(self, p, show_smart=True):
31 p.add_option('-d', '--diff',
32 dest='all', action='store_true',
33 help="show full info and commit diff including remote branches")
34 p.add_option('-o', '--overview',
35 dest='overview', action='store_true',
36 help='show overview of all local commits')
37 p.add_option('-b', '--current-branch',
38 dest="current_branch", action="store_true",
39 help="consider only checked out branches")
40 p.add_option('-l', '--local-only',
41 dest="local", action="store_true",
42 help="Disable all remote operations")
43
44
45 def Execute(self, opt, args):
46 self.out = _Coloring(self.manifest.globalConfig)
47 self.heading = self.out.printer('heading', attr = 'bold')
48 self.headtext = self.out.printer('headtext', fg = 'yellow')
49 self.redtext = self.out.printer('redtext', fg = 'red')
50 self.sha = self.out.printer("sha", fg = 'yellow')
51 self.text = self.out.printer('text')
52 self.dimtext = self.out.printer('dimtext', attr = 'dim')
53
54 self.opt = opt
55
56 mergeBranch = self.manifest.manifestProject.config.GetBranch("default").merge
57
58 self.heading("Manifest branch: ")
59 self.headtext(self.manifest.default.revisionExpr)
60 self.out.nl()
61 self.heading("Manifest merge branch: ")
62 self.headtext(mergeBranch)
63 self.out.nl()
64
65 self.printSeparator()
66
67 if not opt.overview:
68 self.printDiffInfo(args)
69 else:
70 self.printCommitOverview(args)
71
72 def printSeparator(self):
73 self.text("----------------------------")
74 self.out.nl()
75
76 def printDiffInfo(self, args):
77 try:
78 projs = self.GetProjects(args)
79 except NoSuchProjectError:
80 return
81
82 for p in projs:
83 self.heading("Project: ")
84 self.headtext(p.name)
85 self.out.nl()
86
87 self.heading("Mount path: ")
88 self.headtext(p.worktree)
89 self.out.nl()
90
91 self.heading("Current revision: ")
92 self.headtext(p.revisionExpr)
93 self.out.nl()
94
95 localBranches = p.GetBranches().keys()
96 self.heading("Local Branches: ")
97 self.redtext(str(len(localBranches)))
98 if len(localBranches) > 0:
99 self.text(" [")
100 self.text(", ".join(localBranches))
101 self.text("]")
102 self.out.nl()
103
104 if self.opt.all:
105 self.findRemoteLocalDiff(p)
106
107 self.printSeparator()
108
109 def findRemoteLocalDiff(self, project):
110 #Fetch all the latest commits
111 if not self.opt.local:
112 project.Sync_NetworkHalf(quiet=True, current_branch_only=True)
113
114 logTarget = R_M + self.manifest.default.revisionExpr
115
116 bareTmp = project.bare_git._bare
117 project.bare_git._bare = False
118 localCommits = project.bare_git.rev_list(
119 '--abbrev=8',
120 '--abbrev-commit',
121 '--pretty=oneline',
122 logTarget + "..",
123 '--')
124
125 originCommits = project.bare_git.rev_list(
126 '--abbrev=8',
127 '--abbrev-commit',
128 '--pretty=oneline',
129 ".." + logTarget,
130 '--')
131 project.bare_git._bare = bareTmp
132
133 self.heading("Local Commits: ")
134 self.redtext(str(len(localCommits)))
135 self.dimtext(" (on current branch)")
136 self.out.nl()
137
138 for c in localCommits:
139 split = c.split()
140 self.sha(split[0] + " ")
141 self.text("".join(split[1:]))
142 self.out.nl()
143
144 self.printSeparator()
145
146 self.heading("Remote Commits: ")
147 self.redtext(str(len(originCommits)))
148 self.out.nl()
149
150 for c in originCommits:
151 split = c.split()
152 self.sha(split[0] + " ")
153 self.text("".join(split[1:]))
154 self.out.nl()
155
156 def printCommitOverview(self, args):
157 all_branches = []
158 for project in self.GetProjects(args):
159 br = [project.GetUploadableBranch(x)
160 for x in project.GetBranches().keys()]
161 br = [x for x in br if x]
162 if self.opt.current_branch:
163 br = [x for x in br if x.name == project.CurrentBranch]
164 all_branches.extend(br)
165
166 if not all_branches:
167 return
168
169 self.out.nl()
170 self.heading('Projects Overview')
171 project = None
172
173 for branch in all_branches:
174 if project != branch.project:
175 project = branch.project
176 self.out.nl()
177 self.headtext(project.relpath)
178 self.out.nl()
179
180 commits = branch.commits
181 date = branch.date
182 self.text('%s %-33s (%2d commit%s, %s)' % (
183 branch.name == project.CurrentBranch and '*' or ' ',
184 branch.name,
185 len(commits),
186 len(commits) != 1 and 's' or '',
187 date))
188 self.out.nl()
189
190 for commit in commits:
191 split = commit.split()
192 self.text('{0:38}{1} '.format('','-'))
193 self.sha(split[0] + " ")
194 self.text("".join(split[1:]))
195 self.out.nl()
diff --git a/subcmds/init.py b/subcmds/init.py
index 7aaa7f17..eeadc70d 100644
--- a/subcmds/init.py
+++ b/subcmds/init.py
@@ -279,14 +279,14 @@ to update the working directory files.
279 print() 279 print()
280 print("Testing colorized output (for 'repo diff', 'repo status'):") 280 print("Testing colorized output (for 'repo diff', 'repo status'):")
281 281
282 for c in ['black','red','green','yellow','blue','magenta','cyan']: 282 for c in ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan']:
283 out.write(' ') 283 out.write(' ')
284 out.printer(fg=c)(' %-6s ', c) 284 out.printer(fg=c)(' %-6s ', c)
285 out.write(' ') 285 out.write(' ')
286 out.printer(fg='white', bg='black')(' %s ' % 'white') 286 out.printer(fg='white', bg='black')(' %s ' % 'white')
287 out.nl() 287 out.nl()
288 288
289 for c in ['bold','dim','ul','reverse']: 289 for c in ['bold', 'dim', 'ul', 'reverse']:
290 out.write(' ') 290 out.write(' ')
291 out.printer(fg='black', attr=c)(' %-6s ', c) 291 out.printer(fg='black', attr=c)(' %-6s ', c)
292 out.nl() 292 out.nl()
diff --git a/subcmds/overview.py b/subcmds/overview.py
index 9e6100b4..418459ae 100644
--- a/subcmds/overview.py
+++ b/subcmds/overview.py
@@ -55,8 +55,11 @@ are displayed.
55 def __init__(self, config): 55 def __init__(self, config):
56 Coloring.__init__(self, config, 'status') 56 Coloring.__init__(self, config, 'status')
57 self.project = self.printer('header', attr='bold') 57 self.project = self.printer('header', attr='bold')
58 self.text = self.printer('text')
58 59
59 out = Report(all_branches[0].project.config) 60 out = Report(all_branches[0].project.config)
61 out.text("Deprecated. See repo info -o.")
62 out.nl()
60 out.project('Projects Overview') 63 out.project('Projects Overview')
61 out.nl() 64 out.nl()
62 65
diff --git a/subcmds/sync.py b/subcmds/sync.py
index a64f2c45..5b3dca78 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -150,22 +150,22 @@ later is required to fix a server side protocol bug.
150 p.add_option('-f', '--force-broken', 150 p.add_option('-f', '--force-broken',
151 dest='force_broken', action='store_true', 151 dest='force_broken', action='store_true',
152 help="continue sync even if a project fails to sync") 152 help="continue sync even if a project fails to sync")
153 p.add_option('-l','--local-only', 153 p.add_option('-l', '--local-only',
154 dest='local_only', action='store_true', 154 dest='local_only', action='store_true',
155 help="only update working tree, don't fetch") 155 help="only update working tree, don't fetch")
156 p.add_option('-n','--network-only', 156 p.add_option('-n', '--network-only',
157 dest='network_only', action='store_true', 157 dest='network_only', action='store_true',
158 help="fetch only, don't update working tree") 158 help="fetch only, don't update working tree")
159 p.add_option('-d','--detach', 159 p.add_option('-d', '--detach',
160 dest='detach_head', action='store_true', 160 dest='detach_head', action='store_true',
161 help='detach projects back to manifest revision') 161 help='detach projects back to manifest revision')
162 p.add_option('-c','--current-branch', 162 p.add_option('-c', '--current-branch',
163 dest='current_branch_only', action='store_true', 163 dest='current_branch_only', action='store_true',
164 help='fetch only current branch from server') 164 help='fetch only current branch from server')
165 p.add_option('-q','--quiet', 165 p.add_option('-q', '--quiet',
166 dest='quiet', action='store_true', 166 dest='quiet', action='store_true',
167 help='be more quiet') 167 help='be more quiet')
168 p.add_option('-j','--jobs', 168 p.add_option('-j', '--jobs',
169 dest='jobs', action='store', type='int', 169 dest='jobs', action='store', type='int',
170 help="projects to fetch simultaneously (default %d)" % self.jobs) 170 help="projects to fetch simultaneously (default %d)" % self.jobs)
171 p.add_option('-m', '--manifest-name', 171 p.add_option('-m', '--manifest-name',
@@ -197,62 +197,62 @@ later is required to fix a server side protocol bug.
197 help=SUPPRESS_HELP) 197 help=SUPPRESS_HELP)
198 198
199 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event): 199 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
200 """Main function of the fetch threads when jobs are > 1. 200 """Main function of the fetch threads when jobs are > 1.
201 201
202 Args: 202 Args:
203 opt: Program options returned from optparse. See _Options(). 203 opt: Program options returned from optparse. See _Options().
204 project: Project object for the project to fetch. 204 project: Project object for the project to fetch.
205 lock: Lock for accessing objects that are shared amongst multiple 205 lock: Lock for accessing objects that are shared amongst multiple
206 _FetchHelper() threads. 206 _FetchHelper() threads.
207 fetched: set object that we will add project.gitdir to when we're done 207 fetched: set object that we will add project.gitdir to when we're done
208 (with our lock held). 208 (with our lock held).
209 pm: Instance of a Project object. We will call pm.update() (with our 209 pm: Instance of a Project object. We will call pm.update() (with our
210 lock held). 210 lock held).
211 sem: We'll release() this semaphore when we exit so that another thread 211 sem: We'll release() this semaphore when we exit so that another thread
212 can be started up. 212 can be started up.
213 err_event: We'll set this event in the case of an error (after printing 213 err_event: We'll set this event in the case of an error (after printing
214 out info about the error). 214 out info about the error).
215 """ 215 """
216 # We'll set to true once we've locked the lock. 216 # We'll set to true once we've locked the lock.
217 did_lock = False 217 did_lock = False
218 218
219 # Encapsulate everything in a try/except/finally so that: 219 # Encapsulate everything in a try/except/finally so that:
220 # - We always set err_event in the case of an exception. 220 # - We always set err_event in the case of an exception.
221 # - We always make sure we call sem.release(). 221 # - We always make sure we call sem.release().
222 # - We always make sure we unlock the lock if we locked it. 222 # - We always make sure we unlock the lock if we locked it.
223 try:
223 try: 224 try:
224 try: 225 start = time.time()
225 start = time.time() 226 success = project.Sync_NetworkHalf(
226 success = project.Sync_NetworkHalf( 227 quiet=opt.quiet,
227 quiet=opt.quiet, 228 current_branch_only=opt.current_branch_only,
228 current_branch_only=opt.current_branch_only, 229 clone_bundle=not opt.no_clone_bundle)
229 clone_bundle=not opt.no_clone_bundle) 230 self._fetch_times.Set(project, time.time() - start)
230 self._fetch_times.Set(project, time.time() - start) 231
231 232 # Lock around all the rest of the code, since printing, updating a set
232 # Lock around all the rest of the code, since printing, updating a set 233 # and Progress.update() are not thread safe.
233 # and Progress.update() are not thread safe. 234 lock.acquire()
234 lock.acquire() 235 did_lock = True
235 did_lock = True 236
236 237 if not success:
237 if not success: 238 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
238 print('error: Cannot fetch %s' % project.name, file=sys.stderr) 239 if opt.force_broken:
239 if opt.force_broken: 240 print('warn: --force-broken, continuing to sync',
240 print('warn: --force-broken, continuing to sync', 241 file=sys.stderr)
241 file=sys.stderr) 242 else:
242 else: 243 raise _FetchError()
243 raise _FetchError()
244 244
245 fetched.add(project.gitdir) 245 fetched.add(project.gitdir)
246 pm.update() 246 pm.update()
247 except _FetchError: 247 except _FetchError:
248 err_event.set() 248 err_event.set()
249 except: 249 except:
250 err_event.set() 250 err_event.set()
251 raise 251 raise
252 finally: 252 finally:
253 if did_lock: 253 if did_lock:
254 lock.release() 254 lock.release()
255 sem.release() 255 sem.release()
256 256
257 def _Fetch(self, projects, opt): 257 def _Fetch(self, projects, opt):
258 fetched = set() 258 fetched = set()
@@ -379,36 +379,36 @@ later is required to fix a server side protocol bug.
379 if path not in new_project_paths: 379 if path not in new_project_paths:
380 # If the path has already been deleted, we don't need to do it 380 # If the path has already been deleted, we don't need to do it
381 if os.path.exists(self.manifest.topdir + '/' + path): 381 if os.path.exists(self.manifest.topdir + '/' + path):
382 project = Project( 382 project = Project(
383 manifest = self.manifest, 383 manifest = self.manifest,
384 name = path, 384 name = path,
385 remote = RemoteSpec('origin'), 385 remote = RemoteSpec('origin'),
386 gitdir = os.path.join(self.manifest.topdir, 386 gitdir = os.path.join(self.manifest.topdir,
387 path, '.git'), 387 path, '.git'),
388 worktree = os.path.join(self.manifest.topdir, path), 388 worktree = os.path.join(self.manifest.topdir, path),
389 relpath = path, 389 relpath = path,
390 revisionExpr = 'HEAD', 390 revisionExpr = 'HEAD',
391 revisionId = None, 391 revisionId = None,
392 groups = None) 392 groups = None)
393 393
394 if project.IsDirty(): 394 if project.IsDirty():
395 print('error: Cannot remove project "%s": uncommitted changes' 395 print('error: Cannot remove project "%s": uncommitted changes'
396 'are present' % project.relpath, file=sys.stderr) 396 'are present' % project.relpath, file=sys.stderr)
397 print(' commit changes, then run sync again', 397 print(' commit changes, then run sync again',
398 file=sys.stderr) 398 file=sys.stderr)
399 return -1 399 return -1
400 else: 400 else:
401 print('Deleting obsolete path %s' % project.worktree, 401 print('Deleting obsolete path %s' % project.worktree,
402 file=sys.stderr) 402 file=sys.stderr)
403 shutil.rmtree(project.worktree) 403 shutil.rmtree(project.worktree)
404 # Try deleting parent subdirs if they are empty 404 # Try deleting parent subdirs if they are empty
405 project_dir = os.path.dirname(project.worktree) 405 project_dir = os.path.dirname(project.worktree)
406 while project_dir != self.manifest.topdir: 406 while project_dir != self.manifest.topdir:
407 try: 407 try:
408 os.rmdir(project_dir) 408 os.rmdir(project_dir)
409 except OSError: 409 except OSError:
410 break 410 break
411 project_dir = os.path.dirname(project_dir) 411 project_dir = os.path.dirname(project_dir)
412 412
413 new_project_paths.sort() 413 new_project_paths.sort()
414 fd = open(file_path, 'w') 414 fd = open(file_path, 'w')
diff --git a/subcmds/upload.py b/subcmds/upload.py
index a6ada337..e314032a 100644
--- a/subcmds/upload.py
+++ b/subcmds/upload.py
@@ -50,7 +50,7 @@ def _SplitEmails(values):
50class Upload(InteractiveCommand): 50class Upload(InteractiveCommand):
51 common = True 51 common = True
52 helpSummary = "Upload changes for code review" 52 helpSummary = "Upload changes for code review"
53 helpUsage=""" 53 helpUsage = """
54%prog [--re --cc] [<project>]... 54%prog [--re --cc] [<project>]...
55""" 55"""
56 helpDescription = """ 56 helpDescription = """
@@ -312,23 +312,23 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
312 312
313 # Check if there are local changes that may have been forgotten 313 # Check if there are local changes that may have been forgotten
314 if branch.project.HasChanges(): 314 if branch.project.HasChanges():
315 key = 'review.%s.autoupload' % branch.project.remote.review 315 key = 'review.%s.autoupload' % branch.project.remote.review
316 answer = branch.project.config.GetBoolean(key) 316 answer = branch.project.config.GetBoolean(key)
317 317
318 # if they want to auto upload, let's not ask because it could be automated 318 # if they want to auto upload, let's not ask because it could be automated
319 if answer is None: 319 if answer is None:
320 sys.stdout.write('Uncommitted changes in ' + branch.project.name + ' (did you forget to amend?). Continue uploading? (y/N) ') 320 sys.stdout.write('Uncommitted changes in ' + branch.project.name + ' (did you forget to amend?). Continue uploading? (y/N) ')
321 a = sys.stdin.readline().strip().lower() 321 a = sys.stdin.readline().strip().lower()
322 if a not in ('y', 'yes', 't', 'true', 'on'): 322 if a not in ('y', 'yes', 't', 'true', 'on'):
323 print("skipping upload", file=sys.stderr) 323 print("skipping upload", file=sys.stderr)
324 branch.uploaded = False 324 branch.uploaded = False
325 branch.error = 'User aborted' 325 branch.error = 'User aborted'
326 continue 326 continue
327 327
328 # Check if topic branches should be sent to the server during upload 328 # Check if topic branches should be sent to the server during upload
329 if opt.auto_topic is not True: 329 if opt.auto_topic is not True:
330 key = 'review.%s.uploadtopic' % branch.project.remote.review 330 key = 'review.%s.uploadtopic' % branch.project.remote.review
331 opt.auto_topic = branch.project.config.GetBoolean(key) 331 opt.auto_topic = branch.project.config.GetBoolean(key)
332 332
333 branch.UploadForReview(people, auto_topic=opt.auto_topic, draft=opt.draft) 333 branch.UploadForReview(people, auto_topic=opt.auto_topic, draft=opt.draft)
334 branch.uploaded = True 334 branch.uploaded = True
@@ -355,11 +355,11 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
355 print() 355 print()
356 356
357 for branch in todo: 357 for branch in todo:
358 if branch.uploaded: 358 if branch.uploaded:
359 print('[OK ] %-15s %s' % ( 359 print('[OK ] %-15s %s' % (
360 branch.project.relpath + '/', 360 branch.project.relpath + '/',
361 branch.name), 361 branch.name),
362 file=sys.stderr) 362 file=sys.stderr)
363 363
364 if have_errors: 364 if have_errors:
365 sys.exit(1) 365 sys.exit(1)
@@ -397,7 +397,7 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
397 reviewers = _SplitEmails(opt.reviewers) 397 reviewers = _SplitEmails(opt.reviewers)
398 if opt.cc: 398 if opt.cc:
399 cc = _SplitEmails(opt.cc) 399 cc = _SplitEmails(opt.cc)
400 people = (reviewers,cc) 400 people = (reviewers, cc)
401 401
402 if not pending: 402 if not pending:
403 print("no branches ready for upload", file=sys.stderr) 403 print("no branches ready for upload", file=sys.stderr)
diff --git a/tests/test_git_config.py b/tests/test_git_config.py
index 5b1770e7..3d4b9970 100644
--- a/tests/test_git_config.py
+++ b/tests/test_git_config.py
@@ -4,49 +4,49 @@ import unittest
4import git_config 4import git_config
5 5
6def fixture(*paths): 6def fixture(*paths):
7 """Return a path relative to test/fixtures. 7 """Return a path relative to test/fixtures.
8 """ 8 """
9 return os.path.join(os.path.dirname(__file__), 'fixtures', *paths) 9 return os.path.join(os.path.dirname(__file__), 'fixtures', *paths)
10 10
11class GitConfigUnitTest(unittest.TestCase): 11class GitConfigUnitTest(unittest.TestCase):
12 """Tests the GitConfig class. 12 """Tests the GitConfig class.
13 """
14 def setUp(self):
15 """Create a GitConfig object using the test.gitconfig fixture.
16 """
17 config_fixture = fixture('test.gitconfig')
18 self.config = git_config.GitConfig(config_fixture)
19
20 def test_GetString_with_empty_config_values(self):
21 """
22 Test config entries with no value.
23
24 [section]
25 empty
26
27 """
28 val = self.config.GetString('section.empty')
29 self.assertEqual(val, None)
30
31 def test_GetString_with_true_value(self):
32 """
33 Test config entries with a string value.
34
35 [section]
36 nonempty = true
37
38 """
39 val = self.config.GetString('section.nonempty')
40 self.assertEqual(val, 'true')
41
42 def test_GetString_from_missing_file(self):
43 """
44 Test missing config file
13 """ 45 """
14 def setUp(self): 46 config_fixture = fixture('not.present.gitconfig')
15 """Create a GitConfig object using the test.gitconfig fixture. 47 config = git_config.GitConfig(config_fixture)
16 """ 48 val = config.GetString('empty')
17 config_fixture = fixture('test.gitconfig') 49 self.assertEqual(val, None)
18 self.config = git_config.GitConfig(config_fixture)
19
20 def test_GetString_with_empty_config_values(self):
21 """
22 Test config entries with no value.
23
24 [section]
25 empty
26
27 """
28 val = self.config.GetString('section.empty')
29 self.assertEqual(val, None)
30
31 def test_GetString_with_true_value(self):
32 """
33 Test config entries with a string value.
34
35 [section]
36 nonempty = true
37
38 """
39 val = self.config.GetString('section.nonempty')
40 self.assertEqual(val, 'true')
41
42 def test_GetString_from_missing_file(self):
43 """
44 Test missing config file
45 """
46 config_fixture = fixture('not.present.gitconfig')
47 config = git_config.GitConfig(config_fixture)
48 val = config.GetString('empty')
49 self.assertEqual(val, None)
50 50
51if __name__ == '__main__': 51if __name__ == '__main__':
52 unittest.main() 52 unittest.main()