Exemple #1
0
 def test_actor_get_uid_laziness_not_called(self, mock_get_uid):
     env = {
         "GIT_AUTHOR_NAME": "John Doe",
         "GIT_AUTHOR_EMAIL": "*****@*****.**",
         "GIT_COMMITTER_NAME": "Jane Doe",
         "GIT_COMMITTER_EMAIL": "*****@*****.**",
     }
     os.environ.update(env)
     for cr in (None, self.rorepo.config_reader()):
         Actor.committer(cr)
         Actor.author(cr)
     self.assertFalse(mock_get_uid.called)
Exemple #2
0
    def append_entry(cls, config_reader, filepath, oldbinsha, newbinsha,
                     message):
        """Append a new log entry to the revlog at filepath.
        
        :param config_reader: configuration reader of the repository - used to obtain
            user information. May be None
        :param filepath: full path to the log file
        :param oldbinsha: binary sha of the previous commit
        :param newbinsha: binary sha of the current commit
        :param message: message describing the change to the reference
        :param write: If True, the changes will be written right away. Otherwise
            the change will not be written
        :return: RefLogEntry objects which was appended to the log
        :note: As we are append-only, concurrent access is not a problem as we 
            do not interfere with readers."""
        if len(oldbinsha) != 20 or len(newbinsha) != 20:
            raise ValueError("Shas need to be given in binary format")
        #END handle sha type
        assure_directory_exists(filepath, is_file=True)
        entry = RefLogEntry((bin_to_hex(oldbinsha), bin_to_hex(newbinsha),
                             Actor.committer(config_reader),
                             (int(time.time()), time.altzone), message))

        lf = LockFile(filepath)
        lf._obtain_lock_or_raise()

        fd = open(filepath, 'a')
        try:
            fd.write(repr(entry))
        finally:
            fd.close()
            lf._release_lock()
        #END handle write operation

        return entry
Exemple #3
0
    def append_entry(cls, config_reader, filepath, oldbinsha, newbinsha, message):
        """Append a new log entry to the revlog at filepath.

        :param config_reader: configuration reader of the repository - used to obtain
            user information. May be None
        :param filepath: full path to the log file
        :param oldbinsha: binary sha of the previous commit
        :param newbinsha: binary sha of the current commit
        :param message: message describing the change to the reference
        :param write: If True, the changes will be written right away. Otherwise
            the change will not be written
        :return: RefLogEntry objects which was appended to the log
        :note: As we are append-only, concurrent access is not a problem as we 
            do not interfere with readers."""
        if len(oldbinsha) != 20 or len(newbinsha) != 20:
            raise ValueError("Shas need to be given in binary format")
        # END handle sha type
        assure_directory_exists(filepath, is_file=True)
        entry = RefLogEntry((bin_to_hex(oldbinsha), bin_to_hex(newbinsha), Actor.committer(
            config_reader), (int(time.time()), time.altzone), message))

        lf = LockFile(filepath)
        lf._obtain_lock_or_raise()

        fd = open(filepath, 'a')
        try:
            fd.write(repr(entry))
        finally:
            fd.close()
            lf._release_lock()
        # END handle write operation

        return entry
Exemple #4
0
 def test_actor_get_uid_laziness_called(self, mock_get_uid):
     mock_get_uid.return_value = "user"
     for cr in (None, self.rorepo.config_reader()):
         committer = Actor.committer(cr)
         author = Actor.author(cr)
         if cr is None:  # otherwise, use value from config_reader
             self.assertEqual(committer.name, 'user')
             self.assertTrue(committer.email.startswith('user@'))
             self.assertEqual(author.name, 'user')
             self.assertTrue(committer.email.startswith('user@'))
     self.assertTrue(mock_get_uid.called)
     self.assertEqual(mock_get_uid.call_count, 4)
Exemple #5
0
    def append_entry(cls,
                     config_reader: Union[Actor, 'GitConfigParser',
                                          'SectionConstraint', None],
                     filepath: PathLike,
                     oldbinsha: bytes,
                     newbinsha: bytes,
                     message: str,
                     write: bool = True) -> 'RefLogEntry':
        """Append a new log entry to the revlog at filepath.

        :param config_reader: configuration reader of the repository - used to obtain
            user information. May also be an Actor instance identifying the committer directly or None.
        :param filepath: full path to the log file
        :param oldbinsha: binary sha of the previous commit
        :param newbinsha: binary sha of the current commit
        :param message: message describing the change to the reference
        :param write: If True, the changes will be written right away. Otherwise
            the change will not be written

        :return: RefLogEntry objects which was appended to the log

        :note: As we are append-only, concurrent access is not a problem as we
            do not interfere with readers."""

        if len(oldbinsha) != 20 or len(newbinsha) != 20:
            raise ValueError("Shas need to be given in binary format")
        # END handle sha type
        assure_directory_exists(filepath, is_file=True)
        first_line = message.split('\n')[0]
        if isinstance(config_reader, Actor):
            committer = config_reader  # mypy thinks this is Actor | Gitconfigparser, but why?
        else:
            committer = Actor.committer(config_reader)
        entry = RefLogEntry((bin_to_hex(oldbinsha).decode('ascii'),
                             bin_to_hex(newbinsha).decode('ascii'), committer,
                             (int(_time.time()), _time.altzone), first_line))

        if write:
            lf = LockFile(filepath)
            lf._obtain_lock_or_raise()
            fd = open(filepath, 'ab')
            try:
                fd.write(entry.format().encode(defenc))
            finally:
                fd.close()
                lf._release_lock()
            # END handle write operation
        return entry
