Skip to content

Commit 129f90a

Browse files
committed
Multiple partly critical bugfixes related to index handling
1 parent 5705018 commit 129f90a

File tree

5 files changed

+47
-18
lines changed

5 files changed

+47
-18
lines changed

lib/git/index/base.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
TemporaryFileSwap,
3232
post_clear_cache,
3333
default_index,
34+
git_working_dir
3435
)
3536

3637
import git.objects
@@ -122,7 +123,7 @@ def _set_cache_(self, attr):
122123
# END exception handling
123124

124125
stream = file_contents_ro(fd, stream=True, allow_mmap=True)
125-
126+
126127
try:
127128
self._deserialize(stream)
128129
finally:
@@ -192,6 +193,10 @@ def write(self, file_path = None, ignore_tree_extension_data=False):
192193
automatically
193194
194195
:return: self"""
196+
# make sure we have our entries read before getting a write lock
197+
# else it would be done when streaming. This can happen
198+
# if one doesn't change the index, but writes it right away
199+
self.entries
195200
lfd = LockedFD(file_path or self._file_path)
196201
stream = lfd.open(write=True, stream=True)
197202

@@ -345,7 +350,7 @@ def _stat_mode_to_index_mode(cls, mode):
345350
return S_IFLNK
346351
if S_ISDIR(mode) or S_IFMT(mode) == cls.S_IFGITLINK: # submodules
347352
return cls.S_IFGITLINK
348-
return S_IFREG | 644 | (mode & 0100) # blobs with or without executable bit
353+
return S_IFREG | 0644 | (mode & 0100) # blobs with or without executable bit
349354

350355
# UTILITIES
351356
def _iter_expand_paths(self, paths):
@@ -577,6 +582,7 @@ def _preprocess_add_items(self, items):
577582
# END for each item
578583
return (paths, entries)
579584

585+
@git_working_dir
580586
def add(self, items, force=True, fprogress=lambda *args: None, path_rewriter=None):
581587
"""Add files from the working tree, specific blobs or BaseIndexEntries
582588
to the index. The underlying index file will be written immediately, hence
@@ -688,7 +694,6 @@ def store_path(filepath):
688694
fprogress(filepath, False, filepath)
689695
istream = self.repo.odb.store(IStream(Blob.type, st.st_size, stream))
690696
fprogress(filepath, True, filepath)
691-
692697
return BaseIndexEntry((self._stat_mode_to_index_mode(st.st_mode),
693698
istream.binsha, 0, filepath))
694699
# END utility method
@@ -706,8 +711,6 @@ def store_path(filepath):
706711

707712
# HANDLE ENTRIES
708713
if entries:
709-
# TODO: Add proper IndexEntries to ourselves, and write the index
710-
# just once. Currently its done twice at least
711714
null_mode_entries = [ e for e in entries if e.mode == 0 ]
712715
if null_mode_entries:
713716
raise ValueError("At least one Entry has a null-mode - please use index.remove to remove files for clarity")
@@ -750,6 +753,7 @@ def store_path(filepath):
750753
# add the new entries to this instance, and write it
751754
for entry in entries_added:
752755
self.entries[(entry.path, 0)] = IndexEntry.from_base(entry)
756+
753757
self.write()
754758

