mirror of
https://gerrit.googlesource.com/git-repo
synced 2024-12-21 07:16:21 +00:00
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>
This commit is contained in:
parent
d1f70d9929
commit
8ad8a0e61d
72
project.py
72
project.py
@ -708,28 +708,28 @@ class Project(object):
|
||||
syncbuf.later1(self, _doff)
|
||||
return
|
||||
|
||||
if merge == rev:
|
||||
try:
|
||||
old_merge = self.bare_git.rev_parse('%s@{1}' % merge)
|
||||
except GitError:
|
||||
old_merge = merge
|
||||
if old_merge == '0000000000000000000000000000000000000000' \
|
||||
or old_merge == '':
|
||||
old_merge = merge
|
||||
else:
|
||||
# The upstream switched on us. Time to cross our fingers
|
||||
# and pray that the old upstream also wasn't in the habit
|
||||
# of rebasing itself.
|
||||
#
|
||||
# If the upstream switched on us, warn the user.
|
||||
#
|
||||
if merge != rev:
|
||||
syncbuf.info(self, "manifest switched %s...%s", merge, rev)
|
||||
old_merge = merge
|
||||
|
||||
if rev == old_merge:
|
||||
upstream_lost = []
|
||||
else:
|
||||
upstream_lost = self._revlist(not_rev(rev), old_merge)
|
||||
# Examine the local commits not in the remote. Find the
|
||||
# last one attributed to this user, if any.
|
||||
#
|
||||
local_changes = self._revlist(
|
||||
not_rev(merge),
|
||||
HEAD,
|
||||
format='%H %ce')
|
||||
|
||||
if not upstream_lost and not upstream_gain:
|
||||
last_mine = None
|
||||
cnt_mine = 0
|
||||
for commit in local_changes:
|
||||
commit_id, committer_email = commit.split(' ', 2)
|
||||
if committer_email == self.UserEmail:
|
||||
last_mine = commit_id
|
||||
cnt_mine += 1
|
||||
|
||||
if not local_changes and not upstream_gain:
|
||||
# Trivially no changes caused by the upstream.
|
||||
#
|
||||
return
|
||||
@ -738,25 +738,24 @@ class Project(object):
|
||||
syncbuf.fail(self, _DirtyError())
|
||||
return
|
||||
|
||||
if upstream_lost:
|
||||
if cnt_mine < len(local_changes):
|
||||
# Upstream rebased. Not everything in HEAD
|
||||
# may have been caused by the user.
|
||||
# was created by this user.
|
||||
#
|
||||
syncbuf.info(self,
|
||||
"discarding %d commits removed from upstream",
|
||||
len(upstream_lost))
|
||||
len(local_changes) - cnt_mine)
|
||||
|
||||
branch.remote = rem
|
||||
branch.merge = self.revision
|
||||
branch.Save()
|
||||
|
||||
my_changes = self._revlist(not_rev(old_merge), HEAD)
|
||||
if my_changes:
|
||||
if cnt_mine > 0:
|
||||
def _dorebase():
|
||||
self._Rebase(upstream = old_merge, onto = rev)
|
||||
self._Rebase(upstream = '%s^1' % last_mine, onto = rev)
|
||||
self._CopyFiles()
|
||||
syncbuf.later2(self, _dorebase)
|
||||
elif upstream_lost:
|
||||
elif local_changes:
|
||||
try:
|
||||
self._ResetHard(rev)
|
||||
self._CopyFiles()
|
||||
@ -1141,11 +1140,11 @@ class Project(object):
|
||||
def _gitdir_path(self, path):
|
||||
return os.path.join(self.gitdir, path)
|
||||
|
||||
def _revlist(self, *args):
|
||||
cmd = []
|
||||
cmd.extend(args)
|
||||
cmd.append('--')
|
||||
return self.work_git.rev_list(*args)
|
||||
def _revlist(self, *args, **kw):
|
||||
a = []
|
||||
a.extend(args)
|
||||
a.append('--')
|
||||
return self.work_git.rev_list(*a, **kw)
|
||||
|
||||
@property
|
||||
def _allrefs(self):
|
||||
@ -1270,8 +1269,11 @@ class Project(object):
|
||||
self.update_ref('-d', name, old)
|
||||
self._project.bare_ref.deleted(name)
|
||||
|
||||
def rev_list(self, *args):
|
||||
cmdv = ['rev-list']
|
||||
def rev_list(self, *args, **kw):
|
||||
if 'format' in kw:
|
||||
cmdv = ['log', '--pretty=format:%s' % kw['format']]
|
||||
else:
|
||||
cmdv = ['rev-list']
|
||||
cmdv.extend(args)
|
||||
p = GitCommand(self._project,
|
||||
cmdv,
|
||||
@ -1280,7 +1282,9 @@ class Project(object):
|
||||
capture_stderr = True)
|
||||
r = []
|
||||
for line in p.process.stdout:
|
||||
r.append(line[:-1])
|
||||
if line[-1] == '\n':
|
||||
line = line[:-1]
|
||||
r.append(line)
|
||||
if p.Wait() != 0:
|
||||
raise GitError('%s rev-list %s: %s' % (
|
||||
self._project.name,
|
||||
|
Loading…
Reference in New Issue
Block a user