Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(496)

Side by Side Diff: git_drover.py

Issue 1387223002: Attempt at making git-drover work on Windows (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: don't monkey patch Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « git_common.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2015 The Chromium Authors. All rights reserved. 2 # Copyright 2015 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 """git drover: A tool for merging changes to release branches.""" 5 """git drover: A tool for merging changes to release branches."""
6 6
7 import argparse 7 import argparse
8 import functools 8 import functools
9 import logging 9 import logging
10 import os 10 import os
11 import shutil 11 import shutil
12 import subprocess 12 import subprocess
13 import sys 13 import sys
14 import tempfile 14 import tempfile
15 15
16 import git_common 16 import git_common
17 17
18 18
19 class Error(Exception): 19 class Error(Exception):
20 pass 20 pass
21 21
22 22
23 if os.name == 'nt':
24 # This is a just-good-enough emulation of os.symlink for drover to work on
25 # Windows. It uses junctioning of directories (most of the contents of
26 # the .git directory), but copies files. Note that we can't use
27 # CreateSymbolicLink or CreateHardLink here, as they both require elevation.
28 # Creating reparse points is what we want for the directories, but doing so
29 # is a relatively messy set of DeviceIoControl work at the API level, so we
30 # simply shell to `mklink /j` instead.
31 def emulate_symlink_windows(source, link_name):
32 if os.path.isdir(source):
33 subprocess.check_call(['mklink', '/j',
34 link_name.replace('/', '\\'),
35 source.replace('/', '\\')],
36 shell=True)
37 else:
38 shutil.copy(source, link_name)
39 mk_symlink = emulate_symlink_windows
40 else:
41 mk_symlink = os.symlink
42
43
23 class _Drover(object): 44 class _Drover(object):
24 45
25 def __init__(self, branch, revision, parent_repo, dry_run): 46 def __init__(self, branch, revision, parent_repo, dry_run):
26 self._branch = branch 47 self._branch = branch
27 self._branch_ref = 'refs/remotes/branch-heads/%s' % branch 48 self._branch_ref = 'refs/remotes/branch-heads/%s' % branch
28 self._revision = revision 49 self._revision = revision
29 self._parent_repo = os.path.abspath(parent_repo) 50 self._parent_repo = os.path.abspath(parent_repo)
30 self._dry_run = dry_run 51 self._dry_run = dry_run
31 self._workdir = None 52 self._workdir = None
32 self._branch_name = None 53 self._branch_name = None
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
65 def _cleanup(self): 86 def _cleanup(self):
66 if self._branch_name: 87 if self._branch_name:
67 try: 88 try:
68 self._run_git_command(['cherry-pick', '--abort']) 89 self._run_git_command(['cherry-pick', '--abort'])
69 except Error: 90 except Error:
70 pass 91 pass
71 self._run_git_command(['checkout', '--detach']) 92 self._run_git_command(['checkout', '--detach'])
72 self._run_git_command(['branch', '-D', self._branch_name]) 93 self._run_git_command(['branch', '-D', self._branch_name])
73 if self._workdir: 94 if self._workdir:
74 logging.debug('Deleting %s', self._workdir) 95 logging.debug('Deleting %s', self._workdir)
75 shutil.rmtree(self._workdir) 96 if os.name == 'nt':
97 # Use rmdir to properly handle the junctions we created.
98 subprocess.check_call(['rmdir', '/s', '/q', self._workdir], shell=True)
99 else:
100 shutil.rmtree(self._workdir)
76 self._dev_null_file.close() 101 self._dev_null_file.close()
77 102
78 @staticmethod 103 @staticmethod
79 def _confirm(message): 104 def _confirm(message):
80 """Show a confirmation prompt with the given message. 105 """Show a confirmation prompt with the given message.
81 106
82 Returns: 107 Returns:
83 A bool representing whether the user wishes to continue. 108 A bool representing whether the user wishes to continue.
84 """ 109 """
85 result = '' 110 result = ''
86 while result not in ('y', 'n'): 111 while result not in ('y', 'n'):
87 try: 112 try:
88 result = raw_input('%s Continue (y/n)? ' % message) 113 result = raw_input('%s Continue (y/n)? ' % message)
89 except EOFError: 114 except EOFError:
90 result = 'n' 115 result = 'n'
91 return result == 'y' 116 return result == 'y'
92 117
93 def _check_inputs(self): 118 def _check_inputs(self):
94 """Check the input arguments and ensure the parent repo is up to date.""" 119 """Check the input arguments and ensure the parent repo is up to date."""
95 120
96 if not os.path.isdir(self._parent_repo): 121 if not os.path.isdir(self._parent_repo):
97 raise Error('Invalid parent repo path %r' % self._parent_repo) 122 raise Error('Invalid parent repo path %r' % self._parent_repo)
98 if not hasattr(os, 'symlink'):
99 raise Error('Symlink support is required')
100 123
101 self._run_git_command(['--help'], error_message='Unable to run git') 124 self._run_git_command(['--help'], error_message='Unable to run git')
102 self._run_git_command(['status'], 125 self._run_git_command(['status'],
103 error_message='%r is not a valid git repo' % 126 error_message='%r is not a valid git repo' %
104 os.path.abspath(self._parent_repo)) 127 os.path.abspath(self._parent_repo))
105 self._run_git_command(['fetch', 'origin'], 128 self._run_git_command(['fetch', 'origin'],
106 error_message='Failed to fetch origin') 129 error_message='Failed to fetch origin')
107 self._run_git_command( 130 self._run_git_command(
108 ['rev-parse', '%s^{commit}' % self._branch_ref], 131 ['rev-parse', '%s^{commit}' % self._branch_ref],
109 error_message='Branch %s not found' % self._branch_ref) 132 error_message='Branch %s not found' % self._branch_ref)
(...skipping 23 matching lines...) Expand all
133 differs from git-new-workdir in that the config is forked instead of shared. 156 differs from git-new-workdir in that the config is forked instead of shared.
134 This is so the new workdir can be a sparse checkout without affecting 157 This is so the new workdir can be a sparse checkout without affecting
135 |self._parent_repo|. 158 |self._parent_repo|.
136 """ 159 """
137 parent_git_dir = os.path.abspath(self._run_git_command( 160 parent_git_dir = os.path.abspath(self._run_git_command(
138 ['rev-parse', '--git-dir']).strip()) 161 ['rev-parse', '--git-dir']).strip())
139 self._workdir = tempfile.mkdtemp(prefix='drover_%s_' % self._branch) 162 self._workdir = tempfile.mkdtemp(prefix='drover_%s_' % self._branch)
140 logging.debug('Creating checkout in %s', self._workdir) 163 logging.debug('Creating checkout in %s', self._workdir)
141 git_dir = os.path.join(self._workdir, '.git') 164 git_dir = os.path.join(self._workdir, '.git')
142 git_common.make_workdir_common(parent_git_dir, git_dir, self.FILES_TO_LINK, 165 git_common.make_workdir_common(parent_git_dir, git_dir, self.FILES_TO_LINK,
143 self.FILES_TO_COPY) 166 self.FILES_TO_COPY, mk_symlink)
144 self._run_git_command(['config', 'core.sparsecheckout', 'true']) 167 self._run_git_command(['config', 'core.sparsecheckout', 'true'])
145 with open(os.path.join(git_dir, 'info', 'sparse-checkout'), 'w') as f: 168 with open(os.path.join(git_dir, 'info', 'sparse-checkout'), 'w') as f:
146 f.write('codereview.settings') 169 f.write('codereview.settings')
147 170
148 branch_name = os.path.split(self._workdir)[-1] 171 branch_name = os.path.split(self._workdir)[-1]
149 self._run_git_command(['checkout', '-b', branch_name, self._branch_ref]) 172 self._run_git_command(['checkout', '-b', branch_name, self._branch_ref])
150 self._branch_name = branch_name 173 self._branch_name = branch_name
151 174
152 def _prepare_cherry_pick(self): 175 def _prepare_cherry_pick(self):
153 self._run_git_command(['cherry-pick', '-x', self._revision], 176 self._run_git_command(['cherry-pick', '-x', self._revision],
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
245 try: 268 try:
246 cherry_pick_change(options.branch, options.cherry_pick, 269 cherry_pick_change(options.branch, options.cherry_pick,
247 options.parent_checkout, options.dry_run) 270 options.parent_checkout, options.dry_run)
248 except Error as e: 271 except Error as e:
249 logging.error(e.message) 272 logging.error(e.message)
250 sys.exit(128) 273 sys.exit(128)
251 274
252 275
253 if __name__ == '__main__': 276 if __name__ == '__main__':
254 main() 277 main()
OLDNEW
« no previous file with comments | « git_common.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698