summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn O. Pearce <sop@google.com>2009-05-29 18:28:25 -0700
committerShawn O. Pearce <sop@google.com>2009-05-29 18:45:17 -0700
commit8ad8a0e61d919e76f521f3124c91bd46fbaa84e2 (patch)
treefba74635a3cab4aecd5f9d7dd8672756db3e67e8
parentd1f70d9929ddd2748ccc9c1dd2f9603068e1f3e6 (diff)
downloadgit-repo-8ad8a0e61d919e76f521f3124c91bd46fbaa84e2.tar.gz
Change DWIMery hack for dealing with rewound remote branch
The trick of looking at the reflog for the remote tracking branch and only going back one commit works some of the time, but not all of the time. Its sort of relying on the fact that the user didn't use `repo sync -n` or `git fetch` to only update the tracking branches and skip the working directory update. Doing this right requires looking through the history of the SHA-1 source (what the upstream used to be) and finding a spot where the DAG diveraged away suddenly, and consider that to be the rewind point. That's really difficult to do, as we don't have a clear picture of what that old point was. A close approximation is to list all of the commits that are in HEAD, but not the new upstream, and rebase all of those where the committer email address is this user's email address. In most cases, this will effectively rebase only the user's new original work. If the user is the project maintainer and rewound the branch themselves, and they don't want all of the commits they have created to be rebased onto the new upstream, they should handle the rebase on their own, after the sync is complete. Signed-off-by: Shawn O. Pearce <sop@google.com>
-rw-r--r--project.py74
1 files changed, 39 insertions, 35 deletions
diff --git a/project.py b/project.py
index 53fd38ee..cab47152 100644
--- a/project.py
+++ b/project.py
@@ -708,28 +708,28 @@ class Project(object):
708 syncbuf.later1(self, _doff) 708 syncbuf.later1(self, _doff)
709 return 709 return
710 710
711 if merge == rev: 711 # If the upstream switched on us, warn the user.
712 try: 712 #
713 old_merge = self.bare_git.rev_parse('%s@{1}' % merge) 713 if merge != rev:
714 except GitError:
715 old_merge = merge
716 if old_merge == '0000000000000000000000000000000000000000' \
717 or old_merge == '':
718 old_merge = merge
719 else:
720 # The upstream switched on us. Time to cross our fingers
721 # and pray that the old upstream also wasn't in the habit
722 # of rebasing itself.
723 #
724 syncbuf.info(self, "manifest switched %s...%s", merge, rev) 714 syncbuf.info(self, "manifest switched %s...%s", merge, rev)
725 old_merge = merge
726 715
727 if rev == old_merge: 716 # Examine the local commits not in the remote. Find the
728 upstream_lost = [] 717 # last one attributed to this user, if any.
729 else: 718 #
730 upstream_lost = self._revlist(not_rev(rev), old_merge) 719 local_changes = self._revlist(
731 720 not_rev(merge),
732 if not upstream_lost and not upstream_gain: 721 HEAD,
722 format='%H %ce')
723
724 last_mine = None
725 cnt_mine = 0
726 for commit in local_changes:
727 commit_id, committer_email = commit.split(' ', 2)
728 if committer_email == self.UserEmail:
729 last_mine = commit_id
730 cnt_mine += 1
731
732 if not local_changes and not upstream_gain:
733 # Trivially no changes caused by the upstream. 733 # Trivially no changes caused by the upstream.
734 # 734 #
735 return 735 return
@@ -738,25 +738,24 @@ class Project(object):
738 syncbuf.fail(self, _DirtyError()) 738 syncbuf.fail(self, _DirtyError())
739 return 739 return
740 740
741 if upstream_lost: 741 if cnt_mine < len(local_changes):
742 # Upstream rebased. Not everything in HEAD 742 # Upstream rebased. Not everything in HEAD
743 # may have been caused by the user. 743 # was created by this user.
744 # 744 #
745 syncbuf.info(self, 745 syncbuf.info(self,
746 "discarding %d commits removed from upstream", 746 "discarding %d commits removed from upstream",
747 len(upstream_lost)) 747 len(local_changes) - cnt_mine)
748 748
749 branch.remote = rem 749 branch.remote = rem
750 branch.merge = self.revision 750 branch.merge = self.revision
751 branch.Save() 751 branch.Save()
752 752
753 my_changes = self._revlist(not_rev(old_merge), HEAD) 753 if cnt_mine > 0:
754 if my_changes:
755 def _dorebase(): 754 def _dorebase():
756 self._Rebase(upstream = old_merge, onto = rev) 755 self._Rebase(upstream = '%s^1' % last_mine, onto = rev)
757 self._CopyFiles() 756 self._CopyFiles()
758 syncbuf.later2(self, _dorebase) 757 syncbuf.later2(self, _dorebase)
759 elif upstream_lost: 758 elif local_changes:
760 try: 759 try:
761 self._ResetHard(rev) 760 self._ResetHard(rev)
762 self._CopyFiles() 761 self._CopyFiles()
@@ -1141,11 +1140,11 @@ class Project(object):
1141 def _gitdir_path(self, path): 1140 def _gitdir_path(self, path):
1142 return os.path.join(self.gitdir, path) 1141 return os.path.join(self.gitdir, path)
1143 1142
1144 def _revlist(self, *args): 1143 def _revlist(self, *args, **kw):
1145 cmd = [] 1144 a = []
1146 cmd.extend(args) 1145 a.extend(args)
1147 cmd.append('--') 1146 a.append('--')
1148 return self.work_git.rev_list(*args) 1147 return self.work_git.rev_list(*a, **kw)
1149 1148
1150 @property 1149 @property
1151 def _allrefs(self): 1150 def _allrefs(self):
@@ -1270,8 +1269,11 @@ class Project(object):
1270 self.update_ref('-d', name, old) 1269 self.update_ref('-d', name, old)
1271 self._project.bare_ref.deleted(name) 1270 self._project.bare_ref.deleted(name)
1272 1271
1273 def rev_list(self, *args): 1272 def rev_list(self, *args, **kw):
1274 cmdv = ['rev-list'] 1273 if 'format' in kw:
1274 cmdv = ['log', '--pretty=format:%s' % kw['format']]
1275 else:
1276 cmdv = ['rev-list']
1275 cmdv.extend(args) 1277 cmdv.extend(args)
1276 p = GitCommand(self._project, 1278 p = GitCommand(self._project,
1277 cmdv, 1279 cmdv,
@@ -1280,7 +1282,9 @@ class Project(object):
1280 capture_stderr = True) 1282 capture_stderr = True)
1281 r = [] 1283 r = []
1282 for line in p.process.stdout: 1284 for line in p.process.stdout:
1283 r.append(line[:-1]) 1285 if line[-1] == '\n':
1286 line = line[:-1]
1287 r.append(line)
1284 if p.Wait() != 0: 1288 if p.Wait() != 0:
1285 raise GitError('%s rev-list %s: %s' % ( 1289 raise GitError('%s rev-list %s: %s' % (
1286 self._project.name, 1290 self._project.name,