Commits

Patrick Mézard committed 556d2d2

climport: implement regular code path

  • Participants
  • Parent commits 1d73e9e

Comments (0)

Files changed (3)

lib/codereview/codereview.py

 import re
 import stat
 import subprocess
+import tempfile
 import threading
 import time
 
 	else:
 		ui.status('Issue closed. URL: %s\n' % cl.url)
 
+def findpatchedfiles(lines):
+	"""Extract (src, dst) files from a mercurial git patch mangled as an svn
+	diff for codereview.
+	"""
+	src, dst = None, None
+	for line in lines:
+		if line.startswith('Index: '):
+			# Might break files with leading/trailing whitespaces. Ignore them.
+			src = dst = line[len('Index: '):].strip()
+			continue
+		if line.startswith('rename from '):
+			src = line[len('rename from '):]
+		elif line.startswith('copy from '):
+			src = line[len('copy from '):]
+		elif line.startswith('rename to '):
+			dst = line[len('rename to '):]
+		elif line.startswith('copy to '):
+			dst = line[len('copy to '):]
+	return src, dst
+
+def rewritepatch(cl, patch):
+	"""Turn a codereview diff into a git patch with a header suitable to
+	hg import consumption.
+	"""
+	# Split on Index: boundaries
+	reseps = re.compile(r'^={60,}\s*$')
+	groups = [[]]
+	for line in patch.split('\n'):
+		if reseps.search(line):
+			continue
+		if line.startswith('Index: '):
+			groups.append([])
+		groups[-1].append(line)
+
+	rewritten = [
+		'# HG changeset patch',
+		'# User %s' % (cl.dict.get('owner_email') or 'unknown author'),
+	]
+	rewritten.extend(cl.desc.split('\n'))
+	rewritten.append('')
+	for group in groups:
+		src, dst = findpatchedfiles(group)
+		header = 'diff --git a/%s b/%s' % (src, dst)
+		rewritten.append(header)
+		if group and group[0].startswith('Index: '):
+			rewritten.extend(group[1:])
+		else:
+			rewritten.extend(group)
+	return '\n'.join(rewritten)
+
 @hgcommand
 def climport(ui, repo, clname, **opts):
-	"""import a change list from code review server"""
+	"""import a change list from code review server
+	
+	Retrieves the latest diff for specified changelist and import it on top
+	of the current changeset. If the patch fails to apply, rejected hunks may
+	be left as .rej files in the working directory.
+	"""
 	if codereview_disabled:
 		raise hg_util.Abort(codereview_disabled)
 	cl, vers, patch, err = DownloadCL(ui, repo, clname, skip_contributors=True)
 	if opts.get('dry_run'):
 		ui.write(patch)
 		return 0
-	raise hg_util.Abort("not implemented")
+	# Write the patch somewhere and import it
+	patch = rewritepatch(cl, patch)
+	fd, path = tempfile.mkstemp(prefix='CL%s' % clname, suffix='.diff')
+	os.close(fd)
+	hg_util.writefile(path, patch)
+	try:
+		ret = commands.import_(ui, repo, path, base='', strip=1)
+		if ret:
+			hg_util.Abort(ret)
+	finally:
+		hg_util.unlink(path)
+	ctx = repo['.']
+	desc = ctx.description().splitlines()
+	if desc:
+		desc = desc[0].strip()
+	else:
+		desc = '(no description)'
+	ui.status('CL %s imported as:\n' % clname)
+	ui.status('%d:%s %s\n' % (ctx.rev(), str(ctx), desc))
 
 def clidkw(**args):
 	""":clid: String. Codereview changelist identifier or the empty string."""

tests/test-climport.t

   +++ b/d
   @@ -0,0 +1,1 @@
   +d
+
+  $ hg climport ${clid}
+  applying .*CL.*.diff (re)
+  CL \d+ imported as: (re)
+  4:\S{12} change (re)
+
+  $ hg export -g .
+  # HG changeset patch
+  # User patrick@mezard.eu
+  # Date \d+ \d+ (re)
+  #      .* (re)
+  # Node ID .* (re)
+  # Parent  .* (re)
+  change
+  
+  diff --git a/a b/a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,2 @@
+   a
+  +a
+  diff --git a/a b/aa
+  copy from a
+  copy to aa
+  diff --git a/b b/bb
+  rename from b
+  rename to bb
+  index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d33e3942cbbab79d0b3271f8f97e531afd6fff61
+  GIT binary patch
+  literal 7
+  Oc${NM%FIhFsssQCm;#0X
+  
+  diff --git a/c b/c
+  deleted file mode 100644
+  --- a/c
+  +++ /dev/null
+  @@ -1,1 +0,0 @@
+  -c
+  diff --git a/d b/d
+  new file mode 100644
+  --- /dev/null
+  +++ b/d
+  @@ -0,0 +1,1 @@
+  +d
+
+Test climport failure
+
+  $ hg up -C .^
+  3 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  $ echo z > a
+  $ hg ci -m changea
+  created new head
+  $ hg climport ${clid}
+  applying .* (re)
+  patching file a
+  Hunk #1 FAILED at 0
+  1 out of 1 hunks FAILED -- saving rejects to file a.rej
+  abort: patch failed to apply
+  [255]
+  $ cat a.rej
+  --- a
+  +++ a
+  @@ -1,1 +1,2 @@
+  -a
+  +a
+  +a
+
   $ hg clclose --delete ${rev}
   Issue deleted. URL: https://codereview.appspot.com/\d+ (re)
 

tests/test-clpush.t

 
 Import invalid changelist
 
+  $ hg climport 2>&1 | grep invalid
+  hg climport: invalid arguments
   $ hg climport 65010045
   .*HTTP Error 404: Not Found (re)
   abort: error loading CL \d+: cannot load CL \d+ from server (re)
 
 Test clclose arguments
 
-  $ hg clclose --no-email
+  $ hg clclose --no-email 2>&1 | grep invalid
   hg clclose: invalid arguments
-  hg clclose [OPTIONS] REV
-  
-  close a change list
-  
-  options:
-  
-    --delete   delete changelist
-    --no-email update the changelist but do not email
-  
-  use "hg help clclose" to show the full help text
-  [255]
 
 Close and delete the CL