diff options
-rw-r--r-- | command.py | 27 | ||||
-rw-r--r-- | docs/manifest_submodule.txt | 130 | ||||
-rw-r--r-- | docs/manifest_xml.txt (renamed from docs/manifest-format.txt) | 0 | ||||
-rw-r--r-- | git_config.py | 41 | ||||
-rw-r--r-- | git_refs.py | 1 | ||||
-rwxr-xr-x | main.py | 6 | ||||
-rw-r--r-- | manifest.py | 51 | ||||
-rw-r--r-- | manifest_loader.py | 34 | ||||
-rw-r--r-- | manifest_submodule.py | 474 | ||||
-rw-r--r-- | manifest_xml.py | 80 | ||||
-rw-r--r-- | project.py | 77 | ||||
-rwxr-xr-x | repo | 9 | ||||
-rw-r--r-- | subcmds/download.py | 3 | ||||
-rw-r--r-- | subcmds/forall.py | 6 | ||||
-rw-r--r-- | subcmds/help.py | 3 | ||||
-rw-r--r-- | subcmds/init.py | 78 | ||||
-rw-r--r-- | subcmds/manifest.py | 81 | ||||
-rw-r--r-- | subcmds/selfupdate.py | 1 | ||||
-rw-r--r-- | subcmds/sync.py | 19 | ||||
-rw-r--r-- | subcmds/upload.py | 22 |
20 files changed, 998 insertions, 145 deletions
@@ -17,6 +17,8 @@ import os | |||
17 | import optparse | 17 | import optparse |
18 | import sys | 18 | import sys |
19 | 19 | ||
20 | import manifest_loader | ||
21 | |||
20 | from error import NoSuchProjectError | 22 | from error import NoSuchProjectError |
21 | 23 | ||
22 | class Command(object): | 24 | class Command(object): |
@@ -24,7 +26,6 @@ class Command(object): | |||
24 | """ | 26 | """ |
25 | 27 | ||
26 | common = False | 28 | common = False |
27 | manifest = None | ||
28 | _optparse = None | 29 | _optparse = None |
29 | 30 | ||
30 | def WantPager(self, opt): | 31 | def WantPager(self, opt): |
@@ -57,10 +58,25 @@ class Command(object): | |||
57 | """ | 58 | """ |
58 | raise NotImplementedError | 59 | raise NotImplementedError |
59 | 60 | ||
61 | @property | ||
62 | def manifest(self): | ||
63 | return self.GetManifest() | ||
64 | |||
65 | def GetManifest(self, reparse=False, type=None): | ||
66 | return manifest_loader.GetManifest(self.repodir, | ||
67 | reparse=reparse, | ||
68 | type=type) | ||
69 | |||
60 | def GetProjects(self, args, missing_ok=False): | 70 | def GetProjects(self, args, missing_ok=False): |
61 | """A list of projects that match the arguments. | 71 | """A list of projects that match the arguments. |
62 | """ | 72 | """ |
63 | all = self.manifest.projects | 73 | all = self.manifest.projects |
74 | |||
75 | mp = self.manifest.manifestProject | ||
76 | if mp.relpath == '.': | ||
77 | all = dict(all) | ||
78 | all[mp.name] = mp | ||
79 | |||
64 | result = [] | 80 | result = [] |
65 | 81 | ||
66 | if not args: | 82 | if not args: |
@@ -81,7 +97,9 @@ class Command(object): | |||
81 | for p in all.values(): | 97 | for p in all.values(): |
82 | by_path[p.worktree] = p | 98 | by_path[p.worktree] = p |
83 | 99 | ||
84 | if os.path.exists(path): | 100 | try: |
101 | project = by_path[path] | ||
102 | except KeyError: | ||
85 | while path \ | 103 | while path \ |
86 | and path != '/' \ | 104 | and path != '/' \ |
87 | and path != self.manifest.topdir: | 105 | and path != self.manifest.topdir: |
@@ -90,11 +108,6 @@ class Command(object): | |||
90 | break | 108 | break |
91 | except KeyError: | 109 | except KeyError: |
92 | path = os.path.dirname(path) | 110 | path = os.path.dirname(path) |
93 | else: | ||
94 | try: | ||
95 | project = by_path[path] | ||
96 | except KeyError: | ||
97 | pass | ||
98 | 111 | ||
99 | if not project: | 112 | if not project: |
100 | raise NoSuchProjectError(arg) | 113 | raise NoSuchProjectError(arg) |
diff --git a/docs/manifest_submodule.txt b/docs/manifest_submodule.txt new file mode 100644 index 00000000..e7d1f643 --- /dev/null +++ b/docs/manifest_submodule.txt | |||
@@ -0,0 +1,130 @@ | |||
1 | repo Manifest Format (submodule) | ||
2 | ================================ | ||
3 | |||
4 | A repo manifest describes the structure of a repo client; that is | ||
5 | the directories that are visible and where they should be obtained | ||
6 | from with git. | ||
7 | |||
8 | The basic structure of a manifest is a bare Git repository holding | ||
9 | a 'gitmodules' file in the top level directory, and one or more | ||
10 | gitlink references pointing at commits from the referenced projects. | ||
11 | This is the same structure as used by 'git submodule'. | ||
12 | |||
13 | Manifests are inherently version controlled, since they are kept | ||
14 | within a Git repository. Updates to manifests are automatically | ||
15 | obtained by clients during `repo sync`. | ||
16 | |||
17 | .gitmodules | ||
18 | =========== | ||
19 | |||
20 | The '.gitmodules' file, located in the top-level directory of the | ||
21 | client's working tree (or manifest repository), is a text file with | ||
22 | a syntax matching the requirements of 'git config'. | ||
23 | |||
24 | This file contains one subsection per project (also called a | ||
25 | submodule by git), and the subsection value is a unique name to | ||
26 | describe the project. Each submodule section must contain the | ||
27 | following required keys: | ||
28 | |||
29 | * path | ||
30 | * url | ||
31 | |||
32 | submodule.<name>.path | ||
33 | --------------------- | ||
34 | |||
35 | Defines the path, relative to the top-level directory of the client's | ||
36 | working tree, where the project is expected to be checked out. The | ||
37 | path name must not end with a '/'. All paths must be unique within | ||
38 | the .gitmodules file. | ||
39 | |||
40 | At the specified path within the manifest repository a gitlink | ||
41 | tree entry (an entry with file mode 160000) must exist referencing | ||
42 | a commit SHA-1 from the project. This tree entry specifies the | ||
43 | exact version of the project that `repo sync` will synchronize the | ||
44 | client's working tree to. | ||
45 | |||
46 | submodule.<name>.url | ||
47 | -------------------- | ||
48 | |||
49 | Defines a URL from where the project repository can be cloned. | ||
50 | By default `repo sync` will clone from this URL whenever a user | ||
51 | needs to access this project. | ||
52 | |||
53 | submodule.<name>.revision | ||
54 | ------------------------- | ||
55 | |||
56 | Name of the branch in the project repository that Gerrit Code Review | ||
57 | should automatically refresh the project's gitlink entry from. | ||
58 | |||
59 | If set, during submit of a change within the referenced project, | ||
60 | Gerrit Code Review will automatically update the manifest | ||
61 | repository's corresponding gitlink to the new commit SHA-1 of | ||
62 | this branch. | ||
63 | |||
64 | Valid values are a short branch name (e.g. 'master'), a full ref | ||
65 | name (e.g. 'refs/heads/master'), or '.' to request using the same | ||
66 | branch name as the manifest branch itself. Since '.' automatically | ||
67 | uses the manifest branch, '.' is the recommended value. | ||
68 | |||
69 | If this key is not set, Gerrit Code Review will NOT automatically | ||
70 | update the gitlink. An unset key requires the manifest maintainer | ||
71 | to manually update the gitlink when it is necessary to reference | ||
72 | a different revision of the project. | ||
73 | |||
74 | submodule.<name>.update | ||
75 | ----------------------- | ||
76 | |||
77 | This key is not supported by repo. If set, it will be ignored. | ||
78 | |||
79 | .review | ||
80 | ======= | ||
81 | |||
82 | The optional '.review' file, located in the top-level directory of | ||
83 | the client's working tree (or manifest repository), is a text file | ||
84 | with a syntax matching the requirements of 'git config'. | ||
85 | |||
86 | This file describes how `repo upload` should interact with the | ||
87 | project's preferred code review system. | ||
88 | |||
89 | review.url | ||
90 | ---------- | ||
91 | |||
92 | URL of the default Gerrit Code Review server. If a project does | ||
93 | not have a specific URL in the '.review' file, this default URL | ||
94 | will be used instead. | ||
95 | |||
96 | review.<name>.url | ||
97 | ----------------- | ||
98 | |||
99 | Project specific URL of the Gerrit Code Review server, for the | ||
100 | submodule whose project name is <name>. | ||
101 | |||
102 | Example | ||
103 | ======= | ||
104 | |||
105 | $ cat .gitmodules | ||
106 | [submodule "app/Clock"] | ||
107 | path = clock | ||
108 | url = git://vcs.example.com/ClockWidget.git | ||
109 | revision = . | ||
110 | [submodule "app/Browser"] | ||
111 | path = net/browser | ||
112 | url = git://netgroup.example.com/network/web/Browser.git | ||
113 | revision = . | ||
114 | |||
115 | $ cat .review | ||
116 | [review] | ||
117 | url = vcs-gerrit.example.com | ||
118 | [review "app/Browser"] | ||
119 | url = netgroup.example.com | ||
120 | |||
121 | In the above example, the app/Clock project will send its code | ||
122 | reviews to the default server, vcs-gerrit.example.com, while | ||
123 | app/Browser will send its code reviews to netgroup.example.com. | ||
124 | |||
125 | See Also | ||
126 | ======== | ||
127 | |||
128 | * http://www.kernel.org/pub/software/scm/git/docs/gitmodules.html | ||
129 | * http://www.kernel.org/pub/software/scm/git/docs/git-config.html | ||
130 | * http://code.google.com/p/gerrit/ | ||
diff --git a/docs/manifest-format.txt b/docs/manifest_xml.txt index da0e69ff..da0e69ff 100644 --- a/docs/manifest-format.txt +++ b/docs/manifest_xml.txt | |||
diff --git a/git_config.py b/git_config.py index 45a2d257..4a42c047 100644 --- a/git_config.py +++ b/git_config.py | |||
@@ -19,6 +19,8 @@ import re | |||
19 | import subprocess | 19 | import subprocess |
20 | import sys | 20 | import sys |
21 | import time | 21 | import time |
22 | import urllib2 | ||
23 | |||
22 | from signal import SIGTERM | 24 | from signal import SIGTERM |
23 | from urllib2 import urlopen, HTTPError | 25 | from urllib2 import urlopen, HTTPError |
24 | from error import GitError, UploadError | 26 | from error import GitError, UploadError |
@@ -71,6 +73,14 @@ class GitConfig(object): | |||
71 | else: | 73 | else: |
72 | self._pickle = pickleFile | 74 | self._pickle = pickleFile |
73 | 75 | ||
76 | def ClearCache(self): | ||
77 | if os.path.exists(self._pickle): | ||
78 | os.remove(self._pickle) | ||
79 | self._cache_dict = None | ||
80 | self._section_dict = None | ||
81 | self._remotes = {} | ||
82 | self._branches = {} | ||
83 | |||
74 | def Has(self, name, include_defaults = True): | 84 | def Has(self, name, include_defaults = True): |
75 | """Return true if this configuration file has the key. | 85 | """Return true if this configuration file has the key. |
76 | """ | 86 | """ |
@@ -254,9 +264,11 @@ class GitConfig(object): | |||
254 | finally: | 264 | finally: |
255 | fd.close() | 265 | fd.close() |
256 | except IOError: | 266 | except IOError: |
257 | os.remove(self._pickle) | 267 | if os.path.exists(self._pickle): |
268 | os.remove(self._pickle) | ||
258 | except cPickle.PickleError: | 269 | except cPickle.PickleError: |
259 | os.remove(self._pickle) | 270 | if os.path.exists(self._pickle): |
271 | os.remove(self._pickle) | ||
260 | 272 | ||
261 | def _ReadGit(self): | 273 | def _ReadGit(self): |
262 | """ | 274 | """ |
@@ -356,10 +368,14 @@ class RefSpec(object): | |||
356 | _ssh_cache = {} | 368 | _ssh_cache = {} |
357 | _ssh_master = True | 369 | _ssh_master = True |
358 | 370 | ||
359 | def _open_ssh(host, port): | 371 | def _open_ssh(host, port=None): |
360 | global _ssh_master | 372 | global _ssh_master |
361 | 373 | ||
362 | key = '%s:%s' % (host, port) | 374 | if port is not None: |
375 | key = '%s:%s' % (host, port) | ||
376 | else: | ||
377 | key = host | ||
378 | |||
363 | if key in _ssh_cache: | 379 | if key in _ssh_cache: |
364 | return True | 380 | return True |
365 | 381 | ||
@@ -372,10 +388,13 @@ def _open_ssh(host, port): | |||
372 | 388 | ||
373 | command = ['ssh', | 389 | command = ['ssh', |
374 | '-o','ControlPath %s' % _ssh_sock(), | 390 | '-o','ControlPath %s' % _ssh_sock(), |
375 | '-p',str(port), | ||
376 | '-M', | 391 | '-M', |
377 | '-N', | 392 | '-N', |
378 | host] | 393 | host] |
394 | |||
395 | if port is not None: | ||
396 | command[3:3] = ['-p',str(port)] | ||
397 | |||
379 | try: | 398 | try: |
380 | Trace(': %s', ' '.join(command)) | 399 | Trace(': %s', ' '.join(command)) |
381 | p = subprocess.Popen(command) | 400 | p = subprocess.Popen(command) |
@@ -417,7 +436,7 @@ def _preconnect(url): | |||
417 | if ':' in host: | 436 | if ':' in host: |
418 | host, port = host.split(':') | 437 | host, port = host.split(':') |
419 | else: | 438 | else: |
420 | port = 22 | 439 | port = None |
421 | if scheme in ('ssh', 'git+ssh', 'ssh+git'): | 440 | if scheme in ('ssh', 'git+ssh', 'ssh+git'): |
422 | return _open_ssh(host, port) | 441 | return _open_ssh(host, port) |
423 | return False | 442 | return False |
@@ -425,7 +444,7 @@ def _preconnect(url): | |||
425 | m = URI_SCP.match(url) | 444 | m = URI_SCP.match(url) |
426 | if m: | 445 | if m: |
427 | host = m.group(1) | 446 | host = m.group(1) |
428 | return _open_ssh(host, 22) | 447 | return _open_ssh(host) |
429 | 448 | ||
430 | return False | 449 | return False |
431 | 450 | ||
@@ -492,23 +511,25 @@ class Remote(object): | |||
492 | try: | 511 | try: |
493 | info = urlopen(u).read() | 512 | info = urlopen(u).read() |
494 | if info == 'NOT_AVAILABLE': | 513 | if info == 'NOT_AVAILABLE': |
495 | raise UploadError('Upload over ssh unavailable') | 514 | raise UploadError('%s: SSH disabled' % self.review) |
496 | if '<' in info: | 515 | if '<' in info: |
497 | # Assume the server gave us some sort of HTML | 516 | # Assume the server gave us some sort of HTML |
498 | # response back, like maybe a login page. | 517 | # response back, like maybe a login page. |
499 | # | 518 | # |
500 | raise UploadError('Cannot read %s:\n%s' % (u, info)) | 519 | raise UploadError('%s: Cannot parse response' % u) |
501 | 520 | ||
502 | self._review_protocol = 'ssh' | 521 | self._review_protocol = 'ssh' |
503 | self._review_host = info.split(" ")[0] | 522 | self._review_host = info.split(" ")[0] |
504 | self._review_port = info.split(" ")[1] | 523 | self._review_port = info.split(" ")[1] |
524 | except urllib2.URLError, e: | ||
525 | raise UploadError('%s: %s' % (self.review, e.reason[1])) | ||
505 | except HTTPError, e: | 526 | except HTTPError, e: |
506 | if e.code == 404: | 527 | if e.code == 404: |
507 | self._review_protocol = 'http-post' | 528 | self._review_protocol = 'http-post' |
508 | self._review_host = None | 529 | self._review_host = None |
509 | self._review_port = None | 530 | self._review_port = None |
510 | else: | 531 | else: |
511 | raise UploadError('Cannot guess Gerrit version') | 532 | raise UploadError('Upload over ssh unavailable') |
512 | 533 | ||
513 | REVIEW_CACHE[u] = ( | 534 | REVIEW_CACHE[u] = ( |
514 | self._review_protocol, | 535 | self._review_protocol, |
diff --git a/git_refs.py b/git_refs.py index ac8ed0c1..b24a0b4e 100644 --- a/git_refs.py +++ b/git_refs.py | |||
@@ -21,7 +21,6 @@ HEAD = 'HEAD' | |||
21 | R_HEADS = 'refs/heads/' | 21 | R_HEADS = 'refs/heads/' |
22 | R_TAGS = 'refs/tags/' | 22 | R_TAGS = 'refs/tags/' |
23 | R_PUB = 'refs/published/' | 23 | R_PUB = 'refs/published/' |
24 | R_M = 'refs/remotes/m/' | ||
25 | 24 | ||
26 | 25 | ||
27 | class GitRefs(object): | 26 | class GitRefs(object): |
@@ -32,11 +32,9 @@ from git_config import close_ssh | |||
32 | from command import InteractiveCommand | 32 | from command import InteractiveCommand |
33 | from command import MirrorSafeCommand | 33 | from command import MirrorSafeCommand |
34 | from command import PagedCommand | 34 | from command import PagedCommand |
35 | from editor import Editor | ||
36 | from error import ManifestInvalidRevisionError | 35 | from error import ManifestInvalidRevisionError |
37 | from error import NoSuchProjectError | 36 | from error import NoSuchProjectError |
38 | from error import RepoChangedException | 37 | from error import RepoChangedException |
39 | from manifest_xml import XmlManifest | ||
40 | from pager import RunPager | 38 | from pager import RunPager |
41 | 39 | ||
42 | from subcmds import all as all_commands | 40 | from subcmds import all as all_commands |
@@ -61,6 +59,8 @@ class _Repo(object): | |||
61 | def __init__(self, repodir): | 59 | def __init__(self, repodir): |
62 | self.repodir = repodir | 60 | self.repodir = repodir |
63 | self.commands = all_commands | 61 | self.commands = all_commands |
62 | # add 'branch' as an alias for 'branches' | ||
63 | all_commands['branch'] = all_commands['branches'] | ||
64 | 64 | ||
65 | def _Run(self, argv): | 65 | def _Run(self, argv): |
66 | name = None | 66 | name = None |
@@ -97,8 +97,6 @@ class _Repo(object): | |||
97 | sys.exit(1) | 97 | sys.exit(1) |
98 | 98 | ||
99 | cmd.repodir = self.repodir | 99 | cmd.repodir = self.repodir |
100 | cmd.manifest = XmlManifest(cmd.repodir) | ||
101 | Editor.globalConfig = cmd.manifest.globalConfig | ||
102 | 100 | ||
103 | if not isinstance(cmd, MirrorSafeCommand) and cmd.manifest.IsMirror: | 101 | if not isinstance(cmd, MirrorSafeCommand) and cmd.manifest.IsMirror: |
104 | print >>sys.stderr, \ | 102 | print >>sys.stderr, \ |
diff --git a/manifest.py b/manifest.py new file mode 100644 index 00000000..f737e866 --- /dev/null +++ b/manifest.py | |||
@@ -0,0 +1,51 @@ | |||
1 | # | ||
2 | # Copyright (C) 2009 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 | |||
16 | import os | ||
17 | |||
18 | from error import ManifestParseError | ||
19 | from editor import Editor | ||
20 | from git_config import GitConfig | ||
21 | from project import MetaProject | ||
22 | |||
23 | class Manifest(object): | ||
24 | """any manifest format""" | ||
25 | |||
26 | def __init__(self, repodir): | ||
27 | self.repodir = os.path.abspath(repodir) | ||
28 | self.topdir = os.path.dirname(self.repodir) | ||
29 | self.globalConfig = GitConfig.ForUser() | ||
30 | Editor.globalConfig = self.globalConfig | ||
31 | |||
32 | self.repoProject = MetaProject(self, 'repo', | ||
33 | gitdir = os.path.join(repodir, 'repo/.git'), | ||
34 | worktree = os.path.join(repodir, 'repo')) | ||
35 | |||
36 | @property | ||
37 | def IsMirror(self): | ||
38 | return self.manifestProject.config.GetBoolean('repo.mirror') | ||
39 | |||
40 | @property | ||
41 | def projects(self): | ||
42 | return {} | ||
43 | |||
44 | def InitBranch(self): | ||
45 | pass | ||
46 | |||
47 | def SetMRefs(self, project): | ||
48 | pass | ||
49 | |||
50 | def Upgrade_Local(self, old): | ||
51 | raise ManifestParseError, 'unsupported upgrade path' | ||
diff --git a/manifest_loader.py b/manifest_loader.py new file mode 100644 index 00000000..467cb42a --- /dev/null +++ b/manifest_loader.py | |||
@@ -0,0 +1,34 @@ | |||
1 | # | ||
2 | # Copyright (C) 2009 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 | |||
16 | from manifest_submodule import SubmoduleManifest | ||
17 | from manifest_xml import XmlManifest | ||
18 | |||
19 | def ParseManifest(repodir, type=None): | ||
20 | if type: | ||
21 | return type(repodir) | ||
22 | if SubmoduleManifest.Is(repodir): | ||
23 | return SubmoduleManifest(repodir) | ||
24 | return XmlManifest(repodir) | ||
25 | |||
26 | _manifest = None | ||
27 | |||
28 | def GetManifest(repodir, reparse=False, type=None): | ||
29 | global _manifest | ||
30 | if _manifest is None \ | ||
31 | or reparse \ | ||
32 | or (type and _manifest.__class__ != type): | ||
33 | _manifest = ParseManifest(repodir, type=type) | ||
34 | return _manifest | ||
diff --git a/manifest_submodule.py b/manifest_submodule.py new file mode 100644 index 00000000..92f187a0 --- /dev/null +++ b/manifest_submodule.py | |||
@@ -0,0 +1,474 @@ | |||
1 | # | ||
2 | # Copyright (C) 2009 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 | |||
16 | import sys | ||
17 | import os | ||
18 | import shutil | ||
19 | |||
20 | from error import GitError | ||
21 | from error import ManifestParseError | ||
22 | from git_command import GitCommand | ||
23 | from git_config import GitConfig | ||
24 | from git_config import IsId | ||
25 | from manifest import Manifest | ||
26 | from progress import Progress | ||
27 | from project import RemoteSpec | ||
28 | from project import Project | ||
29 | from project import MetaProject | ||
30 | from project import R_HEADS | ||
31 | from project import HEAD | ||
32 | from project import _lwrite | ||
33 | |||
34 | import manifest_xml | ||
35 | |||
36 | GITLINK = '160000' | ||
37 | |||
38 | def _rmdir(dir, top): | ||
39 | while dir != top: | ||
40 | try: | ||
41 | os.rmdir(dir) | ||
42 | except OSError: | ||
43 | break | ||
44 | dir = os.path.dirname(dir) | ||
45 | |||
46 | def _rmref(gitdir, ref): | ||
47 | os.remove(os.path.join(gitdir, ref)) | ||
48 | log = os.path.join(gitdir, 'logs', ref) | ||
49 | if os.path.exists(log): | ||
50 | os.remove(log) | ||
51 | _rmdir(os.path.dirname(log), gitdir) | ||
52 | |||
53 | def _has_gitmodules(d): | ||
54 | return os.path.exists(os.path.join(d, '.gitmodules')) | ||
55 | |||
56 | class SubmoduleManifest(Manifest): | ||
57 | """manifest from .gitmodules file""" | ||
58 | |||
59 | @classmethod | ||
60 | def Is(cls, repodir): | ||
61 | return _has_gitmodules(os.path.dirname(repodir)) \ | ||
62 | or _has_gitmodules(os.path.join(repodir, 'manifest')) \ | ||
63 | or _has_gitmodules(os.path.join(repodir, 'manifests')) | ||
64 | |||
65 | @classmethod | ||
66 | def IsBare(cls, p): | ||
67 | try: | ||
68 | p.bare_git.cat_file('-e', '%s:.gitmodules' % p.GetRevisionId()) | ||
69 | except GitError: | ||
70 | return False | ||
71 | return True | ||
72 | |||
73 | def __init__(self, repodir): | ||
74 | Manifest.__init__(self, repodir) | ||
75 | |||
76 | gitdir = os.path.join(repodir, 'manifest.git') | ||
77 | config = GitConfig.ForRepository(gitdir = gitdir) | ||
78 | |||
79 | if config.GetBoolean('repo.mirror'): | ||
80 | worktree = os.path.join(repodir, 'manifest') | ||
81 | relpath = None | ||
82 | else: | ||
83 | worktree = self.topdir | ||
84 | relpath = '.' | ||
85 | |||
86 | self.manifestProject = MetaProject(self, '__manifest__', | ||
87 | gitdir = gitdir, | ||
88 | worktree = worktree, | ||
89 | relpath = relpath) | ||
90 | self._modules = GitConfig(os.path.join(worktree, '.gitmodules'), | ||
91 | pickleFile = os.path.join( | ||
92 | repodir, '.repopickle_gitmodules' | ||
93 | )) | ||
94 | self._review = GitConfig(os.path.join(worktree, '.review'), | ||
95 | pickleFile = os.path.join( | ||
96 | repodir, '.repopickle_review' | ||
97 | )) | ||
98 | self._Unload() | ||
99 | |||
100 | @property | ||
101 | def projects(self): | ||
102 | self._Load() | ||
103 | return self._projects | ||
104 | |||
105 | def InitBranch(self): | ||
106 | m = self.manifestProject | ||
107 | if m.CurrentBranch is None: | ||
108 | b = m.revisionExpr | ||
109 | if b.startswith(R_HEADS): | ||
110 | b = b[len(R_HEADS):] | ||
111 | return m.StartBranch(b) | ||
112 | return True | ||
113 | |||
114 | def SetMRefs(self, project): | ||
115 | if project.revisionId is None: | ||
116 | # Special project, e.g. the manifest or repo executable. | ||
117 | # | ||
118 | return | ||
119 | |||
120 | ref = 'refs/remotes/m' | ||
121 | cur = project.bare_ref.get(ref) | ||
122 | exp = project.revisionId | ||
123 | if cur != exp: | ||
124 | msg = 'manifest set to %s' % exp | ||
125 | project.bare_git.UpdateRef(ref, exp, message = msg, detach = True) | ||
126 | |||
127 | ref = 'refs/remotes/m-revision' | ||
128 | cur = project.bare_ref.symref(ref) | ||
129 | exp = project.revisionExpr | ||
130 | if exp is None: | ||
131 | if cur: | ||
132 | _rmref(project.gitdir, ref) | ||
133 | elif cur != exp: | ||
134 | remote = project.GetRemote(project.remote.name) | ||
135 | dst = remote.ToLocal(exp) | ||
136 | msg = 'manifest set to %s (%s)' % (exp, dst) | ||
137 | project.bare_git.symbolic_ref('-m', msg, ref, dst) | ||
138 | |||
139 | def Upgrade_Local(self, old): | ||
140 | if isinstance(old, manifest_xml.XmlManifest): | ||
141 | self.FromXml_Local_1(old, checkout=True) | ||
142 | self.FromXml_Local_2(old) | ||
143 | else: | ||
144 | raise ManifestParseError, 'cannot upgrade manifest' | ||
145 | |||
146 | def FromXml_Local_1(self, old, checkout): | ||
147 | os.rename(old.manifestProject.gitdir, | ||
148 | os.path.join(old.repodir, 'manifest.git')) | ||
149 | |||
150 | oldmp = old.manifestProject | ||
151 | oldBranch = oldmp.CurrentBranch | ||
152 | b = oldmp.GetBranch(oldBranch).merge | ||
153 | if not b: | ||
154 | raise ManifestParseError, 'cannot upgrade manifest' | ||
155 | if b.startswith(R_HEADS): | ||
156 | b = b[len(R_HEADS):] | ||
157 | |||
158 | newmp = self.manifestProject | ||
159 | self._CleanOldMRefs(newmp) | ||
160 | if oldBranch != b: | ||
161 | newmp.bare_git.branch('-m', oldBranch, b) | ||
162 | newmp.config.ClearCache() | ||
163 | |||
164 | old_remote = newmp.GetBranch(b).remote.name | ||
165 | act_remote = self._GuessRemoteName(old) | ||
166 | if old_remote != act_remote: | ||
167 | newmp.bare_git.remote('rename', old_remote, act_remote) | ||
168 | newmp.config.ClearCache() | ||
169 | newmp.remote.name = act_remote | ||
170 | print >>sys.stderr, "Assuming remote named '%s'" % act_remote | ||
171 | |||
172 | if checkout: | ||
173 | for p in old.projects.values(): | ||
174 | for c in p.copyfiles: | ||
175 | if os.path.exists(c.abs_dest): | ||
176 | os.remove(c.abs_dest) | ||
177 | newmp._InitWorkTree() | ||
178 | else: | ||
179 | newmp._LinkWorkTree() | ||
180 | |||
181 | _lwrite(os.path.join(newmp.worktree,'.git',HEAD), | ||
182 | 'ref: refs/heads/%s\n' % b) | ||
183 | |||
184 | def _GuessRemoteName(self, old): | ||
185 | used = {} | ||
186 | for p in old.projects.values(): | ||
187 | n = p.remote.name | ||
188 | used[n] = used.get(n, 0) + 1 | ||
189 | |||
190 | remote_name = 'origin' | ||
191 | remote_used = 0 | ||
192 | for n in used.keys(): | ||
193 | if remote_used < used[n]: | ||
194 | remote_used = used[n] | ||
195 | remote_name = n | ||
196 | return remote_name | ||
197 | |||
198 | def FromXml_Local_2(self, old): | ||
199 | shutil.rmtree(old.manifestProject.worktree) | ||
200 | os.remove(old._manifestFile) | ||
201 | |||
202 | my_remote = self._Remote().name | ||
203 | new_base = os.path.join(self.repodir, 'projects') | ||
204 | old_base = os.path.join(self.repodir, 'projects.old') | ||
205 | os.rename(new_base, old_base) | ||
206 | os.makedirs(new_base) | ||
207 | |||
208 | info = [] | ||
209 | pm = Progress('Converting projects', len(self.projects)) | ||
210 | for p in self.projects.values(): | ||
211 | pm.update() | ||
212 | |||
213 | old_p = old.projects.get(p.name) | ||
214 | old_gitdir = os.path.join(old_base, '%s.git' % p.relpath) | ||
215 | if not os.path.isdir(old_gitdir): | ||
216 | continue | ||
217 | |||
218 | parent = os.path.dirname(p.gitdir) | ||
219 | if not os.path.isdir(parent): | ||
220 | os.makedirs(parent) | ||
221 | os.rename(old_gitdir, p.gitdir) | ||
222 | _rmdir(os.path.dirname(old_gitdir), self.repodir) | ||
223 | |||
224 | if not os.path.isdir(p.worktree): | ||
225 | os.makedirs(p.worktree) | ||
226 | |||
227 | if os.path.isdir(os.path.join(p.worktree, '.git')): | ||
228 | p._LinkWorkTree(relink=True) | ||
229 | |||
230 | self._CleanOldMRefs(p) | ||
231 | if old_p and old_p.remote.name != my_remote: | ||
232 | info.append("%s/: renamed remote '%s' to '%s'" \ | ||
233 | % (p.relpath, old_p.remote.name, my_remote)) | ||
234 | p.bare_git.remote('rename', old_p.remote.name, my_remote) | ||
235 | p.config.ClearCache() | ||
236 | |||
237 | self.SetMRefs(p) | ||
238 | pm.end() | ||
239 | for i in info: | ||
240 | print >>sys.stderr, i | ||
241 | |||
242 | def _CleanOldMRefs(self, p): | ||
243 | all_refs = p._allrefs | ||
244 | for ref in all_refs.keys(): | ||
245 | if ref.startswith(manifest_xml.R_M): | ||
246 | if p.bare_ref.symref(ref) != '': | ||
247 | _rmref(p.gitdir, ref) | ||
248 | else: | ||
249 | p.bare_git.DeleteRef(ref, all_refs[ref]) | ||
250 | |||
251 | def FromXml_Definition(self, old): | ||
252 | """Convert another manifest representation to this one. | ||
253 | """ | ||
254 | mp = self.manifestProject | ||
255 | gm = self._modules | ||
256 | gr = self._review | ||
257 | |||
258 | fd = open(os.path.join(mp.worktree, '.gitignore'), 'ab') | ||
259 | fd.write('/.repo\n') | ||
260 | fd.close() | ||
261 | |||
262 | sort_projects = list(old.projects.keys()) | ||
263 | sort_projects.sort() | ||
264 | |||
265 | b = mp.GetBranch(mp.CurrentBranch).merge | ||
266 | if b.startswith(R_HEADS): | ||
267 | b = b[len(R_HEADS):] | ||
268 | |||
269 | info = [] | ||
270 | pm = Progress('Converting manifest', len(sort_projects)) | ||
271 | for p in sort_projects: | ||
272 | pm.update() | ||
273 | p = old.projects[p] | ||
274 | |||
275 | gm.SetString('submodule.%s.path' % p.name, p.relpath) | ||
276 | gm.SetString('submodule.%s.url' % p.name, p.remote.url) | ||
277 | |||
278 | if gr.GetString('review.url') is None: | ||
279 | gr.SetString('review.url', p.remote.review) | ||
280 | elif gr.GetString('review.url') != p.remote.review: | ||
281 | gr.SetString('review.%s.url' % p.name, p.remote.review) | ||
282 | |||
283 | r = p.revisionExpr | ||
284 | if r and not IsId(r): | ||
285 | if r.startswith(R_HEADS): | ||
286 | r = r[len(R_HEADS):] | ||
287 | if r == b: | ||
288 | r = '.' | ||
289 | gm.SetString('submodule.%s.revision' % p.name, r) | ||
290 | |||
291 | for c in p.copyfiles: | ||
292 | info.append('Moved %s out of %s' % (c.src, p.relpath)) | ||
293 | c._Copy() | ||
294 | p.work_git.rm(c.src) | ||
295 | mp.work_git.add(c.dest) | ||
296 | |||
297 | self.SetRevisionId(p.relpath, p.GetRevisionId()) | ||
298 | mp.work_git.add('.gitignore', '.gitmodules', '.review') | ||
299 | pm.end() | ||
300 | for i in info: | ||
301 | print >>sys.stderr, i | ||
302 | |||
303 | def _Unload(self): | ||
304 | self._loaded = False | ||
305 | self._projects = {} | ||
306 | self._revisionIds = None | ||
307 | self.branch = None | ||
308 | |||
309 | def _Load(self): | ||
310 | if not self._loaded: | ||
311 | f = os.path.join(self.repodir, manifest_xml.LOCAL_MANIFEST_NAME) | ||
312 | if os.path.exists(f): | ||
313 | print >>sys.stderr, 'warning: ignoring %s' % f | ||
314 | |||
315 | m = self.manifestProject | ||
316 | b = m.CurrentBranch | ||
317 | if not b: | ||
318 | raise ManifestParseError, 'manifest cannot be on detached HEAD' | ||
319 | b = m.GetBranch(b).merge | ||
320 | if b.startswith(R_HEADS): | ||
321 | b = b[len(R_HEADS):] | ||
322 | self.branch = b | ||
323 | m.remote.name = self._Remote().name | ||
324 | |||
325 | self._ParseModules() | ||
326 | |||
327 | if self.IsMirror: | ||
328 | self._AddMetaProjectMirror(self.repoProject) | ||
329 | self._AddMetaProjectMirror(self.manifestProject) | ||
330 | |||
331 | self._loaded = True | ||
332 | |||
333 | def _ParseModules(self): | ||
334 | byPath = dict() | ||
335 | for name in self._modules.GetSubSections('submodule'): | ||
336 | p = self._ParseProject(name) | ||
337 | if self._projects.get(p.name): | ||
338 | raise ManifestParseError, 'duplicate project "%s"' % p.name | ||
339 | if byPath.get(p.relpath): | ||
340 | raise ManifestParseError, 'duplicate path "%s"' % p.relpath | ||
341 | self._projects[p.name] = p | ||
342 | byPath[p.relpath] = p | ||
343 | |||
344 | for relpath in self._allRevisionIds.keys(): | ||
345 | if relpath not in byPath: | ||
346 | raise ManifestParseError, \ | ||
347 | 'project "%s" not in .gitmodules' \ | ||
348 | % relpath | ||
349 | |||
350 | def _Remote(self): | ||
351 | m = self.manifestProject | ||
352 | b = m.GetBranch(m.CurrentBranch) | ||
353 | return b.remote | ||
354 | |||
355 | def _ResolveUrl(self, url): | ||
356 | if url.startswith('./') or url.startswith('../'): | ||
357 | base = self._Remote().url | ||
358 | try: | ||
359 | base = base[:base.rindex('/')+1] | ||
360 | except ValueError: | ||
361 | base = base[:base.rindex(':')+1] | ||
362 | if url.startswith('./'): | ||
363 | url = url[2:] | ||
364 | while '/' in base and url.startswith('../'): | ||
365 | base = base[:base.rindex('/')+1] | ||
366 | url = url[3:] | ||
367 | return base + url | ||
368 | return url | ||
369 | |||
370 | def _GetRevisionId(self, path): | ||
371 | return self._allRevisionIds.get(path) | ||
372 | |||
373 | @property | ||
374 | def _allRevisionIds(self): | ||
375 | if self._revisionIds is None: | ||
376 | a = dict() | ||
377 | p = GitCommand(self.manifestProject, | ||
378 | ['ls-files','-z','--stage'], | ||
379 | capture_stdout = True) | ||
380 | for line in p.process.stdout.read().split('\0')[:-1]: | ||
381 | l_info, l_path = line.split('\t', 2) | ||
382 | l_mode, l_id, l_stage = l_info.split(' ', 2) | ||
383 | if l_mode == GITLINK and l_stage == '0': | ||
384 | a[l_path] = l_id | ||
385 | p.Wait() | ||
386 | self._revisionIds = a | ||
387 | return self._revisionIds | ||
388 | |||
389 | def SetRevisionId(self, path, id): | ||
390 | self.manifestProject.work_git.update_index( | ||
391 | '--add','--cacheinfo', GITLINK, id, path) | ||
392 | |||
393 | def _ParseProject(self, name): | ||
394 | gm = self._modules | ||
395 | gr = self._review | ||
396 | |||
397 | path = gm.GetString('submodule.%s.path' % name) | ||
398 | if not path: | ||
399 | path = name | ||
400 | |||
401 | revId = self._GetRevisionId(path) | ||
402 | if not revId: | ||
403 | raise ManifestParseError( | ||
404 | 'submodule "%s" has no revision at "%s"' \ | ||
405 | % (name, path)) | ||
406 | |||
407 | url = gm.GetString('submodule.%s.url' % name) | ||
408 | if not url: | ||
409 | url = name | ||
410 | url = self._ResolveUrl(url) | ||
411 | |||
412 | review = gr.GetString('review.%s.url' % name) | ||
413 | if not review: | ||
414 | review = gr.GetString('review.url') | ||
415 | if not review: | ||
416 | review = self._Remote().review | ||
417 | |||
418 | remote = RemoteSpec(self._Remote().name, url, review) | ||
419 | revExpr = gm.GetString('submodule.%s.revision' % name) | ||
420 | if revExpr == '.': | ||
421 | revExpr = self.branch | ||
422 | |||
423 | if self.IsMirror: | ||
424 | relpath = None | ||
425 | worktree = None | ||
426 | gitdir = os.path.join(self.topdir, '%s.git' % name) | ||
427 | else: | ||
428 | worktree = os.path.join(self.topdir, path) | ||
429 | gitdir = os.path.join(self.repodir, 'projects/%s.git' % name) | ||
430 | |||
431 | return Project(manifest = self, | ||
432 | name = name, | ||
433 | remote = remote, | ||
434 | gitdir = gitdir, | ||
435 | worktree = worktree, | ||
436 | relpath = path, | ||
437 | revisionExpr = revExpr, | ||
438 | revisionId = revId) | ||
439 | |||
440 | def _AddMetaProjectMirror(self, m): | ||
441 | m_url = m.GetRemote(m.remote.name).url | ||
442 | if m_url.endswith('/.git'): | ||
443 | raise ManifestParseError, 'refusing to mirror %s' % m_url | ||
444 | |||
445 | name = self._GuessMetaName(m_url) | ||
446 | if name.endswith('.git'): | ||
447 | name = name[:-4] | ||
448 | |||
449 | if name not in self._projects: | ||
450 | m.PreSync() | ||
451 | gitdir = os.path.join(self.topdir, '%s.git' % name) | ||
452 | project = Project(manifest = self, | ||
453 | name = name, | ||
454 | remote = RemoteSpec(self._Remote().name, m_url), | ||
455 | gitdir = gitdir, | ||
456 | worktree = None, | ||
457 | relpath = None, | ||
458 | revisionExpr = m.revisionExpr, | ||
459 | revisionId = None) | ||
460 | self._projects[project.name] = project | ||
461 | |||
462 | def _GuessMetaName(self, m_url): | ||
463 | parts = m_url.split('/') | ||
464 | name = parts[-1] | ||
465 | parts = parts[0:-1] | ||
466 | s = len(parts) - 1 | ||
467 | while s > 0: | ||
468 | l = '/'.join(parts[0:s]) + '/' | ||
469 | r = '/'.join(parts[s:]) + '/' | ||
470 | for p in self._projects.values(): | ||
471 | if p.name.startswith(r) and p.remote.url.startswith(l): | ||
472 | return r + name | ||
473 | s -= 1 | ||
474 | return m_url[m_url.rindex('/') + 1:] | ||
diff --git a/manifest_xml.py b/manifest_xml.py index 7d02f9d6..d888653d 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
@@ -17,12 +17,19 @@ import os | |||
17 | import sys | 17 | import sys |
18 | import xml.dom.minidom | 18 | import xml.dom.minidom |
19 | 19 | ||
20 | from git_config import GitConfig, IsId | 20 | from git_config import GitConfig |
21 | from project import RemoteSpec, Project, MetaProject, R_HEADS, HEAD | 21 | from git_config import IsId |
22 | from manifest import Manifest | ||
23 | from project import RemoteSpec | ||
24 | from project import Project | ||
25 | from project import MetaProject | ||
26 | from project import R_HEADS | ||
27 | from project import HEAD | ||
22 | from error import ManifestParseError | 28 | from error import ManifestParseError |
23 | 29 | ||
24 | MANIFEST_FILE_NAME = 'manifest.xml' | 30 | MANIFEST_FILE_NAME = 'manifest.xml' |
25 | LOCAL_MANIFEST_NAME = 'local_manifest.xml' | 31 | LOCAL_MANIFEST_NAME = 'local_manifest.xml' |
32 | R_M = 'refs/remotes/m/' | ||
26 | 33 | ||
27 | class _Default(object): | 34 | class _Default(object): |
28 | """Project defaults within the manifest.""" | 35 | """Project defaults within the manifest.""" |
@@ -46,19 +53,13 @@ class _XmlRemote(object): | |||
46 | url += '/%s.git' % projectName | 53 | url += '/%s.git' % projectName |
47 | return RemoteSpec(self.name, url, self.reviewUrl) | 54 | return RemoteSpec(self.name, url, self.reviewUrl) |
48 | 55 | ||
49 | class XmlManifest(object): | 56 | class XmlManifest(Manifest): |
50 | """manages the repo configuration file""" | 57 | """manages the repo configuration file""" |
51 | 58 | ||
52 | def __init__(self, repodir): | 59 | def __init__(self, repodir): |
53 | self.repodir = os.path.abspath(repodir) | 60 | Manifest.__init__(self, repodir) |
54 | self.topdir = os.path.dirname(self.repodir) | ||
55 | self.manifestFile = os.path.join(self.repodir, MANIFEST_FILE_NAME) | ||
56 | self.globalConfig = GitConfig.ForUser() | ||
57 | |||
58 | self.repoProject = MetaProject(self, 'repo', | ||
59 | gitdir = os.path.join(repodir, 'repo/.git'), | ||
60 | worktree = os.path.join(repodir, 'repo')) | ||
61 | 61 | ||
62 | self._manifestFile = os.path.join(repodir, MANIFEST_FILE_NAME) | ||
62 | self.manifestProject = MetaProject(self, 'manifests', | 63 | self.manifestProject = MetaProject(self, 'manifests', |
63 | gitdir = os.path.join(repodir, 'manifests.git'), | 64 | gitdir = os.path.join(repodir, 'manifests.git'), |
64 | worktree = os.path.join(repodir, 'manifests')) | 65 | worktree = os.path.join(repodir, 'manifests')) |
@@ -72,18 +73,18 @@ class XmlManifest(object): | |||
72 | if not os.path.isfile(path): | 73 | if not os.path.isfile(path): |
73 | raise ManifestParseError('manifest %s not found' % name) | 74 | raise ManifestParseError('manifest %s not found' % name) |
74 | 75 | ||
75 | old = self.manifestFile | 76 | old = self._manifestFile |
76 | try: | 77 | try: |
77 | self.manifestFile = path | 78 | self._manifestFile = path |
78 | self._Unload() | 79 | self._Unload() |
79 | self._Load() | 80 | self._Load() |
80 | finally: | 81 | finally: |
81 | self.manifestFile = old | 82 | self._manifestFile = old |
82 | 83 | ||
83 | try: | 84 | try: |
84 | if os.path.exists(self.manifestFile): | 85 | if os.path.exists(self._manifestFile): |
85 | os.remove(self.manifestFile) | 86 | os.remove(self._manifestFile) |
86 | os.symlink('manifests/%s' % name, self.manifestFile) | 87 | os.symlink('manifests/%s' % name, self._manifestFile) |
87 | except OSError, e: | 88 | except OSError, e: |
88 | raise ManifestParseError('cannot link manifest %s' % name) | 89 | raise ManifestParseError('cannot link manifest %s' % name) |
89 | 90 | ||
@@ -168,9 +169,15 @@ class XmlManifest(object): | |||
168 | self._Load() | 169 | self._Load() |
169 | return self._default | 170 | return self._default |
170 | 171 | ||
171 | @property | 172 | def InitBranch(self): |
172 | def IsMirror(self): | 173 | m = self.manifestProject |
173 | return self.manifestProject.config.GetBoolean('repo.mirror') | 174 | if m.CurrentBranch is None: |
175 | return m.StartBranch('default') | ||
176 | return True | ||
177 | |||
178 | def SetMRefs(self, project): | ||
179 | if self.branch: | ||
180 | project._InitAnyMRef(R_M + self.branch) | ||
174 | 181 | ||
175 | def _Unload(self): | 182 | def _Unload(self): |
176 | self._loaded = False | 183 | self._loaded = False |
@@ -182,7 +189,10 @@ class XmlManifest(object): | |||
182 | def _Load(self): | 189 | def _Load(self): |
183 | if not self._loaded: | 190 | if not self._loaded: |
184 | m = self.manifestProject | 191 | m = self.manifestProject |
185 | b = m.GetBranch(m.CurrentBranch).merge | 192 | b = m.GetBranch(m.CurrentBranch) |
193 | if b.remote and b.remote.name: | ||
194 | m.remote.name = b.remote.name | ||
195 | b = b.merge | ||
186 | if b is not None and b.startswith(R_HEADS): | 196 | if b is not None and b.startswith(R_HEADS): |
187 | b = b[len(R_HEADS):] | 197 | b = b[len(R_HEADS):] |
188 | self.branch = b | 198 | self.branch = b |
@@ -192,11 +202,11 @@ class XmlManifest(object): | |||
192 | local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME) | 202 | local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME) |
193 | if os.path.exists(local): | 203 | if os.path.exists(local): |
194 | try: | 204 | try: |
195 | real = self.manifestFile | 205 | real = self._manifestFile |
196 | self.manifestFile = local | 206 | self._manifestFile = local |
197 | self._ParseManifest(False) | 207 | self._ParseManifest(False) |
198 | finally: | 208 | finally: |
199 | self.manifestFile = real | 209 | self._manifestFile = real |
200 | 210 | ||
201 | if self.IsMirror: | 211 | if self.IsMirror: |
202 | self._AddMetaProjectMirror(self.repoProject) | 212 | self._AddMetaProjectMirror(self.repoProject) |
@@ -205,17 +215,17 @@ class XmlManifest(object): | |||
205 | self._loaded = True | 215 | self._loaded = True |
206 | 216 | ||
207 | def _ParseManifest(self, is_root_file): | 217 | def _ParseManifest(self, is_root_file): |
208 | root = xml.dom.minidom.parse(self.manifestFile) | 218 | root = xml.dom.minidom.parse(self._manifestFile) |
209 | if not root or not root.childNodes: | 219 | if not root or not root.childNodes: |
210 | raise ManifestParseError, \ | 220 | raise ManifestParseError, \ |
211 | "no root node in %s" % \ | 221 | "no root node in %s" % \ |
212 | self.manifestFile | 222 | self._manifestFile |
213 | 223 | ||
214 | config = root.childNodes[0] | 224 | config = root.childNodes[0] |
215 | if config.nodeName != 'manifest': | 225 | if config.nodeName != 'manifest': |
216 | raise ManifestParseError, \ | 226 | raise ManifestParseError, \ |
217 | "no <manifest> in %s" % \ | 227 | "no <manifest> in %s" % \ |
218 | self.manifestFile | 228 | self._manifestFile |
219 | 229 | ||
220 | for node in config.childNodes: | 230 | for node in config.childNodes: |
221 | if node.nodeName == 'remove-project': | 231 | if node.nodeName == 'remove-project': |
@@ -233,7 +243,7 @@ class XmlManifest(object): | |||
233 | if self._remotes.get(remote.name): | 243 | if self._remotes.get(remote.name): |
234 | raise ManifestParseError, \ | 244 | raise ManifestParseError, \ |
235 | 'duplicate remote %s in %s' % \ | 245 | 'duplicate remote %s in %s' % \ |
236 | (remote.name, self.manifestFile) | 246 | (remote.name, self._manifestFile) |
237 | self._remotes[remote.name] = remote | 247 | self._remotes[remote.name] = remote |
238 | 248 | ||
239 | for node in config.childNodes: | 249 | for node in config.childNodes: |
@@ -241,7 +251,7 @@ class XmlManifest(object): | |||
241 | if self._default is not None: | 251 | if self._default is not None: |
242 | raise ManifestParseError, \ | 252 | raise ManifestParseError, \ |
243 | 'duplicate default in %s' % \ | 253 | 'duplicate default in %s' % \ |
244 | (self.manifestFile) | 254 | (self._manifestFile) |
245 | self._default = self._ParseDefault(node) | 255 | self._default = self._ParseDefault(node) |
246 | if self._default is None: | 256 | if self._default is None: |
247 | self._default = _Default() | 257 | self._default = _Default() |
@@ -252,7 +262,7 @@ class XmlManifest(object): | |||
252 | if self._projects.get(project.name): | 262 | if self._projects.get(project.name): |
253 | raise ManifestParseError, \ | 263 | raise ManifestParseError, \ |
254 | 'duplicate project %s in %s' % \ | 264 | 'duplicate project %s in %s' % \ |
255 | (project.name, self.manifestFile) | 265 | (project.name, self._manifestFile) |
256 | self._projects[project.name] = project | 266 | self._projects[project.name] = project |
257 | 267 | ||
258 | def _AddMetaProjectMirror(self, m): | 268 | def _AddMetaProjectMirror(self, m): |
@@ -324,7 +334,7 @@ class XmlManifest(object): | |||
324 | if remote is None: | 334 | if remote is None: |
325 | raise ManifestParseError, \ | 335 | raise ManifestParseError, \ |
326 | "no remote for project %s within %s" % \ | 336 | "no remote for project %s within %s" % \ |
327 | (name, self.manifestFile) | 337 | (name, self._manifestFile) |
328 | 338 | ||
329 | revisionExpr = node.getAttribute('revision') | 339 | revisionExpr = node.getAttribute('revision') |
330 | if not revisionExpr: | 340 | if not revisionExpr: |
@@ -332,7 +342,7 @@ class XmlManifest(object): | |||
332 | if not revisionExpr: | 342 | if not revisionExpr: |
333 | raise ManifestParseError, \ | 343 | raise ManifestParseError, \ |
334 | "no revision for project %s within %s" % \ | 344 | "no revision for project %s within %s" % \ |
335 | (name, self.manifestFile) | 345 | (name, self._manifestFile) |
336 | 346 | ||
337 | path = node.getAttribute('path') | 347 | path = node.getAttribute('path') |
338 | if not path: | 348 | if not path: |
@@ -340,7 +350,7 @@ class XmlManifest(object): | |||
340 | if path.startswith('/'): | 350 | if path.startswith('/'): |
341 | raise ManifestParseError, \ | 351 | raise ManifestParseError, \ |
342 | "project %s path cannot be absolute in %s" % \ | 352 | "project %s path cannot be absolute in %s" % \ |
343 | (name, self.manifestFile) | 353 | (name, self._manifestFile) |
344 | 354 | ||
345 | if self.IsMirror: | 355 | if self.IsMirror: |
346 | relpath = None | 356 | relpath = None |
@@ -382,7 +392,7 @@ class XmlManifest(object): | |||
382 | if not v: | 392 | if not v: |
383 | raise ManifestParseError, \ | 393 | raise ManifestParseError, \ |
384 | "remote %s not defined in %s" % \ | 394 | "remote %s not defined in %s" % \ |
385 | (name, self.manifestFile) | 395 | (name, self._manifestFile) |
386 | return v | 396 | return v |
387 | 397 | ||
388 | def _reqatt(self, node, attname): | 398 | def _reqatt(self, node, attname): |
@@ -393,5 +403,5 @@ class XmlManifest(object): | |||
393 | if not v: | 403 | if not v: |
394 | raise ManifestParseError, \ | 404 | raise ManifestParseError, \ |
395 | "no %s in <%s> within %s" % \ | 405 | "no %s in <%s> within %s" % \ |
396 | (attname, node.nodeName, self.manifestFile) | 406 | (attname, node.nodeName, self._manifestFile) |
397 | return v | 407 | return v |
@@ -27,7 +27,7 @@ from git_config import GitConfig, IsId | |||
27 | from error import GitError, ImportError, UploadError | 27 | from error import GitError, ImportError, UploadError |
28 | from error import ManifestInvalidRevisionError | 28 | from error import ManifestInvalidRevisionError |
29 | 29 | ||
30 | from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M | 30 | from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB |
31 | 31 | ||
32 | def _lwrite(path, content): | 32 | def _lwrite(path, content): |
33 | lock = '%s.lock' % path | 33 | lock = '%s.lock' % path |
@@ -598,7 +598,7 @@ class Project(object): | |||
598 | return False | 598 | return False |
599 | 599 | ||
600 | if self.worktree: | 600 | if self.worktree: |
601 | self._InitMRef() | 601 | self.manifest.SetMRefs(self) |
602 | else: | 602 | else: |
603 | self._InitMirrorHead() | 603 | self._InitMirrorHead() |
604 | try: | 604 | try: |
@@ -1093,10 +1093,6 @@ class Project(object): | |||
1093 | remote.ResetFetch(mirror=True) | 1093 | remote.ResetFetch(mirror=True) |
1094 | remote.Save() | 1094 | remote.Save() |
1095 | 1095 | ||
1096 | def _InitMRef(self): | ||
1097 | if self.manifest.branch: | ||
1098 | self._InitAnyMRef(R_M + self.manifest.branch) | ||
1099 | |||
1100 | def _InitMirrorHead(self): | 1096 | def _InitMirrorHead(self): |
1101 | self._InitAnyMRef(HEAD) | 1097 | self._InitAnyMRef(HEAD) |
1102 | 1098 | ||
@@ -1115,33 +1111,40 @@ class Project(object): | |||
1115 | msg = 'manifest set to %s' % self.revisionExpr | 1111 | msg = 'manifest set to %s' % self.revisionExpr |
1116 | self.bare_git.symbolic_ref('-m', msg, ref, dst) | 1112 | self.bare_git.symbolic_ref('-m', msg, ref, dst) |
1117 | 1113 | ||
1118 | def _InitWorkTree(self): | 1114 | def _LinkWorkTree(self, relink=False): |
1119 | dotgit = os.path.join(self.worktree, '.git') | 1115 | dotgit = os.path.join(self.worktree, '.git') |
1120 | if not os.path.exists(dotgit): | 1116 | if not relink: |
1121 | os.makedirs(dotgit) | 1117 | os.makedirs(dotgit) |
1122 | 1118 | ||
1123 | for name in ['config', | 1119 | for name in ['config', |
1124 | 'description', | 1120 | 'description', |
1125 | 'hooks', | 1121 | 'hooks', |
1126 | 'info', | 1122 | 'info', |
1127 | 'logs', | 1123 | 'logs', |
1128 | 'objects', | 1124 | 'objects', |
1129 | 'packed-refs', | 1125 | 'packed-refs', |
1130 | 'refs', | 1126 | 'refs', |
1131 | 'rr-cache', | 1127 | 'rr-cache', |
1132 | 'svn']: | 1128 | 'svn']: |
1133 | try: | 1129 | try: |
1134 | src = os.path.join(self.gitdir, name) | 1130 | src = os.path.join(self.gitdir, name) |
1135 | dst = os.path.join(dotgit, name) | 1131 | dst = os.path.join(dotgit, name) |
1136 | if os.path.islink(dst) or not os.path.exists(dst): | 1132 | if relink: |
1137 | os.symlink(relpath(src, dst), dst) | 1133 | os.remove(dst) |
1138 | else: | 1134 | if os.path.islink(dst) or not os.path.exists(dst): |
1139 | raise GitError('cannot overwrite a local work tree') | 1135 | os.symlink(relpath(src, dst), dst) |
1140 | except OSError, e: | 1136 | else: |
1141 | if e.errno == errno.EPERM: | 1137 | raise GitError('cannot overwrite a local work tree') |
1142 | raise GitError('filesystem must support symlinks') | 1138 | except OSError, e: |
1143 | else: | 1139 | if e.errno == errno.EPERM: |
1144 | raise | 1140 | raise GitError('filesystem must support symlinks') |
1141 | else: | ||
1142 | raise | ||
1143 | |||
1144 | def _InitWorkTree(self): | ||
1145 | dotgit = os.path.join(self.worktree, '.git') | ||
1146 | if not os.path.exists(dotgit): | ||
1147 | self._LinkWorkTree() | ||
1145 | 1148 | ||
1146 | _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId()) | 1149 | _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId()) |
1147 | 1150 | ||
@@ -1439,15 +1442,17 @@ class SyncBuffer(object): | |||
1439 | class MetaProject(Project): | 1442 | class MetaProject(Project): |
1440 | """A special project housed under .repo. | 1443 | """A special project housed under .repo. |
1441 | """ | 1444 | """ |
1442 | def __init__(self, manifest, name, gitdir, worktree): | 1445 | def __init__(self, manifest, name, gitdir, worktree, relpath=None): |
1443 | repodir = manifest.repodir | 1446 | repodir = manifest.repodir |
1447 | if relpath is None: | ||
1448 | relpath = '.repo/%s' % name | ||
1444 | Project.__init__(self, | 1449 | Project.__init__(self, |
1445 | manifest = manifest, | 1450 | manifest = manifest, |
1446 | name = name, | 1451 | name = name, |
1447 | gitdir = gitdir, | 1452 | gitdir = gitdir, |
1448 | worktree = worktree, | 1453 | worktree = worktree, |
1449 | remote = RemoteSpec('origin'), | 1454 | remote = RemoteSpec('origin'), |
1450 | relpath = '.repo/%s' % name, | 1455 | relpath = relpath, |
1451 | revisionExpr = 'refs/heads/master', | 1456 | revisionExpr = 'refs/heads/master', |
1452 | revisionId = None) | 1457 | revisionId = None) |
1453 | 1458 | ||
@@ -1455,10 +1460,12 @@ class MetaProject(Project): | |||
1455 | if self.Exists: | 1460 | if self.Exists: |
1456 | cb = self.CurrentBranch | 1461 | cb = self.CurrentBranch |
1457 | if cb: | 1462 | if cb: |
1458 | base = self.GetBranch(cb).merge | 1463 | cb = self.GetBranch(cb) |
1459 | if base: | 1464 | if cb.merge: |
1460 | self.revisionExpr = base | 1465 | self.revisionExpr = cb.merge |
1461 | self.revisionId = None | 1466 | self.revisionId = None |
1467 | if cb.remote and cb.remote.name: | ||
1468 | self.remote.name = cb.remote.name | ||
1462 | 1469 | ||
1463 | @property | 1470 | @property |
1464 | def LastFetch(self): | 1471 | def LastFetch(self): |
@@ -28,7 +28,7 @@ if __name__ == '__main__': | |||
28 | del magic | 28 | del magic |
29 | 29 | ||
30 | # increment this whenever we make important changes to this script | 30 | # increment this whenever we make important changes to this script |
31 | VERSION = (1, 8) | 31 | VERSION = (1, 9) |
32 | 32 | ||
33 | # increment this if the MAINTAINER_KEYS block is modified | 33 | # increment this if the MAINTAINER_KEYS block is modified |
34 | KEYRING_VERSION = (1,0) | 34 | KEYRING_VERSION = (1,0) |
@@ -109,12 +109,17 @@ group = init_optparse.add_option_group('Manifest options') | |||
109 | group.add_option('-u', '--manifest-url', | 109 | group.add_option('-u', '--manifest-url', |
110 | dest='manifest_url', | 110 | dest='manifest_url', |
111 | help='manifest repository location', metavar='URL') | 111 | help='manifest repository location', metavar='URL') |
112 | group.add_option('-o', '--origin', | ||
113 | dest='manifest_origin', | ||
114 | help="use REMOTE instead of 'origin' to track upstream", | ||
115 | metavar='REMOTE') | ||
112 | group.add_option('-b', '--manifest-branch', | 116 | group.add_option('-b', '--manifest-branch', |
113 | dest='manifest_branch', | 117 | dest='manifest_branch', |
114 | help='manifest branch or revision', metavar='REVISION') | 118 | help='manifest branch or revision', metavar='REVISION') |
115 | group.add_option('-m', '--manifest-name', | 119 | group.add_option('-m', '--manifest-name', |
116 | dest='manifest_name', | 120 | dest='manifest_name', |
117 | help='initial manifest file', metavar='NAME.xml') | 121 | help='initial manifest file (deprecated)', |
122 | metavar='NAME.xml') | ||
118 | group.add_option('--mirror', | 123 | group.add_option('--mirror', |
119 | dest='mirror', action='store_true', | 124 | dest='mirror', action='store_true', |
120 | help='mirror the forrest') | 125 | help='mirror the forrest') |
diff --git a/subcmds/download.py b/subcmds/download.py index a6f3aa45..61eadd54 100644 --- a/subcmds/download.py +++ b/subcmds/download.py | |||
@@ -36,6 +36,9 @@ makes it available in your project's local working directory. | |||
36 | pass | 36 | pass |
37 | 37 | ||
38 | def _ParseChangeIds(self, args): | 38 | def _ParseChangeIds(self, args): |
39 | if not args: | ||
40 | self.Usage() | ||
41 | |||
39 | to_get = [] | 42 | to_get = [] |
40 | project = None | 43 | project = None |
41 | 44 | ||
diff --git a/subcmds/forall.py b/subcmds/forall.py index b66313d7..6bd867e7 100644 --- a/subcmds/forall.py +++ b/subcmds/forall.py | |||
@@ -169,6 +169,12 @@ terminal and are not redirected. | |||
169 | else: | 169 | else: |
170 | cwd = project.worktree | 170 | cwd = project.worktree |
171 | 171 | ||
172 | if not os.path.exists(cwd): | ||
173 | if (opt.project_header and opt.verbose) \ | ||
174 | or not opt.project_header: | ||
175 | print >>sys.stderr, 'skipping %s/' % project.relpath | ||
176 | continue | ||
177 | |||
172 | if opt.project_header: | 178 | if opt.project_header: |
173 | stdin = subprocess.PIPE | 179 | stdin = subprocess.PIPE |
174 | stdout = subprocess.PIPE | 180 | stdout = subprocess.PIPE |
diff --git a/subcmds/help.py b/subcmds/help.py index c5979fd6..e2f3074c 100644 --- a/subcmds/help.py +++ b/subcmds/help.py | |||
@@ -94,6 +94,8 @@ See 'repo help --all' for a complete list of recognized commands. | |||
94 | body = getattr(cmd, bodyAttr) | 94 | body = getattr(cmd, bodyAttr) |
95 | except AttributeError: | 95 | except AttributeError: |
96 | return | 96 | return |
97 | if body == '' or body is None: | ||
98 | return | ||
97 | 99 | ||
98 | self.nl() | 100 | self.nl() |
99 | 101 | ||
@@ -163,6 +165,7 @@ See 'repo help --all' for a complete list of recognized commands. | |||
163 | print >>sys.stderr, "repo: '%s' is not a repo command." % name | 165 | print >>sys.stderr, "repo: '%s' is not a repo command." % name |
164 | sys.exit(1) | 166 | sys.exit(1) |
165 | 167 | ||
168 | cmd.repodir = self.repodir | ||
166 | self._PrintCommandHelp(cmd) | 169 | self._PrintCommandHelp(cmd) |
167 | 170 | ||
168 | else: | 171 | else: |
diff --git a/subcmds/init.py b/subcmds/init.py index 75a58f11..cdbbfdf7 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
@@ -21,6 +21,9 @@ from command import InteractiveCommand, MirrorSafeCommand | |||
21 | from error import ManifestParseError | 21 | from error import ManifestParseError |
22 | from project import SyncBuffer | 22 | from project import SyncBuffer |
23 | from git_command import git_require, MIN_GIT_VERSION | 23 | from git_command import git_require, MIN_GIT_VERSION |
24 | from manifest_submodule import SubmoduleManifest | ||
25 | from manifest_xml import XmlManifest | ||
26 | from subcmds.sync import _ReloadManifest | ||
24 | 27 | ||
25 | class Init(InteractiveCommand, MirrorSafeCommand): | 28 | class Init(InteractiveCommand, MirrorSafeCommand): |
26 | common = True | 29 | common = True |
@@ -37,10 +40,6 @@ current working directory. | |||
37 | The optional -b argument can be used to select the manifest branch | 40 | The optional -b argument can be used to select the manifest branch |
38 | to checkout and use. If no branch is specified, master is assumed. | 41 | to checkout and use. If no branch is specified, master is assumed. |
39 | 42 | ||
40 | The optional -m argument can be used to specify an alternate manifest | ||
41 | to be used. If no manifest is specified, the manifest default.xml | ||
42 | will be used. | ||
43 | |||
44 | Switching Manifest Branches | 43 | Switching Manifest Branches |
45 | --------------------------- | 44 | --------------------------- |
46 | 45 | ||
@@ -65,9 +64,15 @@ to update the working directory files. | |||
65 | g.add_option('-b', '--manifest-branch', | 64 | g.add_option('-b', '--manifest-branch', |
66 | dest='manifest_branch', | 65 | dest='manifest_branch', |
67 | help='manifest branch or revision', metavar='REVISION') | 66 | help='manifest branch or revision', metavar='REVISION') |
68 | g.add_option('-m', '--manifest-name', | 67 | g.add_option('-o', '--origin', |
69 | dest='manifest_name', default='default.xml', | 68 | dest='manifest_origin', |
70 | help='initial manifest file', metavar='NAME.xml') | 69 | help="use REMOTE instead of 'origin' to track upstream", |
70 | metavar='REMOTE') | ||
71 | if isinstance(self.manifest, XmlManifest) \ | ||
72 | or not self.manifest.manifestProject.Exists: | ||
73 | g.add_option('-m', '--manifest-name', | ||
74 | dest='manifest_name', default='default.xml', | ||
75 | help='initial manifest file', metavar='NAME.xml') | ||
71 | g.add_option('--mirror', | 76 | g.add_option('--mirror', |
72 | dest='mirror', action='store_true', | 77 | dest='mirror', action='store_true', |
73 | help='mirror the forrest') | 78 | help='mirror the forrest') |
@@ -85,30 +90,42 @@ to update the working directory files. | |||
85 | dest='no_repo_verify', action='store_true', | 90 | dest='no_repo_verify', action='store_true', |
86 | help='do not verify repo source code') | 91 | help='do not verify repo source code') |
87 | 92 | ||
88 | def _SyncManifest(self, opt): | 93 | def _ApplyOptions(self, opt, is_new): |
89 | m = self.manifest.manifestProject | 94 | m = self.manifest.manifestProject |
90 | is_new = not m.Exists | ||
91 | 95 | ||
92 | if is_new: | 96 | if is_new: |
93 | if not opt.manifest_url: | 97 | if opt.manifest_origin: |
94 | print >>sys.stderr, 'fatal: manifest url (-u) is required.' | 98 | m.remote.name = opt.manifest_origin |
95 | sys.exit(1) | ||
96 | |||
97 | if not opt.quiet: | ||
98 | print >>sys.stderr, 'Getting manifest ...' | ||
99 | print >>sys.stderr, ' from %s' % opt.manifest_url | ||
100 | m._InitGitDir() | ||
101 | 99 | ||
102 | if opt.manifest_branch: | 100 | if opt.manifest_branch: |
103 | m.revisionExpr = opt.manifest_branch | 101 | m.revisionExpr = opt.manifest_branch |
104 | else: | 102 | else: |
105 | m.revisionExpr = 'refs/heads/master' | 103 | m.revisionExpr = 'refs/heads/master' |
106 | else: | 104 | else: |
105 | if opt.manifest_origin: | ||
106 | print >>sys.stderr, 'fatal: cannot change origin name' | ||
107 | sys.exit(1) | ||
108 | |||
107 | if opt.manifest_branch: | 109 | if opt.manifest_branch: |
108 | m.revisionExpr = opt.manifest_branch | 110 | m.revisionExpr = opt.manifest_branch |
109 | else: | 111 | else: |
110 | m.PreSync() | 112 | m.PreSync() |
111 | 113 | ||
114 | def _SyncManifest(self, opt): | ||
115 | m = self.manifest.manifestProject | ||
116 | is_new = not m.Exists | ||
117 | |||
118 | if is_new: | ||
119 | if not opt.manifest_url: | ||
120 | print >>sys.stderr, 'fatal: manifest url (-u) is required.' | ||
121 | sys.exit(1) | ||
122 | |||
123 | if not opt.quiet: | ||
124 | print >>sys.stderr, 'Getting manifest ...' | ||
125 | print >>sys.stderr, ' from %s' % opt.manifest_url | ||
126 | m._InitGitDir() | ||
127 | |||
128 | self._ApplyOptions(opt, is_new) | ||
112 | if opt.manifest_url: | 129 | if opt.manifest_url: |
113 | r = m.GetRemote(m.remote.name) | 130 | r = m.GetRemote(m.remote.name) |
114 | r.url = opt.manifest_url | 131 | r.url = opt.manifest_url |
@@ -118,6 +135,7 @@ to update the working directory files. | |||
118 | if opt.mirror: | 135 | if opt.mirror: |
119 | if is_new: | 136 | if is_new: |
120 | m.config.SetString('repo.mirror', 'true') | 137 | m.config.SetString('repo.mirror', 'true') |
138 | m.config.ClearCache() | ||
121 | else: | 139 | else: |
122 | print >>sys.stderr, 'fatal: --mirror not supported on existing client' | 140 | print >>sys.stderr, 'fatal: --mirror not supported on existing client' |
123 | sys.exit(1) | 141 | sys.exit(1) |
@@ -127,14 +145,29 @@ to update the working directory files. | |||
127 | print >>sys.stderr, 'fatal: cannot obtain manifest %s' % r.url | 145 | print >>sys.stderr, 'fatal: cannot obtain manifest %s' % r.url |
128 | sys.exit(1) | 146 | sys.exit(1) |
129 | 147 | ||
148 | if is_new and SubmoduleManifest.IsBare(m): | ||
149 | new = self.GetManifest(reparse=True, type=SubmoduleManifest) | ||
150 | if m.gitdir != new.manifestProject.gitdir: | ||
151 | os.rename(m.gitdir, new.manifestProject.gitdir) | ||
152 | new = self.GetManifest(reparse=True, type=SubmoduleManifest) | ||
153 | m = new.manifestProject | ||
154 | self._ApplyOptions(opt, is_new) | ||
155 | |||
156 | if not is_new: | ||
157 | # Force the manifest to load if it exists, the old graph | ||
158 | # may be needed inside of _ReloadManifest(). | ||
159 | # | ||
160 | self.manifest.projects | ||
161 | |||
130 | syncbuf = SyncBuffer(m.config) | 162 | syncbuf = SyncBuffer(m.config) |
131 | m.Sync_LocalHalf(syncbuf) | 163 | m.Sync_LocalHalf(syncbuf) |
132 | syncbuf.Finish() | 164 | syncbuf.Finish() |
165 | _ReloadManifest(self) | ||
166 | self._ApplyOptions(opt, is_new) | ||
133 | 167 | ||
134 | if is_new or m.CurrentBranch is None: | 168 | if not self.manifest.InitBranch(): |
135 | if not m.StartBranch('default'): | 169 | print >>sys.stderr, 'fatal: cannot create branch in manifest' |
136 | print >>sys.stderr, 'fatal: cannot create default in manifest' | 170 | sys.exit(1) |
137 | sys.exit(1) | ||
138 | 171 | ||
139 | def _LinkManifest(self, name): | 172 | def _LinkManifest(self, name): |
140 | if not name: | 173 | if not name: |
@@ -216,7 +249,8 @@ to update the working directory files. | |||
216 | def Execute(self, opt, args): | 249 | def Execute(self, opt, args): |
217 | git_require(MIN_GIT_VERSION, fail=True) | 250 | git_require(MIN_GIT_VERSION, fail=True) |
218 | self._SyncManifest(opt) | 251 | self._SyncManifest(opt) |
219 | self._LinkManifest(opt.manifest_name) | 252 | if isinstance(self.manifest, XmlManifest): |
253 | self._LinkManifest(opt.manifest_name) | ||
220 | 254 | ||
221 | if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror: | 255 | if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror: |
222 | self._ConfigureUser() | 256 | self._ConfigureUser() |
diff --git a/subcmds/manifest.py b/subcmds/manifest.py index 4374a9d0..dcd3df17 100644 --- a/subcmds/manifest.py +++ b/subcmds/manifest.py | |||
@@ -17,14 +17,25 @@ import os | |||
17 | import sys | 17 | import sys |
18 | 18 | ||
19 | from command import PagedCommand | 19 | from command import PagedCommand |
20 | from manifest_submodule import SubmoduleManifest | ||
21 | from manifest_xml import XmlManifest | ||
22 | |||
23 | def _doc(name): | ||
24 | r = os.path.dirname(__file__) | ||
25 | r = os.path.dirname(r) | ||
26 | fd = open(os.path.join(r, 'docs', name)) | ||
27 | try: | ||
28 | return fd.read() | ||
29 | finally: | ||
30 | fd.close() | ||
20 | 31 | ||
21 | class Manifest(PagedCommand): | 32 | class Manifest(PagedCommand): |
22 | common = False | 33 | common = False |
23 | helpSummary = "Manifest inspection utility" | 34 | helpSummary = "Manifest inspection utility" |
24 | helpUsage = """ | 35 | helpUsage = """ |
25 | %prog [-o {-|NAME.xml} [-r]] | 36 | %prog [options] |
26 | """ | 37 | """ |
27 | _helpDescription = """ | 38 | _xmlHelp = """ |
28 | 39 | ||
29 | With the -o option, exports the current manifest for inspection. | 40 | With the -o option, exports the current manifest for inspection. |
30 | The manifest and (if present) local_manifest.xml are combined | 41 | The manifest and (if present) local_manifest.xml are combined |
@@ -35,23 +46,30 @@ in a Git repository for use during future 'repo init' invocations. | |||
35 | 46 | ||
36 | @property | 47 | @property |
37 | def helpDescription(self): | 48 | def helpDescription(self): |
38 | help = self._helpDescription + '\n' | 49 | help = '' |
39 | r = os.path.dirname(__file__) | 50 | if isinstance(self.manifest, XmlManifest): |
40 | r = os.path.dirname(r) | 51 | help += self._xmlHelp + '\n' + _doc('manifest_xml.txt') |
41 | fd = open(os.path.join(r, 'docs', 'manifest-format.txt')) | 52 | if isinstance(self.manifest, SubmoduleManifest): |
42 | for line in fd: | 53 | help += _doc('manifest_submodule.txt') |
43 | help += line | ||
44 | fd.close() | ||
45 | return help | 54 | return help |
46 | 55 | ||
47 | def _Options(self, p): | 56 | def _Options(self, p): |
48 | p.add_option('-r', '--revision-as-HEAD', | 57 | if isinstance(self.manifest, XmlManifest): |
49 | dest='peg_rev', action='store_true', | 58 | p.add_option('--upgrade', |
50 | help='Save revisions as current HEAD') | 59 | dest='upgrade', action='store_true', |
51 | p.add_option('-o', '--output-file', | 60 | help='Upgrade XML manifest to submodule') |
52 | dest='output_file', | 61 | p.add_option('-r', '--revision-as-HEAD', |
53 | help='File to save the manifest to', | 62 | dest='peg_rev', action='store_true', |
54 | metavar='-|NAME.xml') | 63 | help='Save revisions as current HEAD') |
64 | p.add_option('-o', '--output-file', | ||
65 | dest='output_file', | ||
66 | help='File to save the manifest to', | ||
67 | metavar='-|NAME.xml') | ||
68 | |||
69 | def WantPager(self, opt): | ||
70 | if isinstance(self.manifest, XmlManifest) and opt.upgrade: | ||
71 | return False | ||
72 | return True | ||
55 | 73 | ||
56 | def _Output(self, opt): | 74 | def _Output(self, opt): |
57 | if opt.output_file == '-': | 75 | if opt.output_file == '-': |
@@ -64,13 +82,38 @@ in a Git repository for use during future 'repo init' invocations. | |||
64 | if opt.output_file != '-': | 82 | if opt.output_file != '-': |
65 | print >>sys.stderr, 'Saved manifest to %s' % opt.output_file | 83 | print >>sys.stderr, 'Saved manifest to %s' % opt.output_file |
66 | 84 | ||
85 | def _Upgrade(self): | ||
86 | old = self.manifest | ||
87 | |||
88 | if isinstance(old, SubmoduleManifest): | ||
89 | print >>sys.stderr, 'error: already upgraded' | ||
90 | sys.exit(1) | ||
91 | |||
92 | old._Load() | ||
93 | for p in old.projects.values(): | ||
94 | if not os.path.exists(p.gitdir) \ | ||
95 | or not os.path.exists(p.worktree): | ||
96 | print >>sys.stderr, 'fatal: project "%s" missing' % p.relpath | ||
97 | sys.exit(1) | ||
98 | |||
99 | new = SubmoduleManifest(old.repodir) | ||
100 | new.FromXml_Local_1(old, checkout=False) | ||
101 | new.FromXml_Definition(old) | ||
102 | new.FromXml_Local_2(old) | ||
103 | print >>sys.stderr, 'upgraded manifest; commit result manually' | ||
104 | |||
67 | def Execute(self, opt, args): | 105 | def Execute(self, opt, args): |
68 | if args: | 106 | if args: |
69 | self.Usage() | 107 | self.Usage() |
70 | 108 | ||
71 | if opt.output_file is not None: | 109 | if isinstance(self.manifest, XmlManifest): |
72 | self._Output(opt) | 110 | if opt.upgrade: |
73 | return | 111 | self._Upgrade() |
112 | return | ||
113 | |||
114 | if opt.output_file is not None: | ||
115 | self._Output(opt) | ||
116 | return | ||
74 | 117 | ||
75 | print >>sys.stderr, 'error: no operation to perform' | 118 | print >>sys.stderr, 'error: no operation to perform' |
76 | print >>sys.stderr, 'error: see repo help manifest' | 119 | print >>sys.stderr, 'error: see repo help manifest' |
diff --git a/subcmds/selfupdate.py b/subcmds/selfupdate.py index 4f46a129..46aa3a19 100644 --- a/subcmds/selfupdate.py +++ b/subcmds/selfupdate.py | |||
@@ -55,6 +55,7 @@ need to be performed by an end-user. | |||
55 | print >>sys.stderr, "error: can't update repo" | 55 | print >>sys.stderr, "error: can't update repo" |
56 | sys.exit(1) | 56 | sys.exit(1) |
57 | 57 | ||
58 | rp.bare_git.gc('--auto') | ||
58 | _PostRepoFetch(rp, | 59 | _PostRepoFetch(rp, |
59 | no_repo_verify = opt.no_repo_verify, | 60 | no_repo_verify = opt.no_repo_verify, |
60 | verbose = True) | 61 | verbose = True) |
diff --git a/subcmds/sync.py b/subcmds/sync.py index ceb81eaa..d89c2b8c 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -117,6 +117,8 @@ later is required to fix a server side protocol bug. | |||
117 | print >>sys.stderr, 'error: Cannot fetch %s' % project.name | 117 | print >>sys.stderr, 'error: Cannot fetch %s' % project.name |
118 | sys.exit(1) | 118 | sys.exit(1) |
119 | pm.end() | 119 | pm.end() |
120 | for project in projects: | ||
121 | project.bare_git.gc('--auto') | ||
120 | return fetched | 122 | return fetched |
121 | 123 | ||
122 | def UpdateProjectList(self): | 124 | def UpdateProjectList(self): |
@@ -215,7 +217,14 @@ uncommitted changes are present' % project.relpath | |||
215 | # bail out now; the rest touches the working tree | 217 | # bail out now; the rest touches the working tree |
216 | return | 218 | return |
217 | 219 | ||
218 | self.manifest._Unload() | 220 | if mp.HasChanges: |
221 | syncbuf = SyncBuffer(mp.config) | ||
222 | mp.Sync_LocalHalf(syncbuf) | ||
223 | if not syncbuf.Finish(): | ||
224 | sys.exit(1) | ||
225 | _ReloadManifest(self) | ||
226 | mp = self.manifest.manifestProject | ||
227 | |||
219 | all = self.GetProjects(args, missing_ok=True) | 228 | all = self.GetProjects(args, missing_ok=True) |
220 | missing = [] | 229 | missing = [] |
221 | for project in all: | 230 | for project in all: |
@@ -242,6 +251,14 @@ uncommitted changes are present' % project.relpath | |||
242 | if not syncbuf.Finish(): | 251 | if not syncbuf.Finish(): |
243 | sys.exit(1) | 252 | sys.exit(1) |
244 | 253 | ||
254 | def _ReloadManifest(cmd): | ||
255 | old = cmd.manifest | ||
256 | new = cmd.GetManifest(reparse=True) | ||
257 | |||
258 | if old.__class__ != new.__class__: | ||
259 | print >>sys.stderr, 'NOTICE: manifest format has changed ***' | ||
260 | new.Upgrade_Local(old) | ||
261 | |||
245 | def _PostRepoUpgrade(manifest): | 262 | def _PostRepoUpgrade(manifest): |
246 | for project in manifest.projects.values(): | 263 | for project in manifest.projects.values(): |
247 | if project.Exists: | 264 | if project.Exists: |
diff --git a/subcmds/upload.py b/subcmds/upload.py index aea399b6..2ab6a484 100644 --- a/subcmds/upload.py +++ b/subcmds/upload.py | |||
@@ -55,11 +55,11 @@ added to the respective list of users, and emails are sent to any | |||
55 | new users. Users passed as --reviewers must already be registered | 55 | new users. Users passed as --reviewers must already be registered |
56 | with the code review system, or the upload will fail. | 56 | with the code review system, or the upload will fail. |
57 | 57 | ||
58 | If the --replace option is passed the user can designate which | 58 | If the --replace option (deprecated) is passed the user can designate |
59 | existing change(s) in Gerrit match up to the commits in the branch | 59 | which existing change(s) in Gerrit match up to the commits in the |
60 | being uploaded. For each matched pair of change,commit the commit | 60 | branch being uploaded. For each matched pair of change,commit the |
61 | will be added as a new patch set, completely replacing the set of | 61 | commit will be added as a new patch set, completely replacing the |
62 | files and description associated with the change in Gerrit. | 62 | set of files and description associated with the change in Gerrit. |
63 | 63 | ||
64 | Configuration | 64 | Configuration |
65 | ------------- | 65 | ------------- |
@@ -92,7 +92,7 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
92 | def _Options(self, p): | 92 | def _Options(self, p): |
93 | p.add_option('--replace', | 93 | p.add_option('--replace', |
94 | dest='replace', action='store_true', | 94 | dest='replace', action='store_true', |
95 | help='Upload replacement patchesets from this branch') | 95 | help='Upload replacement patchsets from this branch (deprecated)') |
96 | p.add_option('--re', '--reviewers', | 96 | p.add_option('--re', '--reviewers', |
97 | type='string', action='append', dest='reviewers', | 97 | type='string', action='append', dest='reviewers', |
98 | help='Request reviews from these people.') | 98 | help='Request reviews from these people.') |
@@ -273,15 +273,19 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
273 | have_errors = True | 273 | have_errors = True |
274 | 274 | ||
275 | print >>sys.stderr, '' | 275 | print >>sys.stderr, '' |
276 | print >>sys.stderr, '--------------------------------------------' | 276 | print >>sys.stderr, '----------------------------------------------------------------------' |
277 | 277 | ||
278 | if have_errors: | 278 | if have_errors: |
279 | for branch in todo: | 279 | for branch in todo: |
280 | if not branch.uploaded: | 280 | if not branch.uploaded: |
281 | print >>sys.stderr, '[FAILED] %-15s %-15s (%s)' % ( | 281 | if len(str(branch.error)) <= 30: |
282 | fmt = ' (%s)' | ||
283 | else: | ||
284 | fmt = '\n (%s)' | ||
285 | print >>sys.stderr, ('[FAILED] %-15s %-15s' + fmt) % ( | ||
282 | branch.project.relpath + '/', \ | 286 | branch.project.relpath + '/', \ |
283 | branch.name, \ | 287 | branch.name, \ |
284 | branch.error) | 288 | str(branch.error)) |
285 | print >>sys.stderr, '' | 289 | print >>sys.stderr, '' |
286 | 290 | ||
287 | for branch in todo: | 291 | for branch in todo: |