|
@@ -408,6 +408,57 @@ def add(repo=".", paths=None):
|
|
return (relpaths, ignored)
|
|
return (relpaths, ignored)
|
|
|
|
|
|
|
|
|
|
|
|
+def _is_subdir(subdir, parentdir):
|
|
|
|
+ """Check whether subdir is parentdir or a subdir of parentdir
|
|
|
|
+
|
|
|
|
+ If parentdir or subdir is a relative path, it will be disamgibuated
|
|
|
|
+ relative to the pwd.
|
|
|
|
+ """
|
|
|
|
+ parentdir_abs = os.path.realpath(parentdir) + os.path.sep
|
|
|
|
+ subdir_abs = os.path.realpath(subdir) + os.path.sep
|
|
|
|
+ return subdir_abs.startswith(parentdir_abs)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+# TODO: option to remove ignored files also, in line with `git clean -fdx`
|
|
|
|
+def clean(repo=".", target_dir=None):
|
|
|
|
+ """Remove any untracked files from the target directory recursively
|
|
|
|
+
|
|
|
|
+ Equivalent to running `git clean -fd` in target_dir.
|
|
|
|
+
|
|
|
|
+ :param repo: Repository where the files may be tracked
|
|
|
|
+ :param target_dir: Directory to clean - current directory if None
|
|
|
|
+ """
|
|
|
|
+ if target_dir is None:
|
|
|
|
+ target_dir = os.getcwd()
|
|
|
|
+
|
|
|
|
+ if not _is_subdir(target_dir, repo):
|
|
|
|
+ raise ValueError("target_dir must be in the repo's working dir")
|
|
|
|
+
|
|
|
|
+ with open_repo_closing(repo) as r:
|
|
|
|
+ index = r.open_index()
|
|
|
|
+ ignore_manager = IgnoreFilterManager.from_repo(r)
|
|
|
|
+
|
|
|
|
+ paths_in_wd = _walk_working_dir_paths(target_dir, r.path)
|
|
|
|
+ # Reverse file visit order, so that files and subdirectories are
|
|
|
|
+ # removed before containing directory
|
|
|
|
+ for ap, is_dir in reversed(list(paths_in_wd)):
|
|
|
|
+ if is_dir:
|
|
|
|
+ # All subdirectories and files have been removed if untracked,
|
|
|
|
+ # so dir contains no tracked files iff it is empty.
|
|
|
|
+ is_empty = len(os.listdir(ap)) == 0
|
|
|
|
+ if is_empty:
|
|
|
|
+ os.rmdir(ap)
|
|
|
|
+ else:
|
|
|
|
+ ip = path_to_tree_path(r.path, ap)
|
|
|
|
+ is_tracked = ip in index
|
|
|
|
+
|
|
|
|
+ rp = os.path.relpath(ap, r.path)
|
|
|
|
+ is_ignored = ignore_manager.is_ignored(rp)
|
|
|
|
+
|
|
|
|
+ if not is_tracked and not is_ignored:
|
|
|
|
+ os.remove(ap)
|
|
|
|
+
|
|
|
|
+
|
|
def remove(repo=".", paths=None, cached=False):
|
|
def remove(repo=".", paths=None, cached=False):
|
|
"""Remove files from the staging area.
|
|
"""Remove files from the staging area.
|
|
|
|
|
|
@@ -900,14 +951,12 @@ def status(repo=".", ignored=False):
|
|
return GitStatus(tracked_changes, unstaged_changes, untracked_changes)
|
|
return GitStatus(tracked_changes, unstaged_changes, untracked_changes)
|
|
|
|
|
|
|
|
|
|
-def get_untracked_paths(frompath, basepath, index):
|
|
|
|
- """Get untracked paths.
|
|
|
|
|
|
+def _walk_working_dir_paths(frompath, basepath):
|
|
|
|
+ """Get path, is_dir for files in working dir from frompath
|
|
|
|
|
|
- ;param frompath: Path to walk
|
|
|
|
|
|
+ :param frompath: Path to begin walk
|
|
:param basepath: Path to compare to
|
|
:param basepath: Path to compare to
|
|
- :param index: Index to check against
|
|
|
|
"""
|
|
"""
|
|
- # If nothing is specified, add all non-ignored files.
|
|
|
|
for dirpath, dirnames, filenames in os.walk(frompath):
|
|
for dirpath, dirnames, filenames in os.walk(frompath):
|
|
# Skip .git and below.
|
|
# Skip .git and below.
|
|
if '.git' in dirnames:
|
|
if '.git' in dirnames:
|
|
@@ -918,8 +967,24 @@ def get_untracked_paths(frompath, basepath, index):
|
|
filenames.remove('.git')
|
|
filenames.remove('.git')
|
|
if dirpath != basepath:
|
|
if dirpath != basepath:
|
|
continue
|
|
continue
|
|
|
|
+
|
|
|
|
+ if dirpath != frompath:
|
|
|
|
+ yield dirpath, True
|
|
|
|
+
|
|
for filename in filenames:
|
|
for filename in filenames:
|
|
- ap = os.path.join(dirpath, filename)
|
|
|
|
|
|
+ filepath = os.path.join(dirpath, filename)
|
|
|
|
+ yield filepath, False
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def get_untracked_paths(frompath, basepath, index):
|
|
|
|
+ """Get untracked paths.
|
|
|
|
+
|
|
|
|
+ ;param frompath: Path to walk
|
|
|
|
+ :param basepath: Path to compare to
|
|
|
|
+ :param index: Index to check against
|
|
|
|
+ """
|
|
|
|
+ for ap, is_dir in _walk_working_dir_paths(frompath, basepath):
|
|
|
|
+ if not is_dir:
|
|
ip = path_to_tree_path(basepath, ap)
|
|
ip = path_to_tree_path(basepath, ap)
|
|
if ip not in index:
|
|
if ip not in index:
|
|
yield os.path.relpath(ap, frompath)
|
|
yield os.path.relpath(ap, frompath)
|