Skip to content

Commit 90d6903

Browse files
committed
cygwin, #525: Try to make it work with Cygwin's Git.
+ Make `Git.polish_url()` convert paths into Cygwin-friendly paths. + Add utility and soe TCs for funcs for detecting cygwin and converting abs-paths to `/cygdrive/c/...`.
1 parent 57a6e9a commit 90d6903

File tree

5 files changed

+250
-67
lines changed

5 files changed

+250
-67
lines changed

Diff for: git/cmd.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
)
3232
from git.exc import CommandError
3333
from git.odict import OrderedDict
34+
from git.util import is_cygwin_git, cygpath
3435

3536
from .exc import (
3637
GitCommandError,
@@ -190,9 +191,24 @@ def __setstate__(self, d):
190191
# Override this value using `Git.USE_SHELL = True`
191192
USE_SHELL = False
192193

194+
@classmethod
195+
def is_cygwin(cls):
196+
return is_cygwin_git(cls.GIT_PYTHON_GIT_EXECUTABLE)
197+
193198
@classmethod
194199
def polish_url(cls, url):
195-
return url.replace("\\\\", "\\").replace("\\", "/")
200+
if cls.is_cygwin():
201+
"""Remove any backslahes from urls to be written in config files.
202+
203+
Windows might create config-files containing paths with backslashed,
204+
but git stops liking them as it will escape the backslashes.
205+
Hence we undo the escaping just to be sure.
206+
"""
207+
url = cygpath(url)
208+
else:
209+
url = url.replace("\\\\", "\\").replace("\\", "/")
210+
211+
return url
196212

197213
class AutoInterrupt(object):
198214
"""Kill/Interrupt the stored process instance once this instance goes out of scope. It is

Diff for: git/repo/base.py

+30-45
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,21 @@
44
# This module is part of GitPython and is released under
55
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
66

7-
from git.exc import (
8-
InvalidGitRepositoryError,
9-
NoSuchPathError,
10-
GitCommandError
11-
)
12-
from git.cmd import (
13-
Git,
14-
handle_process_output
15-
)
16-
from git.refs import (
17-
HEAD,
18-
Head,
19-
Reference,
20-
TagReference,
21-
)
22-
from git.objects import (
23-
Submodule,
24-
RootModule,
25-
Commit
26-
)
27-
from git.util import (
28-
Actor,
29-
finalize_process
30-
)
31-
from git.index import IndexFile
32-
from git.config import GitConfigParser
33-
from git.remote import (
34-
Remote,
35-
add_progress,
36-
to_progress_instance
37-
)
38-
39-
from git.db import GitCmdObjectDB
7+
from collections import namedtuple
8+
import logging
9+
import os
10+
import re
11+
import sys
4012

4113
from gitdb.util import (
4214
join,
4315
isfile,
4416
hex_to_bin
4517
)
4618

47-
from .fun import (
48-
rev_parse,
49-
is_git_dir,
50-
find_git_dir,
51-
touch,
19+
from git.cmd import (
20+
Git,
21+
handle_process_output
5222
)
5323
from git.compat import (
5424
text_type,
@@ -58,12 +28,17 @@
5828
range,
5929
is_win,
6030
)
31+
from git.config import GitConfigParser
32+
from git.db import GitCmdObjectDB
33+
from git.exc import InvalidGitRepositoryError, NoSuchPathError, GitCommandError
34+
from git.index import IndexFile
35+
from git.objects import Submodule, RootModule, Commit
36+
from git.refs import HEAD, Head, Reference, TagReference
37+
from git.remote import Remote, add_progress, to_progress_instance
38+
from git.util import Actor, finalize_process
39+
40+
from .fun import rev_parse, is_git_dir, find_git_dir, touch
6141

62-
import os
63-
import sys
64-
import re
65-
import logging
66-
from collections import namedtuple
6742

6843
log = logging.getLogger(__name__)
6944

@@ -875,12 +850,22 @@ def _clone(cls, git, url, path, odb_default_type, progress, **kwargs):
875850
progress = to_progress_instance(progress)
876851

877852
odbt = kwargs.pop('odbt', odb_default_type)
878-
proc = git.clone(url, path, with_extended_output=True, as_process=True,
853+
854+
## A bug win cygwin's Git, when `--bare`
855+
# it prepends the basename of the `url` into the `path::
856+
# git clone --bare /cygwin/a/foo.git C:\\Work
857+
# becomes::
858+
# git clone --bare /cygwin/a/foo.git /cygwin/a/C:\\Work
859+
#
860+
clone_path = (Git.polish_url(path)
861+
if Git.is_cygwin() and 'bare' in kwargs
862+
else path)
863+
proc = git.clone(Git.polish_url(url), clone_path, with_extended_output=True, as_process=True,
879864
v=True, **add_progress(kwargs, git, progress))
880865
if progress:
881866
handle_process_output(proc, None, progress.new_message_handler(), finalize_process)
882867
else:
883-
(stdout, stderr) = proc.communicate() # FIXME: Will block of outputs are big!
868+
(stdout, stderr) = proc.communicate() # FIXME: Will block if outputs are big!
884869
log.debug("Cmd(%s)'s unused stdout: %s", getattr(proc, 'args', ''), stdout)
885870
finalize_process(proc, stderr=stderr)
886871

Diff for: git/test/lib/helper.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
'GIT_REPO', 'GIT_DAEMON_PORT'
3333
)
3434

35-
log = logging.getLogger('git.util')
35+
log = logging.getLogger(__name__)
3636

3737
#{ Routines
3838

Diff for: git/test/test_util.py

+55-12
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,19 @@
55
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
66

77
import tempfile
8+
import time
9+
from unittest.case import skipIf
10+
11+
import ddt
812

13+
from git.cmd import dashify
14+
from git.compat import string_types, is_win
15+
from git.objects.util import (
16+
altz_to_utctz_str,
17+
utctz_to_altz,
18+
verify_utctz,
19+
parse_date,
20+
)
921
from git.test.lib import (
1022
TestBase,
1123
assert_equal
@@ -15,19 +27,9 @@
1527
BlockingLockFile,
1628
get_user_id,
1729
Actor,
18-
IterableList
30+
IterableList,
31+
cygpath,
1932
)
20-
from git.objects.util import (
21-
altz_to_utctz_str,
22-
utctz_to_altz,
23-
verify_utctz,
24-
parse_date,
25-
)
26-
from git.cmd import dashify
27-
from git.compat import string_types, is_win
28-
29-
import time
30-
import ddt
3133

3234

3335
class TestIterableMember(object):
@@ -52,6 +54,47 @@ def setup(self):
5254
"array": [42],
5355
}
5456

57+
@skipIf(not is_win, "Paths specifically for Windows.")
58+
@ddt.data(
59+
(r'foo\bar', 'foo/bar'),
60+
(r'foo/bar', 'foo/bar'),
61+
(r'./bar', 'bar'),
62+
(r'.\bar', 'bar'),
63+
(r'../bar', '../bar'),
64+
(r'..\bar', '../bar'),
65+
(r'../bar/.\foo/../chu', '../bar/chu'),
66+
67+
(r'C:\Users', '/cygdrive/c/Users'),
68+
(r'C:\d/e', '/cygdrive/c/d/e'),
69+
70+
(r'\\?\a:\com', '/cygdrive/a/com'),
71+
(r'\\?\a:/com', '/cygdrive/a/com'),
72+
73+
(r'\\server\C$\Users', '//server/C$/Users'),
74+
(r'\\server\C$', '//server/C$'),
75+
(r'\\server\BAR/', '//server/BAR/'),
76+
(r'\\?\UNC\server\D$\Apps', '//server/D$/Apps'),
77+
78+
(r'D:/Apps', '/cygdrive/d/Apps'),
79+
(r'D:/Apps\fOO', '/cygdrive/d/Apps/fOO'),
80+
(r'D:\Apps/123', '/cygdrive/d/Apps/123'),
81+
)
82+
def test_cygpath_ok(self, case):
83+
wpath, cpath = case
84+
self.assertEqual(cygpath(wpath), cpath or wpath)
85+
86+
@skipIf(not is_win, "Paths specifically for Windows.")
87+
@ddt.data(
88+
(r'C:Relative', None),
89+
(r'D:Apps\123', None),
90+
(r'D:Apps/123', None),
91+
(r'\\?\a:rel', None),
92+
(r'\\share\a:rel', None),
93+
)
94+
def test_cygpath_invalids(self, case):
95+
wpath, cpath = case
96+
self.assertEqual(cygpath(wpath), cpath or wpath.replace('\\', '/'))
97+
5598
def test_it_should_dashify(self):
5699
assert_equal('this-is-my-argument', dashify('this_is_my_argument'))
57100
assert_equal('foo', dashify('foo'))

0 commit comments

Comments
 (0)