Пример #1
0
def post_checkout_branch(branch, database, old_head_ref, new_head_ref):
    """\
    Handle a switch in branches. Fun times ahead.
    """
    if get_last_branch() == branch:
        logging.warning("Already on branch %s.", branch)
        return
     
    # Still a work in progress           
    #check_branch_rename(branch, database)

    if not exists_database(database + branch): 
        # The branched DB doesn't exist, create it from our current copy.
        # XXX: This has problems! Currently we only support branching from the current branch...
        # I think I can solve this by searching through all the branches, and finding the corresponding name
        # for old_ref.
        src_branch = get_last_branch()
        copy_database(database, database + branch)
        
        # Create the "base.sql" file for this branch - any changes to the branch will be relative to its starting
        # point - and NOT to production.
        create_base_sql(branch, database)
        
        patches = [patch for patch in list_applied_patches(src_branch, database) if patch not in list_merged_patches(src_branch, branch, database) and patch not in list_ignored_patches(src_branch, database)]
        add_merged_patches(patches, 'base.sql', src_branch, branch, database)
        

    # XXX: Use reflog here rather to get the last branch.
    rename_database(database, database + get_last_branch())
    rename_database(database + branch, database)
    
    set_last_branch(branch)
    add_event_to_db(database, "branch-change", time.time())
Пример #2
0
def status():
    branch = git.current_branch()
    print "# On branch %s, tracking database %s" % (branch, database)
    
    tracked = [db[len(database):] for db in list_databases() if db.startswith(database)]
    tracked[tracked.index("")] = branch
    print "# Currently tracking local branches: %s" % tracked

    print "# Patches applied:"
    print "# %s" % list_applied_patches(branch, database)
    print "# Patches not applied:"
    print "# %s" % [patch for patch in list_file_patches(branch, database) if patch not in list_applied_patches(branch, database) and patch not in list_ignored_patches(branch, database)]
    print "# Patches ignored:"
    print "# %s" % list_ignored_patches(branch, database)
    print "# Patches stashed:"
    print "# %s" % list_stashed_patches(branch, database)


    last_commit = git.last_commit_time()
    if fast_check_sqlchanges(database, last_commit):
        with amalgamated_sql(branch, database) as amalgamated_sql_file:
            sql_difference = calculate_difference(amalgamated_sql_file, database, [filters.filter_auto_increment, lambda input: filters.filter_renames(input, database, git.last_commit_time())])
        if sql_difference:
            print "# Untracked SQL code since last commit at '%s'." % (last_commit)
        else:
            print "# SQL database clean."
    else:
        print "# SQL database clean."
Пример #3
0
    def __create_sql(self):
        if self.branch == "master":
            self.tmpfile.write(open(production_sql(self.database)).read())
        else:
            self.tmpfile.write(open(base_sql(self.branch, self.database)).read())

        for patch in list_applied_patches(self.branch, self.database):
            with open(patch, "r") as infile:
                self.tmpfile.write(infile.read())
        self.tmpfile.flush()
Пример #4
0
def post_merge(dst_branch, database, squash):
    merge_info = git.reflog(False)[0]
    res = re.search(r".*: merge (\w+):.*", merge_info)
    
    if res:
        src_branch = res.group(1)
        logging.info("Branch '%s' was merged with us, '%s'", src_branch, dst_branch)
        logging.debug("Amalgamating patches from branch '%s' into one SQL file, and importing.", src_branch)
        patches = [patch for patch in list_applied_patches(src_branch, database) if (
                   patch not in list_merged_patches(src_branch, dst_branch, database) and 
                   patch not in list_ignored_patches(src_branch, database) and 
                   patch not in list_patches_resulting_from_merge(dst_branch, src_branch, database))]
        
        if not patches:
            logging.info("No patches to apply.")
            return
        
        output_file = NamedTemporaryFile(suffix=".sql", delete=False)
        for patch in patches:
            with open(patch, "r") as patchfile:
                output_file.write(patchfile.read())
        output_file.flush()
                    
        try:
            apply_patch(database, output_file.name, True)
            
            if dst_branch == "master":
                base_dir = os.path.join("sql", "development", database)
                patch_filename = free_patch_name()
            else:
                base_dir = os.path.join(".git", "gitdb", "patches", dst_branch, database)
                patch_filename = free_patch_name()

            patch_name = os.path.join(base_dir, patch_filename)
            makedirs(base_dir)
            
            shutil.copy(output_file.name, patch_name)
            add_patch_to_db(dst_branch, database, patch_name)
            add_merged_patches(patches, patch_name, src_branch, dst_branch, database)

            if dst_branch == "master": 
                # If we are on master, add the SQL file and amend the commit.
                git.add(patch_name)
                git.amend_commit()

        except MySqlException:
            # Rollback occured.
            logging.error("SQL patch did not apply cleanly. This means that while the files in branch %s will have been updated, the database %s is not. Please bring the database to a consistent state manually (you can use the SQL information stored in %s for help), then run:", dst_branch, database, output_file.name)
            logging.error("foobar")
    else:
        handle_pull(dst_branch, database)
