summaryrefslogtreecommitdiffstats
path: root/bin/git-un-diff-whitespace
blob: 5e0549d899e7afcefeb8d2f2d18702761a3e38d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#!/usr/bin/python

# Strip trailing whitespace ONLY in modified sections in the latest commit.
# Usage:
# <edit files>
# $ git commit
# $ git un-diff-whitespace
# Copyright 2010 Colin Walters <walters@verbum.org>
# Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php)

import os,sys,re,subprocess,hashlib

hunk_re = re.compile(r'^@@ -\d+,\d+ \+(\d+),(\d+) @@')
ws_re = re.compile(r'^(.*?)[ \t]+$')
file_re = re.compile(r'^\+\+\+ b/(.+)')

def hashfile(path):
  h = hashlib.sha1()
  f = open(path)
  b = f.read(8192)
  while b != '':
    h.update(b)
    b = f.read(8192)
  f.close()
  return h.hexdigest()

def strip_whitespace_regions(filename, hunks):
  if len(hunks) == 0:
    return False
  filename_t = filename + '.tmp'
  tmpf = open(filename_t, 'w')
  f = open(filename)
  i = 0
  hunk = hunks[0]
  for line in f:
    if (hunk is None) or (i < hunk[0]):
      tmpf.write(line)
    elif i < (hunk[0] + hunk[1]):
      nline = ws_re.sub(r'\1', line) 
      tmpf.write(nline) 
    else:
      hunks = hunks[1:]
      if len(hunks) == 0:
        hunk = None
      else:
        hunk = hunks[0] 
      tmpf.write(line)
    i = i+1
  tmpf.close()
  f.close()
  old = hashfile(filename)
  new = hashfile(filename_t)
  if old != new:
    os.rename(filename_t, filename)
    print "wrote " + filename
    return True
  else:
    os.unlink(filename_t)
    return False

def main():
  toplevel = subprocess.Popen(['git', 'rev-parse', '--show-toplevel'], stdout=subprocess.PIPE).communicate()[0]
  os.chdir(toplevel[:-1])
  diff = subprocess.Popen(['git', 'diff'], stdout=subprocess.PIPE).communicate()[0]
  if diff != '':
    print "You have unstaged changes; Commit them or 'git stash'"
    sys.exit(1)
  commit_proc = subprocess.Popen(['git', 'show', '-p', 'HEAD'], stdout=subprocess.PIPE,
                                 stderr=sys.stderr)
  diff_lines = commit_proc.stdout.readlines()
  commit_proc.wait()
  curfile = None
  hunks = []
  files = []
  modified_files = []
  for line in diff_lines:
    if curfile is not None:
      match = hunk_re.match(line) 
      if match:
        hunks.append((int(match.group(1)),int(match.group(2))))
        continue
    match = file_re.match(line)
    if match:
      filename = match.group(1)
      modified = strip_whitespace_regions(curfile, hunks)
      curfile = filename
      files.append(filename)
      if modified:
        modified_files.append(filename)
      hunks = []
  if curfile:
    modified = strip_whitespace_regions(curfile, hunks)
    if modified:
      modified_files.append(curfile)
  if modified_files:
    add_args = ['git', 'add']
    add_args.extend(modified_files)
    subprocess.check_call(add_args, stdout=sys.stdout, stderr=sys.stderr)
  else:
    print "No trailing whitespace found in HEAD"

if __name__ == '__main__':
  main()