def push_file(self, project, filename, contents, branch="master", fast_forward=True, message=None): """ Push a new file with the given contents to the given project It is assumed that the project has beed created """ src = project.replace(".git", "") repo_src = self.src.join(src) git = qisrc.git.Git(repo_src.strpath) if git.get_current_branch() != branch: git.checkout("--force", "-B", branch) if not fast_forward: git.reset("--hard", "HEAD~1") to_write = repo_src.join(filename) if not message: if to_write.check(file=True): message = "Update %s" % filename else: message = "Add %s" % filename repo_src.ensure(filename, file=True) repo_src.join(filename).write(contents) git.add(filename) git.commit("--message", message) if fast_forward: git.push("origin", "%s:%s" % (branch, branch)) else: git.push("origin", "--force", "%s:%s" % (branch, branch))
def push_file(self, project, filename, contents, branch="master", fast_forward=True, message=None): """ Push a new file with the given contents to the given project It is assumed that the project has been created """ src = project.replace(".git", "") repo_src = self.src.join(src) git = qisrc.git.Git(repo_src.strpath) if git.get_current_branch() != branch: git.checkout("--force", "-B", branch) if not fast_forward: git.reset("--hard", "HEAD~1") to_write = repo_src.join(filename) if not message: if to_write.check(file=True): message = "Update %s" % filename else: message = "Add %s" % filename repo_src.ensure(filename, file=True) repo_src.join(filename).write(contents) git.add(filename) git.commit("--message", message) if fast_forward: git.push("origin", "%s:%s" % (branch, branch)) else: git.push("origin", "--force", "%s:%s" % (branch, branch))
def push_submodule(self, project, submodule_url, destination_dir, branch="master", fast_forward=True, message=None): """ Push a submodule to the given project. It is assumed that the project has been created. """ src = project.replace(".git", "") repo_src = self.src.join(src) git = qisrc.git.Git(repo_src.strpath) if git.get_current_branch() != branch: git.checkout("--force", "-B", branch) if not fast_forward: git.reset("--hard", "HEAD~1") to_write = repo_src.join(destination_dir) if to_write.exists(): raise RuntimeError("path %s already exists" % destination_dir) if not message: message = "Add submodule %s" % destination_dir git.call("submodule", "add", submodule_url, destination_dir) git.add(destination_dir) git.commit("--message", message) if fast_forward: git.push("origin", "%s:%s" % (branch, branch)) else: git.push("origin", "--force", "%s:%s" % (branch, branch))
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 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 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 _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 push_tag(self, project, tag, branch="master", fast_forward=True): """ push tag on project """ src = project.replace(".git", "") repo_src = self.src.join(src) git = qisrc.git.Git(repo_src.strpath) if git.get_current_branch() != branch: git.checkout("--force", "-B", branch) if not fast_forward: git.reset("--hard", "HEAD~1") # tag the branch git.call("tag", tag) if fast_forward: git.push("origin", tag) else: git.push("origin", "--force", tag)
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 _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 _switch_to_git_ref(git, ref_to_reset_to, is_tag): """ Switch To Git Ref """ if is_tag: # checkout puts the git current repo in a detached HEAD status # As setting a tag in the manifest means oubvioulsy a read-only git copy, # The detached HEAD prevents an user to accidentally pull up to the original branch HEAD and commits things rc, out = git.checkout(ref_to_reset_to, "--", raises=False) else: # reset --hard keeps the attached HEAD, and that's probably what the user needs to be able to track changes # and commits some things. If the user wants its git copy in read-only, he can use a tag instead of a branch rc, out = git.reset("--hard", ref_to_reset_to, "--", raises=False) return rc, out
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)
def _clone_missing(self, git_project, repo, worktree_clone=None): # You may be wondering why we use `git clone` when used with the --clone # option, and a combination of `git init`, `git fetch`, `git checkout` in # the general case. # The reason is that in the general case, we can have the following scenario: # - first foo/bar is created # - then foo/ is added to the manifest # In that case, trying to call `git clone` in foo/ will fail # # But when we are using --clone we know we have sorted the repos to clone # by their relative paths in the worktree, (see qisrc.sync.compute_repo_diff) # so we know foo/ will always be created before foo/bar/ branch = repo.default_branch fixed_ref = repo.fixed_ref clone_url = repo.clone_url git = qisrc.git.Git(git_project.path) remote_name = repo.default_remote.name remote_ref = "%s/%s" % (remote_name, branch) clone_project = None ok = True message = "" # Only use a local clone if: # * worktree_clone is set # * we find a matching project in the worktree_clone use_local = False if worktree_clone: clone_project = worktree_clone.find_repo(repo) if clone_project: use_local = True if use_local: # Only need to create the parent directory, `git clone` will take # care of the rest to_make = os.path.dirname(git_project.path) qisys.sh.mkdir(to_make) (ok, message) = git.local_clone(clone_project, clone_url, remote_name=remote_name, branch=branch) else: # Need to create the full path, since we will use # `git init ; git fetch ; git checkout` qisys.sh.mkdir(git_project.path, recursive=True) (ok, message) = git.safe_clone(clone_url, remote_name=remote_name, branch=branch) if not ok: ui.error("Cloning repo failed") ui.error(message) # If `git clone` fails, it's possible that the path does # not exist if os.path.exists(git_project.path) and git.is_empty(): qisys.sh.rm(git_project.path) self.worktree.remove_project(repo.src) return False if fixed_ref: rc, out = git.reset("--hard", fixed_ref, raises=False) if rc != 0: ui.error("Failed to reset to fixed ref") ui.error(out) 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) 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