From 85711885e606a195db2272e3ddf6300debba49f9 Mon Sep 17 00:00:00 2001 From: federico-g Date: Mon, 29 Apr 2024 00:28:16 +0200 Subject: [PATCH 1/4] 8.4. A detour: the check-ignore command --- libtft.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/libtft.py b/libtft.py index 18986d0..b5f134c 100644 --- a/libtft.py +++ b/libtft.py @@ -380,3 +380,45 @@ def cmd_hash_object(args): with open(args.path, "rb") as fd: sha = object_hash(fd, args.type.encode(), repo) print(sha) + + +argsp = argsubparsers.add_parser("check-ignore", help = "Check path(s) against ignore rules.") +argsp.add_argument("path", nargs="+", help="Paths to check") + +#Check-ignore function +def cmd_check_ignore(args): + repo = repo_find() + rules = gitignore_read(repo) + for path in args.path: + if check_ignore(rules, path): + print(path) + +def gitignore_parse1(raw): + raw = raw.strip() #remove space + + if not raw or raw[0] == "#": + return None + elif raw[0] == "!": + return (raw[1:], False) + elif raw[0] == "\\": + return (raw[1:], True) + else: + return (raw, True) + +def gitignore_parse(lines): + ret = list() #rules list + + for line in lines: + parsed = gitignore_parse1(line) + if parsed: + ret.append(parsed) + + return ret + +class GitIgnore(object): + absolute = None + scoped = None + + def __init__(self, absolute, scoped): + self.absolute = absolute + self.scoped = scoped \ No newline at end of file From 5600b80192b56c4a309c513078d29e7b86414d29 Mon Sep 17 00:00:00 2001 From: federico-g Date: Tue, 30 Apr 2024 00:23:27 +0200 Subject: [PATCH 2/4] update of 8.4 --- .vscode/settings.json | 5 +++++ libtft.py | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b242572 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "githubPullRequests.ignoredPullRequestBranches": [ + "main" + ] +} \ No newline at end of file diff --git a/libtft.py b/libtft.py index b5f134c..f6f3c11 100644 --- a/libtft.py +++ b/libtft.py @@ -421,4 +421,42 @@ class GitIgnore(object): def __init__(self, absolute, scoped): self.absolute = absolute - self.scoped = scoped \ No newline at end of file + self.scoped = scoped + +def gitignore_read(repo): + ret = GitIgnore(absolute=list(), scoped=dict()) + + #read local configuration: .git/info/exclude + repo_file = os.path.join(repo.gitfir, "info/exclude") + if os.path.exists(repo_file): + with open(repo_file, "r") as f: + ret.absolute.append(gitignore_parse(f.readlines())) + + #global configuration + if "XDG_CONFIG_HOME" in os.environ: + config_home = os.environ["XDG_CONFIG_HOME"] + else: + config_home = os.path.expanduser("~/.config") + global_file = os.path.join(config_home,"git/ignore") + + if os.path.exists(global_file): + with open(global_file, "r") as f: + ret.absolute.append(gitignore_parse(f.readlines())) + + # .gitignore files in the index + index = index_read(repo) + for entry in index.entries: + if entry.name == ".gitignore" or entry.name.endswitch("/.gitignore"): + dir_name = os.path.dirname(entry.name) + contents = object_read(repo, entry.sha) + lines = contents.blobdata.decode("utf8").splitlines() + ret.scoped[dir_name] = gitignore_parse(lines) + return ret + +#function check match with rules +def check_ignore1(rules, path): + result = None # nothing matched + for(pattern, value) in rules: + if fnmatch(path, pattern): + result = value + return result #true or false From b569548820c5328e1a28973d79997a92a10a9402 Mon Sep 17 00:00:00 2001 From: federico-g Date: Wed, 1 May 2024 23:44:02 +0200 Subject: [PATCH 3/4] end of implementation --- git-python-rewrite | 1 + libtft.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 160000 git-python-rewrite diff --git a/git-python-rewrite b/git-python-rewrite new file mode 160000 index 0000000..8571188 --- /dev/null +++ b/git-python-rewrite @@ -0,0 +1 @@ +Subproject commit 85711885e606a195db2272e3ddf6300debba49f9 diff --git a/libtft.py b/libtft.py index f6f3c11..098a726 100644 --- a/libtft.py +++ b/libtft.py @@ -460,3 +460,36 @@ def check_ignore1(rules, path): if fnmatch(path, pattern): result = value return result #true or false + +def check_ignore_scoped(rules, path): + #Check ignore rules in parent directories + parent = os.path.dirname(path) + while True: + if parent in rules: + result = check_ignore1(rules[parent], path) + if result != None: + return result + if parent == "": + break + parent = os.path.dirname(parent) + return None + +def check_ignore_absolute(rules, path): + #Check ignore rules in absolute paths + parent = os.path.dirname(path) + for ruleset in rules: + result = check_ignore1(ruleset, path) + if result != None: + return result + return False # This is a reasonable default at this point. + +def check_ignore(rules, path): + #Check if a given path is ignored based on the provided ignore rules + if os.path.isabs(path): + raise Exception("This function requires path to be relative to the repository's root") + + result = check_ignore_scoped(rules.scoped, path) + if result != None: + return result + + return check_ignore_absolute(rules.absolute, path) From b56ba717c3cf1b2239ffb22b1cc5f5169cbd2da4 Mon Sep 17 00:00:00 2001 From: federico-g Date: Wed, 8 May 2024 23:27:45 +0200 Subject: [PATCH 4/4] Add remove command --- libtft.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/libtft.py b/libtft.py index 098a726..1867039 100644 --- a/libtft.py +++ b/libtft.py @@ -493,3 +493,51 @@ def check_ignore(rules, path): return result return check_ignore_absolute(rules.absolute, path) + +#Rm command +argsp = argsubparsers.add_parser("rm", help="Remove files from the working tree and the index.") +argsp.add_argument("path", nargs="+", help="Files to remove") + +def cmd_rm(args): + repo = repo_find() + rm(repo, args.path) + +#Remove function +def rm(repo, paths, delete=True, skip_missing=False): + # Find and read the index + index = index_read(repo) + + worktree = repo.worktree + os.sep + + # Make paths absolute + abspaths = list() + for path in paths: + abspath = os.path.abspath(path) + if abspath.startswith(worktree): + abspaths.append(abspath) + else: + raise Exception("Cannot remove paths outside of worktree: {}".format(paths)) + + kept_entries = list() + remove = list() + + for e in index.entries: + full_path = os.path.join(repo.worktree, e.name) + + if full_path in abspaths: + remove.append(full_path) + abspaths.remove(full_path) + else: + kept_entries.append(e) # Preserve entry + + if len(abspaths) > 0 and not skip_missing: + raise Exception("Cannot remove paths not in the index: {}".format(abspaths)) + + #Delete files + if delete: + for path in remove: + os.unlink(path) + + #Index update + index.entries = kept_entries + index_write(repo, index) \ No newline at end of file