| 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 |