diff --git a/git_cl.py b/git_cl.py index 244259df3..99950320d 100755 --- a/git_cl.py +++ b/git_cl.py @@ -92,33 +92,55 @@ def ask_for_data(prompt): sys.exit(1) +def git_set_branch_value(key, value): + branch = Changelist().GetBranch() + if branch: + git_key = 'branch.%s.%s' % (branch, key) + RunGit(['config', '--int', git_key, "%d" % value]) + + +def git_get_branch_default(key, default): + branch = Changelist().GetBranch() + if branch: + git_key = 'branch.%s.%s' % (branch, key) + (_, stdout) = RunGitWithCode(['config', '--int', '--get', git_key]) + try: + return int(stdout.strip()) + except ValueError: + pass + return default + + def add_git_similarity(parser): parser.add_option( - '--similarity', metavar='SIM', type='int', action='store', default=None, + '--similarity', metavar='SIM', type='int', action='store', help='Sets the percentage that a pair of files need to match in order to' ' be considered copies (default 50)') + parser.add_option( + '--find-copies', action='store_true', + help='Allows git to look for copies.') + parser.add_option( + '--no-find-copies', action='store_false', dest='find_copies', + help='Disallows git from looking for copies.') old_parser_args = parser.parse_args def Parse(args): options, args = old_parser_args(args) - branch = Changelist().GetBranch() - key = 'branch.%s.git-cl-similarity' % branch if options.similarity is None: - if branch: - (_, stdout) = RunGitWithCode(['config', '--int', '--get', key]) - try: - options.similarity = int(stdout.strip()) - except ValueError: - pass - options.similarity = options.similarity or 50 + options.similarity = git_get_branch_default('git-cl-similarity', 50) else: - if branch: - print('Note: Saving similarity of %d%% in git config.' - % options.similarity) - RunGit(['config', '--int', key, str(options.similarity)]) + print('Note: Saving similarity of %d%% in git config.' + % options.similarity) + git_set_branch_value('git-cl-similarity', options.similarity) + + options.similarity = max(0, min(options.similarity, 100)) - options.similarity = max(1, min(options.similarity, 100)) + if options.find_copies is None: + options.find_copies = bool( + git_get_branch_default('git-find-copies', True)) + else: + git_set_branch_value('git-find-copies', int(options.find_copies)) print('Using %d%% similarity for rename/copy detection. ' 'Override with --similarity.' % options.similarity) @@ -169,7 +191,7 @@ def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): return None -def print_stats(similarity, args): +def print_stats(similarity, find_copies, args): """Prints statistics about the change to the user.""" # --no-ext-diff is broken in some versions of Git, so try to work around # this by overriding the environment (but there is still a problem if the @@ -177,9 +199,16 @@ def print_stats(similarity, args): env = os.environ.copy() if 'GIT_EXTERNAL_DIFF' in env: del env['GIT_EXTERNAL_DIFF'] + + if find_copies: + similarity_options = ['--find-copies-harder', '-l100000', + '-C%s' % similarity] + else: + similarity_options = ['-M%s' % similarity] + return subprocess2.call( - ['git', 'diff', '--no-ext-diff', '--stat', '--find-copies-harder', - '-C%s' % similarity, '-l100000'] + args, env=env) + ['git', 'diff', '--no-ext-diff', '--stat'] + similarity_options + args, + env=env) class Settings(object): @@ -1073,6 +1102,8 @@ def RietveldUpload(options, args, cl): upload_args.extend(['--cc', cc]) upload_args.extend(['--git_similarity', str(options.similarity)]) + if not options.find_copies: + upload_args.extend(['--git_no_find_copies']) # Include the upstream repo's URL in the change -- this is useful for # projects that have their source spread across multiple repos. @@ -1176,7 +1207,7 @@ def CMDupload(parser, args): if not options.reviewers and hook_results.reviewers: options.reviewers = hook_results.reviewers - print_stats(options.similarity, args) + print_stats(options.similarity, options.find_copies, args) if settings.GetIsGerrit(): return GerritUpload(options, args, cl) return RietveldUpload(options, args, cl) @@ -1310,7 +1341,7 @@ def SendUpstream(parser, args, cmd): branches = [base_branch, cl.GetBranchRef()] if not options.force: - print_stats(options.similarity, branches) + print_stats(options.similarity, options.find_copies, branches) ask_for_data('About to commit; enter to confirm.') # We want to squash all this branch's commits into one commit with the proper diff --git a/tests/git_cl_test.py b/tests/git_cl_test.py index 3ca0f2b33..56b9b30d7 100755 --- a/tests/git_cl_test.py +++ b/tests/git_cl_test.py @@ -93,11 +93,12 @@ class TestGitCl(TestCase): return result @classmethod - def _upload_calls(cls, similarity): - return cls._git_base_calls(similarity) + cls._git_upload_calls() + def _upload_calls(cls, similarity, find_copies): + return (cls._git_base_calls(similarity, find_copies) + + cls._git_upload_calls()) @staticmethod - def _git_base_calls(similarity): + def _git_base_calls(similarity, find_copies): if similarity is None: similarity = '50' similarity_call = ((['git', 'config', '--int', '--get', @@ -105,11 +106,31 @@ class TestGitCl(TestCase): else: similarity_call = ((['git', 'config', '--int', 'branch.master.git-cl-similarity', similarity],), '') + + if find_copies is None: + find_copies = True + find_copies_call = ((['git', 'config', '--int', '--get', + 'branch.master.git-find-copies'],), '') + else: + val = str(int(find_copies)) + find_copies_call = ((['git', 'config', '--int', + 'branch.master.git-find-copies', val],), '') + + if find_copies: + stat_call = ((['git', 'diff', '--no-ext-diff', '--stat', + '--find-copies-harder', '-l100000', '-C'+similarity, + 'master...'],), '+dat') + else: + stat_call = ((['git', 'diff', '--no-ext-diff', '--stat', + '-M'+similarity, 'master...'],), '+dat') + return [ ((['git', 'config', 'gerrit.host'],), ''), ((['git', 'config', 'rietveld.server'],), 'codereview.example.com'), ((['git', 'symbolic-ref', 'HEAD'],), 'master'), similarity_call, + ((['git', 'symbolic-ref', 'HEAD'],), 'master'), + find_copies_call, ((['git', 'update-index', '--refresh', '-q'],), ''), ((['git', 'diff-index', 'HEAD'],), ''), ((['git', 'symbolic-ref', 'HEAD'],), 'master'), @@ -123,9 +144,7 @@ class TestGitCl(TestCase): ((['git', 'config', 'branch.master.rietveldpatchset'],), ''), ((['git', 'log', '--pretty=format:%s%n%n%b', 'master...'],), 'foo'), ((['git', 'config', 'user.email'],), 'me@example.com'), - ((['git', 'diff', '--no-ext-diff', '--stat', '--find-copies-harder', - '-C'+similarity, '-l100000', 'master...'],), - '+dat'), + stat_call, ((['git', 'log', '--pretty=format:%s\n\n%b', 'master..'],), 'desc\n'), ] @@ -157,6 +176,9 @@ class TestGitCl(TestCase): ((['git', 'config', '--int', '--get', 'branch.working.git-cl-similarity'],), ''), ((['git', 'symbolic-ref', 'HEAD'],), 'refs/heads/working'), + ((['git', 'config', '--int', '--get', + 'branch.working.git-find-copies'],), ''), + ((['git', 'symbolic-ref', 'HEAD'],), 'refs/heads/working'), ((['git', 'config', 'branch.working.merge'],), 'refs/heads/master'), ((['git', 'config', 'branch.working.remote'],), 'origin'), ((['git', 'rev-list', '--merges', @@ -205,7 +227,7 @@ class TestGitCl(TestCase): def _dcommit_calls_3(cls): return [ ((['git', 'diff', '--no-ext-diff', '--stat', '--find-copies-harder', - '-C50', '-l100000', 'refs/remotes/origin/master', + '-l100000', '-C50', 'refs/remotes/origin/master', 'refs/heads/working'],), (' PRESUBMIT.py | 2 +-\n' ' 1 files changed, 1 insertions(+), 1 deletions(-)\n')), @@ -229,7 +251,7 @@ class TestGitCl(TestCase): ] @staticmethod - def _cmd_line(description, args, similarity): + def _cmd_line(description, args, similarity, find_copies): """Returns the upload command line passed to upload.RealMain().""" return [ 'upload', '--assume_yes', '--server', @@ -237,7 +259,8 @@ class TestGitCl(TestCase): '--message', description ] + args + [ '--cc', 'joe@example.com', - '--git_similarity', similarity or '50', + '--git_similarity', similarity or '50' + ] + (['--git_no_find_copies'] if find_copies == False else []) + [ 'master...' ] @@ -253,7 +276,15 @@ class TestGitCl(TestCase): similarity = upload_args[upload_args.index('--similarity')+1] except ValueError: similarity = None - self.calls = self._upload_calls(similarity) + + if '--find-copies' in upload_args: + find_copies = True + elif '--no-find-copies' in upload_args: + find_copies = False + else: + find_copies = None + + self.calls = self._upload_calls(similarity, find_copies) def RunEditor(desc, _): self.assertEquals( '# Enter a description of the change.\n' @@ -264,7 +295,8 @@ class TestGitCl(TestCase): return returned_description self.mock(git_cl.gclient_utils, 'RunEditor', RunEditor) def check_upload(args): - cmd_line = self._cmd_line(final_description, reviewers, similarity) + cmd_line = self._cmd_line(final_description, reviewers, similarity, + find_copies) self.assertEquals(cmd_line, args) return 1, 2 self.mock(git_cl.upload, 'RealMain', check_upload) @@ -286,6 +318,14 @@ class TestGitCl(TestCase): 'desc\n\nBUG=\n', []) + def test_keep_find_copies(self): + self._run_reviewer_test( + ['--no-find-copies'], + 'desc\n\nBUG=\n', + '# Blah blah comment.\ndesc\n\nBUG=\n', + 'desc\n\nBUG=\n', + []) + def test_reviewers_cmd_line(self): # Reviewer is passed as-is description = 'desc\n\nR=foo@example.com\nBUG=\n' @@ -337,7 +377,7 @@ class TestGitCl(TestCase): mock = FileMock() try: - self.calls = self._git_base_calls(None) + self.calls = self._git_base_calls(None, None) def RunEditor(desc, _): return desc self.mock(git_cl.gclient_utils, 'RunEditor', RunEditor) @@ -371,6 +411,9 @@ class TestGitCl(TestCase): ((['git', 'symbolic-ref', 'HEAD'],), 'master'), ((['git', 'config', '--int', '--get', 'branch.master.git-cl-similarity'],), ''), + ((['git', 'symbolic-ref', 'HEAD'],), 'master'), + ((['git', 'config', '--int', '--get', + 'branch.master.git-find-copies'],), ''), ((['git', 'update-index', '--refresh', '-q'],), ''), ((['git', 'diff-index', 'HEAD'],), ''), ((['git', 'symbolic-ref', 'HEAD'],), 'master'), @@ -385,7 +428,7 @@ class TestGitCl(TestCase): ((['git', 'log', '--pretty=format:%s%n%n%b', 'master...'],), 'foo'), ((['git', 'config', 'user.email'],), 'me@example.com'), ((['git', 'diff', '--no-ext-diff', '--stat', '--find-copies-harder', - '-C50', '-l100000', 'master...'],), + '-l100000', '-C50', 'master...'],), '+dat'), ]