Skip to content

Commit 50c6226

Browse files
quic-kaushiklLUCI
authored andcommitted
Prevent leftover bare gitdirs after failed sync attempts
The gitdir for a project may be left in a state with bare=true due to a previous failed sync. In this state, during a subsequent sync attempt, repo will skip initializing the gitdir (since the directory already exists) and directly attempt to checkout the worktree, which will fail because the project is bare. To reduce the chance of this happening, initialize the gitdir in a temp directory and move it once it is ready. Bug: 457478027 Change-Id: I4767494a3a54e7734174eae3a0d939fa9d174288 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/524203 Tested-by: Kaushik Lingarkar <[email protected]> Commit-Queue: Kaushik Lingarkar <[email protected]> Reviewed-by: Mike Frysinger <[email protected]> Reviewed-by: Gavin Mak <[email protected]>
1 parent 1e4b288 commit 50c6226

File tree

1 file changed

+53
-17
lines changed

1 file changed

+53
-17
lines changed

project.py

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import datetime
1516
import errno
1617
import filecmp
1718
import glob
@@ -3073,8 +3074,13 @@ def _FastForward(self, head, ffonly=False, quiet=True):
30733074
raise GitError(f"{self.name} merge {head} ", project=self.name)
30743075

30753076
def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False):
3077+
# Prefix for temporary directories created during gitdir initialization.
3078+
TMP_GITDIR_PREFIX = ".tmp-project-initgitdir-"
30763079
init_git_dir = not os.path.exists(self.gitdir)
30773080
init_obj_dir = not os.path.exists(self.objdir)
3081+
tmp_gitdir = None
3082+
curr_gitdir = self.gitdir
3083+
curr_config = self.config
30783084
try:
30793085
# Initialize the bare repository, which contains all of the objects.
30803086
if init_obj_dir:
@@ -3094,27 +3100,33 @@ def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False):
30943100
# well.
30953101
if self.objdir != self.gitdir:
30963102
if init_git_dir:
3097-
os.makedirs(self.gitdir)
3103+
os.makedirs(os.path.dirname(self.gitdir), exist_ok=True)
3104+
tmp_gitdir = tempfile.mkdtemp(
3105+
prefix=TMP_GITDIR_PREFIX,
3106+
dir=os.path.dirname(self.gitdir),
3107+
)
3108+
curr_config = GitConfig.ForRepository(
3109+
gitdir=tmp_gitdir, defaults=self.manifest.globalConfig
3110+
)
3111+
curr_gitdir = tmp_gitdir
30983112

30993113
if init_obj_dir or init_git_dir:
31003114
self._ReferenceGitDir(
3101-
self.objdir, self.gitdir, copy_all=True
3115+
self.objdir, curr_gitdir, copy_all=True
31023116
)
31033117
try:
3104-
self._CheckDirReference(self.objdir, self.gitdir)
3118+
self._CheckDirReference(self.objdir, curr_gitdir)
31053119
except GitError as e:
31063120
if force_sync:
3107-
logger.error(
3108-
"Retrying clone after deleting %s", self.gitdir
3109-
)
31103121
try:
3111-
platform_utils.rmtree(os.path.realpath(self.gitdir))
3112-
if self.worktree and os.path.exists(
3113-
os.path.realpath(self.worktree)
3114-
):
3115-
platform_utils.rmtree(
3116-
os.path.realpath(self.worktree)
3117-
)
3122+
rm_dirs = (
3123+
tmp_gitdir,
3124+
self.gitdir,
3125+
self.worktree,
3126+
)
3127+
for d in rm_dirs:
3128+
if d and os.path.exists(d):
3129+
platform_utils.rmtree(os.path.realpath(d))
31183130
return self._InitGitDir(
31193131
mirror_git=mirror_git,
31203132
force_sync=False,
@@ -3165,18 +3177,21 @@ def _expanded_ref_dirs():
31653177
m = self.manifest.manifestProject.config
31663178
for key in ["user.name", "user.email"]:
31673179
if m.Has(key, include_defaults=False):
3168-
self.config.SetString(key, m.GetString(key))
3180+
curr_config.SetString(key, m.GetString(key))
31693181
if not self.manifest.EnableGitLfs:
3170-
self.config.SetString(
3182+
curr_config.SetString(
31713183
"filter.lfs.smudge", "git-lfs smudge --skip -- %f"
31723184
)
3173-
self.config.SetString(
3185+
curr_config.SetString(
31743186
"filter.lfs.process", "git-lfs filter-process --skip"
31753187
)
3176-
self.config.SetBoolean(
3188+
curr_config.SetBoolean(
31773189
"core.bare", True if self.manifest.IsMirror else None
31783190
)
31793191

3192+
if tmp_gitdir:
3193+
platform_utils.rename(tmp_gitdir, self.gitdir)
3194+
tmp_gitdir = None
31803195
if not init_obj_dir:
31813196
# The project might be shared (obj_dir already initialized), but
31823197
# such information is not available here. Instead of passing it,
@@ -3193,6 +3208,27 @@ def _expanded_ref_dirs():
31933208
if init_git_dir and os.path.exists(self.gitdir):
31943209
platform_utils.rmtree(self.gitdir)
31953210
raise
3211+
finally:
3212+
# Clean up the temporary directory created during the process,
3213+
# as well as any stale ones left over from previous attempts.
3214+
if tmp_gitdir and os.path.exists(tmp_gitdir):
3215+
platform_utils.rmtree(tmp_gitdir)
3216+
3217+
age_threshold = datetime.timedelta(days=1)
3218+
now = datetime.datetime.now()
3219+
for tmp_dir in glob.glob(
3220+
os.path.join(
3221+
os.path.dirname(self.gitdir), f"{TMP_GITDIR_PREFIX}*"
3222+
)
3223+
):
3224+
try:
3225+
mtime = datetime.datetime.fromtimestamp(
3226+
os.path.getmtime(tmp_dir)
3227+
)
3228+
if now - mtime > age_threshold:
3229+
platform_utils.rmtree(tmp_dir)
3230+
except OSError:
3231+
pass
31963232

31973233
def _UpdateHooks(self, quiet=False):
31983234
if os.path.exists(self.objdir):

0 commit comments

Comments
 (0)