Compare commits

..

6 Commits

Author SHA1 Message Date
c484b41a89 Don't always derefence ref 2024-07-06 19:50:52 +02:00
fe5ed910a3 Add change 34 to instructions 2024-07-06 19:49:59 +02:00
9c53919802 Dereference refs when reading and writing 2024-06-29 18:48:21 +02:00
30ce8c84e4 Add change 33 to instructions 2024-06-29 18:47:23 +02:00
6841a97d18 Create RefValue container 2024-06-14 16:29:58 +02:00
556c16c081 Add change 32 to instructions 2024-06-14 16:29:25 +02:00
6 changed files with 69 additions and 15 deletions

8
how_to/Change_32.md Normal file
View File

@@ -0,0 +1,8 @@
- data: Create Refvalue container
To make working with symbolic refs easier, we will create a `Refvalue` container
to represent the value of a ref. `Refvalue` will have a property symbolic that
will say whether it's a symbolic or a direct ref.
This change is just refactoring, we will wrap every OID that is written or read
from a ref in a `RefValue`.

17
how_to/Change_33.md Normal file
View File

@@ -0,0 +1,17 @@
data: Dereference refs when reading and writing
Now we'll dereference symbolic refs not only when reading them but also when
writing them.
We'll implement a helper function called `_get_ref_internal` which will return
the path and the value of the last ref pointed by a symbolic ref. In simple words:
- When given a non-symbolic ref, `_get_ref_internal` will return the ref name
and value.
- When given a symbolic ref, `_get_ref_internal` will dereference the ref
recursively, and then return the name of the last (non-symbolic) ref that points
to an OID, plus its value.
Now `update_ref` will use `_get_ref_internal` to know which ref it needs to update.
Additionally, we'll use `_get_ref_internal` in `get_ref`.

15
how_to/Change_34.md Normal file
View File

@@ -0,0 +1,15 @@
- data: Don't always dereference refs (for `ugit k`)
Actually, it's not always desirable to dereference a ref all the way. Sometimes
we would like to know at which ref a symbolic ref points, rather than the final
OID. Or we would like to update a ref directly, rather then updating the last
ref in the chain.
One such usecase is `ugit k`. When visualizing refs it would be nice to see
which ref points to which ref. We will see another usecase soon.
To accomodate this, we will add a `deref` option to `get_ref`, `iter_refs` and
`update_ref`. If they will be called with `deref=False`, they will work on the
raw value of a ref and not dereference any symbolic refs.
Then we will update `k` to use `deref=False`.

View File

@@ -84,7 +84,7 @@ def read_tree(tree_oid):
def commit(message): def commit(message):
commit = f"tree {write_tree()}\n" commit = f"tree {write_tree()}\n"
HEAD = data.get_ref("HEAD") HEAD = data.get_ref("HEAD").value
if HEAD: if HEAD:
commit += f"parent {HEAD}\n" commit += f"parent {HEAD}\n"
@@ -93,23 +93,23 @@ def commit(message):
oid = data.hash_object(commit.encode(), "commit") oid = data.hash_object(commit.encode(), "commit")
data.update_ref("HEAD", oid) data.update_ref("HEAD", data.RefValue(symbolic=False, value=oid))
return oid return oid
def create_tag(name, oid): def create_tag(name, oid):
data.update_ref(f"refs/tags/{name}", oid) data.update_ref(f"refs/tags/{name}", data.RefValue(symbolic=False, value=oid))
def checkout(oid): def checkout(oid):
commit = get_commit(oid) commit = get_commit(oid)
read_tree(commit.tree) read_tree(commit.tree)
data.update_ref("HEAD", oid) data.update_ref("HEAD", data.RefValue(symbolic=False, value=oid))
def create_branch(name, oid): def create_branch(name, oid):
data.update_ref(f"refs/heads/{name}", oid) data.update_ref(f"refs/heads/{name}", data.RefValue(symbolic=False, value=oid))
Commit = namedtuple("Commit", ["tree", "parent", "message"]) Commit = namedtuple("Commit", ["tree", "parent", "message"])
@@ -161,8 +161,8 @@ def get_oid(name):
f"refs/heads/{name}", f"refs/heads/{name}",
] ]
for ref in refs_to_try: for ref in refs_to_try:
if data.get_ref(ref): if data.get_ref(ref, deref=False).value:
return data.get_ref(ref) return data.get_ref(ref).value
# Name is SHA1 # Name is SHA1
is_hex = all(c in string.hexdigits for c in name) is_hex = all(c in string.hexdigits for c in name)

View File

@@ -121,10 +121,11 @@ def k(args):
dot = "digraph commits {\n" dot = "digraph commits {\n"
oids = set() oids = set()
for refname, ref in data.iter_refs(): for refname, ref in data.iter_refs(deref=False):
dot += f"'{refname}' [shape=note]\n" dot += f"'{refname}' [shape=note]\n"
dot += f"'{refname}' -> '{ref}'\n" dot += f"'{refname}' -> '{ref.value}'\n"
oids.add(ref) if not ref.symbolic:
oids.add(ref.value)
for oid in base.iter_commits_and_parents(oids): for oid in base.iter_commits_and_parents(oids):
commit = base.get_commit(oid) commit = base.get_commit(oid)

View File

@@ -3,6 +3,8 @@ from pathlib import Path, PurePath
import hashlib import hashlib
import os import os
from collections import namedtuple
GIT_DIR = ".ugit" GIT_DIR = ".ugit"
@@ -11,24 +13,35 @@ def init():
Path.mkdir(f"{GIT_DIR}/objects") Path.mkdir(f"{GIT_DIR}/objects")
def update_ref(ref, oid): RefValue = namedtuple("RefValue", ["symbolic", "value"])
def update_ref(ref, value, deref=True):
assert not value.symbolic
ref = _get_ref_internal(ref, deref)[0]
ref_path = f"{GIT_DIR}/{ref}" ref_path = f"{GIT_DIR}/{ref}"
Path.mkdir(ref_path, exist_ok=True) Path.mkdir(ref_path, exist_ok=True)
with open(ref_path, "w") as f: with open(ref_path, "w") as f:
f.write(oid) f.write(value.value)
def get_ref(ref): def get_ref(ref):
return _get_ref_internal(ref)[1]
def _get_ref_internal(ref):
ref_path = f"{GIT_DIR}/{ref}" ref_path = f"{GIT_DIR}/{ref}"
value = None value = None
if Path.is_file(ref_path): if Path.is_file(ref_path):
with open(ref_path) as f: with open(ref_path) as f:
value = f.read().strip() value = f.read().strip()
if value and value.startswith("ref:"): symbolic = bool(value) and value.startswith("ref")
return get_ref(value.split(":", 1)[1].strip()) if symbolic:
value = value.split(":", 1)[1].strip()
return _get_ref_internal(value)
return value return ref, RefValue(symbolic=False, value=value)
def iter_refs(): def iter_refs():