diff --git a/add b/add index 81ed505..ff15bb5 100755 --- a/add +++ b/add @@ -1,43 +1,29 @@ #!/usr/bin/python # update-index.c:add_file_to_cache -import struct +import os import subprocess import sys -# TODO write in C? do all macs have gcc? -# TODO if cache doesn't exist? -index = open('.zit/index').read() -assert index[0:4] == 'DIRC' -assert struct.unpack('>I', index[4:8]) == (2,) -num_entries, = struct.unpack('>I', index[8:12]) -entries = [] -i = 12 -for _ in range(num_entries): - # ignore inode heuristics, file/symlink bit, permissions - i += 40 - sha1 = index[i:i+20].encode('hex') - # just use NUL-terminated? - name_length = struct.unpack('>H', index[i+20:i+22])[0] & 0xfff - name = index[i+22:i+22+name_length] - i += (22 + name_length + 8) / 8 * 8 - entries.append((name, sha1)) +import index -_, filename = sys.argv -# TODO handle deleted files? -sha1 = subprocess.check_output(['hash-object', 'blob', filename]).strip() -# TODO canonicalize filename? -entries.append((filename, sha1)) +filenames = subprocess.check_output(['find'] + sys.argv[1:] + ['!', '-type', 'd']).splitlines() +entries = index.read_index() +for filename in filenames: + entries = [e for e in entries if e[0] != filename] + if os.path.exists(filename): + if os.path.islink(filename): + mode = 0120000 + with open('.zit/blob.tmp', 'w') as f: + f.write(os.readlink(filename)) + sha1 = subprocess.check_output(['hash-object', 'blob', '.zit/blob.tmp']).strip() + else: + if os.access(filename, os.X_OK): + mode = 0100755 + else: + mode = 0100644 + sha1 = subprocess.check_output(['hash-object', 'blob', filename]).strip() + entries.append((filename, mode, bytearray.fromhex(sha1))) entries.sort() -f = open('.zit/index', 'w') -f.write('DIRC') -f.write(struct.pack('>II', 2, len(entries))) -for filename, sha1 in entries: - f.write('\0' * 24) - f.write(struct.pack('>I', 0b1000000110100100)) # file 644 - f.write('\0' * 12) - f.write(bytearray.fromhex(sha1)) - f.write(struct.pack('>H', len(filename))) - f.write(filename) - f.write('\0' * (8 - (len(filename) + 6) % 8)) +index.write_index(entries) diff --git a/branch b/branch index 804fd95..ed66566 100755 --- a/branch +++ b/branch @@ -8,8 +8,5 @@ if [[ $# -eq 0 ]]; then elif [[ $1 == -d ]]; then rm -f .zit/refs/heads/$2 else - branchname=$1 - startpoint=${2-HEAD} - rev=$(rev-parse $startpoint) - update-ref refs/heads/$branchname $rev + update-ref refs/heads/$1 ${2-HEAD} fi diff --git a/cat-file b/cat-file index a839661..74914cf 100755 --- a/cat-file +++ b/cat-file @@ -1,7 +1,6 @@ #!/usr/bin/python # git1.0 cat-file.c sha1_file.c:read_object_with_reference -# TODO follow commit->tree and tag->object import sys import zlib diff --git a/checkout-index b/checkout-index index 3c0836a..fa49836 100755 --- a/checkout-index +++ b/checkout-index @@ -1,33 +1,20 @@ #!/usr/bin/python import os -import struct import subprocess -# TODO dedup w/ write-tree -index = open('.zit/index').read() -assert index[0:4] == 'DIRC' -assert struct.unpack('>I', index[4:8]) == (2,) -num_entries, = struct.unpack('>I', index[8:12]) -entries = [] -i = 12 -for _ in range(num_entries): - # ignore inode heuristics, file/symlink bit, permissions - i += 40 - sha1 = index[i:i+20].encode('hex') - # just use NUL-terminated? - name_length = struct.unpack('>H', index[i+20:i+22])[0] & 0xfff - name = index[i+22:i+22+name_length] - i += (22 + name_length + 8) / 8 * 8 - entries.append((name, sha1)) +import index +entries = index.read_index() # entry.c:checkout_entry -for filename, sha1 in entries: +for filename, mode, sha1 in entries: assert not filename.startswith('/') assert '..' not in filename - # TODO unlink directories? - if os.path.exists(filename): - os.unlink(filename) subprocess.check_call(['mkdir', '-p', './' + os.path.dirname(filename)]) # TODO git 1.0 doesn't seem to unlink deleted files - subprocess.check_call(['cat-file', 'blob', sha1], stdout=open(filename, 'w')) + subprocess.check_call(['cat-file', 'blob', sha1.encode('hex')], stdout=open(filename, 'w')) + if mode == 0100755: + os.chmod(filename, 0755) + elif mode == 0120000: + target = open(filename).read() + os.symlink(target, filename) diff --git a/commit b/commit index 0703122..7dcfc6c 100755 --- a/commit +++ b/commit @@ -1,7 +1,8 @@ #!/bin/bash -e -# TODO unless this is the initial commit -parents='-p HEAD' +if [[ -f .zit/$(symbolic-ref HEAD) ]]; then + parents='-p HEAD' +fi echo '# write a commit message' > .zit/COMMIT_EDITMSG vi .zit/COMMIT_EDITMSG tree=$(write-tree) diff --git a/commit-tree b/commit-tree index b5f3b2e..3916f87 100755 --- a/commit-tree +++ b/commit-tree @@ -1,5 +1,10 @@ #!/bin/bash -e +export ZIT_AUTHOR_NAME="${ZIT_AUTHOR_NAME-Frank N. Stein}" +export ZIT_AUTHOR_EMAIL="${ZIT_AUTHOR_EMAIL-eat@brains.com}" +export ZIT_COMMITTER_NAME="${ZIT_COMMITTER_NAME-$ZIT_AUTHOR_NAME}" +export ZIT_COMMITTER_EMAIL="${ZIT_COMMITTER_EMAIL-$ZIT_AUTHOR_EMAIL}" + echo tree $1 > .zit/commit.tmp shift while [[ $1 == -p ]]; do @@ -7,8 +12,8 @@ while [[ $1 == -p ]]; do echo parent $parent >> .zit/commit.tmp shift; shift done -echo author "Joe Mou $(date +'%s %z')" >> .zit/commit.tmp -echo committer "Joe Mou $(date +'%s %z')" >> .zit/commit.tmp +echo author "$ZIT_AUTHOR_NAME <$ZIT_AUTHOR_EMAIL> $(date +'%s %z')" >> .zit/commit.tmp +echo committer "$ZIT_COMMITTER_NAME <$ZIT_COMMITTER_EMAIL> $(date +'%s %z')" >> .zit/commit.tmp echo >> .zit/commit.tmp cat >> .zit/commit.tmp diff --git a/get-sha1-basic b/get-sha1-basic index 1b17ebf..1fb76e4 100755 --- a/get-sha1-basic +++ b/get-sha1-basic @@ -4,8 +4,5 @@ if [[ ${#1} -eq 40 ]]; then echo $1 else - cat .zit/$(symbolic-ref $1) 2> /dev/null || \ - cat .zit/$(symbolic-ref refs/$1) 2> /dev/null || \ - cat .zit/$(symbolic-ref refs/tags/$1) 2> /dev/null || \ - cat .zit/$(symbolic-ref refs/heads/$1) + cat .zit/$(symbolic-ref $1) 2> /dev/null || cat .zit/$(symbolic-ref refs/heads/$1) fi diff --git a/index.py b/index.py new file mode 100644 index 0000000..ed46fa0 --- /dev/null +++ b/index.py @@ -0,0 +1,36 @@ +import os +import struct + +def write_index(entries): + f = open('.zit/index', 'w') + f.write('DIRC') + f.write(struct.pack('>II', 2, len(entries))) + for filename, mode, rawsha1 in entries: + f.write('\0' * 24) + f.write(struct.pack('>I', mode)) + f.write('\0' * 12) + f.write(rawsha1) + f.write(struct.pack('>H', len(filename))) + f.write(filename) + f.write('\0' * (8 - (len(filename) + 6) % 8)) + +def read_index(): + if not os.path.exists('.zit/index'): + return [] + index = open('.zit/index').read() + assert index[0:4] == 'DIRC' + assert struct.unpack('>I', index[4:8]) == (2,) + num_entries, = struct.unpack('>I', index[8:12]) + i = 12 + entries = [] + for _ in range(num_entries): + # ignore inode heuristics + mode, = struct.unpack('>I', index[i+24:i+28]) + i += 40 + rawsha1 = index[i:i+20] + # just use NUL-terminated? + filename_length = struct.unpack('>H', index[i+20:i+22])[0] & 0xfff + filename = index[i+22:i+22+filename_length] + i += (22 + filename_length + 8) / 8 * 8 + entries.append((filename, mode, rawsha1)) + return entries diff --git a/read-tree b/read-tree index 2b1955a..cb29ad0 100755 --- a/read-tree +++ b/read-tree @@ -5,27 +5,18 @@ import struct import subprocess import sys +import index + _, tree_sha1 = sys.argv tree = subprocess.check_output(['cat-file', 'tree', tree_sha1]) entries = [] while tree: header, rest = tree.split('\0', 1) - flags, filename = header.split(' ', 1) + mode, filename = header.split(' ', 1) rawsha1 = rest[:20] tree = rest[20:] # TODO handle directories - if flags != '040000': - entries.append((flags, filename, rawsha1)) + if mode != '040000': + entries.append((filename, int(mode, 8), rawsha1)) -# TODO dedup with add. this handles flags -f = open('.zit/index', 'w') -f.write('DIRC') -f.write(struct.pack('>II', 2, len(entries))) -for flags, filename, rawsha1 in entries: - f.write('\0' * 24) - f.write(struct.pack('>I', int(flags, 8))) - f.write('\0' * 12) - f.write(rawsha1) - f.write(struct.pack('>H', len(filename))) - f.write(filename) - f.write('\0' * (8 - (len(filename) + 6) % 8)) +index.write_index(entries) diff --git a/rev-parse b/rev-parse index 33ac383..1c4d37b 100755 --- a/rev-parse +++ b/rev-parse @@ -27,7 +27,7 @@ def get_sha1(name): return sha1 elif actual_type == 'commit': assert int(length) == len(contents) - tree = next(line for line in contents.split('\n') + tree = next(line for line in contents.splitlines() if line.startswith('tree ')) name = tree.split(' ')[1] # TODO deref tags? diff --git a/update-ref b/update-ref index 605639d..75f6743 100755 --- a/update-ref +++ b/update-ref @@ -2,6 +2,5 @@ path=.zit/$(symbolic-ref $1) sha1=$(rev-parse $2) -# TODO initialize directory structure instead? mkdir -p $(dirname $path) echo $sha1 > $path diff --git a/write-tree b/write-tree index 63f6ac7..ccb6fc4 100755 --- a/write-tree +++ b/write-tree @@ -1,36 +1,18 @@ #!/usr/bin/python # write-tree.c -import struct import subprocess -import sys -# TODO write in C? do all macs have gcc? -# TODO if cache doesn't exist? -index = open('.zit/index').read() -assert index[0:4] == 'DIRC' -assert struct.unpack('>I', index[4:8]) == (2,) -num_entries, = struct.unpack('>I', index[8:12]) -entries = [] -i = 12 -for _ in range(num_entries): - # ignore inode heuristics, file/symlink bit, permissions - i += 40 - sha1 = index[i:i+20].encode('hex') - # just use NUL-terminated? - name_length = struct.unpack('>H', index[i+20:i+22])[0] & 0xfff - name = index[i+22:i+22+name_length] - i += (22 + name_length + 8) / 8 * 8 - entries.append((name, sha1)) +import index +entries = index.read_index() # TODO write directories recursively? groupby? f = open('.zit/tree.tmp', 'w') -for filename, sha1 in entries: - # TODO directories and file 755 - f.write('100644 ') # file 644 +for filename, mode, rawsha1 in entries: + f.write('{:o} '.format(mode)) f.write(filename) f.write('\0') - f.write(bytearray.fromhex(sha1)) + f.write(rawsha1) f.close() subprocess.check_call(['hash-object', 'tree', '.zit/tree.tmp']) diff --git a/zit b/zit index 8fd538e..5531c4d 100755 --- a/zit +++ b/zit @@ -1,4 +1,4 @@ #!/bin/bash -e -export PATH="${BASH_SOURCE%/*}:$PATH" +export PATH="$(cd "${BASH_SOURCE%/*}"; echo "$PWD"):$PATH" exec "$@"