Skip to content

Commit 86388c5

Browse files
committed
Added first scetch of a transport DB interface which should be enough to allow an own implementation of the git protocol without breaking clients. It also includes interfaces for the fetchinfo and pushinfo types
1 parent 17d9d13 commit 86388c5

File tree

3 files changed

+152
-2
lines changed

3 files changed

+152
-2
lines changed

gitdb/db/base.py

+130-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
from itertools import chain
2323

2424

25-
__all__ = ('ObjectDBR', 'ObjectDBW', 'FileDBBase', 'CompoundDB', 'CachingDB')
25+
__all__ = ( 'ObjectDBR', 'ObjectDBW', 'FileDBBase', 'CompoundDB', 'CachingDB',
26+
'TransportDBMixin', 'RefSpec', 'FetchInfo', 'PushInfo')
2627

2728

2829
class ObjectDBR(object):
@@ -321,5 +322,133 @@ def partial_to_complete_sha_hex(self, partial_hexsha):
321322
return candidate
322323

323324
#} END interface
325+
326+
327+
class RefSpec(object):
328+
"""A refspec is a simple container which provides information about the way
329+
something should be fetched or pushed. It requires to use symbols to describe
330+
the actual objects which is done using reference names (or respective instances
331+
which resolve to actual reference names)."""
332+
__slots__ = ('source', 'destination', 'force')
333+
334+
def __init__(self, source, destination, force=False):
335+
"""initalize the instance with the required values
336+
:param source: reference name or instance. If None, the Destination
337+
is supposed to be deleted."""
338+
self.source = source
339+
self.destination = destination
340+
self.force = force
341+
if self.destination is None:
342+
raise ValueError("Destination must be set")
343+
344+
def __str__(self):
345+
""":return: a git-style refspec"""
346+
s = str(self.source)
347+
if self.source is None:
348+
s = ''
349+
#END handle source
350+
d = str(self.destination)
351+
p = ''
352+
if self.force:
353+
p = '+'
354+
#END handle force
355+
res = "%s%s:%s" % (p, s, d)
356+
357+
def delete_destination(self):
358+
return self.source is None
359+
360+
361+
class PushInfo(object):
362+
"""A type presenting information about the result of a push operation for exactly
363+
one refspec
364+
365+
flags # bitflags providing more information about the result
366+
local_ref # Reference pointing to the local reference that was pushed
367+
# It is None if the ref was deleted.
368+
remote_ref_string # path to the remote reference located on the remote side
369+
remote_ref # Remote Reference on the local side corresponding to
370+
# the remote_ref_string. It can be a TagReference as well.
371+
old_commit # commit at which the remote_ref was standing before we pushed
372+
# it to local_ref.commit. Will be None if an error was indicated
373+
summary # summary line providing human readable english text about the push
374+
"""
375+
__slots__ = tuple()
376+
377+
NEW_TAG, NEW_HEAD, NO_MATCH, REJECTED, REMOTE_REJECTED, REMOTE_FAILURE, DELETED, \
378+
FORCED_UPDATE, FAST_FORWARD, UP_TO_DATE, ERROR = [ 1 << x for x in range(11) ]
379+
380+
381+
class FetchInfo(object):
382+
"""A type presenting information about the fetch operation on exactly one refspec
383+
384+
The following members are defined:
385+
ref # name of the reference to the changed
386+
# remote head or FETCH_HEAD. Implementations can provide
387+
# actual class instance which convert to a respective string
388+
flags # additional flags to be & with enumeration members,
389+
# i.e. info.flags & info.REJECTED
390+
# is 0 if ref is FETCH_HEAD
391+
note # additional notes given by the fetch-pack implementation intended for the user
392+
old_commit # if info.flags & info.FORCED_UPDATE|info.FAST_FORWARD,
393+
# field is set to the previous location of ref as hexsha or None
394+
# Implementors may use their own type too, but it should decay into a
395+
# string of its hexadecimal sha representation"""
396+
__slots__ = tuple()
397+
398+
NEW_TAG, NEW_HEAD, HEAD_UPTODATE, TAG_UPDATE, REJECTED, FORCED_UPDATE, \
399+
FAST_FORWARD, ERROR = [ 1 << x for x in range(8) ]
400+
401+
402+
class TransportDBMixin(object):
403+
"""A database which allows to transport objects from and to different locations
404+
which are specified by urls (location) and refspecs (what to transport,
405+
see http://www.kernel.org/pub/software/scm/git/docs/git-fetch.html).
406+
407+
At the beginning of a transport operation, it will be determined which objects
408+
have to be sent (either by this or by the other side).
409+
410+
Afterwards a pack with the required objects is sent (or received). If there is
411+
nothing to send, the pack will be empty.
412+
413+
The communication itself if implemented using a protocol instance which deals
414+
with the actual formatting of the lines sent."""
415+
# The following variables need to be set by the derived class
416+
#{Configuration
417+
protocol = None
418+
#}end configuraiton
419+
420+
#{ Interface
421+
422+
def fetch(self, url, refspecs, progress=None):
423+
"""Fetch the objects defined by the given refspec from the given url.
424+
:param url: url identifying the source of the objects. It may also be
425+
a symbol from which the respective url can be resolved, like the
426+
name of the remote. The implementation should allow objects as input
427+
as well, these are assumed to resovle to a meaningful string though.
428+
:param refspecs: iterable of reference specifiers or RefSpec instance,
429+
identifying the references to be fetch from the remote.
430+
:param progress: callable which receives progress messages for user consumption
431+
:return: List of binary object shas matching the respective remote ref which
432+
was previously fetched, in the order of the input refspecs.
433+
:note: even if the operation fails, one of the returned FetchInfo instances
434+
may still contain errors or failures in only part of the refspecs.
435+
:raise: if any issue occours during the transport or if the url is not
436+
supported by the protocol.
437+
"""
438+
raise NotImplementedError()
324439

