[Asterisk-code-review] digium git/version parser: Update branch and tag functionality (repotools[master])

George Joseph asteriskteam at digium.com
Wed Mar 14 10:28:02 CDT 2018


George Joseph has submitted this change and it was merged. ( https://gerrit.asterisk.org/8290 )

Change subject: digium_git/version_parser: Update branch and tag functionality
......................................................................

digium_git/version_parser: Update branch and tag functionality

Updated the utility and other support methods to the following modules:

digium_git -
  * Made it so the local_path expands the '~' for the current user.
  * Added ability to add/push multiple branches
  * Add abitlity to delete local and remote branches
  * Added a find_last_tag function that finds the last tag created tag before a
    given version.

version_parser -
  * Added a branch_name method that returns a branch name from the version.
  * Added two functions to check if the version represents either a first pre-
    release, or a first branch release version.

Change-Id: I5a7f44e75ae3d867f9795b7fb0ea5bff526b5429
---
M digium_git.py
M version_parser.py
2 files changed, 173 insertions(+), 22 deletions(-)

Approvals:
  Joshua Colp: Looks good to me, but someone else must approve; Verified
  George Joseph: Looks good to me, approved; Approved for Submit



diff --git a/digium_git.py b/digium_git.py
index 3536522..e28642f 100644
--- a/digium_git.py
+++ b/digium_git.py
@@ -17,6 +17,7 @@
 from progressbar import ProgressBar
 from digium_commits import DigiumCommitMessageParser
 from git import Repo, RemoteProgress
+from version_parser import AsteriskVersion
 
 LOGGER = logging.getLogger(__name__)
 
@@ -166,8 +167,11 @@
 
         self.show_progress = show_progress
         self.current_branch = None
+        self.updated_branches = [] # Local new or modified branches
+        self.deleted_branches = [] # Locally deleted branches
         progress = None
 
+        local_path = os.path.expanduser(local_path)
         if os.path.isdir(local_path):
             self.repo = Repo(local_path)
             origin = self.repo.remotes.origin
@@ -183,7 +187,6 @@
 
         origin.fetch(progress=self.progress)
         origin.fetch(progress=self.progress)
-
 
     @property
     def progress(self):
@@ -208,17 +211,6 @@
             pass
         return False
 
-    def _push_branch(self):
-        """Push the currently checked out local branch to the remote"""
-
-        assert self.current_branch is not None
-
-        LOGGER.debug("Pushing branch {0} to remote".format(self.current_branch))
-        self.repo.remotes.origin.push(refspec='refs/heads/{0}:refs/heads/{1}'.format(
-            self.current_branch, self.current_branch), progress=self.progress)
-        self._set_tracking(self.repo.heads[self.current_branch])
-
-
     def branch_exists(self, name):
         """Determine if a branch exists
 
@@ -232,13 +224,62 @@
 
         return name in self.repo.heads
 
+    def _push_updated_branches(self):
+        """Push new or updated branches to the remote repository."""
+
+        if not self.updated_branches:
+            return
+
+        for b in self.updated_branches:
+            LOGGER.debug("Pushing branch '{0}' to remote".format(b))
+
+            self.repo.remotes.origin.push(
+                refspec='refs/heads/{0}:refs/heads/{1}'.format(
+                    b, b), progress=self.progress)
+
+            self._set_tracking(self.repo.heads[b])
+
+        self.updated_branches = []
+
+    def _push_deleted_branches(self):
+        """Delete a branch(es) from the remote repository."""
+
+        if not self.deleted_branches:
+            return
+
+        for b in self.deleted_branches:
+            try:
+                LOGGER.debug("Pushing/Deleting remote branch '{0}'".format(b))
+                self.repo.remotes.origin.push(':{0}'.format(b), self.progress)
+            except:
+                LOGGER.debug("Remote branch '{0}' does not exist. "
+                             "Nothing to delete".format(b))
+
+        self.deleted_branches = []
+
+    def delete_branch(self, branch):
+        """Delete a branch from the local repository.
+
+        Note: The branch to delete cannot be currently checked out.
+
+        Keyword Arguments:
+        branch - The branch to remove
+        """
+
+        self.deleted_branches.append(branch)
+        self.updated_branches.remove(branch)
+        try:
+            LOGGER.debug("Deleting local branch '{0}'".format(branch))
+            self.repo.delete_head(branch)
+        except:
+            LOGGER.debug("Local branch '{0}' does not exist. "
+                         "Nothing to delete".format(branch))
 
     def _push_tags(self):
         """Push any locally created tags to the remote"""
 
         LOGGER.debug("Pushing any locally created tag(s) to remote")
         self.repo.remotes.origin.push(progress=self.progress, tags=True)
-
 
     def tag_exists(self, name):
         """Determine if a tag exists locally
@@ -253,6 +294,58 @@
 
         return name in self.repo.tags
 
+    def find_tags(self, sub):
+        """Find all tags matching starting with given substring
+
+        Keyword Arguments:
+        sub - The sub string to match against
+
+        Returns:
+        A list of tags that contain the given substring
+        """
+
+        tags = [t for t in self.repo.tags if t.name.startswith(sub)]
+
+        try:
+            tags.sort(key=lambda t: t.tag.tagged_date) # Sort by creation date
+            return tags
+        except AttributeError as e:
+            raise AttributeError("{0} - tag not annotated".format(e))
+
+    def find_last_tag(self, version):
+        """Find the last tag prior to the given version on the same major branch.
+
+        Keyword Arguments:
+        version - A version object used to start the search from
+
+        Returns:
+        A version object representing the located tag, or None if a tag
+        cannot be located.
+        """
+
+        prev = version.get_previous_version()
+        if prev.major != version.major:
+            return None
+
+        if prev.minor == version.minor:
+            # Last tag is the previous version.
+            if self.tag_exists(str(prev)):
+                return prev
+
+            raise ValueError("Tag '{0}' does not exist!".format(prev))
+
+        # Find the last set of tags for a previous version
+        tags = []
+        while prev.major == version.major:
+            tags = self.find_tags(prev.branch_name())
+            if tags: break
+            prev = prev.get_previous_version()
+
+        if prev.major != version.major:
+            return None
+
+        # Last item in list is the last tag
+        return AsteriskVersion.create_from_string(tags[-1].name)
 
     def create_tag(self, name):
         """Create a new local tag
@@ -266,13 +359,15 @@
         self.repo.create_tag(name, message="Create '{0}'".format(name))
         LOGGER.debug("Created tag {0}".format(name))
 
-
     def checkout(self, name):
         """Checkout the specified tag or branch
 
         Keyword Arguments:
         name - The tag or branch to checkout
         """
+
+        if name == self.current_branch:
+            return
 
         self.current_branch = name
 
@@ -286,6 +381,7 @@
             tracking = self.repo.remotes.origin.refs[name]
         except:
             tracking = 'HEAD'
+            self.updated_branches.append(name)
 
         try:
             branch = self.repo.heads[name]
@@ -302,7 +398,6 @@
             self.repo.remotes.origin.pull()
 
         LOGGER.debug("Local branch set to {0}".format(name))
-
 
     def _convert_git_to_digium_commit(self, git_commit):
         """Convert a Git commit into a Digium/Asterisk commit
@@ -322,7 +417,6 @@
                 git_commit.author.email)
         return digium_commit
 
