def conflict_file(left_dir, left_distro, right_dir, right_distro, dest_dir, filename): """Copy both files as conflicts of each other.""" left_src = "%s/%s" % (left_dir, filename) right_src = "%s/%s" % (right_dir, filename) dest = "%s/%s" % (dest_dir, filename) logger.debug("Conflicted: %s", filename) tree.remove(dest) # We need to take care here .. if one of the items involved in a # conflict is a directory then it might have children and we don't want # to throw an error later. # # We get round this by making the directory a symlink to the conflicted # one. # # Fortunately this is so rare it may never happen! if tree.exists(left_src): tree.copyfile(left_src, "%s.%s" % (dest, left_distro.upper())) if os.path.isdir(left_src): os.symlink("%s.%s" % (os.path.basename(dest), left_distro.upper()), dest) if tree.exists(right_src): tree.copyfile(right_src, "%s.%s" % (dest, right_distro.upper())) if os.path.isdir(right_src): os.symlink("%s.%s" % (os.path.basename(dest), right_distro.upper()), dest)
def merge_file(left_dir, left_name, left_distro, base_dir, right_dir, right_name, right_distro, merged_dir, filename): """Merge a file using diff3.""" dest = "%s/%s" % (merged_dir, filename) ensure(dest) with open(dest, "w") as output: status = shell.run(("diff3", "-E", "-m", "-L", left_name, "%s/%s" % (left_dir, filename), "-L", "BASE", "%s/%s" % (base_dir, filename), "-L", right_name, "%s/%s" % (right_dir, filename)), stdout=output, okstatus=(0, 1, 2)) if status != 0: if not tree.exists(dest) or os.stat(dest).st_size == 0: # Probably binary if same_file(os.stat("%s/%s" % (left_dir, filename)), left_dir, os.stat("%s/%s" % (right_dir, filename)), right_dir, filename): logging.debug("binary files are the same: %s", filename) tree.copyfile("%s/%s" % (left_dir, filename), "%s/%s" % (merged_dir, filename)) elif same_file(os.stat("%s/%s" % (base_dir, filename)), base_dir, os.stat("%s/%s" % (left_dir, filename)), left_dir, filename): logging.debug("preserving binary change in %s: %s", right_distro, filename) tree.copyfile("%s/%s" % (right_dir, filename), "%s/%s" % (merged_dir, filename)) elif same_file(os.stat("%s/%s" % (base_dir, filename)), base_dir, os.stat("%s/%s" % (right_dir, filename)), right_dir, filename): logging.debug("preserving binary change in %s: %s", left_distro, filename) tree.copyfile("%s/%s" % (left_dir, filename), "%s/%s" % (merged_dir, filename)) else: logging.debug("binary file conflict: %s", filename) conflict_file(left_dir, left_distro, right_dir, right_distro, merged_dir, filename) return True else: logging.debug("Conflict in %s", filename) return True else: return False
def merge_file(left_dir, left_name, left_distro, base_dir, right_dir, right_name, right_distro, merged_dir, filename): """Merge a file using diff3.""" dest = "%s/%s" % (merged_dir, filename) tree.ensure(dest) with open(dest, "w") as output: status = shell.run(("diff3", "-E", "-m", "-L", left_name, "%s/%s" % (left_dir, filename), "-L", "BASE", "%s/%s" % (base_dir, filename), "-L", right_name, "%s/%s" % (right_dir, filename)), stdout=output, okstatus=(0,1,2)) if status != 0: if not tree.exists(dest) or os.stat(dest).st_size == 0: # Probably binary if same_file(os.stat("%s/%s" % (left_dir, filename)), left_dir, os.stat("%s/%s" % (right_dir, filename)), right_dir, filename): logger.debug("binary files are the same: %s", filename) tree.copyfile("%s/%s" % (left_dir, filename), "%s/%s" % (merged_dir, filename)) elif same_file(os.stat("%s/%s" % (base_dir, filename)), base_dir, os.stat("%s/%s" % (left_dir, filename)), left_dir, filename): logger.debug("preserving binary change in %s: %s", right_distro, filename) tree.copyfile("%s/%s" % (right_dir, filename), "%s/%s" % (merged_dir, filename)) elif same_file(os.stat("%s/%s" % (base_dir, filename)), base_dir, os.stat("%s/%s" % (right_dir, filename)), right_dir, filename): logger.debug("preserving binary change in %s: %s", left_distro, filename) tree.copyfile("%s/%s" % (left_dir, filename), "%s/%s" % (merged_dir, filename)) else: logger.debug("binary file conflict: %s", filename) conflict_file(left_dir, left_distro, right_dir, right_distro, merged_dir, filename) return True else: logger.debug("Conflict in %s", filename) return True else: return False
def do_merge(left_dir, left, base_dir, right_dir, right, merged_dir): """Do the heavy lifting of comparing and merging.""" logger.debug("Producing merge in %s", tree.subdir(ROOT, merged_dir)) conflicts = [] po_files = [] left_name = left.package.name left_distro = left.package.distro.name right_name = right.package.name right_distro = right.package.distro.name # See what format each is and whether they're both quilt left_format = left.getSources()["Format"] right_format = right.getSources()["Format"] both_formats_quilt = left_format == right_format == "3.0 (quilt)" if both_formats_quilt: logger.debug("Only merging debian directory since both " "formats 3.0 (quilt)") # Look for files in the base and merge them if they're in both new # files (removed files get removed) for filename in tree.walk(base_dir): # If both packages are 3.0 (quilt), ignore everything except the # debian directory if both_formats_quilt and not tree.under("debian", filename): continue if tree.under(".pc", filename): # Not interested in merging quilt metadata continue base_stat = os.lstat("%s/%s" % (base_dir, filename)) try: left_stat = os.lstat("%s/%s" % (left_dir, filename)) except OSError: left_stat = None try: right_stat = os.lstat("%s/%s" % (right_dir, filename)) except OSError: right_stat = None if left_stat is None and right_stat is None: # Removed on both sides pass elif left_stat is None: logger.debug("removed from %s: %s", left_distro, filename) if not same_file(base_stat, base_dir, right_stat, right_dir, filename): # Changed on RHS conflict_file(left_dir, left_distro, right_dir, right_distro, merged_dir, filename) conflicts.append(filename) elif right_stat is None: # Removed on RHS only logger.debug("removed from %s: %s", right_distro, filename) if not same_file(base_stat, base_dir, left_stat, left_dir, filename): # Changed on LHS conflict_file(left_dir, left_distro, right_dir, right_distro, merged_dir, filename) conflicts.append(filename) elif S_ISREG(left_stat.st_mode) and S_ISREG(right_stat.st_mode): # Common case: left and right are both files if handle_file(left_stat, left_dir, left_name, left_distro, right_dir, right_stat, right_name, right_distro, base_stat, base_dir, merged_dir, filename, po_files): conflicts.append(filename) elif same_file(left_stat, left_dir, right_stat, right_dir, filename): # left and right are the same, doesn't matter which we keep tree.copyfile("%s/%s" % (right_dir, filename), "%s/%s" % (merged_dir, filename)) elif same_file(base_stat, base_dir, left_stat, left_dir, filename): # right has changed in some way, keep that one logger.debug("preserving non-file change in %s: %s", right_distro, filename) tree.copyfile("%s/%s" % (right_dir, filename), "%s/%s" % (merged_dir, filename)) elif same_file(base_stat, base_dir, right_stat, right_dir, filename): # left has changed in some way, keep that one logger.debug("preserving non-file change in %s: %s", left_distro, filename) tree.copyfile("%s/%s" % (left_dir, filename), "%s/%s" % (merged_dir, filename)) else: # all three differ, mark a conflict conflict_file(left_dir, left_distro, right_dir, right_distro, merged_dir, filename) conflicts.append(filename) # Look for files in the left hand side that aren't in the base, # conflict if new on both sides or copy into the tree for filename in tree.walk(left_dir): # If both packages are 3.0 (quilt), ignore everything except the # debian directory if both_formats_quilt and not tree.under("debian", filename): continue if tree.under(".pc", filename): # Not interested in merging quilt metadata continue if tree.exists("%s/%s" % (base_dir, filename)): continue if not tree.exists("%s/%s" % (right_dir, filename)): logger.debug("new in %s: %s", left_distro, filename) tree.copyfile("%s/%s" % (left_dir, filename), "%s/%s" % (merged_dir, filename)) continue left_stat = os.lstat("%s/%s" % (left_dir, filename)) right_stat = os.lstat("%s/%s" % (right_dir, filename)) if S_ISREG(left_stat.st_mode) and S_ISREG(right_stat.st_mode): # Common case: left and right are both files if handle_file(left_stat, left_dir, left_name, left_distro, right_dir, right_stat, right_name, right_distro, None, None, merged_dir, filename, po_files): conflicts.append(filename) elif same_file(left_stat, left_dir, right_stat, right_dir, filename): # left and right are the same, doesn't matter which we keep tree.copyfile("%s/%s" % (right_dir, filename), "%s/%s" % (merged_dir, filename)) else: # they differ, mark a conflict conflict_file(left_dir, left_distro, right_dir, right_distro, merged_dir, filename) conflicts.append(filename) # Copy new files on the right hand side only into the tree for filename in tree.walk(right_dir): if tree.under(".pc", filename): # Not interested in merging quilt metadata continue if both_formats_quilt and not tree.under("debian", filename): # Always copy right version for quilt non-debian files if not tree.exists("%s/%s" % (left_dir, filename)): logger.debug("new in %s: %s", right_distro, filename) else: if tree.exists("%s/%s" % (base_dir, filename)): continue if tree.exists("%s/%s" % (left_dir, filename)): continue logger.debug("new in %s: %s", right_distro, filename) tree.copyfile("%s/%s" % (right_dir, filename), "%s/%s" % (merged_dir, filename)) # Handle po files separately as they need special merging for filename in po_files: if merge_po(left_dir, right_dir, merged_dir, filename): conflict_file(left_dir, left_distro, right_dir, right_distro, merged_dir, filename) conflicts.append(filename) continue merge_attr(base_dir, left_dir, right_dir, merged_dir, filename) return conflicts
def do_merge(left_dir, left_name, left_format, left_distro, base_dir, right_dir, right_name, right_format, right_distro, merged_dir): """Do the heavy lifting of comparing and merging.""" logger.debug("Producing merge in %s", merged_dir) result = MergeData() po_files = [] both_formats_quilt = left_format == right_format == "3.0 (quilt)" if both_formats_quilt: logger.debug("Only merging debian directory since both " "formats 3.0 (quilt)") # Look for files in the base and merge them if they're in both new # files (removed files get removed) for filename in tree.walk(base_dir): # If both packages are 3.0 (quilt), ignore everything except the # debian directory if both_formats_quilt and not tree.under("debian", filename): continue if tree.under(".pc", filename): # Not interested in merging quilt metadata continue base_stat = os.lstat("%s/%s" % (base_dir, filename)) try: left_stat = os.lstat("%s/%s" % (left_dir, filename)) except OSError: left_stat = None try: right_stat = os.lstat("%s/%s" % (right_dir, filename)) except OSError: right_stat = None if left_stat is None and right_stat is None: # Removed on both sides pass elif left_stat is None: logger.debug("removed from %s: %s", left_distro, filename) if not same_file(base_stat, base_dir, right_stat, right_dir, filename): # Changed on RHS result.conflicts.add(filename) else: result.removed_files.add(filename) elif right_stat is None: # Removed on RHS only logger.debug("removed from %s: %s", right_distro, filename) if not same_file(base_stat, base_dir, left_stat, left_dir, filename): # Changed on LHS result.conflicts.add(filename) elif S_ISREG(left_stat.st_mode) and S_ISREG(right_stat.st_mode): # Common case: left and right are both files handle_file(left_stat, left_dir, left_name, left_distro, right_dir, right_stat, right_name, right_distro, base_stat, base_dir, merged_dir, filename, po_files, result) elif same_file(left_stat, left_dir, right_stat, right_dir, filename): # left and right are the same, doesn't matter which we keep tree.copyfile("%s/%s" % (right_dir, filename), "%s/%s" % (merged_dir, filename)) elif same_file(base_stat, base_dir, left_stat, left_dir, filename): # right has changed in some way, keep that one logger.debug("preserving non-file change in %s: %s", right_distro, filename) tree.copyfile("%s/%s" % (right_dir, filename), "%s/%s" % (merged_dir, filename)) elif same_file(base_stat, base_dir, right_stat, right_dir, filename): # left has changed in some way, keep that one logger.debug("preserving non-file change in %s: %s", left_distro, filename) tree.copyfile("%s/%s" % (left_dir, filename), "%s/%s" % (merged_dir, filename)) result.modified_files.add(filename) else: # all three differ, mark a conflict result.conflicts.add(filename) # Look for files in the left hand side that aren't in the base, # conflict if new on both sides or copy into the tree for filename in tree.walk(left_dir): # If both packages are 3.0 (quilt), ignore everything except the # debian directory if both_formats_quilt and not tree.under("debian", filename): continue if tree.under(".pc", filename): # Not interested in merging quilt metadata continue if tree.exists("%s/%s" % (base_dir, filename)): continue if not tree.exists("%s/%s" % (right_dir, filename)): logger.debug("new in %s: %s", left_distro, filename) tree.copyfile("%s/%s" % (left_dir, filename), "%s/%s" % (merged_dir, filename)) result.added_files.add(filename) continue left_stat = os.lstat("%s/%s" % (left_dir, filename)) right_stat = os.lstat("%s/%s" % (right_dir, filename)) if S_ISREG(left_stat.st_mode) and S_ISREG(right_stat.st_mode): # Common case: left and right are both files handle_file(left_stat, left_dir, left_name, left_distro, right_dir, right_stat, right_name, right_distro, None, None, merged_dir, filename, po_files, result) elif same_file(left_stat, left_dir, right_stat, right_dir, filename): # left and right are the same, doesn't matter which we keep tree.copyfile("%s/%s" % (right_dir, filename), "%s/%s" % (merged_dir, filename)) else: # they differ, mark a conflict result.conflicts.add(filename) # Copy new files on the right hand side only into the tree for filename in tree.walk(right_dir): if tree.under(".pc", filename): # Not interested in merging quilt metadata continue if both_formats_quilt and not tree.under("debian", filename): # Always copy right version for quilt non-debian files if not tree.exists("%s/%s" % (left_dir, filename)): logger.debug("new in %s: %s", right_distro, filename) else: if tree.exists("%s/%s" % (base_dir, filename)): continue if tree.exists("%s/%s" % (left_dir, filename)): continue logger.debug("new in %s: %s", right_distro, filename) tree.copyfile("%s/%s" % (right_dir, filename), "%s/%s" % (merged_dir, filename)) # Handle po files separately as they need special merging for filename in po_files: if not merge_po(left_dir, right_dir, merged_dir, filename): result.conflicts.add(filename) continue merge_attr(base_dir, left_dir, right_dir, merged_dir, filename, result) result.modified_files.add(filename) for conflict in result.conflicts: conflict_file(left_dir, left_distro, right_dir, right_distro, merged_dir, conflict) return result
def do_merge(left_dir, left_name, left_distro, base_dir, right_dir, right_name, right_distro, merged_dir): """Do the heavy lifting of comparing and merging.""" logging.debug("Producing merge in %s", tree.subdir(ROOT, merged_dir)) conflicts = [] po_files = [] # Look for files in the base and merge them if they're in both new # files (removed files get removed) for filename in tree.walk(base_dir): if tree.under(".pc", filename): # Not interested in merging quilt metadata continue base_stat = os.lstat("%s/%s" % (base_dir, filename)) try: left_stat = os.lstat("%s/%s" % (left_dir, filename)) except OSError: left_stat = None try: right_stat = os.lstat("%s/%s" % (right_dir, filename)) except OSError: right_stat = None if left_stat is None and right_stat is None: # Removed on both sides pass elif left_stat is None: logging.debug("removed from %s: %s", left_distro, filename) if not same_file(base_stat, base_dir, right_stat, right_dir, filename): # Changed on RHS conflict_file(left_dir, left_distro, right_dir, right_distro, merged_dir, filename) conflicts.append(filename) elif right_stat is None: # Removed on RHS only logging.debug("removed from %s: %s", right_distro, filename) if not same_file(base_stat, base_dir, left_stat, left_dir, filename): # Changed on LHS conflict_file(left_dir, left_distro, right_dir, right_distro, merged_dir, filename) conflicts.append(filename) elif S_ISREG(left_stat.st_mode) and S_ISREG(right_stat.st_mode): # Common case: left and right are both files if handle_file(left_stat, left_dir, left_name, left_distro, right_dir, right_stat, right_name, right_distro, base_stat, base_dir, merged_dir, filename, po_files): conflicts.append(filename) elif same_file(left_stat, left_dir, right_stat, right_dir, filename): # left and right are the same, doesn't matter which we keep tree.copyfile("%s/%s" % (right_dir, filename), "%s/%s" % (merged_dir, filename)) elif same_file(base_stat, base_dir, left_stat, left_dir, filename): # right has changed in some way, keep that one logging.debug("preserving non-file change in %s: %s", right_distro, filename) tree.copyfile("%s/%s" % (right_dir, filename), "%s/%s" % (merged_dir, filename)) elif same_file(base_stat, base_dir, right_stat, right_dir, filename): # left has changed in some way, keep that one logging.debug("preserving non-file change in %s: %s", left_distro, filename) tree.copyfile("%s/%s" % (left_dir, filename), "%s/%s" % (merged_dir, filename)) else: # all three differ, mark a conflict conflict_file(left_dir, left_distro, right_dir, right_distro, merged_dir, filename) conflicts.append(filename) # Look for files in the left hand side that aren't in the base, # conflict if new on both sides or copy into the tree for filename in tree.walk(left_dir): if tree.under(".pc", filename): # Not interested in merging quilt metadata continue if tree.exists("%s/%s" % (base_dir, filename)): continue if not tree.exists("%s/%s" % (right_dir, filename)): logging.debug("new in %s: %s", left_distro, filename) tree.copyfile("%s/%s" % (left_dir, filename), "%s/%s" % (merged_dir, filename)) continue left_stat = os.lstat("%s/%s" % (left_dir, filename)) right_stat = os.lstat("%s/%s" % (right_dir, filename)) if S_ISREG(left_stat.st_mode) and S_ISREG(right_stat.st_mode): # Common case: left and right are both files if handle_file(left_stat, left_dir, left_name, left_distro, right_dir, right_stat, right_name, right_distro, None, None, merged_dir, filename, po_files): conflicts.append(filename) elif same_file(left_stat, left_dir, right_stat, right_dir, filename): # left and right are the same, doesn't matter which we keep tree.copyfile("%s/%s" % (right_dir, filename), "%s/%s" % (merged_dir, filename)) else: # they differ, mark a conflict conflict_file(left_dir, left_distro, right_dir, right_distro, merged_dir, filename) conflicts.append(filename) # Copy new files on the right hand side only into the tree for filename in tree.walk(right_dir): if tree.under(".pc", filename): # Not interested in merging quilt metadata continue if tree.exists("%s/%s" % (base_dir, filename)): continue if tree.exists("%s/%s" % (left_dir, filename)): continue logging.debug("new in %s: %s", right_distro, filename) tree.copyfile("%s/%s" % (right_dir, filename), "%s/%s" % (merged_dir, filename)) # Handle po files separately as they need special merging for filename in po_files: if merge_po(left_dir, right_dir, merged_dir, filename): conflict_file(left_dir, left_distro, right_dir, right_distro, merged_dir, filename) conflicts.append(filename) continue merge_attr(base_dir, left_dir, right_dir, merged_dir, filename) return conflicts