threads / discuss / 16269

importing mercurial patch

Subject: importing mercurial patch

## tl;dr

6 messages between Nov 11, 2008 and Nov 11, 2008.

replies: 5people: 2as markdown or json

Ondrej Certik· Nov 11, 2008, 10:58 UTC · lore
Hi,

I'd like git to be able to import mercurial-exported patches. This short Python program does it:

------------------------------------- #! /usr/bin/python

import os import sys import re import tempfile

def run(cmd):
    print cmd
    os.system(cmd)
patch = sys.argv[1]
p = open(patch).read()
author = re.search("# User (.+)", p).groups()[0]
p = p.split("\n")
while not p[0].startswith("# Parent"):
    del p[0]
i = 1
while not p[i].startswith("diff -r "):
    i += 1
commit_message = "\n".join(p[1:i])
_, filename = tempfile.mkstemp()
f = open(filename, "w")
f.write(commit_message)
f.close()

run("git apply %s" % patch) run("git ci -a --author='%s' -F %s" % (author, filename) ) ---------------------

How should this be implemented in git? Should I try to extend "git-am.sh" to handle it?

For better imagination, this is how the patch looks like:
http://paste.debian.net/21210/

Thanks for any feedback, Ondrej

Ondrej Certik· Nov 11, 2008, 10:59 UTC · re: Ondrej Certik · lore

Re: importing mercurial patch

On Tue, Nov 11, 2008 at 11:58 AM, Ondrej Certik <ondrej@certik.cz> wrote:
Show 40 quoted lines
> Hi,
>
> I'd like git to be able to import mercurial-exported patches. This
> short Python program does it:
>
>
> -------------------------------------
> #! /usr/bin/python
>
> import os
> import sys
> import re
> import tempfile
>
> def run(cmd):
>    print cmd
>    os.system(cmd)
>
> patch = sys.argv[1]
> p = open(patch).read()
> author = re.search("# User (.+)", p).groups()[0]
> p = p.split("\n")
> while not p[0].startswith("# Parent"):
>    del p[0]
> i = 1
> while not p[i].startswith("diff -r "):
>    i += 1
> commit_message = "\n".join(p[1:i])
> _, filename = tempfile.mkstemp()
> f = open(filename, "w")
> f.write(commit_message)
> f.close()
>
> run("git apply %s" % patch)
> run("git ci -a --author='%s' -F %s" % (author, filename) )
> ---------------------
>
>
> How should this be implemented in git? Should I try to extend
> "git-am.sh" to handle it?

Just to make it clear --- I will of course use sh or C with git, I only used Python above because that's the language I know the best.

Ondrej
Johannes Schindelin· Nov 11, 2008, 12:00 UTC · re: Ondrej Certik · lore

Re: importing mercurial patch

Hi,
On Tue, 11 Nov 2008, Ondrej Certik wrote:
> I'd like git to be able to import mercurial-exported patches.
Have you seen
	http://repo.or.cz/w/fast-export.git/
?

I had many problems with it, so many that I started to write my own hg-fast-export together with Dirkjan Ochtman, a very nice Mercurial guy (actually, he started writing it, and I tried to fix it, but it still does not work due to merge mishandling).

But then I saw that finally, they started work on it again. It is somewhat sloppy, having a large part in Python and a large part in shell, which could have been avoided, but at least it works.

Ciao, Dscho

Ondrej Certik· Nov 11, 2008, 12:18 UTC · re: Johannes Schindelin · lore

Re: importing mercurial patch

On Tue, Nov 11, 2008 at 1:00 PM, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:

Show 20 quoted lines
> Hi,
>
> On Tue, 11 Nov 2008, Ondrej Certik wrote:
>
>> I'd like git to be able to import mercurial-exported patches.
>
> Have you seen
>
>        http://repo.or.cz/w/fast-export.git/
>
> ?
>
> I had many problems with it, so many that I started to write my own
> hg-fast-export together with Dirkjan Ochtman, a very nice Mercurial guy
> (actually, he started writing it, and I tried to fix it, but it still does
> not work due to merge mishandling).
>
> But then I saw that finally, they started work on it again.  It is
> somewhat sloppy, having a large part in Python and a large part in shell,
> which could have been avoided, but at least it works.

Yes, I use fast-export to convert from hg to git on the fly. But as I understood it, you need to have the whole repository converted. So if someone sends the patch in the mercurial format, I just want to apply it to git, instead of applying it to hg first and then converting. Well, you are right, that this is definitely one way of doing it.

But imho if git supported mercurial patches, life would be a lot easier.
Ondrej
Johannes Schindelin· Nov 11, 2008, 14:41 UTC · re: Ondrej Certik · lore

Re: importing mercurial patch

Hi,
On Tue, 11 Nov 2008, Ondrej Certik wrote:
> But imho if git supported mercurial patches, life would be a lot easier.
Mine would not be.

BTW I had to be online (which is not always the case when I read email) to access the pastebin, which made it more of a hassle to look at it than I deem necessary. Besides, it is bad because in 3 days, that pastie will be gone. Not nice.

So here is it, for the pleasure of others:
	# HG changeset patch
	# User Vinzent Steinberg <vinzent.steinberg@gmail.com>
	# Date 1226338168 -3600
	# Node ID 23efeaf89f7089d94307526ec0536eb6f4382213
	# Parent  dab6435e04fd083d66bbfa897cbe15ab9660b9e6
	<commit subject>
	<commit body>
	diff -r <commit name> -r <commit name> <filename>
	--- a/<filename> <date>
	--- b/<filename> <date>
	@@ <line range pair> @@
	...

