Skip to content

Commit

Permalink
Fixes #101 and some more
Browse files Browse the repository at this point in the history
  • Loading branch information
ArneBachmann committed Dec 27, 2017
1 parent 03be659 commit 2ec4a76
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 17 deletions.
35 changes: 22 additions & 13 deletions sos/sos.coco
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Usage: {cmd} <command> [<argument>] [<option1>, ...] When operating in of
--soft Don't move or rename files, only the tracking pattern
rm [<filename or glob pattern>] Remove a tracking pattern. Only useful after "offline --track" or "offline --picky"

ls List file tree and mark changes and tracking status
ls [<folder path>] [--patterns] List file tree and mark changes and tracking status
status List branches and display repository status
log [--changes] List commits of current branch
config [set/unset/show/add/rm] [<param> [<value>]] Configure user-global defaults.
Expand Down Expand Up @@ -550,6 +550,9 @@ def status() -> None:
m.loadBranches() # knows current branch
current:int = m.branch
info("//|\\\\ Offline repository status")
print("Content checking %sactivated" % ("" if m.strict else "de"))
print("Data compression %sactivated" % ("" if m.compress else "de"))
print("Repository is in %s mode" % ("tracking" if m.track else ("picky" if m.picky else "simple")))
sl:int = max([len(b.name ?? "") for b in m.branches.values()])
for branch in sorted(m.branches.values(), key = (b) -> b.number):
m.loadBranch(branch.number) # knows commit history
Expand Down Expand Up @@ -712,26 +715,31 @@ def remove(relPath:str, pattern:str) -> None:
m.saveBranches()
info("Removed tracking pattern '%s' for folder '%s'" % (os.path.basename(pattern), os.path.abspath(relPath.replace(SLASH, os.sep))))

def ls(argument:str? = None) -> None:
def ls(argument:str? = None, options:str[] = []) -> None:
''' List specified directory, augmenting with repository metadata. '''
cwd:str = os.getcwd() if argument is None else argument
m:Metadata = Metadata(cwd)
m.loadBranches()
info("Repository is in %s mode" % ("tracking" if m.track else ("picky" if m.picky else "simple")))
relPath:str = os.path.relpath(cwd, m.root).replace(os.sep, SLASH)
trackingPatterns:FrozenSet[str]? = m.getTrackingPatterns() if m.track or m.picky else f{} # for current branch
if '--patterns' in options:
out:str = ajoin("TRK ", [p for p in trackingPatterns if os.path.dirname(p).replace(os.sep, SLASH) == relPath], nl = "\n")
if out: print(out)
return
files:List[str] = list(sorted(entry for entry in os.listdir(cwd) if os.path.isfile(entry)))
trackingPatterns:FrozenSet[str] = m.getTrackingPatterns() if m.track or m.picky else f{} # for current branch
for file in files:
for file in files: # for each file list all tracking patterns that match, or none (e.g. in picky mode after commit)
ignore:str? = None
for ig in m.c.ignores:
if fnmatch.fnmatch(file, ig): ignore = ig; break # remember first match TODO document this
if ig:
for wl in m.c.ignoresWhitelist:
if fnmatch.fnmatch(file, wl): ignore = None; break # found a white list entry for ignored file, undo ignoring it
if ignore is None:
if not ignore:
matches:List[str] = []
for pattern in (p for p in trackingPatterns if os.path.dirname(p).replace(os.sep, SLASH) == relPath): # only patterns matching current folder
if fnmatch.fnmatch(file, os.path.basename(pattern)): matches.append(pattern) # TODO or only file basename?
print("%s %s%s" % ("IGN" if ignore is not None else ("TRK" if len(matches) > 0 else " "), file, ' by "%s"' % ignore if ignore is not None else (" by " + ";".join(['"%s"' % match for match in matches]) if len(matches) > 0 else "")))
if fnmatch.fnmatch(file, os.path.basename(pattern)): matches.append(pattern)
print("%s %s%s" % ("IGN" if ignore is not None else ("TRK" if len(matches) > 0 else "..."), file, ' by "%s"' % ignore if ignore is not None else (" by " + ";".join(['"%s"' % match for match in matches]) if len(matches) > 0 else "")))

