OLD | NEW |
1 # Copyright 2009 Google Inc. All Rights Reserved. | 1 # Copyright 2009 Google Inc. All Rights Reserved. |
2 # | 2 # |
3 # Licensed under the Apache License, Version 2.0 (the "License"); | 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
4 # you may not use this file except in compliance with the License. | 4 # you may not use this file except in compliance with the License. |
5 # You may obtain a copy of the License at | 5 # You may obtain a copy of the License at |
6 # | 6 # |
7 # http://www.apache.org/licenses/LICENSE-2.0 | 7 # http://www.apache.org/licenses/LICENSE-2.0 |
8 # | 8 # |
9 # Unless required by applicable law or agreed to in writing, software | 9 # Unless required by applicable law or agreed to in writing, software |
10 # distributed under the License is distributed on an "AS IS" BASIS, | 10 # distributed under the License is distributed on an "AS IS" BASIS, |
(...skipping 16 matching lines...) Expand all Loading... |
27 | 27 |
28 ### SCM abstraction layer | 28 ### SCM abstraction layer |
29 | 29 |
30 | 30 |
31 # Factory Method for SCM wrapper creation | 31 # Factory Method for SCM wrapper creation |
32 | 32 |
33 def CreateSCM(url=None, root_dir=None, relpath=None, scm_name='svn'): | 33 def CreateSCM(url=None, root_dir=None, relpath=None, scm_name='svn'): |
34 # TODO(maruel): Deduce the SCM from the url. | 34 # TODO(maruel): Deduce the SCM from the url. |
35 scm_map = { | 35 scm_map = { |
36 'svn' : SVNWrapper, | 36 'svn' : SVNWrapper, |
| 37 'git' : GitWrapper, |
37 } | 38 } |
| 39 |
| 40 if url and (url.startswith('git:') or |
| 41 url.startswith('ssh:') or |
| 42 url.endswith('.git')): |
| 43 scm_name = 'git' |
| 44 |
38 if not scm_name in scm_map: | 45 if not scm_name in scm_map: |
39 raise gclient_utils.Error('Unsupported scm %s' % scm_name) | 46 raise gclient_utils.Error('Unsupported scm %s' % scm_name) |
40 return scm_map[scm_name](url, root_dir, relpath, scm_name) | 47 return scm_map[scm_name](url, root_dir, relpath, scm_name) |
41 | 48 |
42 | 49 |
43 # SCMWrapper base class | 50 # SCMWrapper base class |
44 | 51 |
45 class SCMWrapper(object): | 52 class SCMWrapper(object): |
46 """Add necessary glue between all the supported SCM. | 53 """Add necessary glue between all the supported SCM. |
47 | 54 |
48 This is the abstraction layer to bind to different SCM. Since currently only | 55 This is the abstraction layer to bind to different SCM. Since currently only |
49 subversion is supported, a lot of subersionism remains. This can be sorted out | 56 subversion is supported, a lot of subersionism remains. This can be sorted out |
50 once another SCM is supported.""" | 57 once another SCM is supported.""" |
51 def __init__(self, url=None, root_dir=None, relpath=None, | 58 def __init__(self, url=None, root_dir=None, relpath=None, |
52 scm_name='svn'): | 59 scm_name='svn'): |
53 self.scm_name = scm_name | 60 self.scm_name = scm_name |
54 self.url = url | 61 self.url = url |
55 self._root_dir = root_dir | 62 self._root_dir = root_dir |
56 if self._root_dir: | 63 if self._root_dir: |
57 self._root_dir = self._root_dir.replace('/', os.sep) | 64 self._root_dir = self._root_dir.replace('/', os.sep) |
58 self.relpath = relpath | 65 self.relpath = relpath |
59 if self.relpath: | 66 if self.relpath: |
60 self.relpath = self.relpath.replace('/', os.sep) | 67 self.relpath = self.relpath.replace('/', os.sep) |
| 68 if self.relpath and self._root_dir: |
| 69 self.checkout_path = os.path.join(self._root_dir, self.relpath) |
61 | 70 |
62 def FullUrlForRelativeUrl(self, url): | 71 def FullUrlForRelativeUrl(self, url): |
63 # Find the forth '/' and strip from there. A bit hackish. | 72 # Find the forth '/' and strip from there. A bit hackish. |
64 return '/'.join(self.url.split('/')[:4]) + url | 73 return '/'.join(self.url.split('/')[:4]) + url |
65 | 74 |
66 def RunCommand(self, command, options, args, file_list=None): | 75 def RunCommand(self, command, options, args, file_list=None): |
67 # file_list will have all files that are modified appended to it. | 76 # file_list will have all files that are modified appended to it. |
68 if file_list is None: | 77 if file_list is None: |
69 file_list = [] | 78 file_list = [] |
70 | 79 |
71 commands = ['cleanup', 'export', 'update', 'revert', | 80 commands = ['cleanup', 'export', 'update', 'revert', |
72 'status', 'diff', 'pack', 'runhooks'] | 81 'status', 'diff', 'pack', 'runhooks'] |
73 | 82 |
74 if not command in commands: | 83 if not command in commands: |
75 raise gclient_utils.Error('Unknown command %s' % command) | 84 raise gclient_utils.Error('Unknown command %s' % command) |
76 | 85 |
77 if not command in dir(self): | 86 if not command in dir(self): |
78 raise gclient_utils.Error('Command %s not implemnted in %s wrapper' % ( | 87 raise gclient_utils.Error('Command %s not implemnted in %s wrapper' % ( |
79 command, self.scm_name)) | 88 command, self.scm_name)) |
80 | 89 |
81 return getattr(self, command)(options, args, file_list) | 90 return getattr(self, command)(options, args, file_list) |
82 | 91 |
83 | 92 |
| 93 class GitWrapper(SCMWrapper): |
| 94 """Wrapper for Git""" |
| 95 |
| 96 def cleanup(self, options, args, file_list): |
| 97 """Cleanup working copy.""" |
| 98 self._RunGit(['prune']) |
| 99 self._RunGit(['fsck']) |
| 100 self._RunGit(['gc']) |
| 101 |
| 102 def diff(self, options, args, file_list): |
| 103 # NOTE: This function does not currently modify file_list. |
| 104 merge_base = self._RunGit(['merge-base', 'HEAD', 'origin']) |
| 105 print self._RunGit(['diff', merge_base]) |
| 106 |
| 107 def export(self, options, args, file_list): |
| 108 assert len(args) == 1 |
| 109 export_path = os.path.abspath(os.path.join(args[0], self.relpath)) |
| 110 if not os.path.exists(export_path): |
| 111 os.makedirs(export_path) |
| 112 self._RunGit(['checkout-index', '-a', '--prefix=%s/' % export_path]) |
| 113 |
| 114 def update(self, options, args, file_list): |
| 115 """Runs git to update or transparently checkout the working copy. |
| 116 |
| 117 All updated files will be appended to file_list. |
| 118 |
| 119 Raises: |
| 120 Error: if can't get URL for relative path. |
| 121 """ |
| 122 |
| 123 if args: |
| 124 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) |
| 125 |
| 126 components = self.url.split("@") |
| 127 url = components[0] |
| 128 revision = None |
| 129 if options.revision: |
| 130 revision = options.revision |
| 131 elif len(components) == 2: |
| 132 revision = components[1] |
| 133 |
| 134 if not os.path.exists(self.checkout_path): |
| 135 self._RunGit(['clone', '-q', url, self.checkout_path], cwd=self._root_dir) |
| 136 if revision: |
| 137 self._RunGit(['reset', '--hard', revision]) |
| 138 files = self._RunGit(['ls-files']).split() |
| 139 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 140 return |
| 141 |
| 142 self._RunGit(['remote', 'update']) |
| 143 new_base = 'origin' |
| 144 if revision: |
| 145 new_base = revision |
| 146 files = self._RunGit(['diff', new_base, '--name-only']).split() |
| 147 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 148 self._RunGit(['rebase', new_base]) |
| 149 |
| 150 def revert(self, options, args, file_list): |
| 151 """Reverts local modifications. |
| 152 |
| 153 All reverted files will be appended to file_list. |
| 154 """ |
| 155 merge_base = self._RunGit(['merge-base', 'HEAD', 'origin']) |
| 156 files = self._RunGit(['diff', merge_base, '--name-only']).split() |
| 157 print self._RunGit(['reset', '--hard', merge_base]) |
| 158 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 159 |
| 160 def runhooks(self, options, args, file_list): |
| 161 self.status(options, args, file_list) |
| 162 |
| 163 def status(self, options, args, file_list): |
| 164 """Display status information.""" |
| 165 if not os.path.isdir(self.checkout_path): |
| 166 print('\n________ couldn\'t run status in %s:\nThe directory ' |
| 167 'does not exist.' % checkout_path) |
| 168 else: |
| 169 merge_base = self._RunGit(['merge-base', 'HEAD', 'origin']) |
| 170 print self._RunGit(['diff', '--name-status', merge_base]) |
| 171 files = self._RunGit(['diff', '--name-only', merge_base]).split() |
| 172 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 173 |
| 174 def _RunGit(self, args, cwd=None, checkrc=True): |
| 175 if cwd == None: |
| 176 cwd = self.checkout_path |
| 177 cmd = ['git'] |
| 178 cmd.extend(args) |
| 179 sp = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE) |
| 180 if checkrc and sp.returncode: |
| 181 raise gclient_utils.Error('git command %s returned %d' % |
| 182 (args[0], sp.returncode)) |
| 183 return sp.communicate()[0].strip() |
| 184 |
| 185 |
84 class SVNWrapper(SCMWrapper): | 186 class SVNWrapper(SCMWrapper): |
85 """ Wrapper for SVN """ | 187 """ Wrapper for SVN """ |
86 | 188 |
87 def cleanup(self, options, args, file_list): | 189 def cleanup(self, options, args, file_list): |
88 """Cleanup working copy.""" | 190 """Cleanup working copy.""" |
89 command = ['cleanup'] | 191 command = ['cleanup'] |
90 command.extend(args) | 192 command.extend(args) |
91 RunSVN(command, os.path.join(self._root_dir, self.relpath)) | 193 RunSVN(command, os.path.join(self._root_dir, self.relpath)) |
92 | 194 |
93 def diff(self, options, args, file_list): | 195 def diff(self, options, args, file_list): |
(...skipping 495 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
589 # Col 3 | 691 # Col 3 |
590 if wc_status[0].getAttribute('copied') == 'true': | 692 if wc_status[0].getAttribute('copied') == 'true': |
591 statuses[3] = '+' | 693 statuses[3] = '+' |
592 # Col 4 | 694 # Col 4 |
593 if wc_status[0].getAttribute('switched') == 'true': | 695 if wc_status[0].getAttribute('switched') == 'true': |
594 statuses[4] = 'S' | 696 statuses[4] = 'S' |
595 # TODO(maruel): Col 5 and 6 | 697 # TODO(maruel): Col 5 and 6 |
596 item = (''.join(statuses), file) | 698 item = (''.join(statuses), file) |
597 results.append(item) | 699 results.append(item) |
598 return results | 700 return results |
OLD | NEW |