440+
def push(self, url, refspecs, progress=None):
441+
"""Transport the objects identified by the given refspec to the remote
442+
at the given url.
443+
:param url: Decribes the location which is to receive the objects
444+
see fetch() for more details
445+
:param refspecs: iterable of refspecs strings or RefSpec instances
446+
to identify the objects to push
447+
:param progress: see fetch()
448+
:todo: what to return ?
449+
:raise: if any issue arises during transport or if the url cannot be handled"""
450+
raise NotImplementedError()
451+
452+
#}end interface
453+
325454

gitdb/db/pack.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
class PackedDB(FileDBBase, ObjectDBR, CachingDB, LazyMixin):
3131
"""A database operating on a set of object packs"""
3232

33+
# the type to use when instantiating a pack entity
34+
PackEntityCls = PackEntity
35+
3336
# sort the priority list every N queries
3437
# Higher values are better, performance tests don't show this has
3538
# any effect, but it should have one
@@ -156,7 +159,7 @@ def update_cache(self, force=False):
156159
for pack_file in (pack_files - our_pack_files):
157160
# init the hit-counter/priority with the size, a good measure for hit-
158161
# probability. Its implemented so that only 12 bytes will be read
159-
entity = PackEntity(pack_file)
162+
entity = self.PackEntityCls(pack_file)
160163
self._entities.append([entity.pack().size(), entity, entity.index().sha_to_index])
161164
# END for each new packfile
162165

gitdb/test/db/test_base.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright (C) 2010, 2011 Sebastian Thiel ([email protected]) and contributors
2+
#
3+
# This module is part of GitDB and is released under
4+
# the New BSD License: http://www.opensource.org/licenses/bsd-license.php
5+
from lib import *
6+
from gitdb.db import RefSpec
7+
8+
class TestBase(TestDBBase):
9+
10+
@with_rw_directory
11+
def test_basics(self, path):
12+
self.failUnlessRaises(ValueError, RefSpec, None, None)
13+
rs = RefSpec(None, "something")
14+
assert rs.force == False
15+
assert rs.delete_destination()
16+
assert rs.source is None
17+
assert rs.destination == "something"
18+

0 commit comments

Comments
 (0)