Skip to content

Instantly share code, notes, and snippets.

@bcachet
Created November 9, 2011 15:03
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save bcachet/1351683 to your computer and use it in GitHub Desktop.
Save bcachet/1351683 to your computer and use it in GitHub Desktop.
Script to split a unified patch file into several ones
#!/usr/bin/env ruby
#
# splitpatch is a simple script to split a patch up into multiple patch files.
# if the --hunks option is provided on the command line, each hunk gets its
# own patchfile.
#
# Copyright (C) 2007, Peter Hutterer <peter@cs.unisa.edu.au>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
require 'fileutils.rb'
require 'file.rb'
class Splitter
def initialize(file)
@filename = file
end
def validFile?
return File.exist?(@filename) && File.readable?(@filename)
end
# Split the patchfile by files
def splitByFile
outfile = nil
stream = open(@filename)
until (stream.eof?)
line = stream.readline
# we need to create a new file
if (line =~ /--- .*/) == 0
if (outfile)
outfile.close_write
end
#find filename
if (line <=> "--- /dev/null\n") == 0
puts "Patch of a not existing file"
line = stream.readline
end
tokens = line.split(" ")
filename = tokens[-1]
puts "Filename is " + filename + " || Line was: " + line
FileUtils.mkdir_p(File.dirname(filename))
if File.exists?(filename)
puts "File #{filename} already exists. Renaming patch."
appendix = 0
while File.exists?("#{filename}.#{appendix}")
appendix += 1
end
filename << ".#{appendix}"
end
outfile = open(filename, "w")
outfile.write(line)
else
if outfile
outfile.write(line)
end
end
end
end
def splitByHunk
outfile = nil
stream = open(@filename)
filename = ""
counter = 0
header = ""
until (stream.eof?)
line = stream.readline
# we need to create a new file
if (line =~ /--- .*/) == 0
#find filename
tokens = line.split(" ")
tokens = tokens[1].split(":")
tokens = tokens[0].split("/")
filename = tokens[-1]
header = line
# next line is header too
line = stream.readline
header << line
counter = 0
elsif (line =~ /@@ .* @@/) == 0
if (outfile)
outfile.close_write
end
hunkfilename = "#{filename}.#{counter}.patch"
if File.exists?(hunkfilename)
puts "File #{hunkfilename} already exists. Renaming patch."
appendix = 0
while File.exists?("#{hunkfilename}.#{appendix}")
appendix += 1
end
hunkfilename << ".#{appendix}"
end
outfile = open(hunkfilename, "w")
counter += 1
outfile.write(header)
outfile.write(line)
else
if outfile
outfile.write(line)
end
end
end
end
end
######################## MAIN ########################
if ARGV.length < 1 or ARGV.length > 2
puts "Wrong parameter. Usage: splitpatch.rb [--hunks] <patchfile>"
exit 1
elsif ARGV[0] == "--help"
puts "splitpatch splits a patch that is supposed to patch multiple files"
puts "into a set of patches."
puts "Currently splits unified diff patches."
puts "If the --hunk option is given, a new file is created for each hunk."
exit 1
else
s = Splitter.new(ARGV[-1])
if s.validFile?
if ARGV[0] == "--hunks"
s.splitByHunk
else
s.splitByFile
end
else
puts "File does not exist or is not readable"
end
end
@sidcha
Copy link

sidcha commented Aug 16, 2017

Perhaps a little too excessive?

Try this:

cat ../massive.patch | perl -ne 'if(/^diff --git a\/(\S+)/) { close $f; $s=$1; $s =~ s:(/|\.):_:g; $n=sprintf("%03d_%s.patch", ++$i, $s); open $f, ">", $n; print $f $_; next; } print $f $_;'

Copy link

ghost commented Apr 21, 2019

@cbsiddharth, using your perl oneliner, I get Can't use an undefined value as a symbol reference at -e line 1, <> line 1.

@sidcha
Copy link

sidcha commented Apr 21, 2019

@voltagex, what version of perl are you using? it works fine for me in v5.16.3.

Copy link

ghost commented Apr 21, 2019

@sidcha
Copy link

sidcha commented Apr 21, 2019

@voltagex, the meta information above the patch caused the issue. Please try this:

cat massive.patch | perl -ne 'if(/^diff --git a\/(\S+)/) { close $f; $s=$1; $s =~ s:(/|\.):_:g; $n=sprintf("%03d_%s.patch", ++$i, $s); open $f, ">", $n; print $f $_; next; } print $f $_ if $f;'

Notice the if $f added to the last print statement. That's the only change needed.

@sidcha
Copy link

sidcha commented Apr 21, 2019

If you want to retain this, I made a separate gist for this: https://gist.github.com/cbsiddharth/c1d2fce0bd5ef31115c59fc953f00f2f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment