Since 0cfd112 (am: preliminary support for hg patches, 2011-08-29),
git-am.sh could convert mercurial patches to an RFC2822 mail patch
suitable for parsing with git-mailinfo, and queue them in the state
directory for application.

Since 15ced75 (git-am foreign patch support: autodetect some patch
formats, 2009-05-27), git-am.sh was able to auto-detect mercurial
patches by checking if the file begins with the line:

        # HG changeset patch

Re-implement the above in builtin/am.c.

Helped-by: Stefan Beller <sbel...@google.com>
Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 builtin/am.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 73 insertions(+), 1 deletion(-)

diff --git a/builtin/am.c b/builtin/am.c
index 01f8a53..8fe616e 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -81,7 +81,8 @@ enum patch_format {
        PATCH_FORMAT_UNKNOWN = 0,
        PATCH_FORMAT_MBOX,
        PATCH_FORMAT_STGIT,
-       PATCH_FORMAT_STGIT_SERIES
+       PATCH_FORMAT_STGIT_SERIES,
+       PATCH_FORMAT_HG
 };
 
 enum keep_type {
@@ -658,6 +659,11 @@ static int detect_patch_format(const char **paths)
                goto done;
        }
 
+       if (!strcmp(l1.buf, "# HG changeset patch")) {
+               ret = PATCH_FORMAT_HG;
+               goto done;
+       }
+
        strbuf_reset(&l2);
        strbuf_getline_crlf(&l2, fp);
        strbuf_reset(&l3);
@@ -856,6 +862,68 @@ static int split_mail_stgit_series(struct am_state *state, 
const char **paths,
 }
 
 /**
+ * A split_patches_conv() callback that converts a mercurial patch to a RFC2822
+ * message suitable for parsing with git-mailinfo.
+ */
+static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
+{
+       struct strbuf sb = STRBUF_INIT;
+
+       while (!strbuf_getline(&sb, in, '\n')) {
+               const char *str;
+
+               if (skip_prefix(sb.buf, "# User ", &str))
+                       fprintf(out, "From: %s\n", str);
+               else if (skip_prefix(sb.buf, "# Date ", &str)) {
+                       unsigned long timestamp;
+                       long tz, tz2;
+                       char *end;
+
+                       errno = 0;
+                       timestamp = strtoul(str, &end, 10);
+                       if (errno)
+                               return error(_("invalid timestamp"));
+
+                       if (!skip_prefix(end, " ", &str))
+                               return error(_("invalid Date line"));
+
+                       errno = 0;
+                       tz = strtol(str, &end, 10);
+                       if (errno)
+                               return error(_("invalid timezone offset"));
+
+                       if (*end)
+                               return error(_("invalid Date line"));
+
+                       /*
+                        * mercurial's timezone is in seconds west of UTC,
+                        * however git's timezone is in hours + minutes east of
+                        * UTC. Convert it.
+                        */
+                       tz2 = labs(tz) / 3600 * 100 + labs(tz) % 3600 / 60;
+                       if (tz > 0)
+                               tz2 = -tz2;
+
+                       fprintf(out, "Date: %s\n", show_date(timestamp, tz2, 
DATE_RFC2822));
+               } else if (starts_with(sb.buf, "# ")) {
+                       continue;
+               } else {
+                       fprintf(out, "\n%s\n", sb.buf);
+                       break;
+               }
+       }
+
+       strbuf_reset(&sb);
+       while (strbuf_fread(&sb, 8192, in) > 0) {
+               fwrite(sb.buf, 1, sb.len, out);
+               strbuf_reset(&sb);
+       }
+
+       strbuf_release(&sb);
+       return 0;
+}
+
+/**
  * Splits a list of files/directories into individual email patches. Each path
  * in `paths` must be a file/directory that is formatted according to
  * `patch_format`.
@@ -887,6 +955,8 @@ static int split_mail(struct am_state *state, enum 
patch_format patch_format,
                return split_mail_conv(stgit_patch_to_mail, state, paths, 
keep_cr);
        case PATCH_FORMAT_STGIT_SERIES:
                return split_mail_stgit_series(state, paths, keep_cr);
+       case PATCH_FORMAT_HG:
+               return split_mail_conv(hg_patch_to_mail, state, paths, keep_cr);
        default:
                die("BUG: invalid patch_format");
        }
@@ -1938,6 +2008,8 @@ static int parse_opt_patchformat(const struct option 
*opt, const char *arg, int
                *opt_value = PATCH_FORMAT_STGIT;
        else if (!strcmp(arg, "stgit-series"))
                *opt_value = PATCH_FORMAT_STGIT_SERIES;
+       else if (!strcmp(arg, "hg"))
+               *opt_value = PATCH_FORMAT_HG;
        else
                return error(_("Invalid value for --patch-format: %s"), arg);
        return 0;
-- 
2.5.0.rc2.110.gb39b692

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to