Skip to content

Commit ecf94e3

Browse files
authored
Merge pull request #614 from meeseeksmachine/auto-backport-of-pr-586-on-0.10.x
Backport PR #586 on branch 0.10.x (Fix staging diff)
2 parents cad00d4 + c2cc5d5 commit ecf94e3

19 files changed

+307
-161
lines changed

jupyterlab_git/git.py

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -255,15 +255,36 @@ async def status(self, current_path):
255255
"message": my_error,
256256
}
257257

258+
# Add attribute `is_binary`
259+
command = [ # Compare stage to an empty tree see `_is_binary`
260+
"git",
261+
"diff",
262+
"--numstat",
263+
"-z",
264+
"--cached",
265+
"4b825dc642cb6eb9a060e54bf8d69288fbee4904"
266+
]
267+
text_code, text_output, _ = await execute(
268+
command, cwd=os.path.join(self.root_dir, current_path),
269+
)
270+
271+
are_binary = dict()
272+
if text_code == 0:
273+
for line in filter(lambda l: len(l) > 0, strip_and_split(text_output)):
274+
diff, name = line.rsplit("\t", maxsplit=1)
275+
are_binary[name] = diff.startswith("-\t-")
276+
258277
result = []
259278
line_iterable = (line for line in strip_and_split(my_output) if line)
260279
for line in line_iterable:
280+
name = line[3:]
261281
result.append({
262282
"x": line[0],
263283
"y": line[1],
264-
"to": line[3:],
284+
"to": name,
265285
# if file was renamed, next line contains original path
266-
"from": next(line_iterable) if line[0]=='R' else line[3:]
286+
"from": next(line_iterable) if line[0]=='R' else name,
287+
"is_binary": are_binary.get(name, None)
267288
})
268289
return {"code": code, "files": result}
269290

@@ -329,6 +350,7 @@ async def detailed_log(self, selected_hash, current_path):
329350
result = []
330351
line_iterable = iter(strip_and_split(my_output)[1:])
331352
for line in line_iterable:
353+
is_binary = line.startswith("-\t-\t")
332354
insertions, deletions, file = line.split('\t')
333355
insertions = insertions.replace('-', '0')
334356
deletions = deletions.replace('-', '0')
@@ -344,10 +366,11 @@ async def detailed_log(self, selected_hash, current_path):
344366
modified_file_path = file
345367

346368
result.append({
347-
"modified_file_path": modified_file_path,
348-
"modified_file_name": modified_file_name,
349-
"insertion": insertions,
350-
"deletion": deletions,
369+
"modified_file_path": modified_file_path,
370+
"modified_file_name": modified_file_name,
371+
"insertion": insertions,
372+
"deletion": deletions,
373+
"is_binary": is_binary
351374
})
352375
total_insertions += int(insertions)
353376
total_deletions += int(deletions)
@@ -977,7 +1000,7 @@ async def diff_content(self, filename, prev_ref, curr_ref, top_repo_path):
9771000
if curr_ref["special"] == "WORKING":
9781001
curr_content = self.get_content(filename, top_repo_path)
9791002
elif curr_ref["special"] == "INDEX":
980-
is_binary = await self._is_binary(filename, "", top_repo_path)
1003+
is_binary = await self._is_binary(filename, "INDEX", top_repo_path)
9811004
if is_binary:
9821005
raise tornado.web.HTTPError(log_message="Error occurred while executing command to retrieve plaintext diff as file is not UTF-8.")
9831006

@@ -1004,8 +1027,22 @@ async def _is_binary(self, filename, ref, top_repo_path):
10041027
- <https://stackoverflow.com/questions/6119956/how-to-determine-if-git-handles-a-file-as-binary-or-as-text/6134127#6134127>
10051028
- <https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---numstat>
10061029
- <https://git-scm.com/docs/git-diff#_other_diff_formats>
1030+
1031+
Args:
1032+
filename (str): Filename (relative to the git repository)
1033+
ref (str): Commit reference or "INDEX" if file is staged
1034+
top_repo_path (str): Git repository filepath
1035+
1036+
Returns:
1037+
bool: Is file binary?
1038+
1039+
Raises:
1040+
HTTPError: if git command failed
10071041
"""
1008-
command = ["git", "diff", "--numstat", "4b825dc642cb6eb9a060e54bf8d69288fbee4904", ref, "--", filename] # where 4b825... is a magic SHA which represents the empty tree
1042+
if ref == "INDEX":
1043+
command = ["git", "diff", "--numstat", "--cached", "4b825dc642cb6eb9a060e54bf8d69288fbee4904", "--", filename]
1044+
else:
1045+
command = ["git", "diff", "--numstat", "4b825dc642cb6eb9a060e54bf8d69288fbee4904", ref, "--", filename] # where 4b825... is a magic SHA which represents the empty tree
10091046
code, output, error = await execute(command, cwd=top_repo_path)
10101047

10111048
if code != 0:
@@ -1016,10 +1053,7 @@ async def _is_binary(self, filename, ref, top_repo_path):
10161053
raise tornado.web.HTTPError(log_message="Error while determining if file is binary or text '{}'.".format(error))
10171054

10181055
# For binary files, `--numstat` outputs two `-` characters separated by TABs:
1019-
if output.startswith('-\t-\t'):
1020-
return True
1021-
1022-
return False
1056+
return output.startswith('-\t-\t')
10231057

10241058
def remote_add(self, top_repo_path, url, name=DEFAULT_REMOTE_NAME):
10251059
"""Handle call to `git remote add` command.