Пример #5
0
def ignore_patches():
    # Mark certain patches as to be ignored / unignored.
    branch = git.current_branch()
    patches = dict([(i, patch) for i, patch in enumerate(list_file_patches(branch, database)) if patch not in list_applied_patches(branch, database) and patch not in list_ignored_patches(branch, database)])
    
    if not patches:
        print "No patches to be ignored."
        return
        
    pprint(patches)
    try:
        input = raw_input("Please enter a space separated list of patch #s to ignore: ")

        if input != "":
            to_ignore = [int(i) for i in input.split(" ")]
            for ignored in to_ignore:
                add_ignored_to_db(git.current_branch(), database, patches[ignored])

    except:
        print "Invalid input."
        return 
                       
    add_event_to_db(database, "ignored patches, %s: ", time.time())
Пример #6
0
def handle_pull(branch, database):
    logging.info("New patches from pull:")
    if branch != "master":
        logging.warn("gitdb does not currently support remote tracking branches.")
    else:
        patches = [patch for patch in list_file_patches(branch, database) if patch not in list_applied_patches(branch, database) and patch not in list_ignored_patches(branch, database)]
        logging.info(patches)
        
        try:
            apply_patch(database, patches, True)
            for patch in patches:
                add_patch_to_db(branch, database, patch)
            
        except MySqlException:
            logging.error("Patches from pull failed to apply.")
            logging.error("Feel free to do whatever you like to the database to make it consistent. Then run git db merge.")
Пример #7
0
def filter_renames(input, database, start_datetime, use_branch_patches=False):
    logging.debug("Scanning for renames using mysql's binlog...")
    logging.debug("Starting from %s" % start_datetime)
    
    if not use_branch_patches:
        binlog = read_binlog(database, start_datetime)
    else:
        binlog = ""
        for patch in list_applied_patches(use_branch_patches, database):
            binlog += open(patch, "r").read()
            
        print binlog
    
    table_renames = [r"RENAME TABLE (\w*) TO (\w*)",
                     r"ALTER TABLE (`\w+`|\w+).+?RENAME TO (`\w+`|\w+)"
                     ]
    
    table_mappings = bidict()
    for pattern in table_renames:
        re_match = re.compile(pattern, flags=re.IGNORECASE|re.DOTALL)
        
        for statement in binlog:
            results = re_match.search(statement)
            if results:
                old_name, new_name = results.groups()
                old_name = old_name.replace("`", "")
                new_name = new_name.replace("`", "")
                
                logging.info("Detected rename of table from '%s' to '%s'" % (old_name, new_name))
                
                # Check if old_name exists as a new_name / for double ++ renames.
                if old_name in table_mappings.values():
                    table_mappings[table_mappings[:old_name]] = new_name
                else:
                    table_mappings[old_name] = new_name
    
    for old_name, new_name in table_mappings.items():
        input = re.sub("DROP TABLE %s;" % old_name, "", input)
        input = re.sub("CREATE TABLE %s \(.+?\).+?;" % new_name, "RENAME TABLE %s to %s;" % (old_name, new_name), input, flags=re.DOTALL)
    
    column_mappings = bidict()
    data_changes_on_renamed = {}
    
    re_match = re.compile(r"ALTER TABLE (`\w*`|\w*).+?CHANGE (`\w*`) (`\w*`) ([a-zA-Z\(0-9\)_-]*) (.*) (AFTER (?:`\w*`|\w*)|FIRST)", re.IGNORECASE|re.DOTALL)
    for statement in binlog:
        results = re_match.search(statement)
        if results:
            table_name, old_name, new_name, data_info_1, data_info_2, data_info_3 = results.groups()
            table_name = table_name.replace("`", "")
            old_name = old_name.replace("`", "")
            new_name = new_name.replace("`", "")
            
            data_info = data_info_1 + " " + data_info_2
            
            if old_name == new_name:
                data_changes_on_renamed[old_name] = data_info
                continue
            
            if table_name in table_mappings:
                table_name = table_mappings[table_name]
            logging.info("Detected rename of column from '%s' to '%s' on table '%s'" % (old_name, new_name, table_name))
            
            if (table_name, old_name, data_info) in column_mappings.values():
                column_mappings[column_mappings[:(table_name, old_name, data_info)]] = (table_name, new_name, data_info)
            else:
                column_mappings[(table_name, old_name)] = (table_name, new_name, data_info)
    
    for (table_name, old_column), (_, new_column, data_info) in column_mappings.items():
        if new_column in data_changes_on_renamed: 
            data_info = data_changes_on_renamed[new_column]
        
        if table_name in table_mappings or table_name in table_mappings.values():
            # Column renamed on a renamed tabled, we don't need to worry about removing
            # any lines, since they would be incorporated into the table change, place us after the rename.
            new_sql = "ALTER TABLE %s CHANGE %s %s %s;" % (table_name, old_column, new_column, data_info)
            input += new_sql
        else:
            new_sql = "ALTER TABLE %s CHANGE %s %s %s;" % (table_name, old_column, new_column, data_info)
            
            # Verify both first:
            if re.search("ALTER TABLE %s DROP COLUMN %s; # was .*" % (table_name, old_column), input) and re.search("ALTER TABLE %s ADD COLUMN %s.*;" %(table_name, new_column), input):
                input = re.sub("ALTER TABLE %s DROP COLUMN %s; # was .*" % (table_name, old_column), "", input, flags=re.IGNORECASE)
                input = re.sub("ALTER TABLE %s ADD COLUMN %s.*;" %(table_name, new_column), new_sql, input, flags=re.IGNORECASE)
        
    return input