diff options
Diffstat (limited to 'project.py')
| -rw-r--r-- | project.py | 177 |
1 files changed, 156 insertions, 21 deletions
| @@ -111,7 +111,6 @@ class ReviewableBranch(object): | |||
| 111 | self.project = project | 111 | self.project = project |
| 112 | self.branch = branch | 112 | self.branch = branch |
| 113 | self.base = base | 113 | self.base = base |
| 114 | self.replace_changes = None | ||
| 115 | 114 | ||
| 116 | @property | 115 | @property |
| 117 | def name(self): | 116 | def name(self): |
| @@ -149,10 +148,10 @@ class ReviewableBranch(object): | |||
| 149 | R_HEADS + self.name, | 148 | R_HEADS + self.name, |
| 150 | '--') | 149 | '--') |
| 151 | 150 | ||
| 152 | def UploadForReview(self, people): | 151 | def UploadForReview(self, people, auto_topic=False): |
| 153 | self.project.UploadForReview(self.name, | 152 | self.project.UploadForReview(self.name, |
| 154 | self.replace_changes, | 153 | people, |
| 155 | people) | 154 | auto_topic=auto_topic) |
| 156 | 155 | ||
| 157 | def GetPublishedRefs(self): | 156 | def GetPublishedRefs(self): |
| 158 | refs = {} | 157 | refs = {} |
| @@ -203,6 +202,10 @@ class _CopyFile: | |||
| 203 | # remove existing file first, since it might be read-only | 202 | # remove existing file first, since it might be read-only |
| 204 | if os.path.exists(dest): | 203 | if os.path.exists(dest): |
| 205 | os.remove(dest) | 204 | os.remove(dest) |
| 205 | else: | ||
| 206 | dir = os.path.dirname(dest) | ||
| 207 | if not os.path.isdir(dir): | ||
| 208 | os.makedirs(dir) | ||
| 206 | shutil.copy(src, dest) | 209 | shutil.copy(src, dest) |
| 207 | # make the file read-only | 210 | # make the file read-only |
| 208 | mode = os.stat(dest)[stat.ST_MODE] | 211 | mode = os.stat(dest)[stat.ST_MODE] |
| @@ -279,7 +282,7 @@ class Project(object): | |||
| 279 | return os.path.exists(os.path.join(g, 'rebase-apply')) \ | 282 | return os.path.exists(os.path.join(g, 'rebase-apply')) \ |
| 280 | or os.path.exists(os.path.join(g, 'rebase-merge')) \ | 283 | or os.path.exists(os.path.join(g, 'rebase-merge')) \ |
| 281 | or os.path.exists(os.path.join(w, '.dotest')) | 284 | or os.path.exists(os.path.join(w, '.dotest')) |
| 282 | 285 | ||
| 283 | def IsDirty(self, consider_untracked=True): | 286 | def IsDirty(self, consider_untracked=True): |
| 284 | """Is the working directory modified in some way? | 287 | """Is the working directory modified in some way? |
| 285 | """ | 288 | """ |
| @@ -364,6 +367,27 @@ class Project(object): | |||
| 364 | 367 | ||
| 365 | ## Status Display ## | 368 | ## Status Display ## |
| 366 | 369 | ||
| 370 | def HasChanges(self): | ||
| 371 | """Returns true if there are uncommitted changes. | ||
| 372 | """ | ||
| 373 | self.work_git.update_index('-q', | ||
| 374 | '--unmerged', | ||
| 375 | '--ignore-missing', | ||
| 376 | '--refresh') | ||
| 377 | if self.IsRebaseInProgress(): | ||
| 378 | return True | ||
| 379 | |||
| 380 | if self.work_git.DiffZ('diff-index', '--cached', HEAD): | ||
| 381 | return True | ||
| 382 | |||
| 383 | if self.work_git.DiffZ('diff-files'): | ||
| 384 | return True | ||
| 385 | |||
| 386 | if self.work_git.LsOthers(): | ||
| 387 | return True | ||
| 388 | |||
| 389 | return False | ||
| 390 | |||
| 367 | def PrintWorkTreeStatus(self): | 391 | def PrintWorkTreeStatus(self): |
| 368 | """Prints the status of the repository to stdout. | 392 | """Prints the status of the repository to stdout. |
| 369 | """ | 393 | """ |
| @@ -412,7 +436,7 @@ class Project(object): | |||
| 412 | 436 | ||
| 413 | try: f = df[p] | 437 | try: f = df[p] |
| 414 | except KeyError: f = None | 438 | except KeyError: f = None |
| 415 | 439 | ||
| 416 | if i: i_status = i.status.upper() | 440 | if i: i_status = i.status.upper() |
| 417 | else: i_status = '-' | 441 | else: i_status = '-' |
| 418 | 442 | ||
| @@ -530,7 +554,9 @@ class Project(object): | |||
| 530 | return rb | 554 | return rb |
| 531 | return None | 555 | return None |
| 532 | 556 | ||
| 533 | def UploadForReview(self, branch=None, replace_changes=None, people=([],[])): | 557 | def UploadForReview(self, branch=None, |
| 558 | people=([],[]), | ||
| 559 | auto_topic=False): | ||
| 534 | """Uploads the named branch for code review. | 560 | """Uploads the named branch for code review. |
| 535 | """ | 561 | """ |
| 536 | if branch is None: | 562 | if branch is None: |
| @@ -562,13 +588,15 @@ class Project(object): | |||
| 562 | for e in people[1]: | 588 | for e in people[1]: |
| 563 | rp.append('--cc=%s' % sq(e)) | 589 | rp.append('--cc=%s' % sq(e)) |
| 564 | 590 | ||
| 591 | ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch) | ||
| 592 | if auto_topic: | ||
| 593 | ref_spec = ref_spec + '/' + branch.name | ||
| 594 | |||
| 565 | cmd = ['push'] | 595 | cmd = ['push'] |
| 566 | cmd.append('--receive-pack=%s' % " ".join(rp)) | 596 | cmd.append('--receive-pack=%s' % " ".join(rp)) |
| 567 | cmd.append(branch.remote.SshReviewUrl(self.UserEmail)) | 597 | cmd.append(branch.remote.SshReviewUrl(self.UserEmail)) |
| 568 | cmd.append('%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)) | 598 | cmd.append(ref_spec) |
| 569 | if replace_changes: | 599 | |
| 570 | for change_id,commit_id in replace_changes.iteritems(): | ||
| 571 | cmd.append('%s:refs/changes/%s/new' % (commit_id, change_id)) | ||
| 572 | if GitCommand(self, cmd, bare = True).Wait() != 0: | 600 | if GitCommand(self, cmd, bare = True).Wait() != 0: |
| 573 | raise UploadError('Upload failed') | 601 | raise UploadError('Upload failed') |
| 574 | 602 | ||
| @@ -584,19 +612,33 @@ class Project(object): | |||
| 584 | 612 | ||
| 585 | ## Sync ## | 613 | ## Sync ## |
| 586 | 614 | ||
| 587 | def Sync_NetworkHalf(self): | 615 | def Sync_NetworkHalf(self, quiet=False): |
| 588 | """Perform only the network IO portion of the sync process. | 616 | """Perform only the network IO portion of the sync process. |
| 589 | Local working directory/branch state is not affected. | 617 | Local working directory/branch state is not affected. |
| 590 | """ | 618 | """ |
| 591 | if not self.Exists: | 619 | is_new = not self.Exists |
| 592 | print >>sys.stderr | 620 | if is_new: |
| 593 | print >>sys.stderr, 'Initializing project %s ...' % self.name | 621 | if not quiet: |
| 622 | print >>sys.stderr | ||
| 623 | print >>sys.stderr, 'Initializing project %s ...' % self.name | ||
| 594 | self._InitGitDir() | 624 | self._InitGitDir() |
| 595 | 625 | ||
| 596 | self._InitRemote() | 626 | self._InitRemote() |
| 597 | if not self._RemoteFetch(): | 627 | if not self._RemoteFetch(initial=is_new, quiet=quiet): |
| 598 | return False | 628 | return False |
| 599 | 629 | ||
| 630 | #Check that the requested ref was found after fetch | ||
| 631 | # | ||
| 632 | try: | ||
| 633 | self.GetRevisionId() | ||
| 634 | except ManifestInvalidRevisionError: | ||
| 635 | # if the ref is a tag. We can try fetching | ||
| 636 | # the tag manually as a last resort | ||
| 637 | # | ||
| 638 | rev = self.revisionExpr | ||
| 639 | if rev.startswith(R_TAGS): | ||
| 640 | self._RemoteFetch(None, rev[len(R_TAGS):], quiet=quiet) | ||
| 641 | |||
| 600 | if self.worktree: | 642 | if self.worktree: |
| 601 | self.manifest.SetMRefs(self) | 643 | self.manifest.SetMRefs(self) |
| 602 | else: | 644 | else: |
| @@ -978,7 +1020,9 @@ class Project(object): | |||
| 978 | 1020 | ||
| 979 | ## Direct Git Commands ## | 1021 | ## Direct Git Commands ## |
| 980 | 1022 | ||
| 981 | def _RemoteFetch(self, name=None): | 1023 | def _RemoteFetch(self, name=None, tag=None, |
| 1024 | initial=False, | ||
| 1025 | quiet=False): | ||
| 982 | if not name: | 1026 | if not name: |
| 983 | name = self.remote.name | 1027 | name = self.remote.name |
| 984 | 1028 | ||
| @@ -986,14 +1030,84 @@ class Project(object): | |||
| 986 | if self.GetRemote(name).PreConnectFetch(): | 1030 | if self.GetRemote(name).PreConnectFetch(): |
| 987 | ssh_proxy = True | 1031 | ssh_proxy = True |
| 988 | 1032 | ||
| 1033 | if initial: | ||
| 1034 | alt = os.path.join(self.gitdir, 'objects/info/alternates') | ||
| 1035 | try: | ||
| 1036 | fd = open(alt, 'rb') | ||
| 1037 | try: | ||
| 1038 | ref_dir = fd.readline() | ||
| 1039 | if ref_dir and ref_dir.endswith('\n'): | ||
| 1040 | ref_dir = ref_dir[:-1] | ||
| 1041 | finally: | ||
| 1042 | fd.close() | ||
| 1043 | except IOError, e: | ||
| 1044 | ref_dir = None | ||
| 1045 | |||
| 1046 | if ref_dir and 'objects' == os.path.basename(ref_dir): | ||
| 1047 | ref_dir = os.path.dirname(ref_dir) | ||
| 1048 | packed_refs = os.path.join(self.gitdir, 'packed-refs') | ||
| 1049 | remote = self.GetRemote(name) | ||
| 1050 | |||
| 1051 | all = self.bare_ref.all | ||
| 1052 | ids = set(all.values()) | ||
| 1053 | tmp = set() | ||
| 1054 | |||
| 1055 | for r, id in GitRefs(ref_dir).all.iteritems(): | ||
| 1056 | if r not in all: | ||
| 1057 | if r.startswith(R_TAGS) or remote.WritesTo(r): | ||
| 1058 | all[r] = id | ||
| 1059 | ids.add(id) | ||
| 1060 | continue | ||
| 1061 | |||
| 1062 | if id in ids: | ||
| 1063 | continue | ||
| 1064 | |||
| 1065 | r = 'refs/_alt/%s' % id | ||
| 1066 | all[r] = id | ||
| 1067 | ids.add(id) | ||
| 1068 | tmp.add(r) | ||
| 1069 | |||
| 1070 | ref_names = list(all.keys()) | ||
| 1071 | ref_names.sort() | ||
| 1072 | |||
| 1073 | tmp_packed = '' | ||
| 1074 | old_packed = '' | ||
| 1075 | |||
| 1076 | for r in ref_names: | ||
| 1077 | line = '%s %s\n' % (all[r], r) | ||
| 1078 | tmp_packed += line | ||
| 1079 | if r not in tmp: | ||
| 1080 | old_packed += line | ||
| 1081 | |||
| 1082 | _lwrite(packed_refs, tmp_packed) | ||
| 1083 | |||
| 1084 | else: | ||
| 1085 | ref_dir = None | ||
| 1086 | |||
| 989 | cmd = ['fetch'] | 1087 | cmd = ['fetch'] |
| 1088 | if quiet: | ||
| 1089 | cmd.append('--quiet') | ||
| 990 | if not self.worktree: | 1090 | if not self.worktree: |
| 991 | cmd.append('--update-head-ok') | 1091 | cmd.append('--update-head-ok') |
| 992 | cmd.append(name) | 1092 | cmd.append(name) |
| 993 | return GitCommand(self, | 1093 | if tag is not None: |
| 994 | cmd, | 1094 | cmd.append('tag') |
| 995 | bare = True, | 1095 | cmd.append(tag) |
| 996 | ssh_proxy = ssh_proxy).Wait() == 0 | 1096 | |
| 1097 | ok = GitCommand(self, | ||
| 1098 | cmd, | ||
| 1099 | bare = True, | ||
| 1100 | ssh_proxy = ssh_proxy).Wait() == 0 | ||
| 1101 | |||
| 1102 | if initial: | ||
| 1103 | if ref_dir: | ||
| 1104 | if old_packed != '': | ||
| 1105 | _lwrite(packed_refs, old_packed) | ||
| 1106 | else: | ||
| 1107 | os.remove(packed_refs) | ||
| 1108 | self.bare_git.pack_refs('--all', '--prune') | ||
| 1109 | |||
| 1110 | return ok | ||
| 997 | 1111 | ||
| 998 | def _Checkout(self, rev, quiet=False): | 1112 | def _Checkout(self, rev, quiet=False): |
| 999 | cmd = ['checkout'] | 1113 | cmd = ['checkout'] |
| @@ -1031,6 +1145,27 @@ class Project(object): | |||
| 1031 | os.makedirs(self.gitdir) | 1145 | os.makedirs(self.gitdir) |
| 1032 | self.bare_git.init() | 1146 | self.bare_git.init() |
| 1033 | 1147 | ||
| 1148 | mp = self.manifest.manifestProject | ||
| 1149 | ref_dir = mp.config.GetString('repo.reference') | ||
| 1150 | |||
| 1151 | if ref_dir: | ||
| 1152 | mirror_git = os.path.join(ref_dir, self.name + '.git') | ||
| 1153 | repo_git = os.path.join(ref_dir, '.repo', 'projects', | ||
| 1154 | self.relpath + '.git') | ||
| 1155 | |||
| 1156 | if os.path.exists(mirror_git): | ||
| 1157 | ref_dir = mirror_git | ||
| 1158 | |||
| 1159 | elif os.path.exists(repo_git): | ||
| 1160 | ref_dir = repo_git | ||
| 1161 | |||
| 1162 | else: | ||
| 1163 | ref_dir = None | ||
| 1164 | |||
| 1165 | if ref_dir: | ||
| 1166 | _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'), | ||
| 1167 | os.path.join(ref_dir, 'objects') + '\n') | ||
| 1168 | |||
| 1034 | if self.manifest.IsMirror: | 1169 | if self.manifest.IsMirror: |
| 1035 | self.config.SetString('core.bare', 'true') | 1170 | self.config.SetString('core.bare', 'true') |
| 1036 | else: | 1171 | else: |