Exemple #6
0
    def create_from_tree(cls,
                         repo,
                         tree,
                         message,
                         parent_commits=None,
                         head=False,
                         author=None,
                         committer=None,
                         author_date=None,
                         commit_date=None,
                         isolated=False):
        """Commit the given tree, creating a commit object.

        :param repo: Repo object the commit should be part of
        :param tree: Tree object or hex or bin sha
            the tree of the new commit
        :param message: Commit message. It may be an empty string if no message is provided.
            It will be converted to a string in any case.
        :param parent_commits:
            Optional Commit objects to use as parents for the new commit.
            If empty list, the commit will have no parents at all and become
            a root commit.
            If None , the current head commit will be the parent of the
            new commit object
        :param head:
            If True, the HEAD will be advanced to the new commit automatically.
            Else the HEAD will remain pointing on the previous commit. This could
            lead to undesired results when diffing files.
        :param author: The name of the author, optional. If unset, the repository
            configuration is used to obtain this value.
        :param committer: The name of the committer, optional. If unset, the
            repository configuration is used to obtain this value.
        :param author_date: The timestamp for the author field
        :param commit_date: The timestamp for the committer field
        :param isolated: if true, the parent environment is not passed to the git command.

        :return: Commit object representing the new commit

        :note:
            Additional information about the committer and Author are taken from the
            environment or from the git configuration, see git-commit-tree for
            more information"""
        if parent_commits is None:
            try:
                parent_commits = [repo.head.commit]
            except ValueError:
                # empty repositories have no head commit
                parent_commits = []
            # END handle parent commits
        else:
            for p in parent_commits:
                if not isinstance(p, cls):
                    raise ValueError("Parent commit '%r' must be of type %s" %
                                     (p, cls))
            # end check parent commit types
        # END if parent commits are unset

        # retrieve all additional information, create a commit object, and
        # serialize it
        # Generally:
        # * Environment variables override configuration values
        # * Sensible defaults are set according to the git documentation

        # COMMITER AND AUTHOR INFO
        cr = repo.config_reader()
        env = {} if isolated else os.environ

        committer = committer or Actor.committer(cr, isolated=isolated)
        author = author or Actor.author(cr, isolated=isolated)

        # PARSE THE DATES
        unix_time = int(time())
        is_dst = daylight and localtime().tm_isdst > 0
        offset = altzone if is_dst else timezone

        author_date_str = env.get(cls.env_author_date, '')
        if author_date:
            author_time, author_offset = parse_date(author_date)
        elif author_date_str:
            author_time, author_offset = parse_date(author_date_str)
        else:
            author_time, author_offset = unix_time, offset
        # END set author time

        committer_date_str = env.get(cls.env_committer_date, '')
        if commit_date:
            committer_time, committer_offset = parse_date(commit_date)
        elif committer_date_str:
            committer_time, committer_offset = parse_date(committer_date_str)
        else:
            committer_time, committer_offset = unix_time, offset
        # END set committer time

        # assume utf8 encoding
        enc_section, enc_option = cls.conf_encoding.split('.')
        conf_encoding = cr.get_value(enc_section, enc_option,
                                     cls.default_encoding)

        # if the tree is no object, make sure we create one - otherwise
        # the created commit object is invalid
        if isinstance(tree, str):
            tree = repo.tree(tree)
        # END tree conversion

        # CREATE NEW COMMIT
        new_commit = cls(repo, cls.NULL_BIN_SHA, tree, author, author_time,
                         author_offset, committer, committer_time,
                         committer_offset, message, parent_commits,
                         conf_encoding)

        stream = BytesIO()
        new_commit._serialize(stream)
        streamlen = stream.tell()
        stream.seek(0)

        istream = repo.odb.store(IStream(cls.type, streamlen, stream))
        new_commit.binsha = istream.binsha

        if head:
            # need late import here, importing git at the very beginning throws
            # as well ...
            import git.refs
            try:
                repo.head.set_commit(new_commit, logmsg=message)
            except ValueError:
                # head is not yet set to the ref our HEAD points to
                # Happens on first commit
                master = git.refs.Head.create(repo,
                                              repo.head.ref,
                                              new_commit,
                                              logmsg="commit (initial): %s" %
                                              message)
                repo.head.set_reference(master,
                                        logmsg='commit: Switching to %s' %
                                        master)
            # END handle empty repositories
        # END advance head handling

        return new_commit
