Skip to content

Commit f1545bd

Browse files
committed
Merge branch 'submodule'
2 parents a1d1d2c + 7cf2d5f commit f1545bd

29 files changed

+2094
-187
lines changed

lib/git/config.py

+54-18
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,31 @@
1515
from git.odict import OrderedDict
1616
from git.util import LockFile
1717

18-
__all__ = ('GitConfigParser', )
18+
__all__ = ('GitConfigParser', 'SectionConstraint')
1919

2020
class MetaParserBuilder(type):
2121
"""Utlity class wrapping base-class methods into decorators that assure read-only properties"""
2222
def __new__(metacls, name, bases, clsdict):
2323
"""
2424
Equip all base-class methods with a needs_values decorator, and all non-const methods
2525
with a set_dirty_and_flush_changes decorator in addition to that."""
26-
mutating_methods = clsdict['_mutating_methods_']
27-
for base in bases:
28-
methods = ( t for t in inspect.getmembers(base, inspect.ismethod) if not t[0].startswith("_") )
29-
for name, method in methods:
30-
if name in clsdict:
31-
continue
32-
method_with_values = needs_values(method)
33-
if name in mutating_methods:
34-
method_with_values = set_dirty_and_flush_changes(method_with_values)
35-
# END mutating methods handling
36-
37-
clsdict[name] = method_with_values
38-
# END for each base
26+
kmm = '_mutating_methods_'
27+
if kmm in clsdict:
28+
mutating_methods = clsdict[kmm]
29+
for base in bases:
30+
methods = ( t for t in inspect.getmembers(base, inspect.ismethod) if not t[0].startswith("_") )
31+
for name, method in methods:
32+
if name in clsdict:
33+
continue
34+
method_with_values = needs_values(method)
35+
if name in mutating_methods:
36+
method_with_values = set_dirty_and_flush_changes(method_with_values)
37+
# END mutating methods handling
38+
39+
clsdict[name] = method_with_values
40+
# END for each name/method pair
41+
# END for each base
42+
# END if mutating methods configuration is set
3943

4044
new_type = super(MetaParserBuilder, metacls).__new__(metacls, name, bases, clsdict)
4145
return new_type
@@ -63,7 +67,35 @@ def flush_changes(self, *args, **kwargs):
6367
flush_changes.__name__ = non_const_func.__name__
6468
return flush_changes
6569

70+
71+
class SectionConstraint(object):
72+
"""Constrains a ConfigParser to only option commands which are constrained to
73+
always use the section we have been initialized with.
74+
75+
It supports all ConfigParser methods that operate on an option"""
76+
__slots__ = ("_config", "_section_name")
77+
_valid_attrs_ = ("get_value", "set_value", "get", "set", "getint", "getfloat", "getboolean", "has_option",
78+
"remove_section", "remove_option", "options")
6679

80+
def __init__(self, config, section):
81+
self._config = config
82+
self._section_name = section
83+
84+
def __getattr__(self, attr):
85+
if attr in self._valid_attrs_:
86+
return lambda *args, **kwargs: self._call_config(attr, *args, **kwargs)
87+
return super(SectionConstraint,self).__getattribute__(attr)
88+
89+
def _call_config(self, method, *args, **kwargs):
90+
"""Call the configuration at the given method which must take a section name
91+
as first argument"""
92+
return getattr(self._config, method)(self._section_name, *args, **kwargs)
93+
94+
@property
95+
def config(self):
96+
"""return: Configparser instance we constrain"""
97+
return self._config
98+
6799

68100
class GitConfigParser(cp.RawConfigParser, object):
69101
"""Implements specifics required to read git style configuration files.
@@ -249,9 +281,9 @@ def read(self):
249281
if not hasattr(file_object, "seek"):
250282
try:
251283
fp = open(file_object)
284+
close_fp = True
252285
except IOError,e:
253286
continue
254-
close_fp = True
255287
# END fp handling
256288

257289
try:
@@ -286,17 +318,21 @@ def write(self):
286318
:raise IOError: if this is a read-only writer instance or if we could not obtain
287319
a file lock"""
288320
self._assure_writable("write")
289-
self._lock._obtain_lock()
290-
291321

292322
fp = self._file_or_files
293323
close_fp = False
294324

325+
# we have a physical file on disk, so get a lock
326+
if isinstance(fp, (basestring, file)):
327+
self._lock._obtain_lock()
328+
# END get lock for physical files
329+
295330
if not hasattr(fp, "seek"):
296331
fp = open(self._file_or_files, "w")
297332
close_fp = True
298333
else:
299334
fp.seek(0)
335+
# END handle stream or file
300336

301337
# WRITE DATA
302338
try:
@@ -368,7 +404,7 @@ def get_value(self, section, option, default = None):
368404
return valuestr
369405

