Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3bee808

Browse files
committed
index.checkout: Now parsing stderror in the end to determine which files have not been updated as they where modified locally or did not exist. Test was improved to check for this new case.
1 parent 331ddc2 commit 3bee808

File tree

2 files changed

+89
-18
lines changed

2 files changed

+89
-18
lines changed

lib/git/index.py

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,20 @@
2424
from git.utils import SHA1Writer, LazyMixin, ConcurrentWriteOperation, join_path_native
2525

2626

27+
class CheckoutError( Exception ):
28+
"""Thrown if a file could not be checked out from the index as it contained
29+
changes.
30+
31+
The .failed_files attribute contains a list of relative paths that failed
32+
to be checked out as they contained changes that did not exist in the index"""
33+
def __init__(self, message, failed_files):
34+
super(CheckoutError, self).__init__(message)
35+
self.failed_files = failed_files
36+
37+
def __str__(self):
38+
return super(CheckoutError, self).__str__() + ":%s" % self.failed_files
39+
40+
2741
class _TemporaryFileSwap(object):
2842
"""
2943
Utility class moving a file to a temporary location within the same directory
@@ -800,7 +814,7 @@ def add(self, items, force=True, fprogress=lambda *args: None):
800814
# to get suitable progress information, pipe paths to stdin
801815
args = ("--add", "--replace", "--verbose", "--stdin")
802816
proc = self.repo.git.update_index(*args, **{'as_process':True, 'istream':subprocess.PIPE})
803-
make_exc = lambda : GitCommandError(("git-update-index",)+args, 128, proc.stderr.readline())
817+
make_exc = lambda : GitCommandError(("git-update-index",)+args, 128, proc.stderr.read())
804818
added_files = list()
805819

806820
for filepath in self._iter_expand_paths(paths):
@@ -829,7 +843,7 @@ def add(self, items, force=True, fprogress=lambda *args: None):
829843
# send progress for these now.
830844
args = ("-w", "--stdin-paths")
831845
proc = self.repo.git.hash_object(*args, **{'istream':subprocess.PIPE, 'as_process':True})
832-
make_exc = lambda : GitCommandError(("git-hash-object",)+args, 128, proc.stderr.readline())
846+
make_exc = lambda : GitCommandError(("git-hash-object",)+args, 128, proc.stderr.read())
833847
obj_ids = list()
834848
for ei in null_entries_indices:
835849
entry = entries[ei]
@@ -999,13 +1013,16 @@ def checkout(self, paths=None, force=False, fprogress=lambda *args: None, **kwar
9991013
10001014
``paths``
10011015
If None, all paths in the index will be checked out. Otherwise an iterable
1002-
or single path of relative or absolute paths pointing to files is expected.
1003-
The command will ignore paths that do not exist.
1004-
The provided progress information will contain None as path and item.
1016+
of relative or absolute paths or a single path pointing to files in the index
1017+
is expected.
1018+
The command will raise of files do not exist in the index ( as opposed to the
1019+
original git command who ignores them )
1020+
The provided progress information will contain None as path and item if no
1021+
explicit paths are given.
10051022
10061023
``force``
1007-
If True, existing files will be overwritten. If False, these will
1008-
be skipped.
1024+
If True, existing files will be overwritten even if they contain local modifications.
1025+
If False, these will trigger a CheckoutError.
10091026
10101027
``fprogress``
10111028
see Index.add_ for signature and explanation
@@ -1015,28 +1032,66 @@ def checkout(self, paths=None, force=False, fprogress=lambda *args: None, **kwar
10151032
10161033
Returns
10171034
self
1035+
1036+
Raise CheckoutError
1037+
If at least one file failed to be checked out. This is a summary,
1038+
hence it will checkout as many files as it can anyway.
1039+
Raise GitCommandError if error lines could not be parsed - this truly is
1040+
an exceptional state
10181041
"""
10191042
args = ["--index"]
10201043
if force:
10211044
args.append("--force")
10221045

