Exemplo n.º 1
0
 def sync_repo_to_ref(self, repo: Repo, ref: str) -> None:
     repo_path = self.workspace_path / repo.dest
     status = get_git_status(repo_path)
     if status.dirty:
         raise Error(f"git repo is dirty: cannot sync to ref: {ref}")
     try:
         self.run_git(repo_path, "reset", "--hard", ref)
     except Error:
         raise Error("updating ref failed")
Exemplo n.º 2
0
    def check_branch(self, repo: Repo) -> Tuple[Optional[Error], str]:
        """Check that the current branch:
            * exists
            * matches the one in the manifest

        * Raise Error if the branch does not exist (because we can't
          do anything else in that case)

        * _Return_ on Error if the current branch does not match the
          one in the manifest - because we still want to run
          `git merge @upstream` in that case

        * Otherwise, return the current branch
        """
        repo_path = self.workspace_path / repo.dest
        current_branch = None
        try:
            current_branch = get_current_branch(repo_path)
        except Error:
            raise Error("Not on any branch")

        if current_branch and current_branch != repo.branch:
            return (
                IncorrectBranch(actual=current_branch, expected=repo.branch),
                current_branch,
            )
        else:
            return None, current_branch
Exemplo n.º 3
0
def run(args: argparse.Namespace) -> None:
    workspace_path = args.workspace_path or Path.cwd()
    num_jobs = get_num_jobs(args)

    cfg_path = workspace_path / ".tsrc" / "config.yml"

    if cfg_path.exists():
        raise Error(
            f"Workspace already configured. `{cfg_path}` already exists")

    ui.info_1("Configuring workspace in", ui.bold, workspace_path)

    clone_path = workspace_path / ".tsrc/manifest"
    local_manifest = LocalManifest(clone_path)
    local_manifest.init(url=args.manifest_url, branch=args.manifest_branch)
    manifest_branch = local_manifest.current_branch()

    workspace_config = WorkspaceConfig(
        manifest_url=args.manifest_url,
        manifest_branch=manifest_branch,
        clone_all_repos=args.clone_all_repos,
        repo_groups=args.groups or [],
        shallow_clones=args.shallow_clones,
        singular_remote=args.singular_remote,
    )
    workspace_config.save_to_file(cfg_path)

    workspace = Workspace(workspace_path)
    manifest = workspace.get_manifest()
    workspace.repos = repos_from_config(manifest, workspace_config)
    workspace.clone_missing(num_jobs=num_jobs)
    workspace.set_remotes(num_jobs=num_jobs)
    workspace.perform_filesystem_operations()
    ui.info_2("Workspace initialized")
    ui.info_2("Configuration written in", ui.bold, workspace.cfg_path)
Exemplo n.º 4
0
 def clone_repo(self, repo: Repo) -> str:
     """Clone a missing repo."""
     # Note:
     # Must use the correct remote(s) and branch when cloning,
     # *and* must reset the repo to the correct state if `tag` or
     # `sha1` were set in the manifest configuration.
     repo_path = self.workspace_path / repo.dest
     parent = repo_path.parent
     name = repo_path.name
     parent.mkdir(parents=True, exist_ok=True)
     remote = self._choose_remote(repo)
     remote_name = remote.name
     remote_url = remote.url
     clone_args = ["clone", "--origin", remote_name, remote_url]
     ref = None
     if repo.tag:
         ref = repo.tag
     elif repo.branch:
         ref = repo.branch
     if ref:
         clone_args.extend(["--branch", ref])
     if self.shallow:
         clone_args.extend(["--depth", "1"])
     if not repo.ignore_submodules:
         clone_args.append("--recurse-submodules")
     clone_args.append(name)
     try:
         self.run_git(parent, *clone_args)
         summary = f"{repo.dest} cloned from {remote_url}"
         if ref:
             summary += f" (on {ref})"
         return summary
     except Error:
         raise Error("Cloning failed")
Exemplo n.º 5
0
 def check_shallow_with_sha1(self, repo: Repo) -> None:
     if not repo.sha1:
         return
     if self.shallow:
         message = textwrap.dedent(
             f"Cannot use --shallow with a fixed sha1 ({repo.sha1})\n"
             "Consider using a tag instead")
         raise Error(message)
Exemplo n.º 6
0
    def _pick_remotes(self, repo: Repo) -> List[Remote]:
        if self.remote_name:
            for remote in repo.remotes:
                if remote.name == self.remote_name:
                    return [remote]
            message = f"Remote {self.remote_name} not found for repository {repo.dest}"
            raise Error(message)

        return repo.remotes
