Compare commits
6 Commits
7a0f86e49b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c484b41a89 | |||
| fe5ed910a3 | |||
| 9c53919802 | |||
| 30ce8c84e4 | |||
| 6841a97d18 | |||
| 556c16c081 |
8
how_to/Change_32.md
Normal file
8
how_to/Change_32.md
Normal 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
17
how_to/Change_33.md
Normal 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
15
how_to/Change_34.md
Normal 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`.
|
||||||
14
ugit/base.py
14
ugit/base.py
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
23
ugit/data.py
23
ugit/data.py
@@ -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():
|
||||||
|
|||||||
Reference in New Issue
Block a user