summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn O. Pearce <sop@google.com>2008-11-11 17:12:43 -0800
committerShawn O. Pearce <sop@google.com>2008-11-12 09:12:19 -0800
commitc99883fee990a1baa7e0bf5f854c7485b7d0f0d9 (patch)
tree870cfd73b1fc2faf53da6d613ae820984695b305
parentec18b4bac4f3c0760c26e83b8ade01ad1c815a9b (diff)
downloadgit-repo-c99883fee990a1baa7e0bf5f854c7485b7d0f0d9.tar.gz
Teach 'repo upload --replace' how to add replacement patch setsv1.3
Users are prompted with the list of known changes we are about to upload, and they can fill out the current change numbers for any changes which already exist in the data store. For each of those changes the change number and commit id is sent as part of the upload request, so Gerrit can insert the new commit as a new patch set of the existing change, rather than make a new change. This facility permits developers to replace a patch so they can address comments made on a prior version of the same change. Signed-off-by: Shawn O. Pearce <sop@google.com>
-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: