summaryrefslogtreecommitdiffstats
path: root/subcmds/download.py
diff options
context:
space:
mode:
Diffstat (limited to 'subcmds/download.py')
-rw-r--r--subcmds/download.py304
1 files changed, 174 insertions, 130 deletions
diff --git a/subcmds/download.py b/subcmds/download.py
index 15824843..d81d1f8c 100644
--- a/subcmds/download.py
+++ b/subcmds/download.py
@@ -18,143 +18,187 @@ import sys
18from command import Command 18from command import Command
19from error import GitError, NoSuchProjectError 19from error import GitError, NoSuchProjectError
20 20
21CHANGE_RE = re.compile(r'^([1-9][0-9]*)(?:[/\.-]([1-9][0-9]*))?$') 21CHANGE_RE = re.compile(r"^([1-9][0-9]*)(?:[/\.-]([1-9][0-9]*))?$")
22 22
23 23
24class Download(Command): 24class Download(Command):
25 COMMON = True 25 COMMON = True
26 helpSummary = "Download and checkout a change" 26 helpSummary = "Download and checkout a change"
27 helpUsage = """ 27 helpUsage = """
28%prog {[project] change[/patchset]}... 28%prog {[project] change[/patchset]}...
29""" 29"""
30 helpDescription = """ 30 helpDescription = """
31The '%prog' command downloads a change from the review system and 31The '%prog' command downloads a change from the review system and
32makes it available in your project's local working directory. 32makes it available in your project's local working directory.
33If no project is specified try to use current directory as a project. 33If no project is specified try to use current directory as a project.
34""" 34"""
35 35
36 def _Options(self, p): 36 def _Options(self, p):
37 p.add_option('-b', '--branch', 37 p.add_option("-b", "--branch", help="create a new branch first")
38 help='create a new branch first') 38 p.add_option(
39 p.add_option('-c', '--cherry-pick', 39 "-c",
40 dest='cherrypick', action='store_true', 40 "--cherry-pick",
41 help="cherry-pick instead of checkout") 41 dest="cherrypick",
42 p.add_option('-x', '--record-origin', action='store_true', 42 action="store_true",
43 help='pass -x when cherry-picking') 43 help="cherry-pick instead of checkout",
44 p.add_option('-r', '--revert', 44 )
45 dest='revert', action='store_true', 45 p.add_option(
46 help="revert instead of checkout") 46 "-x",
47 p.add_option('-f', '--ff-only', 47 "--record-origin",
48 dest='ffonly', action='store_true', 48 action="store_true",
49 help="force fast-forward merge") 49 help="pass -x when cherry-picking",
50 50 )
51 def _ParseChangeIds(self, opt, args): 51 p.add_option(
52 if not args: 52 "-r",
53 self.Usage() 53 "--revert",
54 54 dest="revert",
55 to_get = [] 55 action="store_true",
56 project = None 56 help="revert instead of checkout",
57 57 )
58 for a in args: 58 p.add_option(
59 m = CHANGE_RE.match(a) 59 "-f",
60 if m: 60 "--ff-only",
61 if not project: 61 dest="ffonly",
62 project = self.GetProjects(".")[0] 62 action="store_true",
63 print('Defaulting to cwd project', project.name) 63 help="force fast-forward merge",
64 chg_id = int(m.group(1)) 64 )
65 if m.group(2): 65
66 ps_id = int(m.group(2)) 66 def _ParseChangeIds(self, opt, args):
67 else: 67 if not args:
68 ps_id = 1 68 self.Usage()
69 refs = 'refs/changes/%2.2d/%d/' % (chg_id % 100, chg_id) 69
70 output = project._LsRemote(refs + '*') 70 to_get = []
71 if output: 71 project = None
72 regex = refs + r'(\d+)' 72
73 rcomp = re.compile(regex, re.I) 73 for a in args:
74 for line in output.splitlines(): 74 m = CHANGE_RE.match(a)
75 match = rcomp.search(line) 75 if m:
76 if match: 76 if not project:
77 ps_id = max(int(match.group(1)), ps_id) 77 project = self.GetProjects(".")[0]
78 to_get.append((project, chg_id, ps_id)) 78 print("Defaulting to cwd project", project.name)
79 else: 79 chg_id = int(m.group(1))
80 projects = self.GetProjects([a], all_manifests=not opt.this_manifest_only) 80 if m.group(2):
81 if len(projects) > 1: 81 ps_id = int(m.group(2))
82 # If the cwd is one of the projects, assume they want that. 82 else:
83 try: 83 ps_id = 1
84 project = self.GetProjects('.')[0] 84 refs = "refs/changes/%2.2d/%d/" % (chg_id % 100, chg_id)
85 except NoSuchProjectError: 85 output = project._LsRemote(refs + "*")
86 project = None 86 if output:
87 if project not in projects: 87 regex = refs + r"(\d+)"
88 print('error: %s matches too many projects; please re-run inside ' 88 rcomp = re.compile(regex, re.I)
89 'the project checkout.' % (a,), file=sys.stderr) 89 for line in output.splitlines():
90 for project in projects: 90 match = rcomp.search(line)
91 print(' %s/ @ %s' % (project.RelPath(local=opt.this_manifest_only), 91 if match:
92 project.revisionExpr), file=sys.stderr) 92 ps_id = max(int(match.group(1)), ps_id)
93 sys.exit(1) 93 to_get.append((project, chg_id, ps_id))
94 else: 94 else:
95 project = projects[0] 95 projects = self.GetProjects(
96 print('Defaulting to cwd project', project.name) 96 [a], all_manifests=not opt.this_manifest_only
97 return to_get 97 )
98 98 if len(projects) > 1:
99 def ValidateOptions(self, opt, args): 99 # If the cwd is one of the projects, assume they want that.
100 if opt.record_origin: 100 try:
101 if not opt.cherrypick: 101 project = self.GetProjects(".")[0]
102 self.OptionParser.error('-x only makes sense with --cherry-pick') 102 except NoSuchProjectError:
103 103 project = None
104 if opt.ffonly: 104 if project not in projects:
105 self.OptionParser.error('-x and --ff are mutually exclusive options') 105 print(
106 106 "error: %s matches too many projects; please "
107 def Execute(self, opt, args): 107 "re-run inside the project checkout." % (a,),
108 for project, change_id, ps_id in self._ParseChangeIds(opt, args): 108 file=sys.stderr,
109 dl = project.DownloadPatchSet(change_id, ps_id) 109 )
110 if not dl: 110 for project in projects:
111 print('[%s] change %d/%d not found' 111 print(
112 % (project.name, change_id, ps_id), 112 " %s/ @ %s"
113 file=sys.stderr) 113 % (
114 sys.exit(1) 114 project.RelPath(
115 115 local=opt.this_manifest_only
116 if not opt.revert and not dl.commits: 116 ),
117 print('[%s] change %d/%d has already been merged' 117 project.revisionExpr,
118 % (project.name, change_id, ps_id), 118 ),
119 file=sys.stderr) 119 file=sys.stderr,
120 continue 120 )
121 121 sys.exit(1)
122 if len(dl.commits) > 1: 122 else:
123 print('[%s] %d/%d depends on %d unmerged changes:' 123 project = projects[0]
124 % (project.name, change_id, ps_id, len(dl.commits)), 124 print("Defaulting to cwd project", project.name)
125 file=sys.stderr) 125 return to_get
126 for c in dl.commits: 126
127 print(' %s' % (c), file=sys.stderr) 127 def ValidateOptions(self, opt, args):
128 128 if opt.record_origin:
129 if opt.cherrypick: 129 if not opt.cherrypick:
130 mode = 'cherry-pick' 130 self.OptionParser.error(
131 elif opt.revert: 131 "-x only makes sense with --cherry-pick"
132 mode = 'revert' 132 )
133 elif opt.ffonly: 133
134 mode = 'fast-forward merge' 134 if opt.ffonly:
135 else: 135 self.OptionParser.error(
136 mode = 'checkout' 136 "-x and --ff are mutually exclusive options"
137 137 )
138 # We'll combine the branch+checkout operation, but all the rest need a 138
139 # dedicated branch start. 139 def Execute(self, opt, args):
140 if opt.branch and mode != 'checkout': 140 for project, change_id, ps_id in self._ParseChangeIds(opt, args):
141 project.StartBranch(opt.branch) 141 dl = project.DownloadPatchSet(change_id, ps_id)
142 142 if not dl:
143 try: 143 print(
144 if opt.cherrypick: 144 "[%s] change %d/%d not found"
145 project._CherryPick(dl.commit, ffonly=opt.ffonly, 145 % (project.name, change_id, ps_id),
146 record_origin=opt.record_origin) 146 file=sys.stderr,
147 elif opt.revert: 147 )
148 project._Revert(dl.commit) 148 sys.exit(1)
149 elif opt.ffonly: 149
150 project._FastForward(dl.commit, ffonly=True) 150 if not opt.revert and not dl.commits:
151 else: 151 print(
152 if opt.branch: 152 "[%s] change %d/%d has already been merged"
153 project.StartBranch(opt.branch, revision=dl.commit) 153 % (project.name, change_id, ps_id),
154 else: 154 file=sys.stderr,
155 project._Checkout(dl.commit) 155 )
156 156 continue
157 except GitError: 157
158 print('[%s] Could not complete the %s of %s' 158 if len(dl.commits) > 1:
159 % (project.name, mode, dl.commit), file=sys.stderr) 159 print(
160 sys.exit(1) 160 "[%s] %d/%d depends on %d unmerged changes:"
161 % (project.name, change_id, ps_id, len(dl.commits)),
162 file=sys.stderr,
163 )
164 for c in dl.commits:
165 print(" %s" % (c), file=sys.stderr)
166
167 if opt.cherrypick:
168 mode = "cherry-pick"
169 elif opt.revert:
170 mode = "revert"
171 elif opt.ffonly:
172 mode = "fast-forward merge"
173 else:
174 mode = "checkout"
175
176 # We'll combine the branch+checkout operation, but all the rest need
177 # a dedicated branch start.
178 if opt.branch and mode != "checkout":
179 project.StartBranch(opt.branch)
180
181 try:
182 if opt.cherrypick:
183 project._CherryPick(
184 dl.commit,
185 ffonly=opt.ffonly,
186 record_origin=opt.record_origin,
187 )
188 elif opt.revert:
189 project._Revert(dl.commit)
190 elif opt.ffonly:
191 project._FastForward(dl.commit, ffonly=True)
192 else:
193 if opt.branch:
194 project.StartBranch(opt.branch, revision=dl.commit)
195 else:
196 project._Checkout(dl.commit)
197
198 except GitError:
199 print(
200 "[%s] Could not complete the %s of %s"
201 % (project.name, mode, dl.commit),
202 file=sys.stderr,
203 )
204 sys.exit(1)