Exemplo n.º 7
0
    def process(self, index: int, count: int, repo: Repo) -> Outcome:
        # We just need to compute a summary here with the log between
        # self.from_ref and self.to_ref
        #
        # Note: make sure that when there is no diff between
        # self.from_ref and self.to_ref, the summary is empty,
        # so that the repo is not shown by OutcomeCollection.print_summary()
        repo_path = self.workspace_path / repo.dest
        if not repo_path.exists():
            raise MissingRepo(repo.dest)

        # The main reason for the `git log` command to fail is if `self.from_ref` or
        # `self.to_ref` references are not found for the repo, so check for this case
        # explicitly
        rc, _ = run_git_captured(repo_path,
                                 "rev-parse",
                                 self.from_ref,
                                 check=False)
        if rc != 0:
            raise Error(f"{self.from_ref} not found")
        rc, _ = run_git_captured(repo_path,
                                 "rev-parse",
                                 self.to_ref,
                                 check=False)
        if rc != 0:
            raise Error(f"{self.to_ref} not found")

        colors = ["green", "reset", "yellow", "reset", "bold blue", "reset"]
        log_format = "%m {}%h{} - {}%d{} %s {}<%an>{}"
        log_format = log_format.format(*("%C({})".format(x) for x in colors))
        cmd = [
            "log",
            "--color=always",
            f"--pretty=format:{log_format}",
            f"{self.from_ref}...{self.to_ref}",
        ]
        rc, out = run_git_captured(repo_path, *cmd, check=True)
        if out:
            lines = [repo.dest, "-" * len(repo.dest), out]
            return Outcome.from_lines(lines)
        else:
            return Outcome.empty()
Exemplo n.º 8
0
 def fetch(self, repo: Repo) -> None:
     repo_path = self.workspace_path / repo.dest
     for remote in self._pick_remotes(repo):
         try:
             self.info_3("Fetching", remote.name)
             cmd = ["fetch", "--tags", "--prune", remote.name]
             if self.force:
                 cmd.append("--force")
             self.run_git(repo_path, *cmd)
         except Error:
             raise Error(f"fetch from '{remote.name}' failed")
Exemplo n.º 9
0
 def process(self, index: int, count: int, item: FileSystemOperation) -> Outcome:
     # Note: we don't want to run this Task in parallel, just in case
     # the order of filesystem operations matters, so we can always
     # return an empty Outcome
     description = item.describe(self.workspace_path)
     self.info_count(index, count, description)
     try:
         item.perform(self.workspace_path)
     except OSError as e:
         raise Error(str(e))
     return Outcome.empty()
Exemplo n.º 10
0
    def _choose_remote(self, repo: Repo) -> Remote:
        if self.remote_name:
            for remote in repo.remotes:
                if remote.name == self.remote_name:
                    return remote
            message = (
                f"Remote '{self.remote_name}' not found for repository '{repo.dest}'"
            )
            raise Error(message)

        return repo.remotes[0]
Exemplo n.º 11
0
 def reset_repo(self, repo: Repo) -> str:
     ref = repo.sha1
     if not ref:
         return ""
     else:
         self.info_2("Resetting", repo.dest, "to", ref)
         repo_path = self.workspace_path / repo.dest
         try:
             self.run_git(repo_path, "reset", "--hard", ref)
         except Error:
             raise Error("Resetting to", ref, "failed")
         summary = f" and reset to {ref}"
         return summary
Exemplo n.º 12
0
def check_link(*, source: Path, target: Path) -> bool:
    remove_link = False
    if source.exists() and not source.is_symlink():
        raise Error("Specified symlink source exists but is not a link")
        return False
    if source.is_symlink():
        if source.exists():
            # symlink exists and points to some target
            current_target = Path(os.readlink(str(source)))
            if current_target.resolve() == target.resolve():
                ui.info_3("Leaving existing link")
                return False
            else:
                ui.info_3("Replacing existing link")
                remove_link = True
        else:
            # symlink exists, but points to a non-existent target
            ui.info_3("Replacing broken link")
            remove_link = True
    if remove_link:
        os.unlink(source)
    return True
Exemplo n.º 13
0
 def sync_repo_to_branch(self, repo: Repo, *, current_branch: str) -> str:
     repo_path = self.workspace_path / repo.dest
     if self.parallel:
         # Note: we want the summary to:
         # * be empty if the repo was already up-to-date
         # * contain the diffstat if the merge with upstream succeeds
         rc, out = run_git_captured(
             repo_path, "log", "--oneline", "HEAD..@{upstream}", check=False
         )
         if rc == 0 and not out:
             return ""
         _, merge_output = run_git_captured(
             repo_path, "merge", "--ff-only", "@{upstream}", check=True
         )
         return merge_output
     else:
         # Note: no summary here, because the output of `git merge`
         # is not captured, so the diffstat or the "Already up to
         # date"  message are directly shown to the user
         try:
             self.run_git(repo_path, "merge", "--ff-only", "@{upstream}")
         except Error:
             raise Error("updating branch failed")
         return ""