jupyterlab_git/tests/test_branch.py

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# local lib
99
from jupyterlab_git.git import Git
1010

11-
from .testutils import FakeContentManager
11+
from .testutils import FakeContentManager, maybe_future
1212

1313

1414
def test_is_remote_branch():
@@ -33,7 +33,7 @@ async def test_get_current_branch_success():
3333

3434
with patch("jupyterlab_git.git.execute") as mock_execute:
3535
# Given
36-
mock_execute.return_value = tornado.gen.maybe_future((0, "feature-foo", ""))
36+
mock_execute.return_value = maybe_future((0, "feature-foo", ""))
3737

3838
# When
3939
actual_response = await (
@@ -59,10 +59,10 @@ async def test_checkout_branch_noref_success():
5959

6060
with patch("jupyterlab_git.git.execute") as mock_execute:
6161
with patch.object(
62-
Git, "_get_branch_reference", return_value=tornado.gen.maybe_future(None)
62+
Git, "_get_branch_reference", return_value=maybe_future(None)
6363
) as mock__get_branch_reference:
6464
# Given
65-
mock_execute.return_value = tornado.gen.maybe_future(
65+
mock_execute.return_value = maybe_future(
6666
(rc, stdout_message, stderr_message)
6767
)
6868

@@ -93,10 +93,10 @@ async def test_checkout_branch_noref_failure():
9393
rc = 1
9494
with patch("jupyterlab_git.git.execute") as mock_execute:
9595
with patch.object(
96-
Git, "_get_branch_reference", return_value=tornado.gen.maybe_future(None)
96+
Git, "_get_branch_reference", return_value=maybe_future(None)
9797
) as mock__get_branch_reference:
9898
# Given
99-
mock_execute.return_value = tornado.gen.maybe_future(
99+
mock_execute.return_value = maybe_future(
100100
(rc, stdout_message, stderr_message)
101101
)
102102

@@ -132,10 +132,10 @@ async def test_checkout_branch_remoteref_success():
132132
with patch.object(
133133
Git,
134134
"_get_branch_reference",
135-
return_value=tornado.gen.maybe_future("refs/remotes/remote_branch"),
135+
return_value=maybe_future("refs/remotes/remote_branch"),
136136
) as mock__get_branch_reference:
137137
# Given
138-
mock_execute.return_value = tornado.gen.maybe_future(
138+
mock_execute.return_value = maybe_future(
139139
(rc, stdout_message, stderr_message)
140140
)
141141

@@ -169,10 +169,10 @@ async def test_checkout_branch_headsref_failure():
169169
with patch.object(
170170
Git,
171171
"_get_branch_reference",
172-
return_value=tornado.gen.maybe_future("refs/heads/local_branch"),
172+
return_value=maybe_future("refs/heads/local_branch"),
173173
) as mock__get_branch_reference:
174174
# Given
175-
mock_execute.return_value = tornado.gen.maybe_future(
175+
mock_execute.return_value = maybe_future(
176176
(rc, stdout_message, stderr_message)
177177
)
178178

@@ -206,10 +206,10 @@ async def test_checkout_branch_headsref_success():
206206
with patch.object(
207207
Git,
208208
"_get_branch_reference",
209-
return_value=tornado.gen.maybe_future("refs/heads/local_branch"),
209+
return_value=maybe_future("refs/heads/local_branch"),
210210
):
211211
# Given
212-
mock_execute.return_value = tornado.gen.maybe_future(
212+
mock_execute.return_value = maybe_future(
213213
(rc, stdout_message, stderr_message)
214214
)
215215

@@ -239,10 +239,10 @@ async def test_checkout_branch_remoteref_failure():
239239
with patch.object(
240240
Git,
241241
"_get_branch_reference",
242-
return_value=tornado.gen.maybe_future("refs/remotes/remote_branch"),
242+
return_value=maybe_future("refs/remotes/remote_branch"),
243243
):
244244
# Given
245-
mock_execute.return_value = tornado.gen.maybe_future(
245+
mock_execute.return_value = maybe_future(
246246
(rc, stdout_message, stderr_message)
247247
)
248248

@@ -272,7 +272,7 @@ async def test_get_branch_reference_success():
272272
branch = "test-branch"
273273
reference = "refs/remotes/origin/test_branch"
274274

275-
mock_execute.return_value = tornado.gen.maybe_future((0, reference, ""))
275+
mock_execute.return_value = maybe_future((0, reference, ""))
276276

277277
# When
278278
actual_response = await Git(FakeContentManager("/bin"))._get_branch_reference(
@@ -294,7 +294,7 @@ async def test_get_branch_reference_failure():
294294
branch = "test-branch"
295295
reference = "test-branch"
296296
# Given
297-
mock_execute.return_value = tornado.gen.maybe_future(
297+
mock_execute.return_value = maybe_future(
298298
(
299299
128,
300300
reference,
@@ -321,7 +321,7 @@ async def test_get_branch_reference_failure():
321321
async def test_get_current_branch_failure():
322322
with patch("jupyterlab_git.git.execute") as mock_execute:
323323
# Given
324-
mock_execute.return_value = tornado.gen.maybe_future(
324+
mock_execute.return_value = maybe_future(
325325
(
326326
128,
327327
"",
@@ -356,7 +356,7 @@ async def test_get_current_branch_detached_success():
356356
" remotes/origin/feature-foo",
357357
" remotes/origin/HEAD",
358358
]
359-
mock_execute.return_value = tornado.gen.maybe_future(
359+
mock_execute.return_value = maybe_future(
360360
(0, "\n".join(process_output), "")
361361
)
362362

@@ -376,7 +376,7 @@ async def test_get_current_branch_detached_success():
376376
async def test_get_current_branch_detached_failure():
377377
with patch("jupyterlab_git.git.execute") as mock_execute:
378378
# Given
379-
mock_execute.return_value = tornado.gen.maybe_future(
379+
mock_execute.return_value = maybe_future(
380380
(
381381
128,
382382
"",
@@ -413,7 +413,7 @@ async def test_get_current_branch_detached_failure():
413413
async def test_get_upstream_branch_success(branch, upstream):
414414
with patch("jupyterlab_git.git.execute") as mock_execute:
415415
# Given
416-
mock_execute.return_value = tornado.gen.maybe_future((0, upstream, ""))
416+
mock_execute.return_value = maybe_future((0, upstream, ""))
417417

418418
# When
419419
actual_response = await Git(FakeContentManager("/bin")).get_upstream_branch(
@@ -451,7 +451,7 @@ async def test_get_upstream_branch_success(branch, upstream):
451451
async def test_get_upstream_branch_failure(outputs, message):
452452
with patch("jupyterlab_git.git.execute") as mock_execute:
453453
# Given
454-
mock_execute.return_value = tornado.gen.maybe_future(outputs)
454+
mock_execute.return_value = maybe_future(outputs)
455455

456456
# When
457457
if message:
@@ -482,7 +482,7 @@ async def test_get_upstream_branch_failure(outputs, message):
482482
async def test_get_tag_success():
483483
with patch("jupyterlab_git.git.execute") as mock_execute:
484484
# Given
485-
mock_execute.return_value = tornado.gen.maybe_future((0, "v0.3.0", ""))
485+
mock_execute.return_value = maybe_future((0, "v0.3.0", ""))
486486

487487
# When
488488
actual_response = await Git(FakeContentManager("/bin"))._get_tag(
@@ -503,8 +503,8 @@ async def test_get_tag_failure():
503503
with patch("jupyterlab_git.git.execute") as mock_execute:
504504
# Given
505505
mock_execute.side_effect = [
506-
tornado.gen.maybe_future((128, "", "fatal: Not a valid object name blah")),
507-
tornado.gen.maybe_future(
506+
maybe_future((128, "", "fatal: Not a valid object name blah")),
507+
maybe_future(
508508
(
509509
128,
510510
"",
@@ -557,7 +557,7 @@ async def test_get_tag_failure():
557557
async def test_no_tags():
558558
with patch("jupyterlab_git.git.execute") as mock_execute:
559559
# Given
560-
mock_execute.return_value = tornado.gen.maybe_future(
560+
mock_execute.return_value = maybe_future(
561561
(128, "", "fatal: No names found, cannot describe anything.\n")
562562
)
563563

@@ -590,9 +590,9 @@ async def test_branch_success():
590590

591591
mock_execute.side_effect = [
592592
# Response for get all refs/heads
593-
tornado.gen.maybe_future((0, "\n".join(process_output_heads), "")),
593+
maybe_future((0, "\n".join(process_output_heads), "")),
594594
# Response for get all refs/remotes
595-
tornado.gen.maybe_future((0, "\n".join(process_output_remotes), "")),
595+
maybe_future((0, "\n".join(process_output_remotes), "")),
596596
]
597597

598598
expected_response = {
@@ -694,7 +694,7 @@ async def test_branch_failure():
694694
"--format=%(refname:short)%09%(objectname)%09%(upstream:short)%09%(HEAD)",
695695
"refs/heads/",
696696
]
697-
mock_execute.return_value = tornado.gen.maybe_future(
697+
mock_execute.return_value = maybe_future(
698698
(
699699
128,
700700
"",
@@ -738,15 +738,15 @@ async def test_branch_success_detached_head():
738738

739739
mock_execute.side_effect = [
740740
# Response for get all refs/heads
741-
tornado.gen.maybe_future((0, "\n".join(process_output_heads), "")),
741+
maybe_future((0, "\n".join(process_output_heads), "")),
742742
# Response for get current branch
743-
tornado.gen.maybe_future(
743+
maybe_future(
744744
(128, "", "fatal: ref HEAD is not a symbolic ref")
745745
),
746746
# Response for get current branch detached
747-
tornado.gen.maybe_future((0, "\n".join(detached_head_output), "")),
747+
maybe_future((0, "\n".join(detached_head_output), "")),
748748
# Response for get all refs/remotes
749-
tornado.gen.maybe_future((0, "\n".join(process_output_remotes), "")),
749+
maybe_future((0, "\n".join(process_output_remotes), "")),
750750
]
751751

752752
expected_response = {

jupyterlab_git/tests/test_clone.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66

77
from jupyterlab_git.git import Git
88

9-
from .testutils import FakeContentManager
9+
from .testutils import FakeContentManager, maybe_future
1010

1111

1212
@pytest.mark.asyncio
1313
async def test_git_clone_success():
1414
with patch("os.environ", {"TEST": "test"}):
1515
with patch("jupyterlab_git.git.execute") as mock_execute:
1616
# Given
17-
mock_execute.return_value = tornado.gen.maybe_future((0, "output", "error"))
17+
mock_execute.return_value = maybe_future((0, "output", "error"))
1818

1919
# When
2020
actual_response = await Git(FakeContentManager("/bin")).clone(
@@ -40,7 +40,7 @@ async def test_git_clone_failure_from_git():
4040
with patch("os.environ", {"TEST": "test"}):
4141
with patch("jupyterlab_git.git.execute") as mock_execute:
4242
# Given
43-
mock_execute.return_value = tornado.gen.maybe_future(
43+
mock_execute.return_value = maybe_future(
4444
(128, "test_output", "fatal: Not a git repository")
4545
)
4646

@@ -66,7 +66,7 @@ async def test_git_clone_with_auth_success():
6666
with patch("os.environ", {"TEST": "test"}):
6767
with patch("jupyterlab_git.git.execute") as mock_authentication:
6868
# Given
69-
mock_authentication.return_value = tornado.gen.maybe_future((0, "", ""))
69+
mock_authentication.return_value = maybe_future((0, "", ""))
7070

7171
# When
7272
auth = {"username": "asdf", "password": "qwerty"}
@@ -95,7 +95,7 @@ async def test_git_clone_with_auth_wrong_repo_url_failure_from_git():
9595
with patch("os.environ", {"TEST": "test"}):
9696
with patch("jupyterlab_git.git.execute") as mock_authentication:
9797
# Given
98-
mock_authentication.return_value = tornado.gen.maybe_future(
98+
mock_authentication.return_value = maybe_future(
9999
(128, "", "fatal: repository 'ghjkhjkl' does not exist")
100100
)
101101

@@ -129,7 +129,7 @@ async def test_git_clone_with_auth_auth_failure_from_git():
129129
with patch("os.environ", {"TEST": "test"}):
130130
with patch("jupyterlab_git.git.execute") as mock_authentication:
131131
# Given
132-
mock_authentication.return_value = tornado.gen.maybe_future(
132+
mock_authentication.return_value = maybe_future(
133133
(
134134
128,
135135
"",

0 commit comments

Comments
 (0)