Commits

Troy Williams committed 6d77971

Separated the script from the private bulk folder

  • Participants

Comments (0)

Files changed (2)

File .hgignore

Empty file added.

File hg_utilities.py

+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+
+"""
+
+This script will take a series of search paths and recursively search them
+for .hg folders. If an .hg folder is found then it is deemed a repository and
+added to a list for processing. It then checks to see if there are any pending
+commits in the repo and notifies the user of such.
+
+Copyright (c) 2011 Troy Williams
+
+License: The MIT License (http://www.opensource.org/licenses/mit-license.php)
+"""
+
+import sys
+import os
+
+__uuid__ = 'ca635163-d33d-4e18-a8d0-53039bea6f18'
+__version__ = '0.4'
+__author__ = 'Troy Williams'
+__email__ = 'troy.williams@bluebill.net'
+__copyright__ = 'Copyright (c) 2011, Troy Williams'
+__date__ = '2011-10-22'
+
+def find_repositories(root):
+    """
+    Search for mecurial repositories and returns the paths that are found.
+
+    It will not continue to search a path once it is determined that a path is a
+    repository.
+    """
+    
+    path_to_search = root
+    repository_folder_indicator = '.hg'
+    hg_repositories = []
+    for current_path, dirs, files in os.walk(path_to_search):
+        if repository_folder_indicator in dirs:            
+            print 'Found repository: %s' % os.path.relpath(current_path,root)
+            hg_repositories.append(current_path)                                    
+            dirs[:] = [] #clear the list of directorys so we stop searching this branch
+
+    return hg_repositories
+
+
+def process_command_line():
+    """
+    From the Docs: http://docs.python.org/library/optparse.html
+    """
+
+    from optparse import OptionParser
+
+    usage = "usage: %prog [options] search_path1 search_path2 search_path3..."
+    parser = OptionParser(usage=usage, version="%prog v" + __version__)
+    parser.add_option("-p", "--pull", action="store_true",
+                                      dest="pull",
+                                      help="Pull remote changes to local \
+                                      repository")
+    parser.add_option("", "--pullupdate", action="store_true",
+                                          dest="pullupdate",
+                                          help="Pull remote changes to local \
+                                                repository and update to \
+                                                tip.")
+    parser.add_option("-u", "--update", action="store_true",
+                                         dest="update",
+                                         help="Update local repositories to \
+                                               tip.")
+    parser.add_option("-s", "--push", action="store_true",
+                                      dest="push",
+                                      help="Push local changes to remote \
+                                            repository.")
+
+    options, args = parser.parse_args(args=None, values=None)
+    if len(args) == 0:
+        parser.error('At least one search path is required!')
+        parser.print_help()
+
+    return options, args
+
+def run_command(command, working_directory, useshell=False):
+    """
+    Takes the list and attempts to run it in the command shell and waits for it
+    to execute. It then returns the results to the caller.
+    """
+    if not command:
+        raise Exception, 'Valid command required - fill the list please!'
+
+    import subprocess
+    p = subprocess.Popen(command,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE,
+                         shell=useshell,
+                         cwd=working_directory)
+
+    output_stream, error_stream = p.communicate()
+    return output_stream, error_stream
+
+
+def hg_update(hg_repository):
+    """
+    Execute the hg update command on the repository
+    """
+    cmd = ['hg', 'update']
+    return run_command(cmd, hg_repository) #tuple of (std.output, std.error)
+
+
+def hg_push(hg_repository):
+    """
+    Execute the hg push command on the repository
+    """
+    cmd = ['hg', 'push']
+    return run_command(cmd, hg_repository) #tuple of (std.output, std.error)
+
+
+def hg_pull(hg_repository):
+    """
+    Issues the hg pull command on the specified repository
+    """
+    cmd = ['hg', 'pull']
+    return run_command(cmd, hg_repository) #tuple of (std.output, std.error)
+
+
+def hg_status(hg_repository):
+    """
+    Issues the hg status command on the specified repository. If anything is
+    returned then the repository has a pending commit and should not have any
+    push or pull operations performed on it.
+    """
+    cmd = ['hg', 'status']
+    return run_command(cmd, hg_repository) #tuple of (std.output, std.error)
+
+
+def hg_incomming(hg_repository):
+    """
+    issues an hg incoming command on the repository
+
+    $ hg incoming
+    comparing with /home/troy/tmp/repos/repo1
+    searching for changes
+    no changes found
+
+    $ hg incoming
+    abort: repository default not found!
+
+
+    $ hg incoming
+    comparing with /home/troy/tmp/repos/repo1
+    searching for changes
+    changeset:   1:f4e4c8ffd56b
+    tag:         tip
+    user:        Troy Williams <troy.williams@bluebill.net>
+    date:        Sat Oct 22 14:52:31 2011 -0400
+    summary:     added a file
+    """
+
+    cmd = ['hg', 'incoming']
+    return run_command(cmd, hg_repository) #tuple of (std.output, std.error)
+
+def find_pending_commits(repositories):
+    """
+    Searches each repository for pending commits. If any are found the repository
+    is added to a new list and returned
+    """
+
+    pending_commits = []
+    for repo in repositories:
+        output, error = hg_status(repo)
+        if output:
+            pending_commits.append(repo)
+
+    return pending_commits
+
+def is_remote_incoming_repo_setup(hg_incoming_message):
+    """
+    interprets the hg incomming message and determines if the repo has a remote
+    repository setup
+
+    hg_incoming_message is a tuple of (std.output, std.error). Check to see
+    if there is the abort message
+    """
+
+    m = 'abort: repository default not found!'
+    return m in hg_incoming_message[1]
+
+
+def update_repositories(repositories):
+    """
+    Takes the list of repositories and updates them
+    """
+    for repo in repositories:
+        print 'updating', repo
+        output, error = hg_update(repo)
+        if error:
+            print error
+            continue
+
+        print output
+
+def pull_repository_changes(repositories):
+    """
+    Takes the list of repositories and pulls the changes
+    """
+    for repo in repositories:
+        print 'Pulling changes for', repo
+        output, error = hg_pull(repo)
+        if error:
+            print error
+            continue
+
+def find_repos_with_default_incoming_sync(repositories):
+    """
+    Takes a list of repositories and finds the ones that are configured to pull
+    remote changes.
+    """
+    repos_with_sync_capabilities = []
+    for repo in repositories:
+        status = hg_incomming(repo) #status is a tuple (std, stderr)
+        if is_remote_incoming_repo_setup(status):
+            print repo, 'is not configured for synchronization'
+        else:
+            repos_with_sync_capabilities.append(repo)
+
+    return repos_with_sync_capabilities
+
+def pull_changes_and_update(repositories):
+    """
+    Takes the repositories and pulls remote changes and then updates them
+    """
+
+    repos_with_sync_capabilities = find_repos_with_default_incoming_sync(repositories)
+    pull_repository_changes(repos_with_sync_capabilities)
+    update_repositories(repos_with_sync_capabilities)
+
+def push_repository_changes(repositories):
+    """
+    Takes the list of repositories and pushes the changes
+    """
+    for repo in repositories:
+        print 'Pushing changes for', repo
+        output, error = hg_push(repo)
+        if error:
+            print error
+            continue
+
+def main():
+    """
+    Parse the command line and issue the appropriate mercurial command.
+    """
+
+    options, args = process_command_line()
+
+    search_paths = []
+    for arg in args:
+        search_paths.append(os.path.abspath(arg))
+
+    if not search_paths:
+        raise Exception('No search paths found!')
+
+    repositories = []
+    for path in search_paths:
+        print 'Searching for repositories in %s...' % path
+        hg_repositories = find_repositories(path)
+        repositories.extend(hg_repositories)
+
+    if len(repositories) == 0:
+        print 'No repositories found...'
+        return 0
+
+    #If any of the repos have pending commits - ignore them and warn the user
+    pending_commits = find_pending_commits(repositories)
+    if pending_commits:
+        repositories = [p for p in repositories if p not in pending_commits]
+        for repo in pending_commits:
+            print repo, 'has pending commits. Please commit the changes!'
+
+    if len(repositories) == 0:
+        print 'No repositories left to process...'
+        return 0
+
+#At this point we have a list of repositories that do not have pending commits
+    if options.pullupdate:
+        pull_changes_and_update(repositories)
+
+    elif options.pull:
+        pull_repository_changes(repositories)
+
+    elif options.push:
+        push_repository_changes(repositories)
+
+    elif options.update:
+        update_repositories(repositories)
+
+    else:
+        print parser.print_help()
+        return -1
+
+    return 0 # success
+
+if __name__ == '__main__':
+    status = main()
+    sys.exit(status)