755759
return entries_added
@@ -902,8 +906,8 @@ def commit(self, message, parent_commits=None, head=True):
902906
Returns
903907
Commit object representing the new commit
904908
"""
905-
tree_sha = self.repo.git.write_tree()
906-
return Commit.create_from_tree(self.repo, tree_sha, message, parent_commits, head)
909+
tree = self.write_tree()
910+
return Commit.create_from_tree(self.repo, tree, message, parent_commits, head)
907911

908912
@classmethod
909913
def _flush_stdin_and_wait(cls, proc, ignore_stdout = False):
@@ -1012,6 +1016,11 @@ def handle_stderr(proc, iter_checked_out_files):
10121016
if isinstance(paths, basestring):
10131017
paths = [paths]
10141018

1019+
# make sure we have our entries loaded before we start checkout_index
1020+
# which will hold a lock on it. We try to get the lock as well during
1021+
# our entries initialization
1022+
self.entries
1023+
10151024
args.append("--stdin")
10161025
kwargs['as_process'] = True
10171026
kwargs['istream'] = subprocess.PIPE

lib/git/index/util.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import tempfile
44
import os
55

6-
__all__ = ( 'TemporaryFileSwap', 'post_clear_cache', 'default_index' )
6+
__all__ = ( 'TemporaryFileSwap', 'post_clear_cache', 'default_index', 'git_working_dir' )
77

88
#{ Aliases
99
pack = struct.pack
@@ -67,4 +67,20 @@ def check_default_index(self, *args, **kwargs):
6767
check_default_index.__name__ = func.__name__
6868
return check_default_index
6969

70+
def git_working_dir(func):
71+
"""Decorator which changes the current working dir to the one of the git
72+
repository in order to assure relative paths are handled correctly"""
73+
def set_git_working_dir(self, *args, **kwargs):
74+
cur_wd = os.getcwd()
75+
os.chdir(self.repo.working_tree_dir)
76+
try:
77+
return func(self, *args, **kwargs)
78+
finally:
79+
os.chdir(cur_wd)
80+
# END handle working dir
81+
# END wrapper
82+
83+
set_git_working_dir.__name__ = func.__name__
84+
return set_git_working_dir
85+
7086
#} END decorators

lib/git/utils.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
from gitdb.util import (
1313
make_sha,
14-
FDStreamWrapper,
1514
LockedFD,
1615
file_contents_ro,
1716
LazyMixin,

test/git/test_index.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ def test_index_merge_tree(self, rw_repo):
182182
index.write()
183183
assert rw_repo.index.entries[manifest_key].hexsha == Diff.NULL_HEX_SHA
184184

185+
# write an unchanged index ( just for the fun of it )
186+
rw_repo.index.write()
187+
185188
# a three way merge would result in a conflict and fails as the command will
186189
# not overwrite any entries in our index and hence leave them unmerged. This is
187190
# mainly a protection feature as the current index is not yet in a tree
@@ -432,6 +435,7 @@ def mixed_iterator():
432435

433436
# same file
434437
entries = index.reset(new_commit).add(['lib/git/head.py']*2, fprogress=self._fprogress_add)
438+
assert entries[0].mode & 0644 == 0644
435439
# would fail, test is too primitive to handle this case
436440
# self._assert_fprogress(entries)
437441
self._reset_progress()
@@ -574,7 +578,7 @@ def make_paths():
574578
@with_rw_repo('HEAD')
575579
def test_compare_write_tree(self, rw_repo):
576580
def write_tree(index):
577-
tree_sha = index.repo.git.write_tree(missing_ok=True)
581+
tree_sha = index.write_tree().sha
578582
return Tree(index.repo, tree_sha, 0, '')
579583
# END git cmd write tree
580584

test/testlib/helper.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def case(self, rw_repo)
7070
Use this if you want to make purely index based adjustments, change refs, create
7171
heads, generally operations that do not need a working tree."""
7272
def bare_repo_creator(self):
73-
repo_dir = tempfile.mktemp("bare_repo")
73+
repo_dir = tempfile.mktemp("bare_repo_%s" % func.__name__)
7474
rw_repo = self.rorepo.clone(repo_dir, shared=True, bare=True)
7575
prev_cwd = os.getcwd()
7676
try:
@@ -96,7 +96,7 @@ def with_rw_repo(working_tree_ref):
9696
assert isinstance(working_tree_ref, basestring), "Decorator requires ref name for working tree checkout"
9797
def argument_passer(func):
9898
def repo_creator(self):
99-
repo_dir = tempfile.mktemp("non_bare_repo")
99+
repo_dir = tempfile.mktemp("non_bare_%s" % func.__name__)
100100
rw_repo = self.rorepo.clone(repo_dir, shared=True, bare=False, n=True)
101101

102102
rw_repo.head.commit = working_tree_ref
@@ -105,11 +105,12 @@ def repo_creator(self):
105105
prev_cwd = os.getcwd()
106106
os.chdir(rw_repo.working_dir)
107107
try:
108-
return func(self, rw_repo)
109-
except:
110-
print >> sys.stderr, "Keeping repo after failure: %s" % repo_dir
111-
raise
112-
else:
108+
try:
109+
return func(self, rw_repo)
110+
except:
111+
print >> sys.stderr, "Keeping repo after failure: %s" % repo_dir
112+
raise
113+
finally:
113114
os.chdir(prev_cwd)
114115
rw_repo.git.clear_cache()
115116
shutil.rmtree(repo_dir, onerror=_rmtree_onerror)
@@ -146,7 +147,7 @@ def case(self, rw_repo, rw_remote_repo)
146147
assert isinstance(working_tree_ref, basestring), "Decorator requires ref name for working tree checkout"
147148
def argument_passer(func):
148149
def remote_repo_creator(self):
149-
remote_repo_dir = tempfile.mktemp("remote_repo")
150+
remote_repo_dir = tempfile.mktemp("remote_repo_%s" % func.__name__)
150151
repo_dir = tempfile.mktemp("remote_clone_non_bare_repo")
151152

152153
rw_remote_repo = self.rorepo.clone(remote_repo_dir, shared=True, bare=True)

0 commit comments

Comments
 (0)