So what I suggest is that you familiarize yourself with builtin-mailsplit.c. Basically you'd need to enhance the is_from_line() function to check this:

	const char *hg_patch_preamble = "# HG changeset patch\n";
	if (len >= strlen(hg_patch_preamble) && !memcmp(line, 
			hg_patch_preamble, strlen(hg_patch_preamble))
		return 1;

Then you need to familiarize yourself with builtin-mailinfo.c. In function mailinfo(), you'd need to work on this:

        /* process the email header */
        while (read_one_header_line(&line, fin))
                check_header(&line, p_hdr_data, 1);

I'd suggest to make the function read_one_header_line() into a handle_one_header_line(), and replace the while loop with this:

	if (!strbuf_getline(&line, fin)) {
		if (!strcmp(line.buf, "# HG changeset patch\n"))
			while (handle_one_hg_header_line(&line, 
					p_hdr_data, fin))
				strbuf_getline(&line, fin);
		else
			while (handle_one_header_line(&line, fin)) {
				check_header(&line, p_hdr_data, 1);
				strbuf_getline(&line, fin);
			}
	}
Implementing handle_one_hg_header_line() should be a breeze:
	static int handle_one_hg_header_line(struct strbuf *line,
			struct strbuf *hdr_data[], FILE *in)
	{
		if (line.buf[0] != '#') {
			strbuf_addbuf(hdr_data[1], line);
			return 0; /* no more headers */
		}
		if (!prefixcmp(line.buf, "# User "))
			strbuf_addstr(hdr_data[0], line.buf + 7);
		else if (!prefixcmp(line.buf, "# Date "))
			strbuf_addstr(hdr_data[2], line.buf + 7);
		return 1;
	}

Okay, this is all utterly untested, and you probably need to trim the newlines from the lines first, and maybe you need to replace the hdr_data[] entries instead of adding to them, but now you have a starting point.

Hth, Dscho

Ondrej Certik· Nov 11, 2008, 23:36 UTC · re: Johannes Schindelin · lore

Re: importing mercurial patch

Hi Johannes!

On Tue, Nov 11, 2008 at 3:41 PM, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:

Show 7 quoted lines
> Hi,
>
> On Tue, 11 Nov 2008, Ondrej Certik wrote:
>
>> But imho if git supported mercurial patches, life would be a lot easier.
>
> Mine would not be.
Because you don't use Mercurial, or is there also some other reason?
> BTW I had to be online (which is not always the case when I read email) to
> access the pastebin, which made it more of a hassle to look at it than I
> deem necessary.  Besides, it is bad because in 3 days, that pastie will be
> gone.  Not nice.
You are right, sorry about that.
Show 71 quoted lines
>
> So here is it, for the pleasure of others:
>
>        # HG changeset patch
>        # User Vinzent Steinberg <vinzent.steinberg@gmail.com>
>        # Date 1226338168 -3600
>        # Node ID 23efeaf89f7089d94307526ec0536eb6f4382213
>        # Parent  dab6435e04fd083d66bbfa897cbe15ab9660b9e6
>        <commit subject>
>
>        <commit body>
>
>        diff -r <commit name> -r <commit name> <filename>
>        --- a/<filename> <date>
>        --- b/<filename> <date>
>        @@ <line range pair> @@
>        ...
>
> So what I suggest is that you familiarize yourself with
> builtin-mailsplit.c.  Basically you'd need to enhance the is_from_line()
> function to check this:
>
>        const char *hg_patch_preamble = "# HG changeset patch\n";
>
>        if (len >= strlen(hg_patch_preamble) && !memcmp(line,
>                        hg_patch_preamble, strlen(hg_patch_preamble))
>                return 1;
>
> Then you need to familiarize yourself with builtin-mailinfo.c.  In
> function mailinfo(), you'd need to work on this:
>
>        /* process the email header */
>        while (read_one_header_line(&line, fin))
>                check_header(&line, p_hdr_data, 1);
>
> I'd suggest to make the function read_one_header_line() into a
> handle_one_header_line(), and replace the while loop with this:
>
>        if (!strbuf_getline(&line, fin)) {
>                if (!strcmp(line.buf, "# HG changeset patch\n"))
>                        while (handle_one_hg_header_line(&line,
>                                        p_hdr_data, fin))
>                                strbuf_getline(&line, fin);
>                else
>                        while (handle_one_header_line(&line, fin)) {
>                                check_header(&line, p_hdr_data, 1);
>                                strbuf_getline(&line, fin);
>                        }
>        }
>
> Implementing handle_one_hg_header_line() should be a breeze:
>
>        static int handle_one_hg_header_line(struct strbuf *line,
>                        struct strbuf *hdr_data[], FILE *in)
>        {
>                if (line.buf[0] != '#') {
>                        strbuf_addbuf(hdr_data[1], line);
>                        return 0; /* no more headers */
>                }
>
>                if (!prefixcmp(line.buf, "# User "))
>                        strbuf_addstr(hdr_data[0], line.buf + 7);
>                else if (!prefixcmp(line.buf, "# Date "))
>                        strbuf_addstr(hdr_data[2], line.buf + 7);
>                return 1;
>        }
>
> Okay, this is all utterly untested, and you probably need to trim the
> newlines from the lines first, and maybe you need to replace the
> hdr_data[] entries instead of adding to them, but now you have a starting
> point.

Thanks a lot for the detailed help, I'll give it a shot and report back in couple days, hopefully with a working patch. :)

Ondrej

← back to recent threads