Skip to content

Commit 82f6afb

Browse files
committed
Replaced a symlink with the real file -- symlink committed by mistake.
- Legacy-Id: 7759
1 parent 2a8fbb4 commit 82f6afb

1 file changed

Lines changed: 261 additions & 1 deletion

File tree

bin/mergeready

Lines changed: 0 additions & 1 deletion
This file was deleted.

bin/mergeready

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
#!/usr/bin/python
2+
# -*- python -*-
3+
"""
4+
NAME
5+
%(program)s - look for SVN commits that are ready to merge
6+
7+
SYNOPSIS
8+
%(program)s [OPTIONS] ARGS
9+
10+
DESCRIPTION
11+
%(program)s looks in the SVN log for commits which are marked with the
12+
phrase 'Commit ready for merge', and compares the resulting list with
13+
the 'svn:mergeinfo' property on the current directory, in order to
14+
work out which (if any) commits are ready to merge, but not yet
15+
merged. The command requires (and checks) that it's running in a
16+
directory named 'trunk', and requires that to be an SVN working copy.
17+
18+
The files (in the top directory of the working copy) 'ready-for-merge'
19+
and 'hold-for-merge' are also consulted for additions and exceptions to
20+
the merge list.
21+
22+
A list of commit date, committer, and branch@revision for each commit
23+
which is marked ready for merge, but not yet merged, is then written
24+
to standard out.
25+
26+
%(options)s
27+
28+
AUTHOR
29+
Written by Henrik Levkowetz, <henrik@tools.ietf.org>
30+
31+
COPYRIGHT
32+
Copyright 2014 Henrik Levkowetz
33+
34+
This program is free software; you can redistribute it and/or modify
35+
it under the terms of the Simplified BSD license as published by the
36+
Open Source Initiative at http://opensource.org/licenses/BSD-2-Clause.
37+
38+
"""
39+
from __future__ import print_function
40+
41+
import sys, os.path, getopt, re
42+
import debug
43+
44+
version = "0.20"
45+
program = os.path.basename(sys.argv[0])
46+
progdir = os.path.dirname(sys.argv[0])
47+
48+
# ----------------------------------------------------------------------
49+
# Parse options
50+
51+
options = ""
52+
for line in re.findall("\n +(if|elif) +opt in \[(.+)\]:\s+#(.+)\n", open(sys.argv[0]).read()):
53+
if not options:
54+
options += "OPTIONS\n"
55+
options += " %-16s %s\n" % (line[1].replace('"', ''), line[2])
56+
options = options.strip()
57+
58+
# with ' < 1:' on the next line, this is a no-op:
59+
if len(sys.argv) < 1:
60+
print(__doc__ % locals())
61+
sys.exit(1)
62+
63+
try:
64+
opts, files = getopt.gnu_getopt(sys.argv[1:], "hvV", ["help", "version","verbose",])
65+
except Exception, e:
66+
print( "%s: %s" % (program, e))
67+
sys.exit(1)
68+
69+
# ----------------------------------------------------------------------
70+
# Handle options
71+
72+
# set default values, if any
73+
opt_verbose = False
74+
75+
# handle individual options
76+
for opt, value in opts:
77+
if opt in ["-h", "--help"]: # Output this help, then exit
78+
print( __doc__ % locals() )
79+
sys.exit(1)
80+
elif opt in ["-v", "--version"]: # Output version information, then exit
81+
print( program, version )
82+
sys.exit(0)
83+
elif opt in ["-V", "--verbose"]: # Output version information, then exit
84+
opt_verbose = True
85+
86+
# ----------------------------------------------------------------------
87+
def say(s):
88+
sys.stderr.write("%s\n" % (s))
89+
90+
# ----------------------------------------------------------------------
91+
def note(s):
92+
if opt_verbose:
93+
sys.stderr.write("%s\n" % (s))
94+
95+
# ----------------------------------------------------------------------
96+
def die(s, error=1):
97+
sys.stderr.write("\n%s: Error: %s\n\n" % (program, s))
98+
sys.exit(error)
99+
100+
# ----------------------------------------------------------------------
101+
# The program itself
102+
103+
import os
104+
import json
105+
106+
cwd = os.getcwd()
107+
108+
if cwd.split(os.path.sep)[-1] != 'trunk':
109+
die("Expected to run this operation in trunk, but the current\ndirectory is '%s'" % cwd)
110+
111+
# ----------------------------------------------------------------------
112+
# Some utility functions
113+
114+
def pipe(cmd, inp=None):
115+
import shlex
116+
from subprocess import Popen, PIPE
117+
args = shlex.split(cmd)
118+
bufsize = 4096
119+
stdin = PIPE if inp else None
120+
pipe = Popen(args, stdin=stdin, stdout=PIPE, stderr=PIPE, bufsize=bufsize)
121+
out, err = pipe.communicate(inp)
122+
code = pipe.returncode
123+
if code != 0:
124+
raise OSError(err)
125+
return out
126+
127+
def split_loginfo(line):
128+
parts = line.split()
129+
rev = parts[0][1:]
130+
who = parts[2]
131+
date = parts[4]
132+
time = parts[5]
133+
when = "%s_%s" % (date, time)
134+
return rev, who, when
135+
136+
# ----------------------------------------------------------------------
137+
138+
# Get repository information
139+
svn_info = {}
140+
for line in pipe('svn info .').splitlines():
141+
if line:
142+
key, value = line.strip().split(':', 1)
143+
svn_info[key] = value.strip()
144+
145+
repo = svn_info["Repository Root"]
146+
head = int(svn_info['Revision'])
147+
148+
# Get current mergeinfo from cache and svn
149+
cachefn = os.path.join(os.environ.get('HOME', '.'), '.mergeinfo')
150+
151+
if os.path.exists(cachefn):
152+
with open(cachefn, "r") as file:
153+
cache = json.load(file)
154+
else:
155+
sys.stderr.write("No merge info cache file found -- will have to extract all information from SVN.\n"+
156+
"This may take some time.\n\n")
157+
cache = {}
158+
mergeinfo = cache[repo] if repo in cache else {}
159+
160+
merged_revs = {}
161+
write_cache = False
162+
for line in pipe('svn propget svn:mergeinfo .').splitlines():
163+
if line in mergeinfo:
164+
merged = mergeinfo[line]
165+
else:
166+
merged = {}
167+
branch, revs = line.strip().split(':',1)
168+
for part in revs.split(','):
169+
if '-' in part:
170+
beg, end = part.split('-')
171+
try:
172+
commit_log = pipe('svn log -v -r %s:%s %s%s' % (beg, end, repo, branch))
173+
for logline in commit_log.splitlines():
174+
if re.search('^r[0-9]+ ', logline):
175+
rev, who, when = split_loginfo(logline)
176+
merged[rev] = branch[1:]
177+
write_cache = True
178+
except OSError:
179+
pass
180+
else:
181+
merged[part] = branch[1:]
182+
write_cache = True
183+
mergeinfo[line] = merged
184+
merged_revs.update(merged)
185+
186+
if write_cache:
187+
cache[repo] = mergeinfo
188+
with open(cachefn, "w") as file:
189+
json.dump(cache, file, indent=2, sort_keys=True)
190+
191+
def get_list(repo, filename):
192+
list = []
193+
with open(filename) as file:
194+
for line in file:
195+
line = line.strip()
196+
if line.startswith('#') or line == "":
197+
continue
198+
try:
199+
changeset = line.split()[0]
200+
branch, rev = changeset.split('@')
201+
if branch.startswith('/'):
202+
branch = branch[1:]
203+
if not (rev in merged_revs and branch == merged_revs[rev]):
204+
list += [(rev, repo, branch),]
205+
#elif rev in merged_revs and not branch == merged_revs[rev]:
206+
# sys.stderr.write('Rev %s: %s != %s' % (rev, branch, merged_revs[rev]))
207+
else:
208+
#sys.stderr.write('Already merged: merged_revs[%s]: %s\n' % (rev, merged_revs[rev]))
209+
pass
210+
except ValueError as e:
211+
sys.stderr.write("Bad changeset specification in %s: '%s': %s\n" % (file.name, changeset, e))
212+
return list
213+
214+
def get_ready_commits(repo, tree):
215+
list = []
216+
commit_log = pipe('svn log -v -r %s:HEAD %s/%s/' % ((head-500), repo, tree))
217+
for line in commit_log.splitlines():
218+
if re.search('^r[0-9]+ ', line):
219+
rev, who, when = split_loginfo(line)
220+
branch = None
221+
continue
222+
if (line.startswith(' M') or line.startswith(' A')) and branch == None:
223+
type, path = line[:4], line[5:]
224+
branch = '/'.join(path.split('/')[1:4])
225+
elif re.search("(?i)(commit ready (for|to) merge)", line):
226+
if not (rev in merged_revs and branch == merged_revs[rev]):
227+
list += [(rev, repo, branch),]
228+
elif rev in merged_revs and not branch == merged_revs[rev]:
229+
sys.stderr.write('Rev %s: %s != %s' % (rev, branch, merged_revs[rev]))
230+
else:
231+
pass
232+
else:
233+
pass
234+
235+
return list
236+
237+
ready = get_list(repo, 'ready-for-merge')
238+
hold = get_list(repo, 'hold-for-merge')
239+
ready += get_ready_commits(repo, 'personal')
240+
ready += get_ready_commits(repo, 'branch/amsl')
241+
242+
ready_commits = {}
243+
for entry in ready:
244+
rev, repo, branch = entry
245+
loginfo = pipe('svn log -v -r %s %s/%s/' % (rev, repo, branch)).splitlines()
246+
try:
247+
rev, who, when = split_loginfo(loginfo[1])
248+
except IndexError:
249+
die("Wrong changeset version in %s@%s ?" % (branch, rev))
250+
for line in loginfo[3:]:
251+
type, path = line[:4], line[5:]
252+
if 'M' in type or 'A' in type:
253+
break
254+
merge_path = os.path.join(*path.split(os.path.sep)[:4])
255+
if not (rev, repo, merge_path) in hold:
256+
ready_commits[when] = "%s %-24s %s@%s" % (when, who+":", merge_path, rev)
257+
258+
keys = ready_commits.keys()
259+
keys.sort()
260+
for key in keys:
261+
print(ready_commits[key])

0 commit comments

Comments
 (0)