Exemple #7
0
    def create_from_tree(cls, repo, tree, message, parent_commits=None, head=False, author=None, committer=None):
        """Commit the given tree, creating a commit object.

        :param repo: Repo object the commit should be part of
        :param tree: Tree object or hex or bin sha
            the tree of the new commit
        :param message: Commit message. It may be an empty string if no message is provided.
            It will be converted to a string in any case.
        :param parent_commits:
            Optional Commit objects to use as parents for the new commit.
            If empty list, the commit will have no parents at all and become
            a root commit.
            If None , the current head commit will be the parent of the
            new commit object
        :param head:
            If True, the HEAD will be advanced to the new commit automatically.
            Else the HEAD will remain pointing on the previous commit. This could
            lead to undesired results when diffing files.
        :param author: The name of the author, optional. If unset, the repository
            configuration is used to obtain this value.
        :param committer: The name of the committer, optional. If unset, the
            repository configuration is used to obtain this value.

        :return: Commit object representing the new commit

        :note:
            Additional information about the committer and Author are taken from the
            environment or from the git configuration, see git-commit-tree for
            more information"""
        if parent_commits is None:
            try:
                parent_commits = [repo.head.commit]
            except ValueError:
                # empty repositories have no head commit
                parent_commits = list()
            # END handle parent commits
        else:
            for p in parent_commits:
                if not isinstance(p, cls):
                    raise ValueError("Parent commit '%r' must be of type %s" % (p, cls))
            # end check parent commit types
        # END if parent commits are unset

        # retrieve all additional information, create a commit object, and
        # serialize it
        # Generally:
        # * Environment variables override configuration values
        # * Sensible defaults are set according to the git documentation

        # COMMITER AND AUTHOR INFO
        cr = repo.config_reader()
        env = os.environ

        committer = committer or Actor.committer(cr)
        author = author or Actor.author(cr)

        # PARSE THE DATES
        unix_time = int(time())
        offset = altzone

        author_date_str = env.get(cls.env_author_date, '')
        if author_date_str:
            author_time, author_offset = parse_date(author_date_str)
        else:
            author_time, author_offset = unix_time, offset
        # END set author time

        committer_date_str = env.get(cls.env_committer_date, '')
        if committer_date_str:
            committer_time, committer_offset = parse_date(committer_date_str)
        else:
            committer_time, committer_offset = unix_time, offset
        # END set committer time

        # assume utf8 encoding
        enc_section, enc_option = cls.conf_encoding.split('.')
        conf_encoding = cr.get_value(enc_section, enc_option, cls.default_encoding)

        # if the tree is no object, make sure we create one - otherwise
        # the created commit object is invalid
        if isinstance(tree, str):
            tree = repo.tree(tree)
        # END tree conversion

        # CREATE NEW COMMIT
        new_commit = cls(repo, cls.NULL_BIN_SHA, tree,
                         author, author_time, author_offset,
                         committer, committer_time, committer_offset,
                         message, parent_commits, conf_encoding)

        stream = BytesIO()
        new_commit._serialize(stream)
        streamlen = stream.tell()
        stream.seek(0)

        istream = repo.odb.store(IStream(cls.type, streamlen, stream))
        new_commit.binsha = istream.binsha

        if head:
            # need late import here, importing git at the very beginning throws
            # as well ...
            import git.refs
            try:
                repo.head.set_commit(new_commit, logmsg=message)
            except ValueError:
                # head is not yet set to the ref our HEAD points to
                # Happens on first commit
                master = git.refs.Head.create(repo, repo.head.ref, new_commit, logmsg="commit (initial): %s" % message)
                repo.head.set_reference(master, logmsg='commit: Switching to %s' % master)
            # END handle empty repositories
        # END advance head handling

        return new_commit
Exemple #8
0
 def test_actor(self):
     for cr in (None, self.rorepo.config_reader()):
         assert isinstance(Actor.committer(cr), Actor)
         assert isinstance(Actor.author(cr), Actor)
Exemple #9
0
 def test_actor(self):
     for cr in (None, self.rorepo.config_reader()):
         self.assertIsInstance(Actor.committer(cr), Actor)
         self.assertIsInstance(Actor.author(cr), Actor)
Exemple #10
0
 def test_actor_get_uid_laziness_called(self, mock_get_uid):
     for cr in (None, self.rorepo.config_reader()):
         Actor.committer(cr)
         Actor.author(cr)
     self.assertTrue(mock_get_uid.called)
     self.assertEqual(mock_get_uid.call_count, 4)