Browse Source

Merge branch 'ks/p4-view-spec'

* ks/p4-view-spec:
  git p4: implement view spec wildcards with "p4 where"
  git p4 test: sanitize P4CHARSET
maint
Junio C Hamano 12 years ago
parent
commit
34022ba21a
  1. 223
      git-p4.py
  2. 3
      t/lib-git-p4.sh

223
git-p4.py

@ -780,11 +780,14 @@ def getClientSpec():
# dictionary of all client parameters # dictionary of all client parameters
entry = specList[0] entry = specList[0]


# the //client/ name
client_name = entry["Client"]

# just the keys that start with "View" # just the keys that start with "View"
view_keys = [ k for k in entry.keys() if k.startswith("View") ] view_keys = [ k for k in entry.keys() if k.startswith("View") ]


# hold this new View # hold this new View
view = View() view = View(client_name)


# append the lines, in order, to the view # append the lines, in order, to the view
for view_num in range(len(view_keys)): for view_num in range(len(view_keys)):
@ -1555,8 +1558,8 @@ class P4Submit(Command, P4UserMap):
for b in body: for b in body:
labelTemplate += "\t" + b + "\n" labelTemplate += "\t" + b + "\n"
labelTemplate += "View:\n" labelTemplate += "View:\n"
for mapping in clientSpec.mappings: for depot_side in clientSpec.mappings:
labelTemplate += "\t%s\n" % mapping.depot_side.path labelTemplate += "\t%s\n" % depot_side


if self.dry_run: if self.dry_run:
print "Would create p4 label %s for tag" % name print "Would create p4 label %s for tag" % name
@ -1568,7 +1571,7 @@ class P4Submit(Command, P4UserMap):


# Use the label # Use the label
p4_system(["tag", "-l", name] + p4_system(["tag", "-l", name] +
["%s@%s" % (mapping.depot_side.path, changelist) for mapping in clientSpec.mappings]) ["%s@%s" % (depot_side, changelist) for depot_side in clientSpec.mappings])


if verbose: if verbose:
print "created p4 label for tag %s" % name print "created p4 label for tag %s" % name
@ -1796,117 +1799,16 @@ class View(object):
"""Represent a p4 view ("p4 help views"), and map files in a """Represent a p4 view ("p4 help views"), and map files in a
repo according to the view.""" repo according to the view."""


class Path(object): def __init__(self, client_name):
"""A depot or client path, possibly containing wildcards.
The only one supported is ... at the end, currently.
Initialize with the full path, with //depot or //client."""

def __init__(self, path, is_depot):
self.path = path
self.is_depot = is_depot
self.find_wildcards()
# remember the prefix bit, useful for relative mappings
m = re.match("(//[^/]+/)", self.path)
if not m:
die("Path %s does not start with //prefix/" % self.path)
prefix = m.group(1)
if not self.is_depot:
# strip //client/ on client paths
self.path = self.path[len(prefix):]

def find_wildcards(self):
"""Make sure wildcards are valid, and set up internal
variables."""

self.ends_triple_dot = False
# There are three wildcards allowed in p4 views
# (see "p4 help views"). This code knows how to
# handle "..." (only at the end), but cannot deal with
# "%%n" or "*". Only check the depot_side, as p4 should
# validate that the client_side matches too.
if re.search(r'%%[1-9]', self.path):
die("Can't handle %%n wildcards in view: %s" % self.path)
if self.path.find("*") >= 0:
die("Can't handle * wildcards in view: %s" % self.path)
triple_dot_index = self.path.find("...")
if triple_dot_index >= 0:
if triple_dot_index != len(self.path) - 3:
die("Can handle only single ... wildcard, at end: %s" %
self.path)
self.ends_triple_dot = True

def ensure_compatible(self, other_path):
"""Make sure the wildcards agree."""
if self.ends_triple_dot != other_path.ends_triple_dot:
die("Both paths must end with ... if either does;\n" +
"paths: %s %s" % (self.path, other_path.path))

