Skip to content

Commit b58e7bf

Browse files
committed
Merge branch 'jh/p4-rcs-expansion-in-bytestring'
The RCS keyword substitution in "git p4" used to be done assuming that the contents are UTF-8 text, which can trigger decoding errors. We now treat the contents as a bytestring for robustness and correctness. * jh/p4-rcs-expansion-in-bytestring: git-p4: resolve RCS keywords in bytes not utf-8 git-p4: open temporary patch file for write only git-p4: add raw option to read_pipelines git-p4: pre-compile RCS keyword regexes git-p4: use with statements to close files after use in patchRCSKeywords
2 parents dcc0cd0 + 70c0d55 commit b58e7bf

File tree

2 files changed

+42
-39
lines changed

2 files changed

+42
-39
lines changed

git-p4.py

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@
5656

5757
p4_access_checked = False
5858

59+
re_ko_keywords = re.compile(br'\$(Id|Header)(:[^$\n]+)?\$')
60+
re_k_keywords = re.compile(br'\$(Id|Header|Author|Date|DateTime|Change|File|Revision)(:[^$\n]+)?\$')
61+
5962
def p4_build_cmd(cmd):
6063
"""Build a suitable p4 command line.
6164
@@ -337,17 +340,19 @@ def p4_read_pipe(c, ignore_error=False, raw=False):
337340
real_cmd = p4_build_cmd(c)
338341
return read_pipe(real_cmd, ignore_error, raw=raw)
339342

340-
def read_pipe_lines(c):
343+
def read_pipe_lines(c, raw=False):
341344
if verbose:
342345
sys.stderr.write('Reading pipe: %s\n' % str(c))
343346

344347
expand = not isinstance(c, list)
345348
p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)
346349
pipe = p.stdout
347-
val = [decode_text_stream(line) for line in pipe.readlines()]
350+
lines = pipe.readlines()
351+
if not raw:
352+
lines = [decode_text_stream(line) for line in lines]
348353
if pipe.close() or p.wait():
349354
die('Command failed: %s' % str(c))
350-
return val
355+
return lines
351356

352357
def p4_read_pipe_lines(c):
353358
"""Specifically invoke p4 on the command supplied. """
@@ -577,20 +582,12 @@ def p4_type(f):
577582
#
578583
def p4_keywords_regexp_for_type(base, type_mods):
579584
if base in ("text", "unicode", "binary"):
580-
kwords = None
581585
if "ko" in type_mods:
582-
kwords = 'Id|Header'
586+
return re_ko_keywords
583587
elif "k" in type_mods:
584-
kwords = 'Id|Header|Author|Date|DateTime|Change|File|Revision'
588+
return re_k_keywords
585589
else:
586590
return None
587-
pattern = r"""
588-
\$ # Starts with a dollar, followed by...
589-
(%s) # one of the keywords, followed by...
590-
(:[^$\n]+)? # possibly an old expansion, followed by...
591-
\$ # another dollar
592-
""" % kwords
593-
return pattern
594591
else:
595592
return None
596593

@@ -1753,18 +1750,13 @@ def prepareLogMessage(self, template, message, jobs):
17531750

17541751
return result
17551752

1756-
def patchRCSKeywords(self, file, pattern):
1757-
# Attempt to zap the RCS keywords in a p4 controlled file matching the given pattern
1753+
def patchRCSKeywords(self, file, regexp):
1754+
# Attempt to zap the RCS keywords in a p4 controlled file matching the given regex
17581755
(handle, outFileName) = tempfile.mkstemp(dir='.')
17591756
try:
1760-
outFile = os.fdopen(handle, "w+")
1761-
inFile = open(file, "r")
1762-
regexp = re.compile(pattern, re.VERBOSE)
1763-
for line in inFile.readlines():
1764-
line = regexp.sub(r'$\1$', line)
1765-
outFile.write(line)
1766-
inFile.close()
1767-
outFile.close()
1757+
with os.fdopen(handle, "wb") as outFile, open(file, "rb") as inFile:
1758+
for line in inFile.readlines():
1759+
outFile.write(regexp.sub(br'$\1$', line))
17681760
# Forcibly overwrite the original file
17691761
os.unlink(file)
17701762
shutil.move(outFileName, file)
@@ -2091,25 +2083,24 @@ def applyCommit(self, id):
20912083
# the patch to see if that's possible.
20922084
if gitConfigBool("git-p4.attemptRCSCleanup"):
20932085
file = None
2094-
pattern = None
20952086
kwfiles = {}
20962087
for file in editedFiles | filesToDelete:
20972088
# did this file's delta contain RCS keywords?
2098-
pattern = p4_keywords_regexp_for_file(file)
2099-
2100-
if pattern:
2089+
regexp = p4_keywords_regexp_for_file(file)
2090+
if regexp:
21012091
# this file is a possibility...look for RCS keywords.
2102-
regexp = re.compile(pattern, re.VERBOSE)
2103-
for line in read_pipe_lines(["git", "diff", "%s^..%s" % (id, id), file]):
2092+
for line in read_pipe_lines(
2093+
["git", "diff", "%s^..%s" % (id, id), file],
2094+
raw=True):
21042095
if regexp.search(line):
21052096
if verbose:
2106-
print("got keyword match on %s in %s in %s" % (pattern, line, file))
2107-
kwfiles[file] = pattern
2097+
print("got keyword match on %s in %s in %s" % (regex.pattern, line, file))
2098+
kwfiles[file] = regexp
21082099
break
21092100

2110-
for file in kwfiles:
2101+
for file, regexp in kwfiles.items():
21112102
if verbose:
2112-
print("zapping %s with %s" % (line,pattern))
2103+
print("zapping %s with %s" % (line, regexp.pattern))
21132104
# File is being deleted, so not open in p4. Must
21142105
# disable the read-only bit on windows.
21152106
if self.isWindows and file not in editedFiles:
@@ -3029,12 +3020,9 @@ def streamOneP4File(self, file, contents):
30293020

30303021
# Note that we do not try to de-mangle keywords on utf16 files,
30313022
# even though in theory somebody may want that.
3032-
pattern = p4_keywords_regexp_for_type(type_base, type_mods)
3033-
if pattern:
3034-
regexp = re.compile(pattern, re.VERBOSE)
3035-
text = ''.join(decode_text_stream(c) for c in contents)
3036-
text = regexp.sub(r'$\1$', text)
3037-
contents = [ encode_text_stream(text) ]
3023+
regexp = p4_keywords_regexp_for_type(type_base, type_mods)
3024+
if regexp:
3025+
contents = [regexp.sub(br'$\1$', c) for c in contents]
30383026

30393027
if self.largeFileSystem:
30403028
(git_mode, contents) = self.largeFileSystem.processContent(git_mode, relPath, contents)

t/t9810-git-p4-rcs.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ test_description='git p4 rcs keywords'
44

55
. ./lib-git-p4.sh
66

7+
CP1252="\223\224"
8+
79
test_expect_success 'start p4d' '
810
start_p4d
911
'
@@ -32,6 +34,9 @@ test_expect_success 'init depot' '
3234
p4 submit -d "filek" &&
3335
p4 add -t text+ko fileko &&
3436
p4 submit -d "fileko" &&
37+
printf "$CP1252" >fileko_cp1252 &&
38+
p4 add -t text+ko fileko_cp1252 &&
39+
p4 submit -d "fileko_cp1252" &&
3540
p4 add -t text file_text &&
3641
p4 submit -d "file_text"
3742
)
@@ -359,4 +364,14 @@ test_expect_failure 'Add keywords in git which do not match the default p4 value
359364
)
360365
'
361366

367+
test_expect_success 'check cp1252 smart quote are preserved through RCS keyword processing' '
368+
test_when_finished cleanup_git &&
369+
git p4 clone --dest="$git" //depot &&
370+
(
371+
cd "$git" &&
372+
printf "$CP1252" >expect &&
373+
test_cmp_bin expect fileko_cp1252
374+
)
375+
'
376+
362377
test_done

0 commit comments

Comments
 (0)