summaryrefslogtreecommitdiffstats
path: root/project.py
diff options
context:
space:
mode:
authorShawn O. Pearce <sop@google.com>2010-12-07 10:31:19 -0800
committerShawn O. Pearce <sop@google.com>2010-12-07 11:13:29 -0800
commit13f3da50d40b89ee5b05f5f3de9542c20edac6d1 (patch)
treed085b6f6b498bde85a1969fce884dd24e88d03d5 /project.py
parent3218c13205694434edb2375ab8a8515554eed366 (diff)
parent2b8db3ce3e7344b9f3b5216637c5af0d54be5656 (diff)
downloadgit-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.py177
1 files changed, 156 insertions, 21 deletions
diff --git a/project.py b/project.py
index 1cea959e..fde98ad7 100644
--- a/project.py
+++ b/project.py
@@ -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: