mirror of
https://gerrit.googlesource.com/git-repo
synced 2024-12-21 07:16:21 +00:00
sync: Warn if partial sync state is detected
Partial syncs are not supported and can lead to strange behavior like deleting files. Explicitly warn users on partial sync. Bug: b/286126621, b/271507654 Change-Id: I471f78ac5942eb855bc34c80af47aa561dfa61e8 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/382154 Reviewed-by: Jason Chang <jasonnc@google.com> Reviewed-by: Aravind Vasudevan <aravindvasudev@google.com> Tested-by: Gavin Mak <gavinmak@google.com> Commit-Queue: Gavin Mak <gavinmak@google.com> Reviewed-by: Mike Frysinger <vapier@google.com> Reviewed-by: Josip Sokcevic <sokcevic@google.com>
This commit is contained in:
parent
f1ddaaa553
commit
f0aeb220de
@ -1866,6 +1866,14 @@ later is required to fix a server side protocol bug.
|
|||||||
mp.config.GetSyncAnalysisStateData(), "current_sync_state"
|
mp.config.GetSyncAnalysisStateData(), "current_sync_state"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self._local_sync_state.PruneRemovedProjects()
|
||||||
|
if self._local_sync_state.IsPartiallySynced():
|
||||||
|
print(
|
||||||
|
"warning: Partial syncs are not supported. For the best "
|
||||||
|
"experience, sync the entire tree.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
|
||||||
if not opt.quiet:
|
if not opt.quiet:
|
||||||
print("repo sync has finished successfully.")
|
print("repo sync has finished successfully.")
|
||||||
|
|
||||||
@ -1975,7 +1983,10 @@ class _LocalSyncState(object):
|
|||||||
_LAST_CHECKOUT = "last_checkout"
|
_LAST_CHECKOUT = "last_checkout"
|
||||||
|
|
||||||
def __init__(self, manifest):
|
def __init__(self, manifest):
|
||||||
self._path = os.path.join(manifest.repodir, ".repo_localsyncstate.json")
|
self._manifest = manifest
|
||||||
|
self._path = os.path.join(
|
||||||
|
self._manifest.repodir, ".repo_localsyncstate.json"
|
||||||
|
)
|
||||||
self._time = time.time()
|
self._time = time.time()
|
||||||
self._state = None
|
self._state = None
|
||||||
self._Load()
|
self._Load()
|
||||||
@ -2023,6 +2034,34 @@ class _LocalSyncState(object):
|
|||||||
except (IOError, TypeError):
|
except (IOError, TypeError):
|
||||||
platform_utils.remove(self._path, missing_ok=True)
|
platform_utils.remove(self._path, missing_ok=True)
|
||||||
|
|
||||||
|
def PruneRemovedProjects(self):
|
||||||
|
"""Remove entries don't exist on disk and save."""
|
||||||
|
if not self._state:
|
||||||
|
return
|
||||||
|
delete = set()
|
||||||
|
for path in self._state:
|
||||||
|
gitdir = os.path.join(self._manifest.topdir, path, ".git")
|
||||||
|
if not os.path.exists(gitdir):
|
||||||
|
delete.add(path)
|
||||||
|
if not delete:
|
||||||
|
return
|
||||||
|
for path in delete:
|
||||||
|
del self._state[path]
|
||||||
|
self.Save()
|
||||||
|
|
||||||
|
def IsPartiallySynced(self):
|
||||||
|
"""Return whether a partial sync state is detected."""
|
||||||
|
self._Load()
|
||||||
|
prev_checkout_t = None
|
||||||
|
for data in self._state.values():
|
||||||
|
checkout_t = data.get(self._LAST_CHECKOUT)
|
||||||
|
if not checkout_t:
|
||||||
|
return True
|
||||||
|
prev_checkout_t = prev_checkout_t or checkout_t
|
||||||
|
if prev_checkout_t != checkout_t:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# This is a replacement for xmlrpc.client.Transport using urllib2
|
# This is a replacement for xmlrpc.client.Transport using urllib2
|
||||||
# and supporting persistent-http[s]. It cannot change hosts from
|
# and supporting persistent-http[s]. It cannot change hosts from
|
||||||
|
@ -175,12 +175,73 @@ class LocalSyncState(unittest.TestCase):
|
|||||||
os.listdir(self.repodir), [".repo_localsyncstate.json"]
|
os.listdir(self.repodir), [".repo_localsyncstate.json"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_partial_sync(self):
|
||||||
|
"""Partial sync state is detected."""
|
||||||
|
with open(self.state._path, "w") as f:
|
||||||
|
f.write(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"projA": {
|
||||||
|
"last_fetch": 5,
|
||||||
|
"last_checkout": 5
|
||||||
|
},
|
||||||
|
"projB": {
|
||||||
|
"last_fetch": 5,
|
||||||
|
"last_checkout": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize state to read from the new file.
|
||||||
|
self.state = self._new_state()
|
||||||
|
projB = mock.MagicMock(relpath="projB")
|
||||||
|
self.assertEqual(self.state.IsPartiallySynced(), False)
|
||||||
|
|
||||||
|
self.state.SetFetchTime(projB)
|
||||||
|
self.state.SetCheckoutTime(projB)
|
||||||
|
self.assertEqual(self.state.IsPartiallySynced(), True)
|
||||||
|
|
||||||
def test_nonexistent_project(self):
|
def test_nonexistent_project(self):
|
||||||
"""Unsaved projects don't have data."""
|
"""Unsaved projects don't have data."""
|
||||||
p = mock.MagicMock(relpath="projC")
|
p = mock.MagicMock(relpath="projC")
|
||||||
self.assertEqual(self.state.GetFetchTime(p), None)
|
self.assertEqual(self.state.GetFetchTime(p), None)
|
||||||
self.assertEqual(self.state.GetCheckoutTime(p), None)
|
self.assertEqual(self.state.GetCheckoutTime(p), None)
|
||||||
|
|
||||||
|
def test_prune_removed_projects(self):
|
||||||
|
"""Removed projects are pruned."""
|
||||||
|
with open(self.state._path, "w") as f:
|
||||||
|
f.write(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"projA": {
|
||||||
|
"last_fetch": 5
|
||||||
|
},
|
||||||
|
"projB": {
|
||||||
|
"last_fetch": 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
def mock_exists(path):
|
||||||
|
if "projA" in path:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
projA = mock.MagicMock(relpath="projA")
|
||||||
|
projB = mock.MagicMock(relpath="projB")
|
||||||
|
self.state = self._new_state()
|
||||||
|
self.assertEqual(self.state.GetFetchTime(projA), 5)
|
||||||
|
self.assertEqual(self.state.GetFetchTime(projB), 7)
|
||||||
|
with mock.patch("os.path.exists", side_effect=mock_exists):
|
||||||
|
self.state.PruneRemovedProjects()
|
||||||
|
self.assertIsNone(self.state.GetFetchTime(projA))
|
||||||
|
|
||||||
|
self.state = self._new_state()
|
||||||
|
self.assertIsNone(self.state.GetFetchTime(projA))
|
||||||
|
self.assertEqual(self.state.GetFetchTime(projB), 7)
|
||||||
|
|
||||||
|
|
||||||
class GetPreciousObjectsState(unittest.TestCase):
|
class GetPreciousObjectsState(unittest.TestCase):
|
||||||
"""Tests for _GetPreciousObjectsState."""
|
"""Tests for _GetPreciousObjectsState."""
|
||||||
|
Loading…
Reference in New Issue
Block a user