def log(options:str[]) -> None:
''' List previous commits on current branch. '''
Expand Down Expand Up @@ -796,14 +804,15 @@ def move(relPath:str, pattern:str, newRelPath:str, newPattern:str, options:List[
if not (force or soft): Exit("File or glob pattern '%s' is not tracked on current branch. 'sos move' only works on tracked patterns" % pattern)
basePattern:str = os.path.basename(pattern) # pure glob without folder
newBasePattern:str = os.path.basename(newPattern)
if basePattern.count("*") != newBasePattern.count("*")\
or (basePattern.count("?") - basePattern.count("[?]")) != (newBasePattern.count("?") - newBasePattern.count("[?]"))\
or (basePattern.count("[") - basePattern.count("\\[")) != (newBasePattern.count("[") - newBasePattern.count("\\["))\
or (basePattern.count("]") - basePattern.count("\\]")) != (newBasePattern.count("]") - newBasePattern.count("\\]")):
if basePattern.count("*") < newBasePattern.count("*")\
or (basePattern.count("?") - basePattern.count("[?]")) < (newBasePattern.count("?") - newBasePattern.count("[?]"))\
or (basePattern.count("[") - basePattern.count("\\[")) < (newBasePattern.count("[") - newBasePattern.count("\\["))\
or (basePattern.count("]") - basePattern.count("\\]")) < (newBasePattern.count("]") - newBasePattern.count("\\]")):
Exit("Glob markers from '%s' to '%s' don't match, cannot move/rename tracked matching files" % (basePattern, newBasePattern))
oldTokens:GlobBlock[]; newToken:GlobBlock[]
oldTokens, newTokens = tokenizeGlobPatterns(os.path.basename(pattern), os.path.basename(newPattern))
matches:Tuple[str,str][] = convertGlobFiles(matching, oldTokens, newTokens) # computes list of source - target filename pairs
if len(s{st[1] for st in matches}) != len(matches): Exit("Some target filenames are not unique and different move/rename actions would point to the same target file")
matches = reorderRenameActions(matches, exitOnConflict = not soft) # attempts to find conflict-free renaming order, or exits
if os.path.exists(newRelPath):
exists:str[] = [filename[1] for filename in matches if os.path.exists(os.path.join(newRelPath, filename[1]).replace(SLASH, os.sep))]
Expand Down Expand Up @@ -838,8 +847,8 @@ def parse(root:str, cwd:str):
elif command[:2] == "di": diff(argument, options, onlys, excps)
elif command[:1] == "h": usage()
elif command[:2] == "lo": log(options)
elif command[:2] == "li": ls(argument)
elif command[:2] == "ls": ls(argument)
elif command[:2] == "li": ls(argument, options)
elif command[:2] == "ls": ls(argument, options)
elif command[:1] == "m": move(relPath, pattern, newRelPath, newPattern, options[1:])
elif command[:2] == "of": offline(argument, options)
elif command[:2] == "on": online(options)
Expand Down
11 changes: 9 additions & 2 deletions sos/tests.coco
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def wrapChannels(func: -> Any) =
logging.getLogger().addHandler(handler)
try: func() # capture output into buf
except Exception as E: buf.write(str(E) + "\n"); traceback.print_exc(file = buf)
except SystemExit as F: buf.write(str(F) + "\n"); traceback.print_exc(file = buf)
logging.getLogger().removeHandler(handler)
sys.argv, sys.stdout, sys.stderr = oldv, oldo, olde
buf.getvalue()
Expand Down Expand Up @@ -596,6 +597,8 @@ class Tests(unittest.TestCase):
_.assertInAny('TRK file1 by "./file*"', out)
_.assertNotInAny(' file1 by "./file*"', out)
_.assertInAny(" foo", out)
out = sos.safeSplit(wrapChannels(() -> sos.ls(options = ["--patterns"])).replace("\r", ""), "\n")
_.assertInAny("TRK ./file*", out)

def testCompression(_):
_.createFile(1)
Expand Down Expand Up @@ -733,7 +736,6 @@ class Tests(unittest.TestCase):
sos.add(".", "./file2")
sos.commit(onlys = f{"./file1"})
_.assertEqual(2, len(os.listdir(branchFolder(0, 1)))) # only meta and file1
import pdb; pdb.set_trace()
sos.commit() # adds also file2
_.assertEqual(2, len(os.listdir(branchFolder(0, 2)))) # only meta and file1
_.createFile(1, "cc") # modify both files
Expand Down Expand Up @@ -802,7 +804,12 @@ class Tests(unittest.TestCase):
sos.commit()
_.assertEqual(2, len(os.listdir(branchFolder(0, 1))))
_.assertTrue(os.path.exists(branchFolder(0, 1) + os.sep + "93b38f90892eb5c57779ca9c0b6fbdf6774daeee3342f56f3e78eb2fe5336c50")) # a1b2
# only rename if actually any files are versioned? or simply what is alife?
_.createFile("1a1b1")
_.createFile("1a1b2")
sos.add(".", "?a?b*")
_.assertIn("not unique", wrapChannels(() -> sos.move(".", "?a?b*", ".", "z?z?"))) # should raise error due to same target name
# TODO only rename if actually any files are versioned? or simply what is alife?
# TODO add test if two single question marks will be moved into adjacent characters

def testFindBase(_):
old = os.getcwd()
Expand Down
4 changes: 2 additions & 2 deletions sos/utility.coco
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ try: Splittable = TypeVar("Splittable", str, bytes)
except: pass # Python 2
def safeSplit(s:Splittable, d:Splittable? = None) -> Splittable[]: return s.split(d ?? ("\n" if isinstance(s, str) else b"\n")) if len(s) > 0 else []

def ajoin(sep:str, seq:str[], nl = "") -> str = sep + (nl + sep).join(seq) if seq else ""
def ajoin(sep:str, seq:str[], nl = "") -> str = (sep + (nl + sep).join(seq)) if seq else ""

def sjoin(*s:Tuple[Any]) -> str = " ".join([str(e) for e in s if e != ''])

Expand Down Expand Up @@ -333,7 +333,7 @@ def tokenizeGlobPatterns(oldPattern:str, newPattern:str) -> Tuple[GlobBlock[], G
ot:List[GlobBlock] = tokenizeGlobPattern(oldPattern)
nt:List[GlobBlock] = tokenizeGlobPattern(newPattern)
# if len(ot) != len(nt): Exit("Source and target patterns can't be translated due to differing number of parsed glob markers and literal strings")
if len([o for o in ot if not o.isLiteral]) != len([n for n in nt if not n.isLiteral]): Exit("Source and target file patterns contain differing number of glob markers and can't be translated")
if len([o for o in ot if not o.isLiteral]) < len([n for n in nt if not n.isLiteral]): Exit("Source and target file patterns contain differing number of glob markers and can't be translated")
if any(O.content != N.content for O, N in zip([o for o in ot if not o.isLiteral], [n for n in nt if not n.isLiteral])): Exit("Source and target file patterns differ in semantics")
(ot, nt)

Expand Down

0 comments on commit 2ec4a76

Please sign in to comment.