On Mon, Feb 26, 2018 at 6:48 AM, Luke Diamand <[email protected]> wrote:
> It takes a list of P4 changelists and generates a patch for
> each one, using "p4 describe".
> [...]
> Signed-off-by: Luke Diamand <[email protected]>
> ---
> diff --git a/git-p4.py b/git-p4.py
> @@ -3749,6 +3761,277 @@ class P4Branches(Command):
> +class P4MakePatch(Command,P4UserMap):
> + def run(self, args):
> + if self.output and not os.path.isdir(self.output):
> + sys.exit("output directory %s does not exist" % self.output)
For consistency with "git format-patch", this could create the output
directory automatically rather than erroring out.
> + if self.strip_depot_prefix:
> + self.clientSpec = getClientSpec()
> + else:
> + self.clientSpec = None
> +
> + self.loadUserMapFromCache()
> + if len(args) < 1:
> + return False
Would it make sense to handle this no-arguments case earlier before
doing work, such as loading the user map from cache?
> + for change in args:
> + self.make_patch(int(change))
> +
> + return True
> +
> + def p4_fetch_delta(self, change, files, shelved=False):
> + cmd = ["describe"]
> + if shelved:
> + cmd += ["-S"]
> + cmd += ["-du"]
> + cmd += ["%s" % change]
> + cmd = p4_build_cmd(cmd)
> +
> + p4 = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE)
> + try:
> + result = p4.stdout.readlines()
> + except EOFError:
> + pass
> + in_diff = False
> + matcher = re.compile('====\s+(.*)#(\d+)\s+\(text\)\s+====')
> + diffmatcher = re.compile("Differences ...")
I'm not familiar with the output of "p4 describe", but does it include
user-supplied text? If so, would it be safer to anchor 'diffmatcher',
for instance, "^Diferences...$"?
> + delta = ""
> + skip_next_blank_line = False
> +
> + for line in result:
> + if diffmatcher.match(line):
Stepping back, does "Differences..." appear on a line of its own? If
so, why does this need to be a regex at all? Can't it just do a direct
string comparison?
> + in_diff = True
> + continue
> +
> + if in_diff:
> +
> + if skip_next_blank_line and \
> + line.rstrip() == "":
> + skip_next_blank_line = False
> + continue
> +
> + m = matcher.match(line)
> + if m:
> + file = self.map_path(m.group(1))
> + ver = m.group(2)
> + delta += "diff --git a%s b%s\n" % (file, file)
> + delta += "--- a%s\n" % file
> + delta += "+++ b%s\n" % file
> + skip_next_blank_line = True
> + else:
> + delta += line
> +
> + delta += "\n"
> +
> + exitCode = p4.wait()
> + if exitCode != 0:
> + raise IOError("p4 '%s' command failed" % str(cmd))
Since p4.stdout.readlines() gathered all output from the command
before massaging it into Git format, can't the p4.wait() be done
earlier, just after the output has been read?
> + return delta
> +
> + def make_patch(self, change):
> + [...]
> + # add new or deleted files
> + for file in files:
> + [...]
> + if add or delete:
> + if add:
> + [...]
> + else:
> + [...]
> +
> + [...]
> +
> + if add:
> + prefix = "+"
> + else:
> + prefix = "-"
> +
> + if delete:
> + [...]
> + else:
> + # added
> + [...]
> +
> + (lines, delta_content) = self.read_file_contents(prefix,
> path_rev)
> +
> + if add:
> + if lines > 0:
> + delta += "@@ -0,0 +1,%d @@\n" % lines
> + else:
> + delta += "@@ -1,%d +0,0 @@\n" % lines
It's not clear why you sometimes check 'add' but other times 'delete'.
Perhaps always used one or the other? For instance:
action = file["action"]
if action == "add" or action == "delete":
if action == "add":
before = "/dev/null"
...
else
...
delta += ...
if action == "add":
...
or something.
> + delta += delta_content
> +
> + if self.output:
> + with open("%s/%s.patch" % (self.output, change), "w") as f:
> + f.write(delta)
> + else:
> + print(delta)
> diff --git a/t/t9832-make-patch.sh b/t/t9832-make-patch.sh
> @@ -0,0 +1,135 @@
> +# Converting P4 changes into patches
> +#
> +# - added, deleted, modified files
> +# - regular commits, shelved commits
> +#
> +# directories and symblinks don't yet work
> +# binary files will never work
s/symblinks/symlinks/
> +test_expect_success 'init depot' '
> + (
> + cd "$cli" &&
> + echo file1 >file1 &&
> + p4 add file1 &&
> + p4 submit -d "change 1" && # cl 1
> + cat >file_to_delete <<-EOF &&
Using -\EOF would signal that you don't expect interpolation within the heredoc.
> + LINE1
> + LINE2
> + EOF
> + echo "non-empty" >file_to_delete &&
> + p4 add file_to_delete &&
> + p4 submit -d "change 2" && # cl 2
> + p4 edit file1 &&
> + cat >>file1 <<-EOF &&
> + LINE1
> + LINE2
> + EOF
> + p4 submit -d "change 3" && # cl 3
> + p4 delete file_to_delete &&
> + echo "file2" >file2 &&
> + p4 add file2 &&
> + p4 submit -d "change 4" # cl 4
> + )
> +'
> +test_expect_success 'add p4 symlink' '
> + (
> + cd "$cli" &&
> + echo "symlink_source" >symlink_source &&
> + ln -s symlink_source symlink &&
Should this test be protected with the SYMLINKS prerequisite?
> + p4 add symlink_source symlink &&
> + p4 submit -d "add symlink" # cl 6
> + )
> +'
> +
> +test_expect_success 'patch from symlink' '
Ditto SYMLINKS?
> + test_when_finished cleanup_git &&
> + cd "$git" &&
> + (
> + git p4 format-patch 6 | patch -p1 &&
> + test_path_is_file depot/symlink_source &&
> + test -L depot/symlink
> + )
> +'