summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xgerrit_upload.py9
-rw-r--r--project.py19
-rw-r--r--subcmds/upload.py59
3 files changed, 83 insertions, 4 deletions
diff --git a/gerrit_upload.py b/gerrit_upload.py
index f8dccdc5..32451408 100755
--- a/gerrit_upload.py
+++ b/gerrit_upload.py
@@ -75,6 +75,7 @@ def UploadBundle(project,
75 dest_branch, 75 dest_branch,
76 src_branch, 76 src_branch,
77 bases, 77 bases,
78 replace_changes = None,
78 save_cookies=True): 79 save_cookies=True):
79 80
80 srv = _GetRpcServer(email, server, save_cookies) 81 srv = _GetRpcServer(email, server, save_cookies)
@@ -113,6 +114,10 @@ def UploadBundle(project,
113 req.dest_branch = str(dest_branch) 114 req.dest_branch = str(dest_branch)
114 for c in revlist: 115 for c in revlist:
115 req.contained_object.append(c) 116 req.contained_object.append(c)
117 for change_id,commit_id in replace_changes.iteritems():
118 r = req.replace.add()
119 r.change_id = change_id
120 r.object_id = commit_id
116 else: 121 else:
117 req = UploadBundleContinue() 122 req = UploadBundleContinue()
118 req.bundle_id = bundle_id 123 req.bundle_id = bundle_id
@@ -148,6 +153,10 @@ def UploadBundle(project,
148 elif rsp.status_code == UploadBundleResponse.UNAUTHORIZED_USER: 153 elif rsp.status_code == UploadBundleResponse.UNAUTHORIZED_USER:
149 reason = ('Unauthorized user. Visit http://%s/hello to sign up.' 154 reason = ('Unauthorized user. Visit http://%s/hello to sign up.'
150 % server) 155 % server)
156 elif rsp.status_code == UploadBundleResponse.UNKNOWN_CHANGE:
157 reason = 'invalid change id'
158 elif rsp.status_code == UploadBundleResponse.CHANGE_CLOSED:
159 reason = 'one or more changes are closed'
151 else: 160 else:
152 reason = 'unknown error ' + str(rsp.status_code) 161 reason = 'unknown error ' + str(rsp.status_code)
153 raise UploadError(reason) 162 raise UploadError(reason)
diff --git a/project.py b/project.py
index 1e25c2c9..39550335 100644
--- a/project.py
+++ b/project.py
@@ -104,6 +104,7 @@ class ReviewableBranch(object):
104 self.project = project 104 self.project = project
105 self.branch = branch 105 self.branch = branch
106 self.base = base 106 self.base = base
107 self.replace_changes = None
107 108
108 @property 109 @property
109 def name(self): 110 def name(self):
@@ -124,6 +125,16 @@ class ReviewableBranch(object):
124 return self._commit_cache 125 return self._commit_cache
125 126
126 @property 127 @property
128 def unabbrev_commits(self):
129 r = dict()
130 for commit in self.project.bare_git.rev_list(
131 not_rev(self.base),
132 R_HEADS + self.name,
133 '--'):
134 r[commit[0:8]] = commit
135 return r
136
137 @property
127 def date(self): 138 def date(self):
128 return self.project.bare_git.log( 139 return self.project.bare_git.log(
129 '--pretty=format:%cd', 140 '--pretty=format:%cd',
@@ -132,7 +143,8 @@ class ReviewableBranch(object):
132 '--') 143 '--')
133 144
134 def UploadForReview(self): 145 def UploadForReview(self):
135 self.project.UploadForReview(self.name) 146 self.project.UploadForReview(self.name,
147 self.replace_changes)
136 148
137 @property 149 @property
138 def tip_url(self): 150 def tip_url(self):
@@ -444,7 +456,7 @@ class Project(object):
444 return rb 456 return rb
445 return None 457 return None
446 458
447 def UploadForReview(self, branch=None): 459 def UploadForReview(self, branch=None, replace_changes=None):
448 """Uploads the named branch for code review. 460 """Uploads the named branch for code review.
449 """ 461 """
450 if branch is None: 462 if branch is None:
@@ -482,7 +494,8 @@ class Project(object):
482 dest_project = branch.remote.projectname, 494 dest_project = branch.remote.projectname,
483 dest_branch = dest_branch, 495 dest_branch = dest_branch,
484 src_branch = R_HEADS + branch.name, 496 src_branch = R_HEADS + branch.name,
485 bases = base_list) 497 bases = base_list,
498 replace_changes = replace_changes)
486 except proto_client.ClientLoginError: 499 except proto_client.ClientLoginError:
487 raise UploadError('Login failure') 500 raise UploadError('Login failure')
488 except urllib2.HTTPError, e: 501 except urllib2.HTTPError, e:
diff --git a/subcmds/upload.py b/subcmds/upload.py
index 9018455f..11f035d7 100644
--- a/subcmds/upload.py
+++ b/subcmds/upload.py
@@ -29,7 +29,7 @@ class Upload(InteractiveCommand):
29 common = True 29 common = True
30 helpSummary = "Upload changes for code review" 30 helpSummary = "Upload changes for code review"
31 helpUsage=""" 31 helpUsage="""
32%prog [<project>]... 32%prog {[<project>]... | --replace <project>}
33""" 33"""
34 helpDescription = """ 34 helpDescription = """
35The '%prog' command is used to send changes to the Gerrit code 35The '%prog' command is used to send changes to the Gerrit code
@@ -46,6 +46,11 @@ no projects are specified, '%prog' will search for uploadable
46changes in all projects listed in the manifest. 46changes in all projects listed in the manifest.
47""" 47"""
48 48
49 def _Options(self, p):
50 p.add_option('--replace',
51 dest='replace', action='store_true',
52 help='Upload replacement patchesets from this branch')
53
49 def _SingleBranch(self, branch): 54 def _SingleBranch(self, branch):
50 project = branch.project 55 project = branch.project
51 name = branch.name 56 name = branch.name
@@ -129,6 +134,50 @@ changes in all projects listed in the manifest.
129 _die("nothing uncommented for upload") 134 _die("nothing uncommented for upload")
130 self._UploadAndReport(todo) 135 self._UploadAndReport(todo)
131 136
137 def _ReplaceBranch(self, project):
138 branch = project.CurrentBranch
139 if not branch:
140 print >>sys.stdout, "no branches ready for upload"
141 return
142 branch = project.GetUploadableBranch(branch)
143 if not branch:
144 print >>sys.stdout, "no branches ready for upload"
145 return
146
147 script = []
148 script.append('# Replacing from branch %s' % branch.name)
149 for commit in branch.commits:
150 script.append('[ ] %s' % commit)
151 script.append('')
152 script.append('# Insert change numbers in the brackets to add a new patch set.')
153 script.append('# To create a new change record, leave the brackets empty.')
154
155 script = Editor.EditString("\n".join(script)).split("\n")
156
157 change_re = re.compile(r'^\[\s*(\d{1,})\s*\]\s*([0-9a-f]{1,}) .*$')
158 to_replace = dict()
159 full_hashes = branch.unabbrev_commits
160
161 for line in script:
162 m = change_re.match(line)
163 if m:
164 f = m.group(2)
165 try:
166 f = full_hashes[f]
167 except KeyError:
168 print 'fh = %s' % full_hashes
169 print >>sys.stderr, "error: commit %s not found" % f
170 sys.exit(1)
171 to_replace[m.group(1)] = f
172
173 if not to_replace:
174 print >>sys.stderr, "error: no replacements specified"
175 print >>sys.stderr, " use 'repo upload' without --replace"
176 sys.exit(1)
177
178 branch.replace_changes = to_replace
179 self._UploadAndReport([branch])
180
132 def _UploadAndReport(self, todo): 181 def _UploadAndReport(self, todo):
133 have_errors = False 182 have_errors = False
134 for branch in todo: 183 for branch in todo:
@@ -168,6 +217,14 @@ changes in all projects listed in the manifest.
168 project_list = self.GetProjects(args) 217 project_list = self.GetProjects(args)
169 pending = [] 218 pending = []
170 219
220 if opt.replace:
221 if len(project_list) != 1:
222 print >>sys.stderr, \
223 'error: --replace requires exactly one project'
224 sys.exit(1)
225 self._ReplaceBranch(project_list[0])
226 return
227
171 for project in project_list: 228 for project in project_list:
172 avail = project.GetUploadableBranches() 229 avail = project.GetUploadableBranches()
173 if avail: 230 if avail: