def _clone_missing(self, git_project, repo): """ Clone Missing """ branch = repo.default_branch fixed_ref = repo.fixed_ref clone_url = repo.clone_url qisys.sh.mkdir(git_project.path, recursive=True) git = qisrc.git.Git(git_project.path) remote_name = repo.default_remote.name try: git.init() git.remote("add", remote_name, clone_url) git.fetch(remote_name, "--quiet") if branch: git.checkout("-b", branch, "%s/%s" % (remote_name, branch)) if fixed_ref: git.checkout("-q", fixed_ref) except Exception: ui.error("Cloning repo failed") if git.is_empty(): qisys.sh.rm(git_project.path) self.worktree.remove_project(repo.src) return False self.save_project_config(git_project) self.load_git_projects() return True
def clever_reset_ref(git_project, ref, raises=True): """ Resets only if needed, fetches only if needed """ try: remote_name = git_project.default_remote.name except AttributeError: error_msg = "Project {} has no default remote, defaulting to origin" ui.error(error_msg.format(git_project.name)) remote_name = "origin" git = qisrc.git.Git(git_project.path) # Deals with "refs/" prefixed ref first if ref.startswith("refs/"): return _reset_hard_to_refs_prefixed_ref(git, remote_name, ref, raises=raises) # Else, ref format is a local name (branch or tag) # Check if this ref exists and if we are already in the expected state rc, ref_sha1 = git.call("rev-parse", ref, "--", raises=False) if rc != 0: # Maybe this is a newly pushed tag, try to fetch: git.fetch(remote_name, "--prune") rc, ref_sha1 = git.call("rev-parse", ref, "--", raises=False) if rc != 0: return False, "Could not parse %s as a valid ref" % ref _, actual_sha1 = git.call("rev-parse", "HEAD", raises=False) if actual_sha1 == ref_sha1: # Nothing to do return None if raises else True, "" # Reset to the ref local name return _reset_hard_to_local_refs_name(git, remote_name, ref, raises=raises)
def print_diff(git_project, manifest_branch, destination_branch): """ Show what to merge in prepare_merge_request style """ git = qisrc.git.Git(git_project.path) default_remote = git_project.default_remote if not default_remote: return True remote_name = default_remote.name if remote_name not in ["origin"]: return True default_branch = git_project.default_branch if not default_branch: return True if git_project.src == "manifest/default": return True git.fetch(remote_name, "--prune") devel_ref = "%s/%s" % (remote_name, manifest_branch) base_ref = "%s/%s" % (remote_name, destination_branch) status, message = qisrc.git.get_status(git, devel_ref, base_ref, with_message=True) pretty_name = git_project.name.split(".git")[0] ui.info(pretty_name, status, message) return status in ["no-diff", "ahead"]
def fetch_manifest(worktree, manifest_git_url, branch="master", profile="default", src="manifest/default"): """ Fetch the manifest for a worktree :param manifest_git_url: A git repository containing a 'manifest.xml' file, ala repo :param branch: The branch to use :param src: The path where to store the clone of the manifest Note: every changes made by the user directly in the manifest repo will be lost! """ manifest = worktree.get_project(src) if not manifest: clone_project(worktree, manifest_git_url, src=src) manifest = worktree.get_project(src) # Make sure manifest project is on the correct, up to date branch: git = qisrc.git.Git(manifest.path) git.set_remote("origin", manifest_git_url) git.set_tracking_branch(branch, "origin") git.checkout("-f", branch, quiet=True) git.fetch(quiet=True) git.reset("--hard", "origin/%s" % branch, quiet=True) filename = profile + ".xml" manifest_file = os.path.join(manifest.path, filename) if not os.path.exists(manifest_file): mess = "Could not find a file named '%s' " % filename mess += "in the repository: %s\n" % manifest_git_url raise Exception(mess) return manifest_file
def clever_reset_ref(git_project, ref, raises=True): """ Resets only if needed, fetches only if needed """ try: remote_name = git_project.default_remote.name except AttributeError: error_msg = "Project {} has no default remote, defaulting to origin" ui.error(error_msg.format(git_project.name)) remote_name = "origin" git = qisrc.git.Git(git_project.path) # Deals with "refs/" prefixed ref first if ref.startswith("refs/"): return _reset_hard_to_refs_prefixed_ref(git, remote_name, ref, raises=raises) # Else, ref format is a local name (branch or tag) # Check if this ref exists and if we are already in the expected state rc, ref_sha1 = git.call("rev-parse", ref, "--", raises=False) if rc != 0: # Maybe this is a newly pushed tag, try to fetch: git.fetch(remote_name) rc, ref_sha1 = git.call("rev-parse", ref, "--", raises=False) if rc != 0: return False, "Could not parse %s as a valid ref" % ref _, actual_sha1 = git.call("rev-parse", "HEAD", raises=False) if actual_sha1 == ref_sha1: # Nothing to do return None if raises else True, "" # Reset to the ref local name return _reset_hard_to_local_refs_name(git, remote_name, ref, raises=raises)
def test_wrong_setup(): git = FakeGit("repo") git.add_result("checkout", 0, "") git.checkout("-f", "master") # pylint: disable-msg=E1101 with pytest.raises(Exception) as e: git.fetch() assert "Unexpected call to fetch" in e.value.message
def test_fake_git_wrong_setup(): git = FakeGit("repo") git.add_result("checkout", 0, "") git.checkout("-f", "master") # pylint: disable-msg=E1101 with pytest.raises(Exception) as e: git.fetch() assert "Unexpected call to fetch" in e.value.args[0]
def push(project, local_ref, remote_branch, bypass_review=False, dry_run=False, reviewers=None, topic=None, draft=False): """ Push the changes for review. Unless review is False, in this case, simply update the remote gerrit branch :param reviewers: A list of reviewers to invite to review """ git = qisrc.git.Git(project.path) review_remote = project.review_remote args = list() if dry_run: args.append("--dry-run") args.append(review_remote.url) if bypass_review: args.append("%s:%s" % (local_ref, remote_branch)) else: ui.info("Pushing code to", review_remote.name, "for review.") if draft: remote_ref = "refs/drafts/%s" % remote_branch else: remote_ref = "refs/for/%s" % remote_branch if topic: remote_ref = "%s/%s" % (remote_ref, topic) args.append("%s:%s" % (local_ref, remote_ref)) if reviewers and not dry_run: # Get the SHA1s that will be pushed so that we can add reviewers remote = project.review_remote remote.parse_url() server = remote.server username = remote.username ssh_port = remote.port ui.info("Fetching", remote.name) git.fetch(remote.name, "--quiet") commits_pushed = git.get_log("%s/%s" % (remote.name, remote_branch), "HEAD") sha1s = [commit["sha1"] for commit in commits_pushed] ui.info("Publishing changes") git.push(*args) if reviewers and not dry_run: ui.info("Adding reviewers...", ("(" + ", ".join(reviewers) + ")")) try: set_reviewers(sha1s, reviewers, username, server, ssh_port) except qisys.command.CommandFailedException as e: ui.warning("Couldn't set reviewers") ui.warning(e) else: ui.info("Done!")
def test_commands_are_logged(): git = FakeGit("repo") git.add_result("fetch", 0, "") git.add_result("reset", 0, "") git.fetch() git.reset("--hard", quiet=True) calls = git.calls assert len(calls) == 2 assert calls[0][0] == ("fetch",) assert calls[1][0] == ("reset", "--hard") assert calls[1][1] == {"quiet" : True}
def _reset_hard_to_local_refs_name(git, remote_name, ref, raises=True): """ deals with the git reset --hard command for short name ref (NOT on the format 'refs/xxx') """ need_to_fetch, _ = git.call("show", "--oneline", ref, "--", raises=False) if need_to_fetch: git.fetch(remote_name, "--tags", "--prune") # else: SHA-1 already exists locally, no need to fetch _, tag_list = git.tag("-l", ref, raises=False) is_tag = ref == tag_list # Perform effective switch rc, out = _switch_to_git_ref(git, ref, is_tag) return None if raises else (rc == 0), out
def test_fake_git_commands_are_logged(): git = FakeGit("repo") git.add_result("fetch", 0, "") git.add_result("reset", 0, "") git.fetch() git.reset("--hard", quiet=True) calls = git.calls assert len(calls) == 2 assert calls[0][0] == ("fetch", ) assert calls[1][0] == ("reset", "--hard") assert calls[1][1] == {"quiet": True}
def _reset_hard_to_local_refs_name(git, remote_name, ref, raises=True): """ deals with the git reset --hard command for short name ref (NOT on the format 'refs/xxx') """ need_to_fetch, _ = git.call("show", "--oneline", ref, "--", raises=False) if need_to_fetch: git.fetch(remote_name, "--tags") # else: SHA-1 already exists locally, no need to fetch _, tag_list = git.tag("-l", ref, raises=False) is_tag = ref == tag_list # Perform effective switch rc, out = _switch_to_git_ref(git, ref, is_tag) return None if raises else (rc == 0), out
def _sync_manifest(self, local_manifest): """ Update the local manifest clone with the remote """ manifest_repo = os.path.join(self.manifests_root, local_manifest.name) git = qisrc.git.Git(manifest_repo) with git.transaction() as transaction: git.fetch("origin") git.checkout("-B", local_manifest.branch) git.reset("--hard", "origin/%s" % local_manifest.branch) if not transaction.ok: ui.warning("Update failed") ui.info(transaction.output) return
def _sync_git(repo, url, branch, ref): git = qisrc.git.Git(repo) git.set_remote("origin", url) if git.get_current_branch() != branch: git.checkout("-B", branch) with git.transaction() as transaction: git.fetch("origin") if ref: to_reset = ref git.reset("--hard", to_reset) else: git.reset("--hard", "origin/%s" % branch) if not transaction.ok: raise Exception("Update failed\n" + transaction.output)
def clever_reset_ref(git_project, ref, raises=True): """ Resets only if needed, fetches only if needed """ try: remote_name = git_project.default_remote.name except AttributeError: error_msg = "Project {} has no default remote, defaulting to origin" ui.error(error_msg.format(git_project.name)) remote_name = "origin" git = qisrc.git.Git(git_project.path) if ref.startswith("refs/"): if raises: git.fetch(remote_name, ref) git.reset("--hard", "FETCH_HEAD") return else: with git.tansaction() as transaction: git.fetch(remote_name, ref) git.reset("--hard", "FETCH_HEAD") return transaction.ok, transaction.output rc, ref_sha1 = git.call("rev-parse", ref, raises=False) if rc != 0: # Maybe this is a newly pushed tag, try to fetch: git.fetch(remote_name) rc, ref_sha1 = git.call("rev-parse", ref, raises=False) if rc != 0: return False, "Could not parse %s as a valid ref" % ref _, actual_sha1 = git.call("rev-parse", "HEAD", raises=False) if actual_sha1 == ref_sha1: # Nothing to do if raises: return else: return True, "" ret, _ = git.call("show", "--oneline", ref, raises=False) if ret == 0: # SHA-1 exists locally if raises: git.reset("--hard", ref) else: rc, out = git.reset("--hard", ref, raises=False) return (rc == 0), out else: # Full fetch in this case if raises: git.fetch(remote_name) git.reset("--hard", ref) else: with git.transaction() as transaction: git.fetch(remote_name) git.reset("--hard", ref) return transaction.ok, transaction.output
def _sync_manifest(self): """ Update the local manifest clone with the remote """ git = qisrc.git.Git(self.manifest_repo) git.set_remote("origin", self.manifest.url) if git.get_current_branch() != self.manifest.branch: git.checkout("-B", self.manifest.branch) with git.transaction() as transaction: git.fetch("origin") if self.manifest.ref: to_reset = self.manifest.ref git.reset("--hard", to_reset) else: git.reset("--hard", "origin/%s" % self.manifest.branch) if not transaction.ok: raise Exception("Update failed\n" + transaction.output)
def push(project, branch, bypass_review=False, dry_run=False, reviewers=None, topic=None): """ Push the changes for review. Unless review is False, in this case, simply update the remote gerrit branch :param reviewers: A list of reviewers to invite to review """ git = qisrc.git.Git(project.path) review_remote = project.review_remote args = list() if dry_run: args.append("--dry-run") args.append(review_remote.url) if bypass_review: args.append("%s:%s" % (branch, branch)) else: ui.info("Pushing code to", review_remote.name, "for review.") remote_ref = "refs/for/%s" % branch if topic: remote_ref = "%s/%s" % (remote_ref, topic) args.append("%s:%s" % (branch, remote_ref)) if reviewers and not dry_run: # Get the SHA1s that will be pushed so that we can add reviewers remote = project.review_remote remote.parse_url() server = remote.server username = remote.username ssh_port = remote.port git.fetch(remote.name) commits_pushed = git.get_log("%s/%s" % (remote.name, branch), "HEAD") sha1s = [commit["sha1"] for commit in commits_pushed] git.push(*args) if reviewers and not dry_run: ui.info("Adding reviewers...") try: for sha1 in sha1s: set_reviewers(sha1, reviewers, username, server, ssh_port) except qisys.command.CommandFailedException as e: ui.warning("Couldn't set reviewers") ui.warning(e) else: ui.info("Done!")
def _reset_hard_to_refs_prefixed_ref(git, remote_name, ref, raises=True): """ deals with the git reset --hard command for long name ref (on the format 'refs/xxx') """ assert ref.startswith("refs/") # Check the ref format to retrieve the effective ref name to switch on if ref.startswith("refs/remotes") and "/{}/".format(remote_name) in ref: # When the ref format begin with refs/remotes/<remote_name>/, that's the format seen by the local (git show-ref) git.fetch(remote_name) ref_to_reset_to = ref is_tag = False else: # The ref is in the format seen by a remote (git ls-remote): # refs/heads/xxx, refs/tags/xxx, refs/remotes/<other_remote>/xxx, refs/merge-requests/xxx git.fetch(remote_name, ref) # This command will write the pointed commit number into the pseudo ref FETCH_HEAD ref_to_reset_to = "FETCH_HEAD" is_tag = ref.startswith("refs/tags") # Perform effective switch rc, out = _switch_to_git_ref(git, ref_to_reset_to, is_tag) return None if raises else (rc == 0), out
def test_fake_git_fake_call(): git = FakeGit("repo") git.add_result("fetch", 0, "") (retcode, _) = git.fetch(raises=False) assert retcode == 0 git2 = FakeGit("repo2") git2.add_result("fetch", 2, "Remote end hung up unexpectedly") (retcode, out) = git2.fetch(raises=False) assert retcode == 2 assert "Remote end hung up" in out
def _reset_hard_to_refs_prefixed_ref(git, remote_name, ref, raises=True): """ deals with the git reset --hard command for long name ref (on the format 'refs/xxx') """ assert ref.startswith("refs/") # Check the ref format to retrieve the effective ref name to switch on if ref.startswith("refs/remotes") and "/{}/".format(remote_name) in ref: # When the ref format begin with refs/remotes/<remote_name>/, that's the format seen by the local (git show-ref) git.fetch(remote_name, "--prune") ref_to_reset_to = ref is_tag = False else: # The ref is in the format seen by a remote (git ls-remote): # refs/heads/xxx, refs/tags/xxx, refs/remotes/<other_remote>/xxx, refs/merge-requests/xxx # This command will write the pointed commit number into the pseudo ref FETCH_HEAD git.fetch(remote_name, ref, "--prune") ref_to_reset_to = "FETCH_HEAD" is_tag = ref.startswith("refs/tags") # Perform effective switch rc, out = _switch_to_git_ref(git, ref_to_reset_to, is_tag) return None if raises else (rc == 0), out
def test_fake_call(): git = FakeGit("repo") git.add_result("fetch", 0, "") (retcode, _) = git.fetch(raises=False) assert retcode == 0 git2 = FakeGit("repo2") git2.add_result("fetch", 2, "Remote end hung up unexpectedly") (retcode, out) = git2.fetch(raises=False) assert retcode == 2 assert "Remote end hung up" in out
def _clone_missing(self, git_project, repo): branch = repo.default_branch clone_url = repo.clone_url qisys.sh.mkdir(git_project.path, recursive=True) git = qisrc.git.Git(git_project.path) remote_name = repo.default_remote.name try: git.init() git.remote("add", remote_name, clone_url) git.fetch(remote_name, "--quiet") git.checkout("-b", branch, "%s/%s" % (remote_name, branch)) except: ui.error("Cloning repo failed") if git.is_empty(): qisys.sh.rm(git_project.path) self.worktree.remove_project(repo.src) return False self.save_project_config(git_project) self.load_git_projects() return True
def _sync_manifest(self): """ Update the local manifest clone with the remote """ if not self.manifest.url: mess = """ \ No manifest set for worktree in {root} Please run `qisrc init MANIFEST_URL` """ raise Exception(mess.format(root=self.git_worktree.root)) git = qisrc.git.Git(self.manifest_repo) git.set_remote("origin", self.manifest.url) if git.get_current_branch() != self.manifest.branch: git.checkout("-B", self.manifest.branch) with git.transaction() as transaction: git.fetch("origin") if self.manifest.ref: to_reset = self.manifest.ref git.reset("--hard", to_reset) else: git.reset("--hard", "origin/%s" % self.manifest.branch) if not transaction.ok: raise Exception("Update failed\n" + transaction.output)
def _clone_missing(self, git_project, repo): branch = repo.default_branch clone_url = repo.clone_url qisys.sh.mkdir(git_project.path, recursive=True) git = qisrc.git.Git(git_project.path) remote_name = repo.default_remote.name try: git.init() git.remote("add", remote_name, clone_url) git.fetch(remote_name, "--quiet") remote_branch = "%s/%s" % (remote_name, branch) rc, _ = git.call("rev-parse", "--verify", "--quiet", remote_branch, raises=False) # When `remote_branch` is invalid, try to checkout `branch` instead git.checkout("-b", branch, branch if rc else remote_branch) except: ui.error("Cloning repo failed") if git.is_empty(): qisys.sh.rm(git_project.path) self.worktree.remove_project(repo.src) return False self.save_project_config(git_project) self.load_git_projects() return True
def clever_reset_ref(git_project, ref): """ Resets only if needed, fetches only if needed """ try: remote_name = git_project.default_remote.name except AttributeError: error_msg = "Project {} has no default remote, defaulting to origin" ui.error(error_msg.format(git_project.name)) remote_name = "origin" git = qisrc.git.Git(git_project.path) if ref.startswith("refs/"): git.fetch(remote_name, ref) git.reset("--hard", "FETCH_HEAD") return _, actual_sha1 = git.call("rev-parse", "HEAD", raises=False) if actual_sha1 == ref: # Nothing to do return ret, _ = git.call("show", "--oneline", ref, raises=False) if ret == 0: # SHA-1 exists locally git.reset("--hard", ref) else: # Full fetch in this case git.fetch(remote_name) git.reset("--hard", ref)