remove the "edenfsctl repository" subcommand

Summary:
We no longer use repository configs, so remove the `repository` subcommand
that supported adding and listing these configurations.

The main information that used to be included in the repository configuration
was the bind mount settings.  This has since been replaced with the
`.eden-redirections` file that is placed directly in each repository.

Reviewed By: wez

Differential Revision: D20876462

fbshipit-source-id: cc7d8e6f0a6a2e04fbf3159417af41a44908b3a8
This commit is contained in:
Adam Simpkins 2020-04-10 13:55:41 -07:00 committed by Facebook GitHub Bot
parent 4910c924a8
commit ef8c3435c4
15 changed files with 32 additions and 428 deletions

View File

@ -293,12 +293,9 @@ class EdenInstance:
"""
return version.format_eden_version(self.get_running_version_parts())
def get_repository_list(
self, parser: Union[configutil.EdenConfigParser, "ConfigUpdater", None] = None
) -> List[str]:
def get_repository_list(self) -> List[str]:
result = []
if not parser:
parser = self._loadConfig()
parser = self._loadConfig()
for section in parser.sections():
header = section.split(" ")
if len(header) == 2 and header[0] == "repository":
@ -415,21 +412,6 @@ class EdenInstance:
]
)
def add_repository(self, name: str, repo_type: str, source: str) -> None:
# Check if repository already exists
with ConfigUpdater(self._user_config_path) as config:
if name in self.get_repository_list(config):
raise UsageError(
"""\
repository %s already exists. You will need to edit the ~/.edenrc config file \
by hand to make changes to the repository or remove it."""
% name
)
# Add repository to INI file
config["repository " + name] = {"type": repo_type, "path": source}
config.save()
def clone(
self, checkout_config: CheckoutConfig, path: str, snapshot_id: str
) -> None:
@ -894,140 +876,6 @@ Do you want to run `eden mount %s` instead?"""
return now - since
class ConfigUpdater(object):
"""
A helper class to safely update an eden config file.
This acquires a lock on the config file, reads it in, and then provide APIs
to save it back. This ensures that another process cannot change the file
in between the time that we read it and when we write it back.
This also saves the file to a temporary name first, then renames it into
place, so that the main config file is always in a good state, and never
has partially written contents.
"""
def __init__(self, path: Path) -> None:
self.path = path
self._lock_path = self.path.with_suffix(".lock")
self._lock_file: Optional[typing.TextIO] = None
self.config = configutil.EdenConfigParser()
# Acquire a lock.
# This makes sure that another process can't modify the config in the
# middle of a read-modify-write operation. (We can't stop a user
# from manually editing the file while we work, but we can stop
# other eden CLI processes.)
self._acquire_lock()
try:
toml_cfg = load_toml_config(self.path)
self.config.read_dict(toml_cfg)
except FileNotFoundError:
pass
def __enter__(self) -> "ConfigUpdater":
return self
def __exit__(
self,
exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
exc_traceback: Optional[types.TracebackType],
) -> bool:
self.close()
return False
def __del__(self) -> None:
self.close()
def sections(self) -> List[str]:
return self.config.sections()
def __setitem__(self, key: str, value: Dict[str, Any]) -> None:
self.config[key] = value
def _acquire_lock(self) -> None:
# Skipping locks on Windows for two reasons:
# First, locking the config file is not a strict requirement even on
# POSIX. Plus we don't have a similar flock implementation on Windows.
# We could make a locking scheme with LockFileEx(), but it would
# have different symentics than flock (For one, we can't unlink a
# locked file).
if sys.platform == "win32":
return
while True:
self._lock_file = typing.cast(typing.TextIO, open(self._lock_path, "w+"))
# pyre-fixme[16]: `Optional` has no attribute `fileno`.
fcntl.flock(self._lock_file.fileno(), fcntl.LOCK_EX)
# The original creator of the lock file will unlink it when
# it is finished. Make sure we grab the lock on the file still on
# disk, and not an unlinked file.
st1 = os.fstat(self._lock_file.fileno())
st2 = os.lstat(self._lock_path)
if st1.st_dev == st2.st_dev and st1.st_ino == st2.st_ino:
# We got the real lock
return
# We acquired a lock on an old deleted file.
# Close it, and try to acquire the current lock file again.
# pyre-fixme[16]: `Optional` has no attribute `close`.
self._lock_file.close()
self._lock_file = None
continue
def _unlock(self) -> None:
if sys.platform == "win32":
return
assert self._lock_file is not None
# Remove the file on disk before we unlock it.
# This way processes currently waiting in _acquire_lock() that already
# opened our lock file will see that it isn't the current file on disk
# once they acquire the lock.
os.unlink(self._lock_path)
# pyre-fixme[16]: `Optional` has no attribute `close`.
self._lock_file.close()
self._lock_file = None
def close(self) -> None:
if self._lock_file is not None:
self._unlock()
def save(self) -> None:
if self._lock_file is None:
raise Exception("Cannot save the config without holding the lock")
try:
st = os.stat(self.path)
perms = st.st_mode & 0o777
except OSError as ex:
if ex.errno != errno.ENOENT:
raise
perms = 0o644
# Write the contents to a temporary file first, then atomically rename
# it to the desired destination. This makes sure the .edenrc file
# always has valid contents at all points in time.
prefix = USER_CONFIG + ".tmp."
dirname = self.path.parent
tmpf = tempfile.NamedTemporaryFile(
"w", dir=str(dirname), prefix=prefix, delete=False
)
try:
toml_config = self.config.to_raw_dict()
toml_data = toml.dumps(typing.cast(Mapping[str, Any], toml_config))
tmpf.write(toml_data)
tmpf.close()
os.chmod(tmpf.name, perms)
os.rename(tmpf.name, self.path)
except BaseException:
# Remove temporary file on error
try:
os.unlink(tmpf.name)
except Exception:
pass
raise
class EdenCheckout:
"""Information about a particular Eden checkout."""