def match_wildcards(self, test_path):
"""See if this test_path matches us, and fill in the value
of the wildcards if so. Returns a tuple of
(True|False, wildcards[]). For now, only the ... at end
is supported, so at most one wildcard."""
if self.ends_triple_dot:
dotless = self.path[:-3]
if test_path.startswith(dotless):
wildcard = test_path[len(dotless):]
return (True, [ wildcard ])
else:
if test_path == self.path:
return (True, [])
return (False, [])

def match(self, test_path):
"""Just return if it matches; don't bother with the wildcards."""
b, _ = self.match_wildcards(test_path)
return b

def fill_in_wildcards(self, wildcards):
"""Return the relative path, with the wildcards filled in
if there are any."""
if self.ends_triple_dot:
return self.path[:-3] + wildcards[0]
else:
return self.path

class Mapping(object):
def __init__(self, depot_side, client_side, overlay, exclude):
# depot_side is without the trailing /... if it had one
self.depot_side = View.Path(depot_side, is_depot=True)
self.client_side = View.Path(client_side, is_depot=False)
self.overlay = overlay # started with "+"
self.exclude = exclude # started with "-"
assert not (self.overlay and self.exclude)
self.depot_side.ensure_compatible(self.client_side)

def __str__(self):
c = " "
if self.overlay:
c = "+"
if self.exclude:
c = "-"
return "View.Mapping: %s%s -> %s" % \
(c, self.depot_side.path, self.client_side.path)

def map_depot_to_client(self, depot_path):
"""Calculate the client path if using this mapping on the
given depot path; does not consider the effect of other
mappings in a view. Even excluded mappings are returned."""
matches, wildcards = self.depot_side.match_wildcards(depot_path)
if not matches:
return ""
client_path = self.client_side.fill_in_wildcards(wildcards)
return client_path

#
# View methods
#
def __init__(self):
self.mappings = [] self.mappings = []
self.client_prefix = "//%s/" % client_name
# cache results of "p4 where" to lookup client file locations
self.client_spec_path_cache = {}


def append(self, view_line): def append(self, view_line):
"""Parse a view line, splitting it into depot and client """Parse a view line, splitting it into depot and client
sides. Append to self.mappings, preserving order.""" sides. Append to self.mappings, preserving order. This
is only needed for tag creation."""


# Split the view line into exactly two words. P4 enforces # Split the view line into exactly two words. P4 enforces
# structure on these lines that simplifies this quite a bit. # structure on these lines that simplifies this quite a bit.
@ -1934,76 +1836,62 @@ class View(object):
depot_side = view_line[0:space_index] depot_side = view_line[0:space_index]
rhs_index = space_index + 1 rhs_index = space_index + 1


if view_line[rhs_index] == '"':
# Second word is double quoted. Make sure there is a
# double quote at the end too.
if not view_line.endswith('"'):
die("View line with rhs quote should end with one: %s" %
view_line)
# skip the quotes
client_side = view_line[rhs_index+1:-1]
else:
client_side = view_line[rhs_index:]

# prefix + means overlay on previous mapping # prefix + means overlay on previous mapping
overlay = False
if depot_side.startswith("+"): if depot_side.startswith("+"):
overlay = True
depot_side = depot_side[1:] depot_side = depot_side[1:]


# prefix - means exclude this path # prefix - means exclude this path, leave out of mappings
exclude = False exclude = False
if depot_side.startswith("-"): if depot_side.startswith("-"):
exclude = True exclude = True
depot_side = depot_side[1:] depot_side = depot_side[1:]


m = View.Mapping(depot_side, client_side, overlay, exclude) if not exclude:
self.mappings.append(m) self.mappings.append(depot_side)


def map_in_client(self, depot_path): def convert_client_path(self, clientFile):
"""Return the relative location in the client where this # chop off //client/ part to make it relative
depot file should live. Returns "" if the file should if not clientFile.startswith(self.client_prefix):
not be mapped in the client.""" die("No prefix '%s' on clientFile '%s'" %
(self.client_prefix, clientFile))
return clientFile[len(self.client_prefix):]


paths_filled = [] def update_client_spec_path_cache(self, files):
client_path = "" """ Caching file paths by "p4 where" batch query """