370406
@needs_values
371-
@set_dirty_and_flush_changes
407+
@set_dirty_and_flush_changes
372408
def set_value(self, section, option, value):
373409
"""Sets the given option in section to the given value.
374410
It will create the section if required, and will not throw as opposed to the default

lib/git/ext/gitdb

Submodule gitdb updated from 78665b1 to 2ddc5ba

lib/git/index/base.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
)
3636

3737
from git.objects import (
38-
Blob,
38+
Blob,
39+
Submodule,
3940
Tree,
4041
Object,
4142
Commit,
@@ -553,7 +554,7 @@ def _preprocess_add_items(self, items):
553554
for item in items:
554555
if isinstance(item, basestring):
555556
paths.append(self._to_relative_path(item))
556-
elif isinstance(item, Blob):
557+
elif isinstance(item, (Blob, Submodule)):
557558
entries.append(BaseIndexEntry.from_blob(item))
558559
elif isinstance(item, BaseIndexEntry):
559560
entries.append(item)
@@ -588,7 +589,7 @@ def add(self, items, force=True, fprogress=lambda *args: None, path_rewriter=Non
588589
589590
They are added at stage 0
590591
591-
- Blob object
592+
- Blob or Submodule object
592593
Blobs are added as they are assuming a valid mode is set.
593594
The file they refer to may or may not exist in the file system, but
594595
must be a path relative to our repository.
@@ -612,6 +613,7 @@ def add(self, items, force=True, fprogress=lambda *args: None, path_rewriter=Non
612613
explicitly set. Please note that Index Entries require binary sha's.
613614
614615
:param force:
616+
**CURRENTLY INEFFECTIVE**
615617
If True, otherwise ignored or excluded files will be
616618
added anyway.
617619
As opposed to the git-add command, we enable this flag by default
@@ -748,7 +750,7 @@ def _items_to_rela_paths(self, items):
748750
may be absolute or relative paths, entries or blobs"""
749751
paths = list()
750752
for item in items:
751-
if isinstance(item, (BaseIndexEntry,Blob)):
753+
if isinstance(item, (BaseIndexEntry,(Blob, Submodule))):
752754
paths.append(self._to_relative_path(item.path))
753755
elif isinstance(item, basestring):
754756
paths.append(self._to_relative_path(item))
@@ -775,7 +777,7 @@ def remove(self, items, working_tree=False, **kwargs):
775777
776778
The path string may include globs, such as *.c.
777779
778-
- Blob object
780+
- Blob Object
779781
Only the path portion is used in this case.
780782
781783
- BaseIndexEntry or compatible type

lib/git/index/fun.py

+33-36
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
CE_NAMEMASK,
3131
CE_STAGESHIFT
3232
)
33+
CE_NAMEMASK_INV = ~CE_NAMEMASK
3334

