diff options
| -rwxr-xr-x | gerrit_upload.py | 9 | ||||
| -rw-r--r-- | project.py | 19 | ||||
| -rw-r--r-- | subcmds/upload.py | 59 |
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) |
| @@ -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 = """ |
| 35 | The '%prog' command is used to send changes to the Gerrit code | 35 | The '%prog' command is used to send changes to the Gerrit code |
| @@ -46,6 +46,11 @@ no projects are specified, '%prog' will search for uploadable | |||
| 46 | changes in all projects listed in the manifest. | 46 | changes 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: |
