def rebase_patches(without_git_rebase=True, directory=None): ### Ensure a clean/valid working environment ensure_clean_working_env(git_status=True, directory=directory) ### Make sure we need to actually call this # Get the current branch current_branch = get_current_branch(directory) if current_branch is None: error("Could not determine current branch.", exit=True) # Get the patches branch patches_branch = 'patches/' + current_branch # Get the current patches.conf config = get_patch_config(patches_branch, directory=directory) ### Execute the rebase if without_git_rebase: non_git_rebase(config['parent'], directory=directory) else: git_rebase(config['parent'], directory=directory) ### Update the patches information # Get the latest configs config = get_patch_config(patches_branch, directory) # Set the base to the current hash (before patches) current_branch_ = get_current_branch(directory) debug('Current branch: ' + current_branch_ or 'could not determine branch') config['base'] = get_commit_hash(current_branch_, directory) debug('New current commit hash after rebase: ' + config['base']) # Set the new upstream hash to the previous upstream hash config['previous'] = get_commit_hash(config['parent'], directory) debug('New parent commit hash after rebase: ' + config['previous']) # Clear the trimbase (it needs to be reapplied) config['trimbase'] = '' # Write the new configs set_patch_config(patches_branch, config, directory)
def rebase_patches(without_git_rebase=True, directory=None): ### Ensure a clean/valid working environment ret = ensure_clean_working_env(git_status=True, directory=directory) if ret != 0: return ret ### Make sure we need to actually call this # Get the current branch current_branch = get_current_branch(directory) # Get the patches branch patches_branch = "patches/" + current_branch # Get the current patches.conf config = get_patch_config(patches_branch, directory=directory) # Get the current upstream commit hash upstream_commit_hash = get_commit_hash(config["parent"], directory) # If the current upstream commit hash is the same as the stored one, noop if upstream_commit_hash == config["previous"]: debug( "Nothing to do: Current branch (" + current_branch + ")'s " "base commit hash is the same as the source branch (" + config["parent"] + ")'s commit hash." ) debug(" Did you forget to update the parent branch first?") debug( " Updating the parent branch can be done by calling " "'git-bloom-patch rebase' on it, or 'git-bloom-import-upsteam'" " if the parent branch is the upstream branch." ) return 0 else: debug( "rebase_patches: " + upstream_commit_hash + " == " + config["previous"] + ": " + str(upstream_commit_hash == config["previous"]) ) ### Execute the rebase if without_git_rebase: non_git_rebase(config["parent"], directory=directory) else: git_rebase(config["parent"], directory=directory) ### Update the patches information # Get the latest configs config = get_patch_config(patches_branch, directory) # Set the base to the current hash (before patches) config["base"] = get_commit_hash(current_branch, directory) # Set the new upstream hash to the previous upstream hash config["previous"] = get_commit_hash(config["parent"], directory) # Clear the trimbase (it needs to be reapplied) config["trimbase"] = "" # Write the new configs set_patch_config(patches_branch, config, directory) return 0
def post_rebase(self, destination): # Determine the current package being generated name = destination.split('/')[-1] distro = destination.split('/')[-2] # Retrieve the stackage stackage, kind = self.packages[name] # Ask to continue if interactive if self.interactive: if not maybe_continue('y'): error("Answered no to continue, aborting.") return code.ANSWERED_NO_TO_CONTINUE ### Start debian generation # Get time of day from dateutil import tz stamp = datetime.datetime.now(tz.tzlocal()) # Convert stackage to debian data data = self.convert_stackage_to_debian_data(stackage, kind) # Get apt_installer from rosdep from rosdep2.catkin_support import get_installer self.apt_installer = get_installer(APT_INSTALLER) # Create debians for each distro with inbranch(destination): self.generate_debian(data, stamp, distro) # Create the tag name for later self.tag_names[destination] = self.generate_tag_name(data) # Update the patch configs patches_branch = 'patches/' + destination config = get_patch_config(patches_branch) # Store it self.store_original_config(config, patches_branch) # Modify the base so import/export patch works config['base'] = get_commit_hash(get_current_branch()) # Set it set_patch_config(patches_branch, config)
def post_rebase(self, destination): name = destination.split('/')[-1] # Retrieve the package package = self.packages[name] # Handle differently if this is an rpm vs distro branch if destination in self.rpm_branches: info("Placing RPM template files into '{0}' branch." .format(destination)) # Then this is an rpm branch # Place the raw template files self.place_template_files() else: # This is a distro specific rpm branch # Determine the current package being generated distro = destination.split('/')[-2] # Create RPMs for each distro with inbranch(destination): data = self.generate_rpm(package, distro) # Create the tag name for later self.tag_names[destination] = self.generate_tag_name(data) # Update the patch configs patches_branch = 'patches/' + destination config = get_patch_config(patches_branch) # Store it self.store_original_config(config, patches_branch) # Modify the base so import/export patch works current_branch = get_current_branch() if current_branch is None: error("Could not determine current branch.", exit=True) config['base'] = get_commit_hash(current_branch) # Set it set_patch_config(patches_branch, config)
def post_rebase(self, destination): name = destination.split('/')[-1] # Retrieve the package package = self.packages[name] # Handle differently if this is an arch vs distro branch if destination in self.arch_branches: info("Placing Arch template files into '{0}' branch." .format(destination)) # Then this is an arch branch # Place the raw template files self.place_template_files() else: # This is a distro specific arch branch # Determine the current package being generated distro = destination.split('/')[-2] # Create Arch packages for each distro with inbranch(destination): # To fit Arch Linux philosophy a bit better, we move all the source files into a subdirectory. # Arch Linux doesn't support source distribution through a subdirectory; therefore we should ideally compress the sources or provide a URL. # At this point in the generator, it is tricky to get the release URL. Furthermore it wouldn't fit bloom's patch mechanism very well. # To work around, we copy the sources to the $srcdir at the beginning inside the prepare() function. temp_dir = mkdtemp(dir='.') for item in os.listdir("."): itemsrc = os.path.abspath(item) if os.path.basename(itemsrc) in ['.', '..', '.git', '.svn', 'arch',os.path.basename(temp_dir)]: continue itemdst = os.path.abspath(os.path.join(temp_dir,item)) execute_command('git mv ' + itemsrc + ' ' + itemdst) execute_command('git mv ' + temp_dir + ' ' + name) execute_command('git commit --amend --no-edit') # Then generate the PKGBUILD data = self.generate_arch(package, distro) # And finally move the PKGBUILD to the root directory of the package. for item in os.listdir("arch"): itemsrc = os.path.abspath(os.path.join("arch",item)) if os.path.basename(itemsrc) in ['.', '..', '.git', '.svn']: continue itemdst = os.path.abspath(item) execute_command('git mv ' + itemsrc + ' ' + itemdst) execute_command('git commit --amend --no-edit') # Create the tag name for later self.tag_names[destination] = self.generate_tag_name(data) # Update the patch configs patches_branch = 'patches/' + destination config = get_patch_config(patches_branch) # Store it self.store_original_config(config, patches_branch) # Modify the base so import/export patch works current_branch = get_current_branch() if current_branch is None: error("Could not determine current branch.", exit=True) config['base'] = get_commit_hash(current_branch) # Set it set_patch_config(patches_branch, config)
def post_rebase(self, destination): name = destination.split('/')[-1] # Retrieve the package package = self.packages[name] # Handle differently if this is a debian vs distro branch if destination in self.debian_branches: info("Placing debian template files into '{0}' branch.".format( destination)) # Then this is a debian branch # Place the raw template files self.place_template_files() else: # This is a distro specific debian branch # Determine the current package being generated distro = destination.split('/')[-2] # Create debians for each distro with inbranch(destination): data = self.generate_debian(package, distro) # Create the tag name for later self.tag_names[destination] = self.generate_tag_name(data) # Update the patch configs patches_branch = 'patches/' + destination config = get_patch_config(patches_branch) # Store it self.store_original_config(config, patches_branch) # Modify the base so import/export patch works current_branch = get_current_branch() if current_branch is None: error("Could not determine current branch.", exit=True) config['base'] = get_commit_hash(current_branch) # Set it set_patch_config(patches_branch, config)
def _trim(config, force, directory): debug("_trim(" + str(config) + ", " + str(force) + ", " + \ str(directory) + ")") if config['trimbase'] != '': warning("It looks like the trim operation has already been done, " "nested trimming is not supported.") if force: warning("Proceeding anyways because of '--force'") else: warning("If you would like to continue anyways use '--force'") return None config['trimbase'] = get_commit_hash(get_current_branch(directory)) tmp_dir = tempfile.mkdtemp() try: # Buckup trim sub directory git_root = get_root() sub_dir = os.path.join(git_root, config['trim']) storage = os.path.join(tmp_dir, config['trim']) shutil.copytree(sub_dir, storage) # Clear out the git repo execute_command('git rm -rf ./*', cwd=directory) # Copy the sub directory back for item in os.listdir(storage): src = os.path.join(storage, item) dst = os.path.join(git_root, item) if os.path.isdir(src): shutil.copytree(src, dst) else: shutil.copy(src, dst) # Stage execute_command('git add ./*', cwd=directory) # Commit cmd = 'git commit -m "Trimmed the branch to only the ' + \ config['trim'] + ' sub directory"' execute_command(cmd, cwd=directory) # Update the patch base to be this commit config['base'] = get_commit_hash(get_current_branch(directory)) finally: if os.path.exists(tmp_dir): shutil.rmtree(tmp_dir) return config
def remove_patches(directory=None): # Get the current branch current_branch = get_current_branch(directory) # Ensure the current branch is valid if current_branch is None: error("Could not determine current branch, are you in a git repo?") return 1 # Construct the patches branch patches_branch = 'patches/' + current_branch try: # See if the patches branch exists if branch_exists(patches_branch, False, directory=directory): if not branch_exists(patches_branch, True, directory=directory): track_branches(patches_branch, directory) else: error("No patches branch (" + patches_branch + ") found, cannot " "remove patches.") return 1 # Get the parent branch from the patches branch config = get_patch_config(patches_branch, directory=directory) parent, spec = config['parent'], config['base'] if None in [parent, spec]: error("Could not retrieve patches info.") return 1 debug("Removing patches from " + current_branch + " back to base " "commit " + spec) # Reset this branch using git revert --no-edit spec current_commit = get_commit_hash(current_branch, directory) command_spec = spec + '..' + current_commit execute_command( 'git revert --no-edit -Xtheirs ' + command_spec, cwd=directory ) # Update the base config['base'] = get_commit_hash(current_branch, directory) set_patch_config(patches_branch, config, directory=directory) finally: if current_branch: checkout(current_branch, directory=directory) return 0
def remove_patches(directory=None): # Get the current branch current_branch = get_current_branch(directory) if current_branch is None: error("Could not determine current branch.", exit=True) # Ensure the current branch is valid if current_branch is None: error("Could not determine current branch, are you in a git repo?", exit=True) # Construct the patches branch patches_branch = 'patches/' + current_branch try: # See if the patches branch exists if branch_exists(patches_branch, False, directory=directory): if not branch_exists(patches_branch, True, directory=directory): track_branches(patches_branch, directory) else: error("No patches branch (" + patches_branch + ") found, cannot " "remove patches.", exit=True) # Get the parent branch from the patches branch config = get_patch_config(patches_branch, directory=directory) parent, spec = config['parent'], config['base'] if None in [parent, spec]: error("Could not retrieve patches info.", exit=True) debug("Removing patches from " + current_branch + " back to base " "commit " + spec) # Reset this branch using git revert --no-edit spec current_commit = get_commit_hash(current_branch, directory) command_spec = spec + '..' + current_commit execute_command('git revert --no-edit -Xtheirs ' + command_spec, cwd=directory) # Update the base config['base'] = get_commit_hash(current_branch, directory) set_patch_config(patches_branch, config, directory=directory) finally: if current_branch: checkout(current_branch, directory=directory)
def _undo(config, directory): debug("_undo(" + str(config) + ", " + str(directory) + ")") # TODO: handle repo with changes # TODO: handle repo with patches applied if config['trimbase'] == '': debug("Branch has not been trimmed previously, undo not required.") return None # Reset with git-revert cmt = get_commit_hash(get_current_branch(directory), directory) cmd = 'git revert --no-edit -Xtheirs ' + config['trimbase'] + '..' + cmt execute_command(cmd, cwd=directory) # Unset the trimbase config['trimbase'] = '' return config
def import_patches(directory=None): # Get current branch current_branch = get_current_branch(directory) # Construct the patches branch name patches_branch = 'patches/' + current_branch # Ensure the patches branch exists and is tracked if branch_exists(patches_branch, False, directory=directory): if not branch_exists(patches_branch, True, directory=directory): track_branches(patches_branch, directory) else: error("The patches branch ({0}) does not ".format(patches_branch) + \ "exist, did you use git-bloom-branch?") return code.BRANCH_DOES_NOT_EXIST # Create a swap space tmp_dir = tempfile.mkdtemp() try: # Get parent branch and base commit from patches branch config = get_patch_config(patches_branch, directory) parent_branch, commit = config['parent'], config['base'] if commit != get_commit_hash(current_branch, directory): warning("The current commit is not the same as the most recent " "rebase commit. This might mean that you have committed " "since the last time you did 'git-bloom-patch export'.") return code.PATCHES_NOT_EXPORTED # Checkout to the patches branch checkout(patches_branch, directory=directory) # Copy the patches to a temp location patches = list_patches(directory) if len(patches) == 0: debug("No patches in the patches branch, nothing to do") return code.NOTHING_TO_DO tmp_dir_patches = [] for patch in patches: tmp_dir_patches.append(os.path.join(tmp_dir, patch)) if directory is not None: patch = os.path.join(directory, patch) shutil.copy(patch, tmp_dir) # Now checkout back to the original branch and import them checkout(current_branch, directory=directory) cmd = 'git am {0}*.patch'.format(tmp_dir + os.sep) execute_command(cmd, cwd=directory) # Notify the user info("Applied {0} patches".format(len(patches))) finally: if current_branch: checkout(current_branch, directory=directory) if os.path.exists(tmp_dir): shutil.rmtree(tmp_dir) return 0
def post_rebase(self, destination): name = destination.split('/')[-1] # Retrieve the stackage stackage, kind = self.packages[name] # Handle differently if this is a debian vs distro branch if destination in self.debian_branches: info("Placing debian template files into '{0}' branch.".format(destination)) # Check for valid CMakeLists.txt if a metapackage if kind == 'package' and is_metapackage(stackage): check_metapackage_for_valid_cmake(name) # Then this is a debian branch # Place the raw template files self.place_tempalte_files() else: # Check for valid CMakeLists.txt if a metapackage if kind == 'package' and is_metapackage(stackage): check_metapackage_for_valid_cmake(name) # This is a distro specific debian branch # Determine the current package being generated distro = destination.split('/')[-2] ### Start debian generation # Get time of day from dateutil import tz stamp = datetime.datetime.now(tz.tzlocal()) # Convert stackage to debian data data = self.convert_stackage_to_debian_data(stackage, kind) # Get apt_installer from rosdep from rosdep2.catkin_support import get_installer self.apt_installer = get_installer(APT_INSTALLER) # Create debians for each distro with inbranch(destination): self.generate_debian(data, stamp, distro) # Create the tag name for later self.tag_names[destination] = self.generate_tag_name(data) # Update the patch configs patches_branch = 'patches/' + destination config = get_patch_config(patches_branch) # Store it self.store_original_config(config, patches_branch) # Modify the base so import/export patch works current_branch = get_current_branch() if current_branch is None: error("Could not determine current branch.", exit=True) config['base'] = get_commit_hash(current_branch) # Set it set_patch_config(patches_branch, config)
def _undo(config, directory): debug("_undo(" + str(config) + ", " + str(directory) + ")") # TODO: handle repo with changes # TODO: handle repo with patches applied if config["trimbase"] == "": debug("Branch has not been trimmed previously, undo not required.") return None # Reset with git-revert current_branch = get_current_branch(directory) if current_branch is None: error("Could not determine current branch.", exit=True) cmt = get_commit_hash(current_branch, directory) cmd = "git revert --no-edit -Xtheirs " + config["trimbase"] + ".." + cmt execute_command(cmd, cwd=directory) # Unset the trimbase config["trimbase"] = "" return config
def _undo(config, directory): debug("_undo(" + str(config) + ", " + str(directory) + ")") # TODO: handle repo with changes # TODO: handle repo with patches applied if config['trimbase'] == '': debug("Branch has not been trimmed previously, undo not required.") return None # Reset with git-revert current_branch = get_current_branch(directory) if current_branch is None: error("Could not determine current branch.", exit=True) cmt = get_commit_hash(current_branch, directory) cmd = 'git revert --no-edit -Xtheirs ' + config['trimbase'] + '..' + cmt execute_command(cmd, cwd=directory) # Unset the trimbase config['trimbase'] = '' return config
def main(sysargs=None): parser = get_argument_parser() parser = add_global_arguments(parser) args = parser.parse_args(sysargs) handle_global_arguments(args) # Check that the current directory is a serviceable git/bloom repo ret = ensure_clean_working_env() if ret != 0: parser.print_usage() return ret # Get the current git branch current_branch = get_current_branch() # Create a working temp directory tmp_dir = create_temporary_directory() cwd = os.getcwd() try: # Get off upstream branch if current_branch == 'upstream': checkout(get_commit_hash('upstream'), directory=cwd) retcode = import_upstream(cwd, tmp_dir, args) # Done! retcode = retcode if retcode is not None else 0 if retcode == 0: info("I'm happy. You should be too.") return retcode finally: # Change back to the original cwd os.chdir(cwd) # Clean up shutil.rmtree(tmp_dir) if current_branch and branch_exists(current_branch, True, cwd): checkout(current_branch, directory=cwd)
def execute_branch(src, dst, interactive, directory=None): """ Changes to the destination branch, creates branch and patches/branch if they do not exist. If the dst branch does not exist yet, then it is created by branching the current working branch or the specified SRC_BRANCH. If the patches/dst branch branch does not exist yet then it is created. If the branches are created successful, then the working branch will be set to the dst branch, otherwise the working branch will remain unchanged. :param src: source branch from which to copy :param dst: destination branch :param interactive: if True actions are summarized before committing :param directory: directory in which to preform this action :raises: subprocess.CalledProcessError if any git calls fail """ # Determine if the srouce branch exists if src is None: error("No source specified and/or not a branch currently", exit=True) if branch_exists(src, local_only=False, directory=directory): if not branch_exists(src, local_only=True, directory=directory): debug("Tracking source branch: {0}".format(src)) track_branches(src, directory) elif tag_exists(src): pass else: error("Specified source branch does not exist: {0}".format(src), exit=True) # Determine if the destination branch needs to be created create_dst_branch = False if branch_exists(dst, local_only=False, directory=directory): if not branch_exists(dst, local_only=True, directory=directory): debug("Tracking destination branch: {0}".format(dst)) track_branches(dst, directory) else: create_dst_branch = True # Determine if the destination patches branch needs to be created create_dst_patches_branch = False dst_patches = "patches/" + dst if branch_exists(dst_patches, False, directory=directory): if not branch_exists(dst_patches, True, directory=directory): track_branches(dst_patches, directory) else: create_dst_patches_branch = True # Summarize if interactive: info("Summary of changes:") if create_dst_branch: info( " " * 22 + "- The specified destination branch, " + ansi("boldon") + dst + ansi("reset") + ", does not exist; it will be created from the source " "branch " + ansi("boldon") + src + ansi("reset") ) if create_dst_patches_branch: info( " " * 22 + "- The destination patches branch, " + ansi("boldon") + dst_patches + ansi("reset") + ", does not exist; it will be created" ) info(" " * 22 + "- The working branch will be set to " + ansi("boldon") + dst + ansi("reset")) if not maybe_continue(): error("Answered no to continue, aborting.", exit=True) # Make changes to the layout current_branch = get_current_branch(directory) try: # Change to the src branch checkout(src, directory=directory) # Create the dst branch if needed if create_dst_branch: create_branch(dst, changeto=True, directory=directory) else: checkout(dst, directory=directory) # Create the dst patches branch if needed if create_dst_patches_branch: create_branch(dst_patches, orphaned=True, directory=directory) # Create the starting config data if it does not exist patches_ls = ls_tree(dst_patches, directory=directory) if "patches.conf" not in patches_ls: # Patches config not setup, set it up config = { "parent": src, "previous": "", "base": get_commit_hash(dst, directory=directory), "trim": "", "trimbase": "", } set_patch_config(dst_patches, config, directory=directory) else: config = get_patch_config(dst_patches, directory=directory) if config["parent"] != src: warning("Updated parent to '{0}' from '{1}'".format(src, config["parent"])) config["parent"] = src config["base"] = get_commit_hash(dst, directory=directory) set_patch_config(dst_patches, config, directory=directory) # Command successful, do not switch back to previous branch current_branch = None finally: if current_branch is not None: checkout(current_branch, directory=directory)
def import_patches(directory=None): # Get current branch current_branch = get_current_branch(directory) if current_branch is None: error("Could not determine current branch.", exit=True) # Construct the patches branch name patches_branch = 'patches/' + current_branch # Ensure the patches branch exists and is tracked if branch_exists(patches_branch, False, directory=directory): if not branch_exists(patches_branch, True, directory=directory): track_branches(patches_branch, directory) else: error("The patches branch ({0}) does not ".format(patches_branch) + "exist, did you use git-bloom-branch?", exit=True) # Create a swap space tmp_dir = tempfile.mkdtemp() try: # Get parent branch and base commit from patches branch config = get_patch_config(patches_branch, directory) parent_branch, commit = config['parent'], config['base'] if commit != get_commit_hash(current_branch, directory): debug( "commit != get_commit_hash(current_branch, directory)" ) debug( "{0} != get_commit_hash({1}, {2}) != {3}".format( commit, current_branch, directory, get_commit_hash(current_branch, directory) ) ) os.system('git log') warning( "The current commit is not the same as the most recent " "rebase commit." ) warning( "This might mean that you have committed since the last " "time you did:" ) warning( " 'git-bloom-patch rebase' or 'git-bloom-patch remove'" ) warning( "Make sure you export any commits you want to save first:" ) warning(" 'git-bloom-patch export'") error("Patches not exported", exit=True) # Checkout to the patches branch checkout(patches_branch, directory=directory) # Copy the patches to a temp location patches = list_patches(directory) if len(patches) == 0: debug("No patches in the patches branch, nothing to do") return -1 # Indicates that nothing was done tmp_dir_patches = [] for patch in patches: tmp_dir_patches.append(os.path.join(tmp_dir, patch)) if directory is not None: patch = os.path.join(directory, patch) shutil.copy(patch, tmp_dir) # Now checkout back to the original branch and import them checkout(current_branch, directory=directory) try: cmd = 'git am {0}*.patch'.format(tmp_dir + os.sep) execute_command(cmd, cwd=directory) except subprocess.CalledProcessError as e: warning("Failed to apply one or more patches for the " "'{0}' branch.".format(str(e))) info('', use_prefix=False) info('', use_prefix=False) info(">>> Resolve any conflicts and when you have resolved this " "problem run 'git am --resolved' and then exit the " "shell using 'exit 0'. <<<", use_prefix=False) info(" To abort use 'exit 1'", use_prefix=False) if 'bash' in os.environ['SHELL']: ret = subprocess.call([ "/bin/bash", "-l", "-c", """\ /bin/bash --rcfile <(echo "if [ -f /etc/bashrc ]; then source /etc/bashrc; fi; \ if [ -f ~/.bashrc ]; then source ~/.bashrc; fi;PS1='(bloom)$PS1'") -i""" ]) else: ret = subprocess.call("$SHELL", shell=True) if ret != 0: error("User failed to resolve patch conflicts, exiting.") sys.exit("'git-bloom-patch import' aborted.") info("User reports that conflicts have been resolved, continuing.") # Notify the user info("Applied {0} patches".format(len(patches))) finally: if current_branch: checkout(current_branch, directory=directory) if os.path.exists(tmp_dir): shutil.rmtree(tmp_dir)
def _trim(config, force, directory): debug("_trim(" + str(config) + ", " + str(force) + ", " + \ str(directory) + ")") if config['trimbase'] != '': warning("It looks like the trim operation has already been done, " "nested trimming is not supported.") if force: warning("Proceeding anyways because of '--force'") else: warning("If you would like to continue anyways use '--force'") return None config['trimbase'] = get_commit_hash(get_current_branch(directory)) tmp_dir = tempfile.mkdtemp() try: # Buckup trim sub directory git_root = get_root() sub_dir = os.path.join(git_root, config['trim']) storage = os.path.join(tmp_dir, config['trim']) shutil.copytree(sub_dir, storage) # Clear out the git repo execute_command('git rm -rf ./*', cwd=directory) # Collect .* files (excluding .git) dot_items = [] for item in os.listdir(git_root): if item in ['.git', '..', '.']: continue if item.startswith('.'): dot_items.append(item) # Remove and .* files missed by 'git rm -rf *' if len(dot_items) > 0: execute_command('git rm -rf ' + ' '.join(dot_items), cwd=directory) # Clear out any untracked files execute_command('git clean -fdx', cwd=directory) # Copy the sub directory back for item in os.listdir(storage): src = os.path.join(storage, item) dst = os.path.join(git_root, item) if os.path.isdir(src): shutil.copytree(src, dst) else: shutil.copy(src, dst) # Stage execute_command('git add ./*', cwd=directory) # Collect .* files dot_items = [] for item in os.listdir(git_root): if item in ['.git', '..', '.']: continue if item.startswith('.'): dot_items.append(item) # Add any .* files missed by 'git add ./*' if len(dot_items) > 0: execute_command('git add ' + ' '.join(dot_items), cwd=directory) # Remove any straggling untracked files execute_command('git clean -dXf', cwd=directory) # Commit cmd = 'git commit -m "Trimmed the branch to only the ' + \ config['trim'] + ' sub directory"' execute_command(cmd, cwd=directory) # Update the patch base to be this commit config['base'] = get_commit_hash(get_current_branch(directory)) finally: if os.path.exists(tmp_dir): shutil.rmtree(tmp_dir) return config
def _trim(config, force, directory): debug("_trim(" + str(config) + ", " + str(force) + ", " + str(directory) + ")") if config['trimbase'] != '': warning("It looks like the trim operation has already been done, " "nested trimming is not supported.") if force: warning("Proceeding anyways because of '--force'") else: warning("If you would like to continue anyways use '--force'") return None current_branch = get_current_branch(directory) if current_branch is None: error("Could not determine current branch.", exit=True) config['trimbase'] = get_commit_hash(current_branch) tmp_dir = tempfile.mkdtemp() try: # Buckup trim sub directory git_root = get_root() sub_dir = os.path.join(git_root, config['trim']) storage = os.path.join(tmp_dir, config['trim']) shutil.copytree(sub_dir, storage) # Clear out any untracked files execute_command('git clean -fdx', cwd=directory) # Collect al files (excluding .git) items = [] for item in os.listdir(git_root): if item in ['.git', '..', '.']: continue items.append(item) # Remove and .* files missed by 'git rm -rf *' if len(items) > 0: execute_command('git rm -rf ' + ' '.join(items), cwd=directory) # Copy the sub directory back for item in os.listdir(storage): src = os.path.join(storage, item) dst = os.path.join(git_root, item) if os.path.isdir(src): shutil.copytree(src, dst) else: shutil.copy(src, dst) # Stage execute_command('git add ./*', cwd=directory) # Collect .* files dot_items = [] for item in os.listdir(git_root): if item in ['.git', '..', '.']: continue if item.startswith('.'): dot_items.append(item) # Add any .* files missed by 'git add ./*' if len(dot_items) > 0: execute_command('git add ' + ' '.join(dot_items), cwd=directory) # Remove any straggling untracked files execute_command('git clean -dXf', cwd=directory) # Commit cmd = 'git commit -m "Trimmed the branch to only the ' + \ config['trim'] + ' sub directory"' execute_command(cmd, cwd=directory) # Update the patch base to be this commit current_branch = get_current_branch(directory) if current_branch is None: error("Could not determine current branch.", exit=True) config['base'] = get_commit_hash(current_branch) finally: if os.path.exists(tmp_dir): shutil.rmtree(tmp_dir) return config
def _trim(config, force, directory): debug("_trim(" + str(config) + ", " + str(force) + ", " + str(directory) + ")") if config["trimbase"] != "": warning("It looks like the trim operation has already been done, " "nested trimming is not supported.") if force: warning("Proceeding anyways because of '--force'") else: warning("If you would like to continue anyways use '--force'") return None current_branch = get_current_branch(directory) if current_branch is None: error("Could not determine current branch.", exit=True) config["trimbase"] = get_commit_hash(current_branch) tmp_dir = tempfile.mkdtemp() try: # Buckup trim sub directory git_root = get_root() sub_dir = os.path.join(git_root, config["trim"]) storage = os.path.join(tmp_dir, config["trim"]) shutil.copytree(sub_dir, storage) # Collect al files (excluding .git) items = [] for item in os.listdir(git_root): if item in [".git", "..", "."]: continue items.append(item) # Remove and .* files missed by 'git rm -rf *' if len(items) > 0: execute_command("git rm -rf " + " ".join(items), cwd=directory) # Clear out any untracked files execute_command("git clean -fdx", cwd=directory) # Copy the sub directory back for item in os.listdir(storage): src = os.path.join(storage, item) dst = os.path.join(git_root, item) if os.path.isdir(src): shutil.copytree(src, dst) else: shutil.copy(src, dst) # Stage execute_command("git add ./*", cwd=directory) # Collect .* files dot_items = [] for item in os.listdir(git_root): if item in [".git", "..", "."]: continue if item.startswith("."): dot_items.append(item) # Add any .* files missed by 'git add ./*' if len(dot_items) > 0: execute_command("git add " + " ".join(dot_items), cwd=directory) # Remove any straggling untracked files execute_command("git clean -dXf", cwd=directory) # Commit cmd = 'git commit -m "Trimmed the branch to only the ' + config["trim"] + ' sub directory"' execute_command(cmd, cwd=directory) # Update the patch base to be this commit current_branch = get_current_branch(directory) if current_branch is None: error("Could not determine current branch.", exit=True) config["base"] = get_commit_hash(current_branch) finally: if os.path.exists(tmp_dir): shutil.rmtree(tmp_dir) return config
def import_upstream(cwd, tmp_dir, args): # Ensure the bloom and upstream branches are tracked locally track_branches(['bloom', 'upstream']) # Create a clone of the bloom_repo to help isolate the activity bloom_repo_clone_dir = os.path.join(tmp_dir, 'bloom_clone') os.makedirs(bloom_repo_clone_dir) os.chdir(bloom_repo_clone_dir) bloom_repo = VcsClient('git', bloom_repo_clone_dir) bloom_repo.checkout('file://{0}'.format(cwd)) # Ensure the bloom and upstream branches are tracked from the original track_branches(['bloom', 'upstream']) ### Fetch the upstream tag upstream_repo = None upstream_repo_dir = os.path.join(tmp_dir, 'upstream_repo') # If explicit svn url just export and git-import-orig if args.explicit_svn_url is not None: if args.upstream_version is None: error("'--explicit-svn-version' must be specified with " "'--explicit-svn-url'") return 1 info("Checking out upstream at version " + ansi('boldon') + \ str(args.upstream_version) + ansi('reset') + \ " from repository at " + ansi('boldon') + \ str(args.explicit_svn_url) + ansi('reset')) upstream_repo = VcsClient('svn', upstream_repo_dir) retcode = try_vcstools_checkout(upstream_repo, args.explicit_svn_url) if retcode != 0: return retcode meta = { 'name': None, 'version': args.upstream_version, 'type': 'manual' } # Else fetching from bloom configs else: # Check for a bloom branch check_for_bloom() # Parse the bloom config file upstream_url, upstream_type, upstream_branch = parse_bloom_conf() # If the upstream_tag is specified, don't search just fetch upstream_repo = VcsClient(upstream_type, upstream_repo_dir) if args.upstream_tag is not None: warning("Using specified upstream tag '" + args.upstream_tag + "'") if upstream_type == 'svn': upstream_url += '/tags/' + args.upstream_tag upstream_tag = '' else: upstream_tag = args.upstream_tag retcode = try_vcstools_checkout(upstream_repo, upstream_url, upstream_tag) if retcode != 0: return retcode meta = { 'name': None, 'version': args.upstream_tag, 'type': 'manual' } # We have to search for the upstream tag else: if args.upstream_devel is not None: warning("Overriding the bloom.conf upstream branch with " + \ args.upstream_devel) devel_branch = args.upstream_devel else: devel_branch = upstream_branch meta = auto_upstream_checkout(upstream_repo, upstream_url, devel_branch) if type(meta) not in [dict] and meta != 0: return meta ### Export the repository version = args.upstream_version if args.upstream_version is not None \ else meta['version'] # Export the repository to a tar ball tarball_prefix = 'upstream-' + str(version) info('Exporting version {0}'.format(version)) tarball_path = os.path.join(tmp_dir, tarball_prefix) if upstream_repo.get_vcs_type_name() == 'svn': upstream_repo.export_repository('', tarball_path) else: if args.upstream_tag is not None: upstream_repo.export_repository(args.upstream_tag, tarball_path) else: upstream_repo.export_repository(version, tarball_path) # Get the gbp version elements from either the last tag or the default last_tag = get_last_tag_by_date() if last_tag == '': gbp_major, gbp_minor, gbp_patch = segment_version(version) else: gbp_major, gbp_minor, gbp_patch = \ get_versions_from_upstream_tag(last_tag) info("The latest upstream tag in the release repository is " + ansi('boldon') + last_tag + ansi('reset')) # Ensure the new version is greater than the last tag last_tag_version = '.'.join([gbp_major, gbp_minor, gbp_patch]) if parse_version(version) < parse_version(last_tag_version): warning("""\ Version discrepancy: The upstream version, {0}, is not newer than the previous \ release version, {1}. """.format(version, last_tag_version)) if parse_version(version) <= parse_version(last_tag_version): if args.replace: if not gbp.has_replace(): error("The '--replace' flag is not supported on this " "version of git-buildpackage.") return 1 # Remove the conflicting tag first warning("""\ The upstream version, {0}, is equal to or less than a previous \ import version. Removing conflicting tag before continuing \ because the '--replace' options was specified.\ """.format(version)) execute_command('git tag -d {0}'.format('upstream/' + version)) execute_command('git push origin :refs/tags/' '{0}'.format('upstream/' + version)) else: warning("""\ The upstream version, {0}, is equal to a previous import version. \ git-buildpackage will fail, if you want to replace the existing \ upstream import use the '--replace' option.\ """.format(version)) # Look for upstream branch if not branch_exists('upstream', local_only=True): create_branch('upstream', orphaned=True, changeto=True) # Go to the master branch bloom_repo.update(get_commit_hash('upstream')) # Detect if git-import-orig is installed tarball_path += '.tar.gz' if gbp.import_orig(tarball_path, args.interactive) != 0: return 1 # Push changes back to the original bloom repo execute_command('git push --all -f') execute_command('git push --tags')
def execute_branch(src, dst, interactive, directory=None): """ Changes to the destination branch, creates branch and patches/branch if they do not exist. If the dst branch does not exist yet, then it is created by branching the current working branch or the specified SRC_BRANCH. If the patches/dst branch branch does not exist yet then it is created. If the branches are created successful, then the working branch will be set to the dst branch, otherwise the working branch will remain unchanged. :param src: source branch from which to copy :param dst: destination branch :param interactive: if True actions are summarized before committing :param directory: directory in which to preform this action :raises: subprocess.CalledProcessError if any git calls fail """ # Determine if the srouce branch exists if src is None: error("No source specified and/or not a branch currently", exit=True) if branch_exists(src, local_only=False, directory=directory): if not branch_exists(src, local_only=True, directory=directory): debug("Tracking source branch: {0}".format(src)) track_branches(src, directory) elif tag_exists(src): pass else: error("Specified source branch does not exist: {0}".format(src), exit=True) # Determine if the destination branch needs to be created create_dst_branch = False if branch_exists(dst, local_only=False, directory=directory): if not branch_exists(dst, local_only=True, directory=directory): debug("Tracking destination branch: {0}".format(dst)) track_branches(dst, directory) else: create_dst_branch = True # Determine if the destination patches branch needs to be created create_dst_patches_branch = False dst_patches = 'patches/' + dst if branch_exists(dst_patches, False, directory=directory): if not branch_exists(dst_patches, True, directory=directory): track_branches(dst_patches, directory) else: create_dst_patches_branch = True # Summarize if interactive: info("Summary of changes:") if create_dst_branch: info(" " * 22 + "- The specified destination branch, " + ansi('boldon') + dst + ansi('reset') + ", does not exist; it will be created from the source " "branch " + ansi('boldon') + src + ansi('reset')) if create_dst_patches_branch: info(" " * 22 + "- The destination patches branch, " + ansi('boldon') + dst_patches + ansi('reset') + ", does not exist; it will be created") info(" " * 22 + "- The working branch will be set to " + ansi('boldon') + dst + ansi('reset')) if not maybe_continue(): error("Answered no to continue, aborting.", exit=True) # Make changes to the layout current_branch = get_current_branch(directory) try: # Change to the src branch checkout(src, directory=directory) # Create the dst branch if needed if create_dst_branch: create_branch(dst, changeto=True, directory=directory) else: checkout(dst, directory=directory) # Create the dst patches branch if needed if create_dst_patches_branch: create_branch(dst_patches, orphaned=True, directory=directory) # Create the starting config data if it does not exist patches_ls = ls_tree(dst_patches, directory=directory) if 'patches.conf' not in patches_ls: # Patches config not setup, set it up config = { 'parent': src, 'previous': '', 'base': get_commit_hash(dst, directory=directory), 'trim': '', 'trimbase': '' } set_patch_config(dst_patches, config, directory=directory) else: config = get_patch_config(dst_patches, directory=directory) if config['parent'] != src: warning("Updated parent to '{0}' from '{1}'".format( src, config['parent'])) config['parent'] = src config['base'] = get_commit_hash(dst, directory=directory) set_patch_config(dst_patches, config, directory=directory) # Command successful, do not switch back to previous branch current_branch = None finally: if current_branch is not None: checkout(current_branch, directory=directory)
def import_patches(directory=None): # Get current branch current_branch = get_current_branch(directory) if current_branch is None: error("Could not determine current branch.", exit=True) # Construct the patches branch name patches_branch = 'patches/' + current_branch # Ensure the patches branch exists and is tracked if branch_exists(patches_branch, False, directory=directory): if not branch_exists(patches_branch, True, directory=directory): track_branches(patches_branch, directory) else: error("The patches branch ({0}) does not ".format(patches_branch) + "exist, did you use git-bloom-branch?", exit=True) # Create a swap space tmp_dir = tempfile.mkdtemp() try: # Get parent branch and base commit from patches branch config = get_patch_config(patches_branch, directory) parent_branch, commit = config['parent'], config['base'] if commit != get_commit_hash(current_branch, directory): debug( "commit != get_commit_hash(current_branch, directory)" ) debug( "{0} != get_commit_hash({1}, {2}) != {3}".format( commit, current_branch, directory, get_commit_hash(current_branch, directory) ) ) os.system('git log') warning( "The current commit is not the same as the most recent " "rebase commit." ) warning( "This might mean that you have committed since the last " "time you did:" ) warning( " 'git-bloom-patch rebase' or 'git-bloom-patch remove'" ) warning( "Make sure you export any commits you want to save first:" ) warning(" 'git-bloom-patch export'") error("Patches not exported", exit=True) # Checkout to the patches branch checkout(patches_branch, directory=directory) # Copy the patches to a temp location patches = list_patches(directory) if len(patches) == 0: debug("No patches in the patches branch, nothing to do") return -1 # Indicates that nothing was done tmp_dir_patches = [] for patch in patches: tmp_dir_patches.append(os.path.join(tmp_dir, patch)) if directory is not None: patch = os.path.join(directory, patch) shutil.copy(patch, tmp_dir) # Now checkout back to the original branch and import them checkout(current_branch, directory=directory) try: cmd = 'git am {0}*.patch'.format(tmp_dir + os.sep) execute_command(cmd, cwd=directory) except subprocess.CalledProcessError as e: warning("Failed to apply one or more patches for the " "'{0}' branch.".format(str(e))) print() print() print(">>> Resolve any conflicts and when you have resolved this " "problem run 'git am --resolved' and then exit the " "shell using 'exit 0'. <<<") print(" To abort use 'exit 1'") if 'bash' in os.environ['SHELL']: ret = subprocess.call([ "/bin/bash", "-l", "-c", """\ /bin/bash --rcfile <(echo "if [ -f /etc/bashrc ]; then source /etc/bashrc; fi; \ if [ -f ~/.bashrc ]; then source ~/.bashrc; fi;PS1='(bloom)$PS1'") -i""" ]) else: ret = subprocess.call("$SHELL", shell=True) if ret != 0: error("User failed to resolve patch conflicts, exiting.") sys.exit("'git-bloom-patch import' aborted.") info("User reports that conflicts have been resolved, continuing.") # Notify the user info("Applied {0} patches".format(len(patches))) finally: if current_branch: checkout(current_branch, directory=directory) if os.path.exists(tmp_dir): shutil.rmtree(tmp_dir)