Git Setup Script

If you’re not already a git expert (I’m not!), use this script to setup your git repository. This script is constantly being updated. See the source file in the git repository to get the latest version.

"""Simplifying git."""

import os
from collections import OrderedDict
import sys
import textwrap
import subprocess
import webbrowser
import glob


wrapper = textwrap.TextWrapper(subsequent_indent = "       ")


def printit(txt):
    global wrapper
    print "\n".join(wrapper.wrap(txt))


def raw_inp(txt):
    txt = "\n".join(wrapper.wrap(txt))
    return raw_input(txt + " ")


def error_check(what_did):
    print
    raw_inp("I just " + what_did + ". Press enter if you don't see any errors, or control-C if you do.")


def runit(cmd, require_interactive = False):
    """Run a command.
    Args:
        cmd (str): The command to run.

    """

    printit("*** Running: " + cmd)

    if require_interactive == False:
        output = "\n".join(run_and_get_output(cmd))

        if "fatal" in output or "Too many arguments" in output or "error:" in output:
            print
            print output
            print
            print raw_inp("I just encountered an ERROR!!! Operation FAILED!!!! Press enter to continue...")
            print
            return False

        print output
        return True
    else:
        os.system(cmd)
        return True


def global_user_name():
    """Sets the global user name."""

    userName = raw_inp("What is the global user name? ")
    printit("")
    runit('git config --global user.name "' + userName + '"')
    printit("")
    #error_check("set the global user name to " + userName)


def global_email():
    """Sets the global email."""

    userEmail = raw_inp("What is the global email? ")
    printit("")
    runit('git config --global user.email "' + userEmail + '"')
    printit("")
    #error_check("set the global email to " + userEmail)


def initialize_this_directory():
    """Initializes this directory as git, commits, and pushes."""

    remoteGit = raw_inp("What is the remote git URL? ")
    printit("")
    if runit('git clone ' + remoteGit + " ."):
        #runit('git init')
        # error_check("initialized this directory to use git")

        #runit('git remote add origin ' + remoteGit)
        #error_check("initialized this directory to use git and added the remote origin")

        # pull()

        #commit()

        #push()
        print
        makeBranch = raw_inp("Do you want to eventually be able to update the remote code so others can take advantage of your changes? (y/n)")

        if makeBranch[:1].upper() == "Y":
            make_branch()


def make_branch():
    printit("Ok. I'll make a branch of the existing code, then.")
    branchName = raw_inp("What do you want to call your personal version of the code? ")
    printit("")
    if runit('git checkout -b ' + branchName):
        error_check("created the branch " + branchName + " and checked it out")


def merge_request():
    """Make a merge request."""

    # First, commit changes to local git
    commit()

    # Now give instructions re. how to create a merge request
    print
    branch_name = get_branch_name()
    project_name = get_project_name()
    url = "https://git.durrantlab.pitt.edu/jdurrant/" + project_name + "/branches"
    raw_inp("To add your changes from this version back into the main version, you need to visit " + url + " and create a MERGE REQUEST for this branch, which is named \"" + branch_name + "\". Press enter and I'll try to open the webpage.")
    webbrowser.open_new(url)


def commit():
    """Commits this git."""

    # pull()
    # error_check("added cloud-saved changes to your local files")

    if not os.path.exists('.gitignore'):
        open('.gitignore', 'w').write("\n".join(wrapper.wrap("# List files you don't want to sync here, so git knows which files to ignore. When you're done, type \":wq\" and press enter.\n")))
    if runit('vi .gitignore', True):
        if runit('git config --global push.default simple'):  # can also be matching, the default in older versions of git.
            if runit('git add .'):
                if runit('git commit', True):
                    error_check("committed the changes")

                    push()  # Off to the cloud.


def run_and_get_output(cmd):
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
    (output, err) = p.communicate()
    waiting = p.wait()
    return output.split("\n")


def get_branch_name():
    output = run_and_get_output("git status")
    branch_name = output[0].split(" ", 2)[2]
    return branch_name


def get_project_name():
    output = run_and_get_output("git remote get-url origin")
    return os.path.basename(output[0].strip())[:-4]


