Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit 9d57c4a

Browse files
kaitohgitster
authored andcommitted
git p4: implement view spec wildcards with "p4 where"
"git p4" does not support many of the view wildcards, such as * and %%n. It only knows the common ... mapping, and exclusions. Redo the entire wildcard code around the idea of directly querying the p4 server for the mapping. For each commit, invoke "p4 where" with committed file paths as args and use the client mapping to decide where the file goes in git. This simplifies a lot of code, and adds support for all wildcards supported by p4. Downside is that there is probably a 20%-ish slowdown with this approach. [pw: redo code and tests] Signed-off-by: Kazuki Saitoh <[email protected]> Signed-off-by: Pete Wyckoff <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 7994603 commit 9d57c4a

File tree

1 file changed

+59
-164
lines changed

1 file changed

+59
-164
lines changed

git-p4.py

Lines changed: 59 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -780,11 +780,14 @@ def getClientSpec():
780780
# dictionary of all client parameters
781781
entry = specList[0]
782782

783+
# the //client/ name
784+
client_name = entry["Client"]
785+
783786
# just the keys that start with "View"
784787
view_keys = [ k for k in entry.keys() if k.startswith("View") ]
785788

786789
# hold this new View
787-
view = View()
790+
view = View(client_name)
788791

789792
# append the lines, in order, to the view
790793
for view_num in range(len(view_keys)):
@@ -1555,8 +1558,8 @@ def exportGitTags(self, gitTags):
15551558
for b in body:
15561559
labelTemplate += "\t" + b + "\n"
15571560
labelTemplate += "View:\n"
1558-
for mapping in clientSpec.mappings:
1559-
labelTemplate += "\t%s\n" % mapping.depot_side.path
1561+
for depot_side in clientSpec.mappings:
1562+
labelTemplate += "\t%s\n" % depot_side
15601563

15611564
if self.dry_run:
15621565
print "Would create p4 label %s for tag" % name
@@ -1568,7 +1571,7 @@ def exportGitTags(self, gitTags):
15681571

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

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

1799-
class Path(object):
1800-
"""A depot or client path, possibly containing wildcards.
1801-
The only one supported is ... at the end, currently.
1802-
Initialize with the full path, with //depot or //client."""
1803-
1804-
def __init__(self, path, is_depot):
1805-
self.path = path
1806-
self.is_depot = is_depot
1807-
self.find_wildcards()
1808-
# remember the prefix bit, useful for relative mappings
1809-
m = re.match("(//[^/]+/)", self.path)
1810-
if not m:
1811-
die("Path %s does not start with //prefix/" % self.path)
1812-
prefix = m.group(1)
1813-
if not self.is_depot:
1814-
# strip //client/ on client paths
1815-
self.path = self.path[len(prefix):]
1816-
1817-
def find_wildcards(self):
1818-
"""Make sure wildcards are valid, and set up internal
1819-
variables."""
1820-
1821-
self.ends_triple_dot = False
1822-
# There are three wildcards allowed in p4 views
1823-
# (see "p4 help views"). This code knows how to
1824-
# handle "..." (only at the end), but cannot deal with
1825-
# "%%n" or "*". Only check the depot_side, as p4 should
1826-
# validate that the client_side matches too.
1827-
if re.search(r'%%[1-9]', self.path):
1828-
die("Can't handle %%n wildcards in view: %s" % self.path)
1829-
if self.path.find("*") >= 0:
1830-
die("Can't handle * wildcards in view: %s" % self.path)
1831-
triple_dot_index = self.path.find("...")
1832-
if triple_dot_index >= 0:
1833-
if triple_dot_index != len(self.path) - 3:
1834-
die("Can handle only single ... wildcard, at end: %s" %
1835-
self.path)
1836-
self.ends_triple_dot = True
1837-
1838-
def ensure_compatible(self, other_path):
1839-
"""Make sure the wildcards agree."""
1840-
if self.ends_triple_dot != other_path.ends_triple_dot:
1841-
die("Both paths must end with ... if either does;\n" +
1842-
"paths: %s %s" % (self.path, other_path.path))
1843-
1844-
def match_wildcards(self, test_path):
1845-
"""See if this test_path matches us, and fill in the value
1846-
of the wildcards if so. Returns a tuple of
1847-
(True|False, wildcards[]). For now, only the ... at end
1848-
is supported, so at most one wildcard."""
1849-
if self.ends_triple_dot:
1850-
dotless = self.path[:-3]
1851-
if test_path.startswith(dotless):
1852-
wildcard = test_path[len(dotless):]
1853-
return (True, [ wildcard ])
1854-
else:
1855-
if test_path == self.path:
1856-
return (True, [])
1857-
return (False, [])
1858-
1859-
def match(self, test_path):
1860-
"""Just return if it matches; don't bother with the wildcards."""
1861-
b, _ = self.match_wildcards(test_path)
1862-
return b
1863-
1864-
def fill_in_wildcards(self, wildcards):
1865-
"""Return the relative path, with the wildcards filled in
1866-
if there are any."""
1867-
if self.ends_triple_dot:
1868-
return self.path[:-3] + wildcards[0]
1869-
else:
1870-
return self.path
1871-
1872-
class Mapping(object):
1873-
def __init__(self, depot_side, client_side, overlay, exclude):
1874-
# depot_side is without the trailing /... if it had one
1875-
self.depot_side = View.Path(depot_side, is_depot=True)
1876-
self.client_side = View.Path(client_side, is_depot=False)
1877-
self.overlay = overlay # started with "+"
1878-
self.exclude = exclude # started with "-"
1879-
assert not (self.overlay and self.exclude)
1880-
self.depot_side.ensure_compatible(self.client_side)
1881-
1882-
def __str__(self):
1883-
c = " "
1884-
if self.overlay:
1885-
c = "+"
1886-
if self.exclude:
1887-
c = "-"
1888-
return "View.Mapping: %s%s -> %s" % \
1889-
(c, self.depot_side.path, self.client_side.path)
1890-
1891-
def map_depot_to_client(self, depot_path):
1892-
"""Calculate the client path if using this mapping on the
1893-
given depot path; does not consider the effect of other
1894-
mappings in a view. Even excluded mappings are returned."""
1895-
matches, wildcards = self.depot_side.match_wildcards(depot_path)
1896-
if not matches:
1897-
return ""
1898-
client_path = self.client_side.fill_in_wildcards(wildcards)
1899-
return client_path
1900-
1901-
#
1902-
# View methods
1903-
#
1904-
def __init__(self):
1802+
def __init__(self, client_name):
19051803
self.mappings = []
1804+
self.client_prefix = "//%s/" % client_name
1805+
# cache results of "p4 where" to lookup client file locations
1806+
self.client_spec_path_cache = {}
19061807

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

