diff options
author | Shawn O. Pearce <sop@google.com> | 2010-12-07 10:31:19 -0800 |
---|---|---|
committer | Shawn O. Pearce <sop@google.com> | 2010-12-07 11:13:29 -0800 |
commit | 13f3da50d40b89ee5b05f5f3de9542c20edac6d1 (patch) | |
tree | d085b6f6b498bde85a1969fce884dd24e88d03d5 /project.py | |
parent | 3218c13205694434edb2375ab8a8515554eed366 (diff) | |
parent | 2b8db3ce3e7344b9f3b5216637c5af0d54be5656 (diff) | |
download | git-repo-13f3da50d40b89ee5b05f5f3de9542c20edac6d1.tar.gz |
Merge branch 'stable'
* stable: (33 commits)
Added feature to print a <notice> from manifest at the end of a sync.
sync: Use --force-broken to continue other projects
upload: Remove --replace option
sync --quiet: be more quiet
sync: Enable use of git clone --reference
Only delete corrupt pickle config files if they exist
Don't allow git fetch to start ControlMaster
Check for existing SSH ControlMaster
Fix for handling values of EDITOR which contain a space.
upload: Fix --replace flag
rebase: Pass through more options
upload: Allow review.HOST.username to override email
upload -t: Automatically include local branch name
Warn users before uploading if there are local changes
sync: Try fetching a tag as a last resort before giving up
rebase: Automatically rebase branch on upstrea
upload: Automatically --cc folks in review.URL.autocopy
Fix format string bugs in grep
Do not invoke ssh with -p argument when no port has been specified.
Allow files to be copied into new folders
...
Conflicts:
git_config.py
manifest_xml.py
subcmds/init.py
subcmds/sync.py
subcmds/upload.py
Change-Id: I4756a6908277e91505c35287a122a775b68f4df5
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: |