-
     def _convert_git_to_digium_commits(self, git_commits):
         """Convert a series of Git commits to Digium/Asterisk commits
 
@@ -339,7 +433,6 @@
             digium_commits.append(digium_commit)
 
         return digium_commits
-
 
     def get_commits_by_tags(self, start, end):
         """Retrieve a sequence of commits between two tags
@@ -362,7 +455,6 @@
                 list(self.repo.iter_commits(rev='{0}..{1}'.format(
                         commit_start, commit_end))))
 
-
     def get_commits_by_date(self, branch, start, end):
         """Retrieve a sequence of commits between two dates
 
@@ -384,7 +476,6 @@
                 list(self.repo.iter_commits(rev=branch,
                     after=commit_start, before=commit_end)))
 
-
     def add_and_commit(self, files, commit_msg):
         """Add and commit modified files
 
@@ -399,9 +490,9 @@
         self.repo.index.add(files)
         self.repo.index.commit(commit_msg)
 
-
     def push_changes(self):
-        """Push any changes (branch, tags, etc...) upstream"""
+        """Push any changes (branches, tags, etc...) upstream"""
 
         self._push_tags()
-        self._push_branch()
+        self._push_updated_branches()
+        self._push_deleted_branches()
diff --git a/version_parser.py b/version_parser.py
index 3a6188b..fcb116e 100644
--- a/version_parser.py
+++ b/version_parser.py
@@ -136,6 +136,66 @@
         previous.major -= 1
         return previous
 
+    def has_modifier(self, mods):
+        """Check to see if any of the given modifiers are part of
+        this version.
+
+        Keyword Arguments:
+        mods - a list of modifiers to search for
+        """
+
+        if not isinstance(mods, list):
+            mods = [mods]
+
+        for m in mods:
+            for p, n in self.modifiers:
+                if m in "{0}{1}".format(p, n):
+                    return True
+        return False
+
+    def branch_name(self, prefix=True):
+        """Retrieve the name of a branch.
+
+        Branches are name from the major.minor numbers, so we only have to
+        use those values when building the branch name.
+        """
+
+        name = '{0}.{1}'.format(self.major, self.minor)
+
+        if prefix and self.prefix:
+            name = '{0}/{1}'.format(self.prefix, name)
+
+        return name
+
+    def is_first(self):
+        """Retrieve whether or not this version represents the first patch
+        release for a branch.
+
+        Returns:
+	True if the version has a patch number of '0' or has a 'cert1'
+        modifier, and does not contain other modifiers (like RCs).
+        """
+
+        return ((self.patch == 0 or self.has_modifier('cert1'))
+                and not self.has_modifier(['rc', 'alpha', 'beta']))
+
+    def is_first_pre(self):
+        """Retrieve whether or not this version represents the first pre-
+        release for a branch.
+
+        Returns:
+        True if the version that has a patch number of '0' or has a 'cert1'
+        modifier, and contains an 'rc1' (or similar) modifier.
+        """
+
+        # Major branch releases start with beta1 so check that first
+        if self.minor == 0 and self.patch == 0:
+            return self.has_modifier('beta1')
+
+        # Otherwise it might be a branch release, so check for rc1
+        return ((self.patch == 0 or self.has_modifier('cert1')) and
+                self.has_modifier('rc1'))
+
     def __repr__(self):
         """Return a representation of the version"""
         ver = ''

-- 
To view, visit https://gerrit.asterisk.org/8290
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-Project: repotools
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: I5a7f44e75ae3d867f9795b7fb0ea5bff526b5429
Gerrit-Change-Number: 8290
Gerrit-PatchSet: 1
Gerrit-Owner: Kevin Harwell <kharwell at digium.com>
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20180314/2e4df5dc/attachment-0001.html>


More information about the asterisk-code-review mailing list