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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy