• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
2# reserved. Use of this source code is governed by a BSD-style license that
3# can be found in the LICENSE file
4
5from __future__ import absolute_import
6from exec_util import exec_cmd
7import os
8import sys
9
10if sys.platform == 'win32':
11  # Force use of the git version bundled with depot_tools.
12  git_exe = 'git.bat'
13else:
14  git_exe = 'git'
15
16
17def is_checkout(path):
18  """ Returns true if the path represents a git checkout. """
19  return os.path.exists(os.path.join(path, '.git'))
20
21
22def is_ancestor(path='.', commit1='HEAD', commit2='master'):
23  """ Returns whether |commit1| is an ancestor of |commit2|. """
24  cmd = "%s merge-base --is-ancestor %s %s" % (git_exe, commit1, commit2)
25  result = exec_cmd(cmd, path)
26  return result['ret'] == 0
27
28
29def get_hash(path='.', branch='HEAD'):
30  """ Returns the git hash for the specified branch/tag/hash. """
31  cmd = "%s rev-parse %s" % (git_exe, branch)
32  result = exec_cmd(cmd, path)
33  if result['out'] != '':
34    return result['out'].strip()
35  return 'Unknown'
36
37
38def get_branch_name(path='.', branch='HEAD'):
39  """ Returns the branch name for the specified branch/tag/hash. """
40  # Returns the branch name if not in detached HEAD state, else an empty string
41  # or "HEAD".
42  cmd = "%s rev-parse --abbrev-ref %s" % (git_exe, branch)
43  result = exec_cmd(cmd, path)
44  if result['out'] != '':
45    name = result['out'].strip()
46    if len(name) > 0 and name != 'HEAD':
47      return name
48
49    # Returns a value like "(HEAD, origin/3729, 3729)".
50    # Ubuntu 14.04 uses Git version 1.9.1 which does not support %D (which
51    # provides the same output but without the parentheses).
52    cmd = "%s log -n 1 --pretty=%%d %s" % (git_exe, branch)
53    result = exec_cmd(cmd, path)
54    if result['out'] != '':
55      return result['out'].strip()[1:-1].split(', ')[-1]
56  return 'Unknown'
57
58
59def get_url(path='.'):
60  """ Returns the origin url for the specified path. """
61  cmd = "%s config --get remote.origin.url" % git_exe
62  result = exec_cmd(cmd, path)
63  if result['out'] != '':
64    return result['out'].strip()
65  return 'Unknown'
66
67
68def get_commit_number(path='.', branch='HEAD'):
69  """ Returns the number of commits in the specified branch/tag/hash. """
70  cmd = "%s rev-list --count %s" % (git_exe, branch)
71  result = exec_cmd(cmd, path)
72  if result['out'] != '':
73    return result['out'].strip()
74  return '0'
75
76
77def get_changed_files(path, hash):
78  """ Retrieves the list of changed files. """
79  if hash == 'unstaged':
80    cmd = "%s diff --name-only" % git_exe
81  elif hash == 'staged':
82    cmd = "%s diff --name-only --cached" % git_exe
83  else:
84    cmd = "%s diff-tree --no-commit-id --name-only -r %s" % (git_exe, hash)
85  result = exec_cmd(cmd, path)
86  if result['out'] != '':
87    files = result['out']
88    if sys.platform == 'win32':
89      # Convert to Unix line endings.
90      files = files.replace('\r\n', '\n')
91    return files.strip().split("\n")
92  return []
93
94
95def get_branch_hashes(path='.', branch='HEAD', ref='origin/master'):
96  """ Returns an ordered list of hashes for commits that have been applied since
97      branching from ref. """
98  cmd = "%s cherry %s %s" % (git_exe, ref, branch)
99  result = exec_cmd(cmd, path)
100  if result['out'] != '':
101    hashes = result['out']
102    if sys.platform == 'win32':
103      # Convert to Unix line endings.
104      hashes = hashes.replace('\r\n', '\n')
105    # Remove the "+ " or "- " prefix.
106    return [line[2:] for line in hashes.strip().split('\n')]
107  return []
108
109
110def write_indented_output(output):
111  """ Apply a fixed amount of intent to lines before printing. """
112  if output == '':
113    return
114  for line in output.split('\n'):
115    line = line.strip()
116    if len(line) == 0:
117      continue
118    sys.stdout.write('\t%s\n' % line)
119
120
121def git_apply_patch_file(patch_path, patch_dir):
122  """ Apply |patch_path| to files in |patch_dir|. """
123  patch_name = os.path.basename(patch_path)
124  sys.stdout.write('\nApply %s in %s\n' % (patch_name, patch_dir))
125
126  if not os.path.isfile(patch_path):
127    sys.stdout.write('... patch file does not exist.\n')
128    return 'fail'
129
130  patch_string = open(patch_path, 'rb').read()
131  if sys.platform == 'win32':
132    # Convert the patch to Unix line endings. This is necessary to avoid
133    # whitespace errors with git apply.
134    patch_string = patch_string.replace(b'\r\n', b'\n')
135
136  # Git apply fails silently if not run relative to a respository root.
137  if not is_checkout(patch_dir):
138    sys.stdout.write('... patch directory is not a repository root.\n')
139    return 'fail'
140
141  config = '-p0 --ignore-whitespace'
142
143  # Output patch contents.
144  cmd = '%s apply %s --numstat' % (git_exe, config)
145  result = exec_cmd(cmd, patch_dir, patch_string)
146  write_indented_output(result['out'].replace('<stdin>', patch_name))
147
148  # Reverse check to see if the patch has already been applied.
149  cmd = '%s apply %s --reverse --check' % (git_exe, config)
150  result = exec_cmd(cmd, patch_dir, patch_string)
151  if result['err'].find('error:') < 0:
152    sys.stdout.write('... already applied (skipping).\n')
153    return 'skip'
154
155  # Normal check to see if the patch can be applied cleanly.
156  cmd = '%s apply %s --check' % (git_exe, config)
157  result = exec_cmd(cmd, patch_dir, patch_string)
158  if result['err'].find('error:') >= 0:
159    sys.stdout.write('... failed to apply:\n')
160    write_indented_output(result['err'].replace('<stdin>', patch_name))
161    return 'fail'
162
163  # Apply the patch file. This should always succeed because the previous
164  # command succeeded.
165  cmd = '%s apply %s' % (git_exe, config)
166  result = exec_cmd(cmd, patch_dir, patch_string)
167  if result['err'] == '':
168    sys.stdout.write('... successfully applied.\n')
169  else:
170    sys.stdout.write('... successfully applied (with warnings):\n')
171    write_indented_output(result['err'].replace('<stdin>', patch_name))
172  return 'apply'
173