def remove_if_equals(self, name, old_ref): """Remove a refname only if it currently equals old_ref. This method does not follow symbolic references. It can be used to perform an atomic compare-and-delete operation. :param name: The refname to delete. :param old_ref: The old sha the refname must refer to, or None to delete unconditionally. :return: True if the delete was successful, False otherwise. """ self._check_refname(name) filename = self.refpath(name) ensure_dir_exists(os.path.dirname(filename)) f = GitFile(filename, 'wb') try: if old_ref is not None: orig_ref = self.read_loose_ref(name) if orig_ref is None: orig_ref = self.get_packed_refs().get(name, None) if orig_ref != old_ref: return False # may only be packed try: os.remove(filename) except OSError as e: if e.errno != errno.ENOENT: raise self._remove_packed_ref(name) finally: # never write, we just wanted the lock f.abort() return True
def remove_if_equals(self, name, old_ref, committer=None, timestamp=None, timezone=None, message=None): """Remove a refname only if it currently equals old_ref. This method does not follow symbolic references. It can be used to perform an atomic compare-and-delete operation. :param name: The refname to delete. :param old_ref: The old sha the refname must refer to, or None to delete unconditionally. :param message: Optional message :return: True if the delete was successful, False otherwise. """ self._check_refname(name) filename = self.refpath(name) ensure_dir_exists(os.path.dirname(filename)) f = GitFile(filename, 'wb') try: if old_ref is not None: orig_ref = self.read_loose_ref(name) if orig_ref is None: orig_ref = self.get_packed_refs().get(name, ZERO_SHA) if orig_ref != old_ref: return False # remove the reference file itself try: os.remove(filename) except OSError as e: if e.errno != errno.ENOENT: # may only be packed raise self._remove_packed_ref(name) self._log(name, old_ref, None, committer=committer, timestamp=timestamp, timezone=timezone, message=message) finally: # never write, we just wanted the lock f.abort() # outside of the lock, clean-up any parent directory that might now # be empty. this ensures that re-creating a reference of the same # name of what was previously a directory works as expected parent = name while True: try: parent, _ = parent.rsplit(b'/', 1) except ValueError: break parent_filename = self.refpath(parent) try: os.rmdir(parent_filename) except OSError: # this can be caused by the parent directory being # removed by another process, being not empty, etc. # in any case, this is non fatal because we already # removed the reference, just ignore it break return True
def add_if_new(self, name, ref): """Add a new reference only if it does not already exist. This method follows symrefs, and only ensures that the last ref in the chain does not exist. :param name: The refname to set. :param ref: The new sha the refname will refer to. :return: True if the add was successful, False otherwise. """ try: realname, contents = self._follow(name) if contents is not None: return False except KeyError: realname = name self._check_refname(realname) filename = self.refpath(realname) ensure_dir_exists(os.path.dirname(filename)) with GitFile(filename, 'wb') as f: if os.path.exists(filename) or name in self.get_packed_refs(): f.abort() return False try: f.write(ref + b'\n') except (OSError, IOError): f.abort() raise return True
def remove_if_equals(self, name, old_ref, committer=None, timestamp=None, timezone=None, message=None): """Remove a refname only if it currently equals old_ref. This method does not follow symbolic references. It can be used to perform an atomic compare-and-delete operation. :param name: The refname to delete. :param old_ref: The old sha the refname must refer to, or None to delete unconditionally. :param message: Optional message :return: True if the delete was successful, False otherwise. """ self._check_refname(name) filename = self.refpath(name) ensure_dir_exists(os.path.dirname(filename)) f = GitFile(filename, 'wb') try: if old_ref is not None: orig_ref = self.read_loose_ref(name) if orig_ref is None: orig_ref = self.get_packed_refs().get(name, ZERO_SHA) if orig_ref != old_ref: return False # remove the reference file itself try: os.remove(filename) except OSError as e: if e.errno != errno.ENOENT: # may only be packed raise self._remove_packed_ref(name) self._log(name, old_ref, None, committer=committer, timestamp=timestamp, timezone=timezone, message=message) finally: # never write, we just wanted the lock f.abort() # outside of the lock, clean-up any parent directory that might now # be empty. this ensures that re-creating a reference of the same # name of what was previously a directory works as expected parent = name while True: try: parent, _ = parent.rsplit(b'/', 1) except ValueError: break parent_filename = self.refpath(parent) try: os.rmdir(parent_filename) except OSError as e: # this can be caused by the parent directory being # removed by another process, being not empty, etc. # in any case, this is non fatal because we already # removed the reference, just ignore it break return True
def add_if_new(self, name, ref): """Add a new reference only if it does not already exist. This method follows symrefs, and only ensures that the last ref in the chain does not exist. :param name: The refname to set. :param ref: The new sha the refname will refer to. :return: True if the add was successful, False otherwise. """ try: realname, contents = self._follow(name) if contents is not None: return False except KeyError: realname = name self._check_refname(realname) filename = self.refpath(realname) ensure_dir_exists(os.path.dirname(filename)) with GitFile(filename, 'wb') as f: if os.path.exists(filename) or name in self.get_packed_refs(): f.abort() return False try: f.write(ref.hex_bytes + b'\n') except (OSError, IOError): f.abort() raise return True
def pull(self): try: remote_refs = self.client.fetch( self.origin_path, self.repo, determine_wants=self.repo.object_store.determine_wants_all) except KeyError: etype, err = sys.exc_info()[:2] # try to work around bug # https://bugs.launchpad.net/dulwich/+bug/1025886 try: # pylint: disable=W0212 self.client._fetch_capabilities.remove('thin-pack') # pylint: enable=W0212 except KeyError: raise etype(err) remote_refs = self.client.fetch( self.origin_path, self.repo, determine_wants=self.repo.object_store.determine_wants_all) tree_id = self.repo[remote_refs['HEAD']].tree # iterate over tree content, giving path and blob sha. for entry in self.repo.object_store.iter_tree_contents(tree_id): entry_in_path = entry.in_path(self.repo.path) ensure_dir_exists(os.path.split(entry_in_path.path)[0]) GitFile(entry_in_path.path, 'wb').write(self.repo[entry.sha].data)
def set_if_equals(self, name, old_ref, new_ref, committer=None, timestamp=None, timezone=None, message=None): """Set a refname to new_ref only if it currently equals old_ref. This method follows all symbolic references, and can be used to perform an atomic compare-and-swap operation. :param name: The refname to set. :param old_ref: The old sha the refname must refer to, or None to set unconditionally. :param new_ref: The new sha the refname will refer to. :param message: Set message for reflog :return: True if the set was successful, False otherwise. """ self._check_refname(name) try: realnames, _ = self.follow(name) realname = realnames[-1] except (KeyError, IndexError): realname = name filename = self.refpath(realname) ensure_dir_exists(os.path.dirname(filename)) with GitFile(filename, 'wb') as f: if old_ref is not None: try: # read again while holding the lock orig_ref = self.read_loose_ref(realname) if orig_ref is None: orig_ref = self.get_packed_refs().get( realname, ZERO_SHA) if orig_ref != old_ref: f.abort() return False except (OSError, IOError): f.abort() raise try: f.write(new_ref + b'\n') except (OSError, IOError): f.abort() raise self._log(realname, old_ref, new_ref, committer=committer, timestamp=timestamp, timezone=timezone, message=message) return True
def set_if_equals(self, name, old_ref, new_ref, committer=None, timestamp=None, timezone=None, message=None): """Set a refname to new_ref only if it currently equals old_ref. This method follows all symbolic references, and can be used to perform an atomic compare-and-swap operation. :param name: The refname to set. :param old_ref: The old sha the refname must refer to, or None to set unconditionally. :param new_ref: The new sha the refname will refer to. :param message: Set message for reflog :return: True if the set was successful, False otherwise. """ self._check_refname(name) try: realnames, _ = self.follow(name) realname = realnames[-1] except (KeyError, IndexError): realname = name filename = self.refpath(realname) # make sure none of the ancestor folders is in packed refs probe_ref = os.path.dirname(realname) packed_refs = self.get_packed_refs() while probe_ref: if packed_refs.get(probe_ref, None) is not None: raise OSError(errno.ENOTDIR, 'Not a directory: {}'.format(filename)) probe_ref = os.path.dirname(probe_ref) ensure_dir_exists(os.path.dirname(filename)) with GitFile(filename, 'wb') as f: if old_ref is not None: try: # read again while holding the lock orig_ref = self.read_loose_ref(realname) if orig_ref is None: orig_ref = self.get_packed_refs().get( realname, ZERO_SHA) if orig_ref != old_ref: f.abort() return False except (OSError, IOError): f.abort() raise try: f.write(new_ref + b'\n') except (OSError, IOError): f.abort() raise self._log(realname, old_ref, new_ref, committer=committer, timestamp=timestamp, timezone=timezone, message=message) return True
def fetch_refs(remote_name = 'origin', local='.'): """ Fetch references from a Git remote repository :param remote_name: <str> git name of remote repository, _default='origin'_ :param local: <str> full path to local repository, _default='.'_ :return entries: <TreeEntry> named tuples """ #import rpdb; rpdb.set_trace() # **Fetch refs from remote** # create a dulwich Repo object from path to local repo r = Repo(local) # local repository objsto = r.object_store # create a ObjectStore object from the local repo determine_wants = objsto.determine_wants_all # built in dulwich function gitdir = os.path.join(local, r.controldir()) # the git folder cnf_file = os.path.join(gitdir, 'config') # path to config cnf = ConfigFile.from_path(cnf_file) # config remote = cnf.get(('remote', remote_name), 'url') # url of remote # correctly parse host path and create dulwich Client object from it client, host_path = get_transport_and_path(remote) remote_refs = client.fetch(host_path, r, determine_wants, sys.stdout.write) # **Store refs fetched by dulwich** dulwich_refs = os.path.join(gitdir, DULWICH_REFS) with open(dulwich_refs, 'wb') as file: writer = csv.writer(file, delimiter=' ') for key, value in remote_refs.items(): writer.writerow([key, value]) # **save remote refs shas for future checkout** remote_dir = os.path.join(gitdir, 'refs', 'remotes', remote_name) # .git/refs/remotes ensure_dir_exists(remote_dir) # built in dulwich function headref = 0 # head branch ref if remote_refs.has_key('HEAD'): headref = remote_refs.pop('HEAD') # sha of HEAD i_head = remote_refs.values().index(headref) # index of head ref head_branch = remote_refs.keys()[i_head] # name of head branch branch_key = head_branch.rsplit('/',1)[-1] # branch head_file = os.path.join(remote_dir, 'HEAD') # path to branch shas file head_ref = '/'.join(['refs','remotes',remote_name,branch_key]) with open(head_file, 'wb') as GitFile: GitFile.write('ref: ' + head_ref + '\n') # remote branch refs for key, value in remote_refs.items(): key = key.rsplit('/',1)[-1] # get just the remote's branch reffile = os.path.join(remote_dir, key) # path to branch shas file with open(reffile, 'wb') as GitFile: GitFile.write(value + '\n') if headref: remote_refs['HEAD'] = headref # restore HEAD sha return remote_refs
def add_if_new( self, name, ref, committer=None, timestamp=None, timezone=None, message=None, ): """Add a new reference only if it does not already exist. This method follows symrefs, and only ensures that the last ref in the chain does not exist. Args: name: The refname to set. ref: The new sha the refname will refer to. message: Optional message for reflog Returns: True if the add was successful, False otherwise. """ try: realnames, contents = self.follow(name) if contents is not None: return False realname = realnames[-1] except (KeyError, IndexError): realname = name self._check_refname(realname) filename = self.refpath(realname) ensure_dir_exists(os.path.dirname(filename)) with GitFile(filename, "wb") as f: if os.path.exists(filename) or name in self.get_packed_refs(): f.abort() return False try: f.write(ref + b"\n") except (OSError, IOError): f.abort() raise else: self._log( name, None, ref, committer=committer, timestamp=timestamp, timezone=timezone, message=message, ) return True
def add_if_new(self, name, ref): """Add a new reference only if it does not already exist.""" self._check_refname(name) filename = self.refpath(name) ensure_dir_exists(os.path.dirname(filename)) f = GitFile(filename, 'wb') try: if os.path.exists(filename) or name in self.get_packed_refs(): f.abort() return False try: f.write(ref+"\n") except (OSError, IOError): f.abort() raise finally: f.close() return True
def set_if_equals(self, name, old_ref, new_ref): """Set a refname to new_ref only if it currently equals old_ref. This method follows all symbolic references, and can be used to perform an atomic compare-and-swap operation. :param name: The refname to set. :param old_ref: The old sha the refname must refer to, or None to set unconditionally. :param new_ref: The new sha the refname will refer to. :return: True if the set was successful, False otherwise. """ self._check_refname(name) try: realname, _ = self._follow(name) except KeyError: realname = name filename = self.refpath(realname) ensure_dir_exists(os.path.dirname(filename)) f = GitFile(filename, 'wb') try: if old_ref is not None: try: # read again while holding the lock orig_ref = self.read_loose_ref(realname) if orig_ref is None: orig_ref = self.get_packed_refs().get(realname, None) if orig_ref != old_ref: f.abort() return False except (OSError, IOError): f.abort() raise try: f.write(new_ref+"\n") except (OSError, IOError): f.abort() raise finally: f.close() return True
def set_if_equals(self, name, old_ref, new_ref): """Set a refname to new_ref only if it currently equals old_ref. This method follows all symbolic references, and can be used to perform an atomic compare-and-swap operation. :param name: The refname to set. :param old_ref: The old sha the refname must refer to, or None to set unconditionally. :param new_ref: The new sha the refname will refer to. :return: True if the set was successful, False otherwise. """ self._check_refname(name) try: realname, _ = self._follow(name) except KeyError: realname = name filename = self.refpath(realname) ensure_dir_exists(os.path.dirname(filename)) f = GitFile(filename, 'wb') try: if old_ref is not None: try: # read again while holding the lock orig_ref = self.read_loose_ref(realname) if orig_ref is None: orig_ref = self.get_packed_refs().get(realname, None) if orig_ref != old_ref: f.abort() return False except (OSError, IOError): f.abort() raise try: f.write(new_ref + "\n") except (OSError, IOError): f.abort() raise finally: f.close() return True
def checkout(repo_path='.', co_ref='HEAD'): """ Checkout a reference from a Git repository :param repo_path: <str> path of repository :param co_ref: <str> name of checkout reference :return entries: <TreeEntry> named tuples """ # TODO: try using index.build_index_from_tree repo = Repo(repo_path) obj_sto = repo.object_store # TODO: catch not a reference tree_id = repo[co_ref].tree # TODO: error out if unstaged or uncommited files entries = [] for entry in obj_sto.iter_tree_contents(tree_id): entry_in_path = entry.in_path(repo.path) path = os.path.split(entry_in_path.path) ensure_dir_exists(path[0]) path = os.path.join(*path) with open(path, 'wb') as GitFile: GitFile.write(repo[entry_in_path.sha].data) os.chmod(path, entry_in_path.mode) entries.append(entry) return entries
def add_if_new(self, name, ref, committer=None, timestamp=None, timezone=None, message=None): """Add a new reference only if it does not already exist. This method follows symrefs, and only ensures that the last ref in the chain does not exist. :param name: The refname to set. :param ref: The new sha the refname will refer to. :param message: Optional message for reflog :return: True if the add was successful, False otherwise. """ try: realnames, contents = self.follow(name) if contents is not None: return False realname = realnames[-1] except (KeyError, IndexError): realname = name self._check_refname(realname) filename = self.refpath(realname) ensure_dir_exists(os.path.dirname(filename)) with GitFile(filename, 'wb') as f: if os.path.exists(filename) or name in self.get_packed_refs(): f.abort() return False try: f.write(ref + b'\n') except (OSError, IOError): f.abort() raise else: self._log(name, None, ref, committer=committer, timestamp=timestamp, timezone=timezone, message=message) return True
def set_if_equals( self, name, old_ref, new_ref, committer=None, timestamp=None, timezone=None, message=None, ): """Set a refname to new_ref only if it currently equals old_ref. This method follows all symbolic references, and can be used to perform an atomic compare-and-swap operation. Args: name: The refname to set. old_ref: The old sha the refname must refer to, or None to set unconditionally. new_ref: The new sha the refname will refer to. message: Set message for reflog Returns: True if the set was successful, False otherwise. """ self._check_refname(name) try: realnames, _ = self.follow(name) realname = realnames[-1] except (KeyError, IndexError): realname = name filename = self.refpath(realname) # make sure none of the ancestor folders is in packed refs probe_ref = os.path.dirname(realname) packed_refs = self.get_packed_refs() while probe_ref: if packed_refs.get(probe_ref, None) is not None: raise NotADirectoryError(filename) probe_ref = os.path.dirname(probe_ref) ensure_dir_exists(os.path.dirname(filename)) with GitFile(filename, "wb") as f: if old_ref is not None: try: # read again while holding the lock orig_ref = self.read_loose_ref(realname) if orig_ref is None: orig_ref = self.get_packed_refs().get(realname, ZERO_SHA) if orig_ref != old_ref: f.abort() return False except (OSError, IOError): f.abort() raise try: f.write(new_ref + b"\n") except (OSError, IOError): f.abort() raise self._log( realname, old_ref, new_ref, committer=committer, timestamp=timestamp, timezone=timezone, message=message, ) return True
def setUp(self): self.gitroot = os.path.dirname(import_repo_to_dir("server_new.export")) self.dest = os.path.join(self.gitroot, "dest") file.ensure_dir_exists(self.dest) run_git_or_fail(["init", "--quiet", "--bare"], cwd=self.dest)
def setUp(self): self.gitroot = os.path.dirname( import_repo_to_dir("server_new.export").rstrip(os.sep)) self.dest = os.path.join(self.gitroot, "dest") file.ensure_dir_exists(self.dest) run_git_or_fail(["init", "--quiet", "--bare"], cwd=self.dest)
def setUp(self): self.gitroot = os.path.dirname( import_repo_to_dir('server_new.export').rstrip(os.sep)) self.dest = os.path.join(self.gitroot, 'dest') file.ensure_dir_exists(self.dest) run_git_or_fail(['init', '--quiet', '--bare'], cwd=self.dest)