Skip to content

Commit e60579d

Browse files
committed
Fix staged-files-only for git add --intent-to-add files
1 parent 2941a11 commit e60579d

File tree

4 files changed

+71
-5
lines changed

4 files changed

+71
-5
lines changed

pre_commit/git.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,20 @@ def get_staged_files():
9797
)[1])
9898

9999

100+
def intent_to_add_files():
101+
_, stdout_binary, _ = cmd_output('git', 'status', '--porcelain', '-z')
102+
parts = list(reversed(zsplit(stdout_binary)))
103+
intent_to_add = []
104+
while parts:
105+
line = parts.pop()
106+
status, filename = line[:3], line[3:]
107+
if status[0] in {'C', 'R'}: # renames / moves have an additional arg
108+
parts.pop()
109+
if status[1] == 'A':
110+
intent_to_add.append(filename)
111+
return intent_to_add
112+
113+
100114
def get_all_files():
101115
return zsplit(cmd_output('git', 'ls-files', '-z')[1])
102116

pre_commit/staged_files_only.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
import os.path
77
import time
88

9+
from pre_commit import git
910
from pre_commit.util import CalledProcessError
1011
from pre_commit.util import cmd_output
1112
from pre_commit.util import mkdirp
13+
from pre_commit.xargs import xargs
1214

1315

1416
logger = logging.getLogger('pre_commit')
@@ -24,11 +26,22 @@ def _git_apply(patch):
2426

2527

2628
@contextlib.contextmanager
27-
def staged_files_only(patch_dir):
28-
"""Clear any unstaged changes from the git working directory inside this
29-
context.
30-
"""
31-
# Determine if there are unstaged files
29+
def _intent_to_add_cleared():
30+
intent_to_add = git.intent_to_add_files()
31+
if intent_to_add:
32+
logger.warning('Unstaged intent-to-add files detected.')
33+
34+
xargs(('git', 'rm', '--cached', '--'), intent_to_add)
35+
try:
36+
yield
37+
finally:
38+
xargs(('git', 'add', '--intent-to-add', '--'), intent_to_add)
39+
else:
40+
yield
41+
42+
43+
@contextlib.contextmanager
44+
def _unstaged_changes_cleared(patch_dir):
3245
tree = cmd_output('git', 'write-tree')[1].strip()
3346
retcode, diff_stdout_binary, _ = cmd_output(
3447
'git', 'diff-index', '--ignore-submodules', '--binary',
@@ -71,3 +84,12 @@ def staged_files_only(patch_dir):
7184
# There weren't any staged files so we don't need to do anything
7285
# special
7386
yield
87+
88+
89+
@contextlib.contextmanager
90+
def staged_files_only(patch_dir):
91+
"""Clear any unstaged changes from the git working directory inside this
92+
context.
93+
"""
94+
with _intent_to_add_cleared(), _unstaged_changes_cleared(patch_dir):
95+
yield

tests/git_test.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,21 @@ def test_get_conflicted_files_non_ascii(in_merge_conflict):
155155
cmd_output('git', 'add', '.')
156156
ret = git.get_conflicted_files()
157157
assert ret == {'conflict_file', 'интервью'}
158+
159+
160+
def test_intent_to_add(in_git_dir):
161+
in_git_dir.join('a').ensure()
162+
cmd_output('git', 'add', '--intent-to-add', 'a')
163+
164+
assert git.intent_to_add_files() == ['a']
165+
166+
167+
def test_status_output_with_rename(in_git_dir):
168+
in_git_dir.join('a').write('1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n')
169+
cmd_output('git', 'add', 'a')
170+
git_commit()
171+
cmd_output('git', 'mv', 'a', 'b')
172+
in_git_dir.join('c').ensure()
173+
cmd_output('git', 'add', '--intent-to-add', 'c')
174+
175+
assert git.intent_to_add_files() == ['c']

tests/staged_files_only_test.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import pytest
1111

12+
from pre_commit import git
1213
from pre_commit.staged_files_only import staged_files_only
1314
from pre_commit.util import cmd_output
1415
from testing.auto_namedtuple import auto_namedtuple
@@ -339,3 +340,14 @@ def test_autocrlf_commited_crlf(in_git_dir, patch_dir):
339340

340341
with staged_files_only(patch_dir):
341342
assert_no_diff()
343+
344+
345+
def test_intent_to_add(in_git_dir, patch_dir):
346+
"""Regression test for #881"""
347+
_write(b'hello\nworld\n')
348+
cmd_output('git', 'add', '--intent-to-add', 'foo')
349+
350+
assert git.intent_to_add_files() == ['foo']
351+
with staged_files_only(patch_dir):
352+
assert_no_diff()
353+
assert git.intent_to_add_files() == ['foo']

0 commit comments

Comments
 (0)