1046+
def handle_stderr(proc):
1047+
stderr = proc.stderr.read()
1048+
if not stderr:
1049+
return
1050+
# line contents:
1051+
# git-checkout-index: this already exists
1052+
failed_files = list()
1053+
unknown_lines = list()
1054+
endings = (' already exists', ' is not in the cache', ' does not exist at stage', ' is unmerged')
1055+
for line in stderr.splitlines():
1056+
if not line.startswith("git checkout-index: ") and not line.startswith("git-checkout-index: "):
1057+
unknown_lines.append(line)
1058+
continue
1059+
# END unkown lines parsing
1060+
1061+
for e in endings:
1062+
if line.endswith(e):
1063+
failed_files.append(line[20:-len(e)])
1064+
break
1065+
# END if ending matches
1066+
# END for each possible ending
1067+
# END for each line
1068+
if unknown_lines:
1069+
raise GitCommandError(("git-checkout-index", ), 128, stderr)
1070+
if failed_files:
1071+
raise CheckoutError("Some files could not be checked out from the index due to local modifications", failed_files)
1072+
# END stderr handler
1073+
10231074
if paths is None:
10241075
args.append("--all")
1076+
kwargs['as_process'] = 1
10251077
fprogress(None, False, None)
1026-
self.repo.git.checkout_index(*args, **kwargs)
1078+
proc = self.repo.git.checkout_index(*args, **kwargs)
1079+
proc.wait()
10271080
fprogress(None, True, None)
1081+
handle_stderr(proc)
10281082
else:
1029-
if not isinstance(paths, (tuple,list)):
1083+
if isinstance(paths, basestring):
10301084
paths = [paths]
10311085

10321086
args.append("--stdin")
1033-
co_proc = self.repo.git.checkout_index(args, as_process=True, istream=subprocess.PIPE, **kwargs)
1034-
make_exc = lambda : GitCommandError(("git-checkout-index",)+args, 128, co_proc.stderr.readline())
1087+
proc = self.repo.git.checkout_index(args, as_process=True, istream=subprocess.PIPE, **kwargs)
1088+
make_exc = lambda : GitCommandError(("git-checkout-index",)+args, 128, proc.stderr.read())
10351089
for path in paths:
10361090
path = self._to_relative_path(path)
1037-
self._write_path_to_stdin(co_proc, path, path, make_exc, fprogress, read_from_stdout=False)
1038-
# END for each path
1039-
self._flush_stdin_and_wait(co_proc)
1091+
self._write_path_to_stdin(proc, path, path, make_exc, fprogress, read_from_stdout=False)
1092+
# END for each path
1093+
self._flush_stdin_and_wait(proc)
1094+
handle_stderr(proc)
10401095
# END paths handling
10411096
return self
10421097

test/git/test_index.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,26 @@ def test_index_file_diffing(self, rw_repo):
225225
self._assert_fprogress([test_file])
226226
assert os.path.exists(test_file)
227227

228-
# checking out non-existing file is ignored/doesn't raise
229-
index.checkout("doesnt_exist_ever.txt.that")
230-
index.checkout(paths=["doesnt/exist"])
231-
228+
# checking out non-existing file throws
229+
self.failUnlessRaises(CheckoutError, index.checkout, "doesnt_exist_ever.txt.that")
230+
self.failUnlessRaises(CheckoutError, index.checkout, paths=["doesnt/exist"])
231+
232+
# checkout file with modifications
233+
append_data = "hello"
234+
fp = open(test_file, "ab")
235+
fp.write(append_data)
236+
fp.close()
237+
try:
238+
index.checkout(test_file)
239+
except CheckoutError, e:
240+
assert len(e.failed_files) == 1 and e.failed_files[0] == os.path.basename(test_file)
241+
assert open(test_file).read().endswith(append_data)
242+
else:
243+
raise AssertionError("Exception CheckoutError not thrown")
244+
245+
# if we force it it should work
246+
index.checkout(test_file, force=True)
247+
assert not open(test_file).read().endswith(append_data)
232248

233249
def _count_existing(self, repo, files):
234250
"""

0 commit comments

Comments
 (0)