19111813
# Split the view line into exactly two words. P4 enforces
19121814
# structure on these lines that simplifies this quite a bit.
@@ -1934,76 +1836,62 @@ def append(self, view_line):
19341836
depot_side = view_line[0:space_index]
19351837
rhs_index = space_index + 1
19361838

1937-
if view_line[rhs_index] == '"':
1938-
# Second word is double quoted. Make sure there is a
1939-
# double quote at the end too.
1940-
if not view_line.endswith('"'):
1941-
die("View line with rhs quote should end with one: %s" %
1942-
view_line)
1943-
# skip the quotes
1944-
client_side = view_line[rhs_index+1:-1]
1945-
else:
1946-
client_side = view_line[rhs_index:]
1947-
19481839
# prefix + means overlay on previous mapping
1949-
overlay = False
19501840
if depot_side.startswith("+"):
1951-
overlay = True
19521841
depot_side = depot_side[1:]
19531842

1954-
# prefix - means exclude this path
1843+
# prefix - means exclude this path, leave out of mappings
19551844
exclude = False
19561845
if depot_side.startswith("-"):
19571846
exclude = True
19581847
depot_side = depot_side[1:]
19591848

1960-
m = View.Mapping(depot_side, client_side, overlay, exclude)
1961-
self.mappings.append(m)
1849+
if not exclude:
1850+
self.mappings.append(depot_side)
19621851

1963-
def map_in_client(self, depot_path):
1964-
"""Return the relative location in the client where this
1965-
depot file should live. Returns "" if the file should
1966-
not be mapped in the client."""
1852+
def convert_client_path(self, clientFile):
1853+
# chop off //client/ part to make it relative
1854+
if not clientFile.startswith(self.client_prefix):
1855+
die("No prefix '%s' on clientFile '%s'" %
1856+
(self.client_prefix, clientFile))
1857+
return clientFile[len(self.client_prefix):]
19671858

1968-
paths_filled = []
1969-
client_path = ""
1859+
def update_client_spec_path_cache(self, files):
1860+
""" Caching file paths by "p4 where" batch query """
19701861

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

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

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

1986-
else:
1987-
# This mapping matched; no need to search any further.
1988-
# But, the mapping could be rejected if the client path
1989-
# has already been claimed by an earlier mapping (i.e.
1990-
# one later in the list, which we are walking backwards).
1991-
already_mapped_in_client = False
1992-
for f in paths_filled:
1993-
# this is View.Path.match
1994-
if f.match(p):
1995-
already_mapped_in_client = True
1996-
break
1997-
if not already_mapped_in_client:
1998-
# Include this file, unless it is from a line that
1999-
# explicitly said to exclude it.
2000-
if not m.exclude:
2001-
client_path = p
1880+
# not found files or unmap files set to ""
1881+
for depotFile in fileArgs:
1882+
if depotFile not in self.client_spec_path_cache:
1883+
self.client_spec_path_cache[depotFile] = ""
20021884

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

2006-
return client_path
1890+
if depot_path in self.client_spec_path_cache:
1891+
return self.client_spec_path_cache[depot_path]
1892+
1893+
die( "Error: %s is not found in client spec path" % depot_path )
1894+
return ""
20071895

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

2021+
if self.clientSpecDirs:
2022+
files = self.extractFilesFromCommit(commit)
2023+
self.clientSpecDirs.update_client_spec_path_cache(files)
2024+
21332025
branches = {}
21342026
fnum = 0
21352027
while commit.has_key("depotFile%s" % fnum):
@@ -2379,6 +2271,9 @@ def commit(self, details, files, branch, parent = ""):
23792271
else:
23802272
sys.stderr.write("Ignoring file outside of prefix: %s\n" % f['path'])
23812273

2274+
if self.clientSpecDirs:
2275+
self.clientSpecDirs.update_client_spec_path_cache(files)
2276+
23822277
self.gitStream.write("commit %s\n" % branch)
23832278
# gitStream.write("mark :%s\n" % details["change"])
23842279
self.committedChanges.add(int(details["change"]))

0 commit comments

Comments
 (0)