View File

@ -352,40 +352,6 @@ class StatusCmd(Subcmd):
return 1
@subcmd("repository", "List all repositories")
class RepositoryCmd(Subcmd):
def setup_parser(self, parser: argparse.ArgumentParser) -> None:
parser.add_argument(
"name", nargs="?", default=None, help="Name of the checkout to mount"
)
parser.add_argument(
"path", nargs="?", default=None, help="Path to the repository to import"
)
def run(self, args: argparse.Namespace) -> int:
instance = get_eden_instance(args)
if args.name and args.path:
repo = util.get_repo(args.path)
if repo is None:
print_stderr("%s does not look like a git or hg repository" % args.path)
return 1
try:
instance.add_repository(
args.name, repo_type=repo.type, source=repo.source
)
except config_mod.UsageError as ex:
print_stderr("error: {}", ex)
return 1
elif args.name or args.path:
print_stderr("repository command called with incorrect arguments")
return 1
else:
repo_list = instance.get_repository_list()
for repo_name in sorted(repo_list):
print(repo_name)
return 0
class ListMountInfo(typing.NamedTuple):
path: Path
data_dir: Path

View File

@ -213,40 +213,6 @@ class TomlConfigTest(
self.assertEqual(cc.scm_type, "hg")
self.assertEqual(cc.default_revision, "master")
def test_add_existing_repo(self) -> None:
self.copy_config_files()
cfg = self.get_config()
with self.assertRaisesRegex(
config_mod.UsageError,
"repository fbsource already exists. You will need to edit "
"the ~/.edenrc config file by hand to make changes to the "
"repository or remove it.",
):
cfg.add_repository("fbsource", "hg", f"/data/users/{self._user}/fbsource")
def test_add_repo(self) -> None:
self.copy_config_files()
cfg = self.get_config()
cfg.add_repository("fbandroid", "hg", f"/data/users/{self._user}/fbandroid")
# Lets reload our config
cfg = self.get_config()
# Check the various config sections
self.assert_core_config(cfg)
exp_repos = ["fbandroid", "fbsource", "git"]
self.assertEqual(cfg.get_repository_list(), exp_repos)
self.assert_fbsource_repo_config(cfg)
self.assert_git_repo_config(cfg)
# Check the newly added repo
cc = cfg.find_config_for_alias("fbandroid")
assert cc is not None
self.assertEqual(cc.backing_repo, Path(f"/data/users/{self._user}/fbandroid"))
self.assertEqual(cc.scm_type, "hg")
self.assertEqual(cc.default_revision, "master")
def test_missing_type_option_in_repository_is_an_error(self) -> None:
self.write_user_config(
"""

View File

@ -240,7 +240,7 @@ class BasicTest(testcase.EdenRepoTest):
self.assertFalse(self.eden.in_proc_mounts(self.mount))
self.assertFalse(os.path.exists(self.mount))
self.eden.clone(self.repo_name, self.mount)
self.eden.clone(self.repo.path, self.mount)
self.assert_checkout_root_entries(self.expected_mount_entries)
self.assertTrue(self.eden.in_proc_mounts(self.mount))

View File

@ -26,10 +26,6 @@ from .lib.service_test_case import (
)
# This is the name of the default repository created by EdenRepoTestBase.
repo_name = "main"
@testcase.eden_repo_test
class CloneTest(testcase.EdenRepoTest):
def populate_repo(self) -> None:
@ -40,7 +36,7 @@ class CloneTest(testcase.EdenRepoTest):
tmp = self.make_temporary_directory()
non_existent_dir = os.path.join(tmp, "foo/bar/baz")
self.eden.run_cmd("clone", repo_name, non_existent_dir)
self.eden.run_cmd("clone", self.repo.path, non_existent_dir)
self.assertTrue(
os.path.isfile(os.path.join(non_existent_dir, "hello")),
msg="clone should succeed in non-existent directory",
@ -56,7 +52,7 @@ class CloneTest(testcase.EdenRepoTest):
symlinked_target = os.path.join(symlink_dir, "bar")
self.eden.run_cmd("clone", repo_name, symlinked_target)
self.eden.run_cmd("clone", self.repo.path, symlinked_target)
self.assertTrue(
os.path.isfile(os.path.join(empty_dir, "hello")),
msg="clone should succeed in empty directory",
@ -77,7 +73,7 @@ class CloneTest(testcase.EdenRepoTest):
empty_dir = os.path.join(tmp, "foo/bar/baz")
os.makedirs(empty_dir)
self.eden.run_cmd("clone", repo_name, empty_dir)
self.eden.run_cmd("clone", self.repo.path, empty_dir)
self.assertTrue(
os.path.isfile(os.path.join(empty_dir, "hello")),
msg="clone should succeed in empty directory",
@ -137,26 +133,9 @@ class CloneTest(testcase.EdenRepoTest):
)
def test_clone_from_eden_repo(self) -> None:
# Add a config alias for a repo with some bind mounts.
edenrc = os.path.join(self.home_dir, ".edenrc")
with open(edenrc, "w") as f:
f.write(
dedent(
f"""\
["repository {repo_name}"]
path = "{self.repo.get_canonical_root()}"
type = "{self.repo.get_type()}"
["bindmounts {repo_name}"]
bm1 = "tmp/bm1"
bm2 = "tmp/bm2"
"""
)
)
# Create an Eden mount from the config alias.
eden_clone1 = self.make_temporary_directory()
self.eden.run_cmd("clone", repo_name, eden_clone1)
self.eden.run_cmd("clone", self.repo.path, eden_clone1)
self.assertFalse(
os.path.isdir(os.path.join(eden_clone1, "tmp/bm1")),
@ -168,16 +147,12 @@ class CloneTest(testcase.EdenRepoTest):
self.eden.run_cmd(
"clone", "--rev", self.repo.get_head_hash(), eden_clone1, eden_clone2
)
self.assertFalse(
os.path.isdir(os.path.join(eden_clone2, "tmp/bm1")),
msg="clone should not mount legacy bind mounts",
)
def test_clone_with_valid_revision_cmd_line_arg_works(self) -> None:
tmp = self.make_temporary_directory()
target = os.path.join(tmp, "foo/bar/baz")
self.eden.run_cmd(
"clone", "--rev", self.repo.get_head_hash(), repo_name, target
"clone", "--rev", self.repo.get_head_hash(), self.repo.path, target
)
self.assertTrue(
os.path.isfile(os.path.join(target, "hello")),
@ -188,7 +163,7 @@ class CloneTest(testcase.EdenRepoTest):
tmp = self.make_temporary_directory()
target = os.path.join(tmp, "foo/bar/baz")
short = self.repo.get_head_hash()[:6]
self.eden.run_cmd("clone", "--rev", short, repo_name, target)
self.eden.run_cmd("clone", "--rev", short, self.repo.path, target)
self.assertTrue(
os.path.isfile(os.path.join(target, "hello")),
msg="clone should succeed with short --snapshop arg.",
@ -202,7 +177,7 @@ class CloneTest(testcase.EdenRepoTest):
f.write("I am not empty.\n")
with self.assertRaises(subprocess.CalledProcessError) as context:
self.eden.run_cmd("clone", repo_name, non_empty_dir)
self.eden.run_cmd("clone", self.repo.path, non_empty_dir)
stderr = context.exception.stderr.decode("utf-8")
self.assertRegex(
stderr,
@ -216,7 +191,7 @@ class CloneTest(testcase.EdenRepoTest):
os.makedirs(empty_dir)
with self.assertRaises(subprocess.CalledProcessError) as context:
self.eden.run_cmd("clone", repo_name, empty_dir, "--rev", "X")
self.eden.run_cmd("clone", self.repo.path, empty_dir, "--rev", "X")
stderr = context.exception.stderr.decode("utf-8")
self.assertIn(
"unable to find hash for commit 'X': ",
@ -233,7 +208,7 @@ class CloneTest(testcase.EdenRepoTest):
f.write("I am not empty.\n")
with self.assertRaises(subprocess.CalledProcessError) as context:
self.eden.run_cmd("clone", repo_name, file_in_directory)
self.eden.run_cmd("clone", self.repo.path, file_in_directory)
stderr = context.exception.stderr.decode("utf-8")
self.assertIn(
f"error: destination path {file_in_directory} is not a directory\n", stderr
@ -246,7 +221,7 @@ class CloneTest(testcase.EdenRepoTest):
f.write("I am not empty.\n")
with self.assertRaises(subprocess.CalledProcessError) as context:
self.eden.run_cmd("clone", repo_name, non_existent_dir)
self.eden.run_cmd("clone", self.repo.path, non_existent_dir)
stderr = context.exception.stderr.decode("utf-8")
self.assertIn(
f"error: destination path {non_existent_dir} is not a directory\n", stderr

View File

@ -17,7 +17,7 @@ class EdenClientTest(testcase.EdenRepoTest):
def test_client_dir_for_mount(self) -> None:
clone_path = pathlib.Path(self.tmp_dir, "test_checkout")
self.eden.run_cmd("clone", self.repo_name, str(clone_path))
self.eden.run_cmd("clone", self.repo.path, str(clone_path))
self.assertEqual(
self.eden.client_dir_for_mount(clone_path),
pathlib.Path(self.eden_dir, "clients", "test_checkout"),

View File

@ -123,9 +123,9 @@ class FsckTest(testcase.EdenRepoTest):
mount3 = Path(self.mounts_dir) / "third_mount"
mount4 = Path(self.mounts_dir) / "fourth_mount"
self.eden.clone(self.repo_name, mount2)
self.eden.clone(self.repo_name, mount3)
self.eden.clone(self.repo_name, mount4)
self.eden.clone(self.repo.path, mount2)
self.eden.clone(self.repo.path, mount3)
self.eden.clone(self.repo.path, mount4)
# Unmount all but mount3
self.eden.unmount(Path(self.mount))

View File

@ -77,7 +77,6 @@ class EdenHgTestCase(testcase.EdenTestCase, metaclass=abc.ABCMeta):
repo: hgrepo.HgRepository
backing_repo: hgrepo.HgRepository
backing_repo_name: str
config_variant_name: str # set by the @hg_test decorator
def setup_eden_test(self) -> None:
@ -86,11 +85,9 @@ class EdenHgTestCase(testcase.EdenTestCase, metaclass=abc.ABCMeta):
# Create the backing repository
self.backing_repo = self.create_backing_repo()
self.backing_repo_name = "backing_repo"
self.eden.add_repository(self.backing_repo_name, self.backing_repo.path)
# Edit the edenrc file to set up post-clone hooks that will correctly
# populate the .hg directory inside the eden client.
self.eden.clone(self.backing_repo_name, self.mount, allow_empty=True)
self.eden.clone(self.backing_repo.path, self.mount, allow_empty=True)
# Now create the repository object that refers to the eden client
self.repo = hgrepo.HgRepository(self.mount, system_hgrc=self.system_hgrc)

View File

@ -439,19 +439,6 @@ class EdenFS(object):
# We expect the new process to fail starting.
pass
def add_repository(self, name: str, repo_path: str) -> None:
"""
Run "eden repository" to define a repository configuration
"""
self.run_cmd("repository", name, repo_path)
def repository_cmd(self) -> str:
"""
Executes "eden repository" to list the repositories,
and returns the output as a string.
"""
return self.run_cmd("repository")
def list_cmd(self) -> Dict[str, Dict[str, Any]]:
"""
Executes "eden list --json" to list the Eden checkouts and returns the result as

View File

@ -292,8 +292,7 @@ class EdenRepoTest(EdenTestCase):
self.populate_repo()
self.report_time("repository setup done")
self.eden.add_repository(self.repo_name, self.repo.path)
self.eden.clone(self.repo_name, self.mount)
self.eden.clone(self.repo.path, self.mount)
self.report_time("eden clone done")
def populate_repo(self) -> None:

View File

@ -47,7 +47,7 @@ class MountTest(testcase.EdenRepoTest):
def test_remove_unmounted_checkout(self) -> None:
# Clone a second checkout mount point
mount2 = os.path.join(self.mounts_dir, "mount2")
self.eden.clone(self.repo_name, mount2)
self.eden.clone(self.repo.path, mount2)
self.assertEqual(
{self.mount: "RUNNING", mount2: "RUNNING"}, self.eden.list_cmd_simple()
)
@ -317,9 +317,9 @@ class MountTest(testcase.EdenRepoTest):
def test_start_with_mount_failures(self) -> None:
# Clone a few other checkouts
mount2 = os.path.join(self.mounts_dir, "extra_mount_1")
self.eden.clone(self.repo_name, mount2)
self.eden.clone(self.repo.path, mount2)
mount3 = os.path.join(self.mounts_dir, "extra_mount_2")
self.eden.clone(self.repo_name, mount3)
self.eden.clone(self.repo.path, mount3)
self.assertEqual(
{self.mount: "RUNNING", mount2: "RUNNING", mount3: "RUNNING"},
self.eden.list_cmd_simple(),

View File

@ -28,7 +28,7 @@ class RCTest(testcase.EdenRepoTest):
mounts = self.eden.run_cmd("list")
self.assertEqual("", mounts, msg="There should be 0 mount paths after remove")
self.eden.clone(self.repo_name, self.mount)
self.eden.clone(self.repo.path, self.mount)
mounts = self.eden.run_cmd("list")
self.assertEqual(f"{self.mount}\n", mounts)
@ -49,7 +49,7 @@ class RCTest(testcase.EdenRepoTest):
mounts = self.eden.list_cmd_simple()
self.assertEqual({}, mounts, msg="There should be 0 paths in the directory map")
self.eden.clone(self.repo_name, self.mount)
self.eden.clone(self.repo.path, self.mount)
self.assertTrue(
os.path.isdir(test_client_dir),
msg="Client name should be restored verbatim because \
@ -61,71 +61,3 @@ class RCTest(testcase.EdenRepoTest):
mounts,
msg="The client directory should have been restored",
)
def test_override_system_config(self) -> None:
system_repo = self.create_repo("system_repo")
system_repo.write_file("hello.txt", "hola\n")
system_repo.commit("Initial commit.")
repo_info = util.get_repo(system_repo.path)
assert repo_info is not None
# Create temporary system config
system_config_dir = self.eden.etc_eden_dir / "config.d"
system_config_dir.mkdir(parents=True, exist_ok=True)
f, path = tempfile.mkstemp(dir=str(system_config_dir), suffix=".toml")
# Add system_repo to system config file
config = textwrap.dedent(
f"""\
["repository {self.repo_name}"]
path = "{repo_info.source}"
type = "{repo_info.type}"
"""
)
os.write(f, config.encode("utf-8"))
os.close(f)
# Clone repository
mount_path = os.path.join(self.mounts_dir, self.repo_name + "-1")
self.eden.clone(self.repo_name, mount_path)
# Verify that clone used repository data from user config
readme = os.path.join(mount_path, "hello.txt")
self.assertFalse(os.path.exists(readme))
hello = os.path.join(mount_path, "readme.txt")
st = os.lstat(hello)
self.assertTrue(stat.S_ISREG(st.st_mode))
with open(hello, "r") as hello_file:
self.assertEqual("test\n", hello_file.read())
# Add system_repo to system config file with new name
repo_name = "repo"
f = os.open(path, os.O_WRONLY)
config = textwrap.dedent(
f"""\
["repository {repo_name}"]
path = "{repo_info.source}"
type = "{repo_info.type}"
"""
)
os.write(f, config.encode("utf-8"))
os.close(f)
# Clone repository
mount_path = os.path.join(self.mounts_dir, repo_name + "-1")
self.eden.clone(repo_name, mount_path)
# Verify that clone used repository data from system config
readme = os.path.join(mount_path, "readme.txt")
self.assertFalse(os.path.exists(readme))
hello = os.path.join(mount_path, "hello.txt")
st = os.lstat(hello)
self.assertTrue(stat.S_ISREG(st.st_mode))
with open(hello, "r") as hello_file:
self.assertEqual("hola\n", hello_file.read())

View File

@ -48,6 +48,7 @@ class RemountTest(testcase.EdenRepoTest):
repo_names = {"git": "git_repo", "hg": "hg_repo"}
git_repo = self.create_git_repo(repo_names["git"])
hg_repo = self.create_hg_repo(repo_names["hg"])
repos = {"git_repo": git_repo, "hg_repo": hg_repo}
git_repo.write_file("hello", "hola\n")
git_repo.commit("Initial commit.")
@ -55,21 +56,17 @@ class RemountTest(testcase.EdenRepoTest):
hg_repo.write_file("hello", "hola\n")
hg_repo.commit("Initial commit.")
self.eden.add_repository(repo_names["git"], git_repo.path)
self.eden.add_repository(repo_names["hg"], hg_repo.path)
# Mount git and hg clients
for name in repo_names.values():
for name, repo in repos.items():
for i in range(3):
self.eden.clone(
name, os.path.join(self.mounts_dir, name + "-" + str(i))
)
self.eden.clone(repo.path, os.path.join(self.mounts_dir, f"{name}-{i}"))
self.eden.shutdown()
self.eden.start()
# Verify that clients are remounted on startup
for scm_type, name in repo_names.items():
for name, repo in repos.items():
scm_type = repo.get_type()
for i in range(3):
mount_path = os.path.join(self.mounts_dir, f"{name}-{i}")
self.assert_checkout_root_entries(
@ -130,7 +127,7 @@ class RemountTest(testcase.EdenRepoTest):
def test_try_remount_existing_mount(self) -> None:
"""Verify trying to mount an existing mount prints a sensible error."""
mount_destination = self.mount + "-0"
self.eden.clone(self.repo_name, mount_destination)
self.eden.clone(self.repo.path, mount_destination)
with self.assertRaises(edenclient.EdenCommandError) as context:
self.eden.run_cmd("mount", mount_destination)
self.assertIn(
@ -199,7 +196,7 @@ class RemountTest(testcase.EdenRepoTest):
def _clone_checkouts(self, num_mounts):
for i in range(num_mounts):
self.eden.clone(self.repo_name, self.mount + "-" + str(i))
self.eden.clone(self.repo.path, self.mount + "-" + str(i))
def _verify_not_mounted(self, num_mounts, main_mounted=False):
# Verify that no clients are remounted. No errors should be thrown here

View File

@ -1,63 +0,0 @@
#!/usr/bin/env python3
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2.
import os
from typing import List
from .lib import testcase
class RepoTest(testcase.EdenTestCase):
"""
Tests for the "eden repository" command.
Note that these tests do not use @testcase.eden_repo_test, since we don't
actually need to run separately with git and mercurial repositories. These
tests don't actually mount anything in eden at all.
"""
def test_list_repository(self) -> None:
self.assertEqual([], self._list_repos())
config = """\
["repository fbsource"]
path = "/data/users/carenthomas/fbsource"
type = "git"
["bindmounts fbsource"]
fbcode-buck-out = "fbcode/buck-out"
fbandroid-buck-out = "fbandroid/buck-out"
fbobjc-buck-out = "fbobjc/buck-out"
buck-out = "buck-out"
["repository git"]
path = "/home/carenthomas/src/git"
type = "git"
["repository hg-crew"]
url = "/data/users/carenthomas/facebook-hg-rpms/hg-crew"
type = "hg"
"""
home_config_file = os.path.join(self.home_dir, ".edenrc")
with open(home_config_file, "w") as f:
f.write(config)
expected = ["fbsource", "git", "hg-crew"]
self.assertEqual(expected, self._list_repos())
def test_add_multiple(self) -> None:
hg_repo = self.create_hg_repo("hg_repo")
git_repo = self.create_git_repo("git_repo")
self.eden.add_repository("hg1", hg_repo.path)
self.assertEqual(["hg1"], self._list_repos())
self.eden.add_repository("hg2", hg_repo.path)
self.assertEqual(["hg1", "hg2"], self._list_repos())
self.eden.add_repository("git1", git_repo.path)
self.assertEqual(["git1", "hg1", "hg2"], self._list_repos())
def _list_repos(self) -> List[str]:
return self.eden.repository_cmd().splitlines()

View File

@ -230,7 +230,7 @@ class CountersTest(testcase.EdenRepoTest):
self.eden.unmount(self.mount_path)
counters = self.get_nonthrift_set(self.get_counters().keys())
mount2 = os.path.join(self.mounts_dir, "mount2")
self.eden.clone(self.repo_name, mount2)
self.eden.clone(self.repo.path, mount2)
self.eden.unmount(Path(mount2))
counters2 = self.get_nonthrift_set(self.get_counters().keys())
self.assertEqual(counters, counters2)