3435
from util import (
3536
pack,
@@ -53,22 +54,6 @@ def stat_mode_to_index_mode(mode):
5354
return S_IFREG | 0644 | (mode & 0100) # blobs with or without executable bit
5455

5556

56-
def write_cache_entry(entry, stream):
57-
"""Write the given entry to the stream"""
58-
beginoffset = stream.tell()
59-
write = stream.write
60-
write(entry[4]) # ctime
61-
write(entry[5]) # mtime
62-
path = entry[3]
63-
plen = len(path) & CE_NAMEMASK # path length
64-
assert plen == len(path), "Path %s too long to fit into index" % entry[3]
65-
flags = plen | entry[2]
66-
write(pack(">LLLLLL20sH", entry[6], entry[7], entry[0],
67-
entry[8], entry[9], entry[10], entry[1], flags))
68-
write(path)
69-
real_size = ((stream.tell() - beginoffset + 8) & ~7)
70-
write("\0" * ((beginoffset + real_size) - stream.tell()))
71-
7257
def write_cache(entries, stream, extension_data=None, ShaStreamCls=IndexFileSHA1Writer):
7358
"""Write the cache represented by entries to a stream
7459
@@ -83,15 +68,29 @@ def write_cache(entries, stream, extension_data=None, ShaStreamCls=IndexFileSHA1
8368
a 4 byte identifier, followed by its size ( 4 bytes )"""
8469
# wrap the stream into a compatible writer
8570
stream = ShaStreamCls(stream)
71+
72+
tell = stream.tell
73+
write = stream.write
8674

8775
# header
8876
version = 2
89-
stream.write("DIRC")
90-
stream.write(pack(">LL", version, len(entries)))
77+
write("DIRC")
78+
write(pack(">LL", version, len(entries)))
9179

9280
# body
9381
for entry in entries:
94-
write_cache_entry(entry, stream)
82+
beginoffset = tell()
83+
write(entry[4]) # ctime
84+
write(entry[5]) # mtime
85+
path = entry[3]
86+
plen = len(path) & CE_NAMEMASK # path length
87+
assert plen == len(path), "Path %s too long to fit into index" % entry[3]
88+
flags = plen | (entry[2] & CE_NAMEMASK_INV) # clear possible previous values
89+
write(pack(">LLLLLL20sH", entry[6], entry[7], entry[0],
90+
entry[8], entry[9], entry[10], entry[1], flags))
91+
write(path)
92+
real_size = ((tell() - beginoffset + 8) & ~7)
93+
write("\0" * ((beginoffset + real_size) - tell()))
9594
# END for each entry
9695

9796
# write previously cached extensions data
@@ -101,21 +100,6 @@ def write_cache(entries, stream, extension_data=None, ShaStreamCls=IndexFileSHA1
101100
# write the sha over the content
102101
stream.write_sha()
103102

104-
def read_entry(stream):
105-
"""Return: One entry of the given stream"""
106-
beginoffset = stream.tell()
107-
read = stream.read
108-
ctime = unpack(">8s", read(8))[0]
109-
mtime = unpack(">8s", read(8))[0]
110-
(dev, ino, mode, uid, gid, size, sha, flags) = \
111-
unpack(">LLLLLL20sH", read(20 + 4 * 6 + 2))
112-
path_size = flags & CE_NAMEMASK
113-
path = read(path_size)
114-
115-
real_size = ((stream.tell() - beginoffset + 8) & ~7)
116-
data = read((beginoffset + real_size) - stream.tell())
117-
return IndexEntry((mode, sha, flags, path, ctime, mtime, dev, ino, uid, gid, size))
118-
119103
def read_header(stream):
120104
"""Return tuple(version_long, num_entries) from the given stream"""
121105
type_id = stream.read(4)
@@ -147,10 +131,23 @@ def read_cache(stream):
147131
version, num_entries = read_header(stream)
148132
count = 0
149133
entries = dict()
134+
135+
read = stream.read
136+
tell = stream.tell
150137
while count < num_entries:
151-
entry = read_entry(stream)
138+
beginoffset = tell()
139+
ctime = unpack(">8s", read(8))[0]
140+
mtime = unpack(">8s", read(8))[0]
141+
(dev, ino, mode, uid, gid, size, sha, flags) = \
142+
unpack(">LLLLLL20sH", read(20 + 4 * 6 + 2))
143+
path_size = flags & CE_NAMEMASK
144+
path = read(path_size)
145+
146+
real_size = ((tell() - beginoffset + 8) & ~7)
147+
data = read((beginoffset + real_size) - tell())
148+
entry = IndexEntry((mode, sha, flags, path, ctime, mtime, dev, ino, uid, gid, size))
152149
# entry_key would be the method to use, but we safe the effort
153-
entries[(entry.path, entry.stage)] = entry
150+
entries[(path, entry.stage)] = entry
154151
count += 1
155152
# END for each entry
156153

lib/git/objects/__init__.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@
33
"""
44
import inspect
55
from base import *
6+
# Fix import dependency - add IndexObject to the util module, so that it can be
7+
# imported by the submodule.base
8+
import submodule.util
9+
submodule.util.IndexObject = IndexObject
10+
from submodule.base import *
11+
from submodule.root import *
12+
13+
# must come after submodule was made available
614
from tag import *
715
from blob import *
8-
from tree import *
916
from commit import *
10-
from submodule import *
17+
from tree import *
1118
from util import Actor
1219

1320
__all__ = [ name for name, obj in locals().items()

lib/git/objects/base.py

+8-13
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,6 @@ def new_from_sha(cls, repo, sha1):
6262
inst.size = oinfo.size
6363
return inst
6464

65-
def _set_self_from_args_(self, args_dict):
66-
"""Initialize attributes on self from the given dict that was retrieved
67-
from locals() in the calling method.
68-
69-
Will only set an attribute on self if the corresponding value in args_dict
70-
is not None"""
71-
for attr, val in args_dict.items():
72-
if attr != "self" and val is not None:
73-
setattr( self, attr, val )
74-
# END set all non-None attributes
75-
7665
def _set_cache_(self, attr):
7766
"""Retrieve object information"""
7867
if attr == "size":
@@ -125,7 +114,10 @@ def stream_data(self, ostream):
125114
class IndexObject(Object):
126115
"""Base for all objects that can be part of the index file , namely Tree, Blob and
127116
SubModule objects"""
128-
__slots__ = ("path", "mode")
117+
__slots__ = ("path", "mode")
118+
119+
# for compatability with iterable lists
120+
_id_attribute_ = 'path'
129121

130122
def __init__(self, repo, binsha, mode=None, path=None):
131123
"""Initialize a newly instanced IndexObject
@@ -140,7 +132,10 @@ def __init__(self, repo, binsha, mode=None, path=None):
140132
Path may not be set of the index object has been created directly as it cannot
141133
be retrieved without knowing the parent tree."""
142134
super(IndexObject, self).__init__(repo, binsha)
143-
self._set_self_from_args_(locals())
135+
if mode is not None:
136+
self.mode = mode
137+
if path is not None:
138+
self.path = path
144139

145140
def __hash__(self):
146141
""":return:

0 commit comments

Comments
 (0)