def status():
    """Get the status of the local git."""

    printit("")
    if runit('git status'):
        raw_inp("[ENTER] ")
        printit("")


def list_of_remote_branches():
    return run_and_get_output("git ls-remote | grep heads | awk '{print $2}' | sed \"s/refs\/heads\///g\"")


def merge_to_master():
    output = list_of_remote_branches()

    #branches = [l[3:].split("]")[0] for l in output if l != ""]
    branches = [l for l in output if not "From " in l]
    print
    printit("I found the following branches:")
    print
    print "\n".join(branches) + "\n"
    branch_to_merge = raw_inp("Type the name of the one you want to merge into master: ")
    if runit('git checkout ' + branch_to_merge):
        if runit('git checkout master'):
            if runit('git pull origin master'):
                if runit('git merge origin/' + branch_to_merge):
                    if runit('git push origin master'):

                        error_check("merged the changes from " + branch_to_merge + " into master")

                        print
                        ans = raw_inp("Assuming the changes from " + branch_to_merge + " have been merged into master (BE SURE TO CHECK!!!!), would you like to delete the " + branch_to_merge + " branch? THIS CANNOT BE UNDONE!!! (y/n)")
                        if ans[:1].upper() == "Y":
                            if runit('git branch -d ' + branch_to_merge):
                                if runit('git push origin --delete ' + branch_to_merge):
                                    error_check("deleted the " + branch_to_merge + " branch")


def push():
    """Pushes local git to remote server."""

    printit("")
    # runit('git push -u origin master')
    if runit('git push --set-upstream origin ' + get_branch_name()):
        printit("")
        error_check("pushed the local changes to the remote server")


def pull():
    """Pulls git from remote server."""

    printit("")
    if runit('git pull'):
        printit("")
        error_check("pulled remote changes to the local computer")


def quit():
    sys.exit(0)


menuData = [
    ("Title", "Select command:"),
    ("Title", "Before Anything"),
    ("Item", ("Set your global user name", global_user_name)),
    ("Item", ("Set your global email", global_email)),
    ("Title", "Starting"),
]

if len(glob.glob("*")) == 0:
    menuData.append(("Item", ("Copy an existing git to this directory", initialize_this_directory)))

if get_branch_name() == "master" and len(glob.glob("*")) != 0:
    menuData.append(("Item", ("Make your own version of this code to work on", make_branch)))

menuData.extend([
    ("Title", "Managing your Files in " + get_branch_name().upper()),
    ("Item", ("Save changes you've made", commit)),
    ("Item", ("Add other people's changes to your code", pull)),
    ("Item", ("Ask the admin to add your changes to the main version of the code", merge_request)),

    # ("Item", ("Get the status of your local git", status)),

    # ("Title", "Remote Syncing"),
    # ("Item", ("Save your local git to the cloud", push)),
    #("Item", ("Add the cloud-saved changes made by others to your own local code", pull)),
    #("Item", ("Merge the changes in your version of the code into the main version", pull)),

    ("Title", "Commands for Jacob (\"Benevolent Dictator for Life\")"),
    ("Item", ("Merge branch into master", merge_to_master)),

    ("Title", "Done"),
    ("Item", ("Quit", quit))
])


def menu():
    num_to_func = {}

    itemcount = 1
    for cmd, action in menuData:
        if cmd.startswith("Title"):
            print ""
            printit(action)
        elif cmd.startswith("Item"):
            txt, func = action
            printit("    " + str(itemcount) + ". " + txt)
            num_to_func[itemcount] = func
            itemcount = itemcount + 1
    print

    choice = raw_inp("Which option? ")

    print

    try:  # Because the user might put in an invalid option.
        num_to_func[int(choice)]()
    except:
        pass

    return choice

if __name__ == "__main__":
    # Check to make sure the current branch exists remotely too.
    # If not, use master.
    if len(glob.glob("*")) != 0:
        try:  # This throws an error if you haven't ever set up the repository before.
            if get_branch_name() not in list_of_remote_branches():
                runit('git checkout master')
                printit("Oooops... please try running the script again...")
        except:
            pass

    while True:
        choice = menu()