summaryrefslogtreecommitdiffstats
path: root/subcmds
diff options
context:
space:
mode:
Diffstat (limited to 'subcmds')
-rw-r--r--subcmds/forall.py17
-rw-r--r--subcmds/gitc_delete.py55
-rw-r--r--subcmds/gitc_init.py82
-rw-r--r--subcmds/help.py17
-rw-r--r--subcmds/init.py4
-rw-r--r--subcmds/rebase.py9
-rw-r--r--subcmds/start.py49
-rw-r--r--subcmds/sync.py178
8 files changed, 386 insertions, 25 deletions
diff --git a/subcmds/forall.py b/subcmds/forall.py
index 96dc99d1..07ee8d58 100644
--- a/subcmds/forall.py
+++ b/subcmds/forall.py
@@ -120,6 +120,9 @@ without iterating through the remaining projects.
120 p.add_option('-r', '--regex', 120 p.add_option('-r', '--regex',
121 dest='regex', action='store_true', 121 dest='regex', action='store_true',
122 help="Execute the command only on projects matching regex or wildcard expression") 122 help="Execute the command only on projects matching regex or wildcard expression")
123 p.add_option('-i', '--inverse-regex',
124 dest='inverse_regex', action='store_true',
125 help="Execute the command only on projects not matching regex or wildcard expression")
123 p.add_option('-g', '--groups', 126 p.add_option('-g', '--groups',
124 dest='groups', 127 dest='groups',
125 help="Execute the command only on projects matching the specified groups") 128 help="Execute the command only on projects matching the specified groups")
@@ -215,10 +218,12 @@ without iterating through the remaining projects.
215 if os.path.isfile(smart_sync_manifest_path): 218 if os.path.isfile(smart_sync_manifest_path):
216 self.manifest.Override(smart_sync_manifest_path) 219 self.manifest.Override(smart_sync_manifest_path)
217 220
218 if not opt.regex: 221 if opt.regex:
219 projects = self.GetProjects(args, groups=opt.groups)
220 else:
221 projects = self.FindProjects(args) 222 projects = self.FindProjects(args)
223 elif opt.inverse_regex:
224 projects = self.FindProjects(args, inverse=True)
225 else:
226 projects = self.GetProjects(args, groups=opt.groups)
222 227
223 os.environ['REPO_COUNT'] = str(len(projects)) 228 os.environ['REPO_COUNT'] = str(len(projects))
224 229
@@ -240,7 +245,8 @@ without iterating through the remaining projects.
240 rc = rc or errno.EINTR 245 rc = rc or errno.EINTR
241 except Exception as e: 246 except Exception as e:
242 # Catch any other exceptions raised 247 # Catch any other exceptions raised
243 print('Got an error, terminating the pool: %r' % e, 248 print('Got an error, terminating the pool: %s: %s' %
249 (type(e).__name__, e),
244 file=sys.stderr) 250 file=sys.stderr)
245 pool.terminate() 251 pool.terminate()
246 rc = rc or getattr(e, 'errno', 1) 252 rc = rc or getattr(e, 'errno', 1)
@@ -254,7 +260,8 @@ without iterating through the remaining projects.
254 try: 260 try:
255 project = self._SerializeProject(p) 261 project = self._SerializeProject(p)
256 except Exception as e: 262 except Exception as e:
257 print('Project list error: %r' % e, 263 print('Project list error on project %s: %s: %s' %
264 (p.name, type(e).__name__, e),
258 file=sys.stderr) 265 file=sys.stderr)
259 return 266 return
260 except KeyboardInterrupt: 267 except KeyboardInterrupt:
diff --git a/subcmds/gitc_delete.py b/subcmds/gitc_delete.py
new file mode 100644
index 00000000..7380c352
--- /dev/null
+++ b/subcmds/gitc_delete.py
@@ -0,0 +1,55 @@
1#
2# Copyright (C) 2015 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 __future__ import print_function
17import os
18import shutil
19import sys
20
21from command import Command, GitcClientCommand
22import gitc_utils
23
24from pyversion import is_python3
25if not is_python3():
26 # pylint:disable=W0622
27 input = raw_input
28 # pylint:enable=W0622
29
30class GitcDelete(Command, GitcClientCommand):
31 common = True
32 visible_everywhere = False
33 helpSummary = "Delete a GITC Client."
34 helpUsage = """
35%prog
36"""
37 helpDescription = """
38This subcommand deletes the current GITC client, deleting the GITC manifest
39and all locally downloaded sources.
40"""
41
42 def _Options(self, p):
43 p.add_option('-f', '--force',
44 dest='force', action='store_true',
45 help='Force the deletion (no prompt).')
46
47 def Execute(self, opt, args):
48 if not opt.force:
49 prompt = ('This will delete GITC client: %s\nAre you sure? (yes/no) ' %
50 self.gitc_manifest.gitc_client_name)
51 response = input(prompt).lower()
52 if not response == 'yes':
53 print('Response was not "yes"\n Exiting...')
54 sys.exit(1)
55 shutil.rmtree(self.gitc_manifest.gitc_client_dir)
diff --git a/subcmds/gitc_init.py b/subcmds/gitc_init.py
new file mode 100644
index 00000000..2726eaec
--- /dev/null
+++ b/subcmds/gitc_init.py
@@ -0,0 +1,82 @@
1#
2# Copyright (C) 2015 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 __future__ import print_function
17import os
18import sys
19
20import gitc_utils
21from command import GitcAvailableCommand
22from manifest_xml import GitcManifest
23from subcmds import init
24import wrapper
25
26
27class GitcInit(init.Init, GitcAvailableCommand):
28 common = True
29 helpSummary = "Initialize a GITC Client."
30 helpUsage = """
31%prog [options] [client name]
32"""
33 helpDescription = """
34The '%prog' command is ran to initialize a new GITC client for use
35with the GITC file system.
36
37This command will setup the client directory, initialize repo, just
38like repo init does, and then downloads the manifest collection
39and installs it in the .repo/directory of the GITC client.
40
41Once this is done, a GITC manifest is generated by pulling the HEAD
42SHA for each project and generates the properly formatted XML file
43and installs it as .manifest in the GITC client directory.
44
45The -c argument is required to specify the GITC client name.
46
47The optional -f argument can be used to specify the manifest file to
48use for this GITC client.
49"""
50
51 def _Options(self, p):
52 super(GitcInit, self)._Options(p)
53 g = p.add_option_group('GITC options')
54 g.add_option('-f', '--manifest-file',
55 dest='manifest_file',
56 help='Optional manifest file to use for this GITC client.')
57 g.add_option('-c', '--gitc-client',
58 dest='gitc_client',
59 help='The name of the gitc_client instance to create or modify.')
60
61 def Execute(self, opt, args):
62 gitc_client = gitc_utils.parse_clientdir(os.getcwd())
63 if not gitc_client or (opt.gitc_client and gitc_client != opt.gitc_client):
64 print('fatal: Please update your repo command. See go/gitc for instructions.', file=sys.stderr)
65 sys.exit(1)
66 self.client_dir = os.path.join(gitc_utils.get_gitc_manifest_dir(),
67 gitc_client)
68 super(GitcInit, self).Execute(opt, args)
69
70 manifest_file = self.manifest.manifestFile
71 if opt.manifest_file:
72 if not os.path.exists(opt.manifest_file):
73 print('fatal: Specified manifest file %s does not exist.' %
74 opt.manifest_file)
75 sys.exit(1)
76 manifest_file = opt.manifest_file
77
78 manifest = GitcManifest(self.repodir, gitc_client)
79 manifest.Override(manifest_file)
80 gitc_utils.generate_gitc_manifest(None, manifest)
81 print('Please run `cd %s` to view your GITC client.' %
82 os.path.join(wrapper.Wrapper().GITC_FS_ROOT_DIR, gitc_client))
diff --git a/subcmds/help.py b/subcmds/help.py
index 4aa3f863..9bb4c8c7 100644
--- a/subcmds/help.py
+++ b/subcmds/help.py
@@ -19,7 +19,8 @@ import sys
19from formatter import AbstractFormatter, DumbWriter 19from formatter import AbstractFormatter, DumbWriter
20 20
21from color import Coloring 21from color import Coloring
22from command import PagedCommand, MirrorSafeCommand 22from command import PagedCommand, MirrorSafeCommand, GitcAvailableCommand, GitcClientCommand
23import gitc_utils
23 24
24class Help(PagedCommand, MirrorSafeCommand): 25class Help(PagedCommand, MirrorSafeCommand):
25 common = False 26 common = False
@@ -54,9 +55,21 @@ Displays detailed usage information about a command.
54 def _PrintCommonCommands(self): 55 def _PrintCommonCommands(self):
55 print('usage: repo COMMAND [ARGS]') 56 print('usage: repo COMMAND [ARGS]')
56 print('The most commonly used repo commands are:') 57 print('The most commonly used repo commands are:')
58
59 def gitc_supported(cmd):
60 if not isinstance(cmd, GitcAvailableCommand) and not isinstance(cmd, GitcClientCommand):
61 return True
62 if self.manifest.isGitcClient:
63 return True
64 if isinstance(cmd, GitcClientCommand):
65 return False
66 if gitc_utils.get_gitc_manifest_dir():
67 return True
68 return False
69
57 commandNames = list(sorted([name 70 commandNames = list(sorted([name
58 for name, command in self.commands.items() 71 for name, command in self.commands.items()
59 if command.common])) 72 if command.common and gitc_supported(command)]))
60 73
61 maxlen = 0 74 maxlen = 0
62 for name in commandNames: 75 for name in commandNames:
diff --git a/subcmds/init.py b/subcmds/init.py
index dbb6ddda..b8e3de5a 100644
--- a/subcmds/init.py
+++ b/subcmds/init.py
@@ -179,7 +179,7 @@ to update the working directory files.
179 r.Save() 179 r.Save()
180 180
181 groups = re.split(r'[,\s]+', opt.groups) 181 groups = re.split(r'[,\s]+', opt.groups)
182 all_platforms = ['linux', 'darwin'] 182 all_platforms = ['linux', 'darwin', 'windows']
183 platformize = lambda x: 'platform-' + x 183 platformize = lambda x: 'platform-' + x
184 if opt.platform == 'auto': 184 if opt.platform == 'auto':
185 if (not opt.mirror and 185 if (not opt.mirror and
@@ -188,7 +188,7 @@ to update the working directory files.
188 elif opt.platform == 'all': 188 elif opt.platform == 'all':
189 groups.extend(map(platformize, all_platforms)) 189 groups.extend(map(platformize, all_platforms))
190 elif opt.platform in all_platforms: 190 elif opt.platform in all_platforms:
191 groups.extend(platformize(opt.platform)) 191 groups.append(platformize(opt.platform))
192 elif opt.platform != 'none': 192 elif opt.platform != 'none':
193 print('fatal: invalid platform flag', file=sys.stderr) 193 print('fatal: invalid platform flag', file=sys.stderr)
194 sys.exit(1) 194 sys.exit(1)
diff --git a/subcmds/rebase.py b/subcmds/rebase.py
index 1bdc1f0b..74796970 100644
--- a/subcmds/rebase.py
+++ b/subcmds/rebase.py
@@ -54,6 +54,11 @@ branch but need to incorporate new upstream changes "underneath" them.
54 p.add_option('--auto-stash', 54 p.add_option('--auto-stash',
55 dest='auto_stash', action='store_true', 55 dest='auto_stash', action='store_true',
56 help='Stash local modifications before starting') 56 help='Stash local modifications before starting')
57 p.add_option('-m', '--onto-manifest',
58 dest='onto_manifest', action='store_true',
59 help='Rebase onto the manifest version instead of upstream '
60 'HEAD. This helps to make sure the local tree stays '
61 'consistent if you previously synced to a manifest.')
57 62
58 def Execute(self, opt, args): 63 def Execute(self, opt, args):
59 all_projects = self.GetProjects(args) 64 all_projects = self.GetProjects(args)
@@ -106,6 +111,10 @@ branch but need to incorporate new upstream changes "underneath" them.
106 if opt.interactive: 111 if opt.interactive:
107 args.append("-i") 112 args.append("-i")
108 113
114 if opt.onto_manifest:
115 args.append('--onto')
116 args.append(project.revisionExpr)
117
109 args.append(upbranch.LocalMerge) 118 args.append(upbranch.LocalMerge)
110 119
111 print('# %s: rebasing %s -> %s' 120 print('# %s: rebasing %s -> %s'
diff --git a/subcmds/start.py b/subcmds/start.py
index 60ad41e0..d1430a9d 100644
--- a/subcmds/start.py
+++ b/subcmds/start.py
@@ -14,11 +14,15 @@
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function 16from __future__ import print_function
17import os
17import sys 18import sys
19
18from command import Command 20from command import Command
19from git_config import IsId 21from git_config import IsId
20from git_command import git 22from git_command import git
23import gitc_utils
21from progress import Progress 24from progress import Progress
25from project import SyncBuffer
22 26
23class Start(Command): 27class Start(Command):
24 common = True 28 common = True
@@ -53,20 +57,57 @@ revision specified in the manifest.
53 print("error: at least one project must be specified", file=sys.stderr) 57 print("error: at least one project must be specified", file=sys.stderr)
54 sys.exit(1) 58 sys.exit(1)
55 59
56 all_projects = self.GetProjects(projects) 60 all_projects = self.GetProjects(projects,
61 missing_ok=bool(self.gitc_manifest))
62
63 # This must happen after we find all_projects, since GetProjects may need
64 # the local directory, which will disappear once we save the GITC manifest.
65 if self.gitc_manifest:
66 gitc_projects = self.GetProjects(projects, manifest=self.gitc_manifest,
67 missing_ok=True)
68 for project in gitc_projects:
69 if project.old_revision:
70 project.already_synced = True
71 else:
72 project.already_synced = False
73 project.old_revision = project.revisionExpr
74 project.revisionExpr = None
75 # Save the GITC manifest.
76 gitc_utils.save_manifest(self.gitc_manifest)
77
78 # Make sure we have a valid CWD
79 if not os.path.exists(os.getcwd()):
80 os.chdir(self.manifest.topdir)
57 81
58 pm = Progress('Starting %s' % nb, len(all_projects)) 82 pm = Progress('Starting %s' % nb, len(all_projects))
59 for project in all_projects: 83 for project in all_projects:
60 pm.update() 84 pm.update()
85
86 if self.gitc_manifest:
87 gitc_project = self.gitc_manifest.paths[project.relpath]
88 # Sync projects that have not been opened.
89 if not gitc_project.already_synced:
90 proj_localdir = os.path.join(self.gitc_manifest.gitc_client_dir,
91 project.relpath)
92 project.worktree = proj_localdir
93 if not os.path.exists(proj_localdir):
94 os.makedirs(proj_localdir)
95 project.Sync_NetworkHalf()
96 sync_buf = SyncBuffer(self.manifest.manifestProject.config)
97 project.Sync_LocalHalf(sync_buf)
98 project.revisionId = gitc_project.old_revision
99
61 # If the current revision is a specific SHA1 then we can't push back 100 # If the current revision is a specific SHA1 then we can't push back
62 # to it; so substitute with dest_branch if defined, or with manifest 101 # to it; so substitute with dest_branch if defined, or with manifest
63 # default revision instead. 102 # default revision instead.
103 branch_merge = ''
64 if IsId(project.revisionExpr): 104 if IsId(project.revisionExpr):
65 if project.dest_branch: 105 if project.dest_branch:
66 project.revisionExpr = project.dest_branch 106 branch_merge = project.dest_branch
67 else: 107 else:
68 project.revisionExpr = self.manifest.default.revisionExpr 108 branch_merge = self.manifest.default.revisionExpr
69 if not project.StartBranch(nb): 109
110 if not project.StartBranch(nb, branch_merge=branch_merge):
70 err.append(project) 111 err.append(project)
71 pm.end() 112 pm.end()
72 113
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 43d450be..4af411c9 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -23,18 +23,26 @@ import shutil
23import socket 23import socket
24import subprocess 24import subprocess
25import sys 25import sys
26import tempfile
26import time 27import time
27 28
28from pyversion import is_python3 29from pyversion import is_python3
29if is_python3(): 30if is_python3():
31 import http.cookiejar as cookielib
32 import urllib.error
30 import urllib.parse 33 import urllib.parse
34 import urllib.request
31 import xmlrpc.client 35 import xmlrpc.client
32else: 36else:
37 import cookielib
33 import imp 38 import imp
39 import urllib2
34 import urlparse 40 import urlparse
35 import xmlrpclib 41 import xmlrpclib
36 urllib = imp.new_module('urllib') 42 urllib = imp.new_module('urllib')
43 urllib.error = urllib2
37 urllib.parse = urlparse 44 urllib.parse = urlparse
45 urllib.request = urllib2
38 xmlrpc = imp.new_module('xmlrpc') 46 xmlrpc = imp.new_module('xmlrpc')
39 xmlrpc.client = xmlrpclib 47 xmlrpc.client = xmlrpclib
40 48
@@ -57,7 +65,9 @@ except ImportError:
57 multiprocessing = None 65 multiprocessing = None
58 66
59from git_command import GIT, git_require 67from git_command import GIT, git_require
68from git_config import GetUrlCookieFile
60from git_refs import R_HEADS, HEAD 69from git_refs import R_HEADS, HEAD
70import gitc_utils
61from project import Project 71from project import Project
62from project import RemoteSpec 72from project import RemoteSpec
63from command import Command, MirrorSafeCommand 73from command import Command, MirrorSafeCommand
@@ -65,6 +75,7 @@ from error import RepoChangedException, GitError, ManifestParseError
65from project import SyncBuffer 75from project import SyncBuffer
66from progress import Progress 76from progress import Progress
67from wrapper import Wrapper 77from wrapper import Wrapper
78from manifest_xml import GitcManifest
68 79
69_ONE_DAY_S = 24 * 60 * 60 80_ONE_DAY_S = 24 * 60 * 60
70 81
@@ -140,6 +151,9 @@ The --optimized-fetch option can be used to only fetch projects that
140are fixed to a sha1 revision if the sha1 revision does not already 151are fixed to a sha1 revision if the sha1 revision does not already
141exist locally. 152exist locally.
142 153
154The --prune option can be used to remove any refs that no longer
155exist on the remote.
156
143SSH Connections 157SSH Connections
144--------------- 158---------------
145 159
@@ -223,6 +237,8 @@ later is required to fix a server side protocol bug.
223 p.add_option('--optimized-fetch', 237 p.add_option('--optimized-fetch',
224 dest='optimized_fetch', action='store_true', 238 dest='optimized_fetch', action='store_true',
225 help='only fetch projects fixed to sha1 if revision does not exist locally') 239 help='only fetch projects fixed to sha1 if revision does not exist locally')
240 p.add_option('--prune', dest='prune', action='store_true',
241 help='delete refs that no longer exist on the remote')
226 if show_smart: 242 if show_smart:
227 p.add_option('-s', '--smart-sync', 243 p.add_option('-s', '--smart-sync',
228 dest='smart_sync', action='store_true', 244 dest='smart_sync', action='store_true',
@@ -294,7 +310,8 @@ later is required to fix a server side protocol bug.
294 force_sync=opt.force_sync, 310 force_sync=opt.force_sync,
295 clone_bundle=not opt.no_clone_bundle, 311 clone_bundle=not opt.no_clone_bundle,
296 no_tags=opt.no_tags, archive=self.manifest.IsArchive, 312 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
297 optimized_fetch=opt.optimized_fetch) 313 optimized_fetch=opt.optimized_fetch,
314 prune=opt.prune)
298 self._fetch_times.Set(project, time.time() - start) 315 self._fetch_times.Set(project, time.time() - start)
299 316
300 # Lock around all the rest of the code, since printing, updating a set 317 # Lock around all the rest of the code, since printing, updating a set
@@ -303,6 +320,7 @@ later is required to fix a server side protocol bug.
303 did_lock = True 320 did_lock = True
304 321
305 if not success: 322 if not success:
323 err_event.set()
306 print('error: Cannot fetch %s' % project.name, file=sys.stderr) 324 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
307 if opt.force_broken: 325 if opt.force_broken:
308 print('warn: --force-broken, continuing to sync', 326 print('warn: --force-broken, continuing to sync',
@@ -313,7 +331,7 @@ later is required to fix a server side protocol bug.
313 fetched.add(project.gitdir) 331 fetched.add(project.gitdir)
314 pm.update() 332 pm.update()
315 except _FetchError: 333 except _FetchError:
316 err_event.set() 334 pass
317 except Exception as e: 335 except Exception as e:
318 print('error: Cannot fetch %s (%s: %s)' \ 336 print('error: Cannot fetch %s (%s: %s)' \
319 % (project.name, type(e).__name__, str(e)), file=sys.stderr) 337 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
@@ -554,19 +572,18 @@ later is required to fix a server side protocol bug.
554 try: 572 try:
555 info = netrc.netrc() 573 info = netrc.netrc()
556 except IOError: 574 except IOError:
557 print('.netrc file does not exist or could not be opened', 575 # .netrc file does not exist or could not be opened
558 file=sys.stderr) 576 pass
559 else: 577 else:
560 try: 578 try:
561 parse_result = urllib.parse.urlparse(manifest_server) 579 parse_result = urllib.parse.urlparse(manifest_server)
562 if parse_result.hostname: 580 if parse_result.hostname:
563 username, _account, password = \ 581 auth = info.authenticators(parse_result.hostname)
564 info.authenticators(parse_result.hostname) 582 if auth:
565 except TypeError: 583 username, _account, password = auth
566 # TypeError is raised when the given hostname is not present 584 else:
567 # in the .netrc file. 585 print('No credentials found for %s in .netrc'
568 print('No credentials found for %s in .netrc' 586 % parse_result.hostname, file=sys.stderr)
569 % parse_result.hostname, file=sys.stderr)
570 except netrc.NetrcParseError as e: 587 except netrc.NetrcParseError as e:
571 print('Error parsing .netrc file: %s' % e, file=sys.stderr) 588 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
572 589
@@ -575,8 +592,12 @@ later is required to fix a server side protocol bug.
575 (username, password), 592 (username, password),
576 1) 593 1)
577 594
595 transport = PersistentTransport(manifest_server)
596 if manifest_server.startswith('persistent-'):
597 manifest_server = manifest_server[len('persistent-'):]
598
578 try: 599 try:
579 server = xmlrpc.client.Server(manifest_server) 600 server = xmlrpc.client.Server(manifest_server, transport=transport)
580 if opt.smart_sync: 601 if opt.smart_sync:
581 p = self.manifest.manifestProject 602 p = self.manifest.manifestProject
582 b = p.GetBranch(p.CurrentBranch) 603 b = p.GetBranch(p.CurrentBranch)
@@ -656,6 +677,42 @@ later is required to fix a server side protocol bug.
656 self._ReloadManifest(manifest_name) 677 self._ReloadManifest(manifest_name)
657 if opt.jobs is None: 678 if opt.jobs is None:
658 self.jobs = self.manifest.default.sync_j 679 self.jobs = self.manifest.default.sync_j
680
681 if self.gitc_manifest:
682 gitc_manifest_projects = self.GetProjects(args,
683 missing_ok=True)
684 gitc_projects = []
685 opened_projects = []
686 for project in gitc_manifest_projects:
687 if project.relpath in self.gitc_manifest.paths and \
688 self.gitc_manifest.paths[project.relpath].old_revision:
689 opened_projects.append(project.relpath)
690 else:
691 gitc_projects.append(project.relpath)
692
693 if not args:
694 gitc_projects = None
695
696 if gitc_projects != [] and not opt.local_only:
697 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
698 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
699 if manifest_name:
700 manifest.Override(manifest_name)
701 else:
702 manifest.Override(self.manifest.manifestFile)
703 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
704 manifest,
705 gitc_projects)
706 print('GITC client successfully synced.')
707
708 # The opened projects need to be synced as normal, therefore we
709 # generate a new args list to represent the opened projects.
710 # TODO: make this more reliable -- if there's a project name/path overlap,
711 # this may choose the wrong project.
712 args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
713 for p in opened_projects]
714 if not args:
715 return
659 all_projects = self.GetProjects(args, 716 all_projects = self.GetProjects(args,
660 missing_ok=True, 717 missing_ok=True,
661 submodules_ok=opt.fetch_submodules) 718 submodules_ok=opt.fetch_submodules)
@@ -850,3 +907,100 @@ class _FetchTimes(object):
850 os.remove(self._path) 907 os.remove(self._path)
851 except OSError: 908 except OSError:
852 pass 909 pass
910
911# This is a replacement for xmlrpc.client.Transport using urllib2
912# and supporting persistent-http[s]. It cannot change hosts from
913# request to request like the normal transport, the real url
914# is passed during initialization.
915class PersistentTransport(xmlrpc.client.Transport):
916 def __init__(self, orig_host):
917 self.orig_host = orig_host
918
919 def request(self, host, handler, request_body, verbose=False):
920 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
921 # Python doesn't understand cookies with the #HttpOnly_ prefix
922 # Since we're only using them for HTTP, copy the file temporarily,
923 # stripping those prefixes away.
924 if cookiefile:
925 tmpcookiefile = tempfile.NamedTemporaryFile()
926 tmpcookiefile.write("# HTTP Cookie File")
927 try:
928 with open(cookiefile) as f:
929 for line in f:
930 if line.startswith("#HttpOnly_"):
931 line = line[len("#HttpOnly_"):]
932 tmpcookiefile.write(line)
933 tmpcookiefile.flush()
934
935 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
936 try:
937 cookiejar.load()
938 except cookielib.LoadError:
939 cookiejar = cookielib.CookieJar()
940 finally:
941 tmpcookiefile.close()
942 else:
943 cookiejar = cookielib.CookieJar()
944
945 proxyhandler = urllib.request.ProxyHandler
946 if proxy:
947 proxyhandler = urllib.request.ProxyHandler({
948 "http": proxy,
949 "https": proxy })
950
951 opener = urllib.request.build_opener(
952 urllib.request.HTTPCookieProcessor(cookiejar),
953 proxyhandler)
954
955 url = urllib.parse.urljoin(self.orig_host, handler)
956 parse_results = urllib.parse.urlparse(url)
957
958 scheme = parse_results.scheme
959 if scheme == 'persistent-http':
960 scheme = 'http'
961 if scheme == 'persistent-https':
962 # If we're proxying through persistent-https, use http. The
963 # proxy itself will do the https.
964 if proxy:
965 scheme = 'http'
966 else:
967 scheme = 'https'
968
969 # Parse out any authentication information using the base class
970 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
971
972 url = urllib.parse.urlunparse((
973 scheme,
974 host,
975 parse_results.path,
976 parse_results.params,
977 parse_results.query,
978 parse_results.fragment))
979
980 request = urllib.request.Request(url, request_body)
981 if extra_headers is not None:
982 for (name, header) in extra_headers:
983 request.add_header(name, header)
984 request.add_header('Content-Type', 'text/xml')
985 try:
986 response = opener.open(request)
987 except urllib.error.HTTPError as e:
988 if e.code == 501:
989 # We may have been redirected through a login process
990 # but our POST turned into a GET. Retry.
991 response = opener.open(request)
992 else:
993 raise
994
995 p, u = xmlrpc.client.getparser()
996 while 1:
997 data = response.read(1024)
998 if not data:
999 break
1000 p.feed(data)
1001 p.close()
1002 return u.close()
1003
1004 def close(self):
1005 pass
1006