sapling/eden/fs/cli/hg_util.py
Muir Manders 29d29e0f14 cli: support ".sl" repos
Summary: Add basic support to sniff ".hg" vs ".sl" dot dir, and otherwise default based on env vars.

Reviewed By: quark-zju

Differential Revision: D39900481

fbshipit-source-id: d9900755f54711a9d067d8fd65f2fc25aa278a7a
2022-10-07 13:57:40 -07:00

135 lines
4.2 KiB
Python

#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2.
# pyre-strict
import binascii
import os
import typing
from pathlib import Path
from typing import BinaryIO, Dict, Tuple
import eden.dirstate
from .config import EdenCheckout
_DEFAULT_EXTRA_HGRC_CONTENTS: str = """\
[extensions]
eden =
share =
[ui]
portablefilenames = ignore
"""
def setup_hg_dir(checkout: EdenCheckout, commit_id: str) -> None:
checkout_hg_dir = checkout.hg_dot_path
try:
checkout_hg_dir.mkdir()
except FileExistsError:
raise Exception(f"{checkout_hg_dir} directory already exists")
# hgrc
hgrc_data = get_hgrc_data(checkout)
(checkout_hg_dir / "hgrc").write_text(hgrc_data)
# requires
requires_data = get_requires_data(checkout)
(checkout_hg_dir / "requires").write_text(requires_data)
# Create the shared and sharedpath files, which tell mercurial where it should
# really look for most of the mercurial state, and that bookmarks should also be
# shared.
#
# Note that the sharedpath file intentionally does not include a trailing newline.
# This matches how it is generated by mercurial.
backing_hg_dir = get_backing_hg_dir(checkout)
(checkout_hg_dir / "sharedpath").write_bytes(bytes(backing_hg_dir))
(checkout_hg_dir / "shared").write_text("bookmarks\n")
# Create an empty bookmarks file
(checkout_hg_dir / "bookmarks").touch()
# Create a branch file with the contents "default\n". Even though we do not
# use branches, we have seen some users with a function in their .bashrc
# that categorically reads the .hg/branch file to include in their prompt.
(checkout_hg_dir / "branch").write_text("default\n")
# Write the parents to the dirstate file.
with typing.cast(BinaryIO, (checkout_hg_dir / "dirstate").open("wb")) as f:
parents = (binascii.unhexlify(commit_id), b"\x00" * 20)
tuples_dict: Dict[str, Tuple[str, int, int]] = {}
copymap: Dict[str, str] = {}
eden.dirstate.write(f, parents, tuples_dict, copymap)
def get_backing_hg_dir(checkout: EdenCheckout) -> Path:
"""Given an EdenCheckout object, return the path to the actual .hg/ directory that
contains the mercurial data store.
This is the path that .hg/sharedpath should point to.
"""
backing_repo = checkout.get_config().backing_repo
return backing_repo / sniff_dot_dir(backing_repo)
def get_hgrc_data(checkout: EdenCheckout) -> str:
backing_hg_dir = get_backing_hg_dir(checkout)
extra_hgrc = checkout.instance.get_config_value(
"hg.extra_hgrc", default=_DEFAULT_EXTRA_HGRC_CONTENTS
)
if extra_hgrc and extra_hgrc[-1] != "\n":
extra_hgrc += "\n"
try:
orig_hgrc = (backing_hg_dir / "hgrc").read_text()
except FileNotFoundError:
# Repositories aren't required to have an .hgrc file
# Make sure the .hg directory itself exists though, to confirm
# we weren't called with incorrect arguments.
if not backing_hg_dir.is_dir():
raise Exception(f"backing repository does not exist: {backing_hg_dir}")
orig_hgrc = ""
return orig_hgrc + "\n" + extra_hgrc
def get_requires_data(checkout: EdenCheckout) -> str:
backing_hg_dir = get_backing_hg_dir(checkout)
try:
orig_requires_data = (backing_hg_dir / "requires").read_text()
requires = set(orig_requires_data.splitlines())
except FileNotFoundError:
requires = set()
# Add eden as a requirement
requires.add("eden")
# Drop other dirstate-related requirements that are specific to
# the backing repository's dirstate.
requires.discard("sqldirstate")
requires.discard("treedirstate")
return "\n".join(sorted(requires)) + "\n"
_possible_dot_dirs = (".hg", ".sl")
def sniff_dot_dir(repo_root: Path) -> str:
for dot_dir in _possible_dot_dirs:
if (repo_root / dot_dir).exists():
return dot_dir
env_ident = os.environ.get("HGIDENTITY", os.environ.get("SLIDENTITY", None))
if env_ident in {"hg", "sl"}:
return "." + env_ident
return _possible_dot_dirs[0]