# look at later entries first # List depot file paths exclude that already cached
for m in self.mappings[::-1]: fileArgs = [f['path'] for f in files if f['path'] not in self.client_spec_path_cache]


# see where will this path end up in the client if len(fileArgs) == 0:
p = m.map_depot_to_client(depot_path) return # All files in cache


if p == "": where_result = p4CmdList(["-x", "-", "where"], stdin=fileArgs)
# Depot path does not belong in client. Must remember for res in where_result:
# this, as previous items should not cause files to if "code" in res and res["code"] == "error":
# exist in this path either. Remember that the list is # assume error is "... file(s) not in client view"
# being walked from the end, which has higher precedence. continue
# Overlap mappings do not exclude previous mappings. if "clientFile" not in res:
if not m.overlay: die("No clientFile from 'p4 where %s'" % depot_path)
paths_filled.append(m.client_side) if "unmap" in res:
# it will list all of them, but only one not unmap-ped
continue
self.client_spec_path_cache[res['depotFile']] = self.convert_client_path(res["clientFile"])


else: # not found files or unmap files set to ""
# This mapping matched; no need to search any further. for depotFile in fileArgs:
# But, the mapping could be rejected if the client path if depotFile not in self.client_spec_path_cache:
# has already been claimed by an earlier mapping (i.e. self.client_spec_path_cache[depotFile] = ""
# one later in the list, which we are walking backwards).
already_mapped_in_client = False
for f in paths_filled:
# this is View.Path.match
if f.match(p):
already_mapped_in_client = True
break
if not already_mapped_in_client:
# Include this file, unless it is from a line that
# explicitly said to exclude it.
if not m.exclude:
client_path = p


# a match, even if rejected, always stops the search def map_in_client(self, depot_path):
break """Return the relative location in the client where this
depot file should live. Returns "" if the file should
not be mapped in the client."""


return client_path if depot_path in self.client_spec_path_cache:
return self.client_spec_path_cache[depot_path]

die( "Error: %s is not found in client spec path" % depot_path )
return ""


class P4Sync(Command, P4UserMap): class P4Sync(Command, P4UserMap):
delete_actions = ( "delete", "move/delete", "purge" ) delete_actions = ( "delete", "move/delete", "purge" )
@ -2130,6 +2018,10 @@ class P4Sync(Command, P4UserMap):
"""Look at each depotFile in the commit to figure out to what """Look at each depotFile in the commit to figure out to what
branch it belongs.""" branch it belongs."""


if self.clientSpecDirs:
files = self.extractFilesFromCommit(commit)
self.clientSpecDirs.update_client_spec_path_cache(files)

branches = {} branches = {}
fnum = 0 fnum = 0
while commit.has_key("depotFile%s" % fnum): while commit.has_key("depotFile%s" % fnum):
@ -2383,6 +2275,9 @@ class P4Sync(Command, P4UserMap):
else: else:
sys.stderr.write("Ignoring file outside of prefix: %s\n" % f['path']) sys.stderr.write("Ignoring file outside of prefix: %s\n" % f['path'])


if self.clientSpecDirs:
self.clientSpecDirs.update_client_spec_path_cache(files)

self.gitStream.write("commit %s\n" % branch) self.gitStream.write("commit %s\n" % branch)
# gitStream.write("mark :%s\n" % details["change"]) # gitStream.write("mark :%s\n" % details["change"])
self.committedChanges.add(int(details["change"])) self.committedChanges.add(int(details["change"]))

3
t/lib-git-p4.sh

@ -48,7 +48,8 @@ P4DPORT=$((10669 + ($testid - $git_p4_test_start)))
P4PORT=localhost:$P4DPORT P4PORT=localhost:$P4DPORT
P4CLIENT=client P4CLIENT=client
P4EDITOR=: P4EDITOR=:
export P4PORT P4CLIENT P4EDITOR unset P4CHARSET
export P4PORT P4CLIENT P4EDITOR P4CHARSET


db="$TRASH_DIRECTORY/db" db="$TRASH_DIRECTORY/db"
cli="$TRASH_DIRECTORY/cli" cli="$TRASH_DIRECTORY/cli"

Loading…
Cancel
Save