{"thread":{"id":"17094","subject":"[RFC PATCH 4/3] Add example git-vcs-p4","startedAt":"2009-01-11T20:12:13Z","lastAt":"2009-01-12T18:36:03Z","messageCount":6,"participants":["Daniel Barkalow","Junio C Hamano","Alex Riesen"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"99992","messageId":"alpine.LNX.1.00.0901110336380.19665@iabervon.org","threadId":"17094","inReplyTo":null,"subject":"[RFC PATCH 4/3] Add example git-vcs-p4","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2009-01-11T20:12:13Z","receivedAt":"2009-01-11T20:12:13Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"This implements the \"list\" and \"import\" commands, and has an\nimplementation of \"export\" which isn't used yet.\n\nSigned-off-by: Daniel Barkalow <barkalow@iabervon.org>\n---\n Documentation/git-vcs-p4.txt |   33 ++\n Makefile                     |    3 +\n builtin.h                    |    2 +\n git.c                        |    2 +\n p4-notes                     |   33 ++\n p4client.c                   |   50 +++\n p4client.h                   |   10 +\n vcs-p4.c                     |  945 ++++++++++++++++++++++++++++++++++++++++++\n vcs-p4.h                     |  119 ++++++\n 9 files changed, 1197 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-vcs-p4.txt\n create mode 100644 p4-notes\n create mode 100644 p4client.c\n create mode 100644 p4client.h\n create mode 100644 vcs-p4.c\n create mode 100644 vcs-p4.h\n\ndiff --git a/Documentation/git-vcs-p4.txt b/Documentation/git-vcs-p4.txt\nnew file mode 100644\nindex 0000000..4039d24\n--- /dev/null\n+++ b/Documentation/git-vcs-p4.txt\n@@ -0,0 +1,33 @@\n+Config\n+------\n+\n+vcs-p4.port::\n+\tThe value to use for P4PORT\n+\n+vcs-p4.client::\n+\tThe value to use for P4CLIENT\n+\n+vcs-p4.codelineformat::\n+\tA regular expression to match valid codelines; a codeline is a\n+\tdirectory that contains exactly those files that belong to a\n+\tversion of a project. Importing history with integrations will\n+\tgenerally discover codelines not explicitly marked to be\n+\timported, found when a file in a known codeline, whose full\n+\tpath is therefore the codeline path plus a relative path, is\n+\tintegrated from a file with a name that ends with that\n+\trelative path. However, files will sometimes be integrated\n+\tfrom non-codelines (that is, from a directory that contains\n+\tunrelated files whose history should not be tracked), and this\n+\toption can be used to ignore some directories.\n+\n+\tNote that, properly, the history of the individual files from\n+\ta non-codeline which got integrated into a codeline should\n+\tcontribute but that this is not presently supported.\n+\n+remotes.*.url::\n+\tThe perforce location of a codeline to track. Other codelines\n+\tmay be discovered by git-vcs-p4, but it will make no attempt\n+\tto get versions in these locations more recent than the last\n+\tversions that contribute at present to the tracked codelines,\n+\tand it will not make them available for matching in \"fetch\"\n+\tpatterns.\ndiff --git a/Makefile b/Makefile\nindex dee97c1..3f40452 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -501,6 +501,9 @@ LIB_OBJS += wt-status.o\n LIB_OBJS += xdiff-interface.o\n LIB_OBJS += preload-index.o\n \n+LIB_OBJS += p4client.o\n+LIB_OBJS += vcs-p4.o\n+\n BUILTIN_OBJS += builtin-add.o\n BUILTIN_OBJS += builtin-annotate.o\n BUILTIN_OBJS += builtin-apply.o\ndiff --git a/builtin.h b/builtin.h\nindex 1495cf6..9039ad5 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -21,6 +21,8 @@ extern int commit_tree(const char *msg, unsigned char *tree,\n \t\tconst char *author);\n extern int check_pager_config(const char *cmd);\n \n+extern int cmd_p4(int argc, const char **argv, const char *prefix);\n+\n extern int cmd_add(int argc, const char **argv, const char *prefix);\n extern int cmd_annotate(int argc, const char **argv, const char *prefix);\n extern int cmd_apply(int argc, const char **argv, const char *prefix);\ndiff --git a/git.c b/git.c\nindex a53e24f..9ba92fe 100644\n--- a/git.c\n+++ b/git.c\n@@ -265,6 +265,8 @@ static void handle_internal_command(int argc, const char **argv)\n {\n \tconst char *cmd = argv[0];\n \tstatic struct cmd_struct commands[] = {\n+\t\t{ \"vcs-p4\", cmd_p4 },\n+\n \t\t{ \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"stage\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"annotate\", cmd_annotate, RUN_SETUP },\ndiff --git a/p4-notes b/p4-notes\nnew file mode 100644\nindex 0000000..bd95903\n--- /dev/null\n+++ b/p4-notes\n@@ -0,0 +1,33 @@\n+People using branches in p4 work like svn, except that the branches\n+are not rooted at predictable places. Furthermore, there is not a\n+uniform tree layout within a depot.\n+\n+Therefore, in order to generate a git repository from p4, it is\n+necessary to specify a root within the depot as the working tree root\n+in git. On the other hand, it should be possible to determine from the\n+p4 history what portions of the depot outside of the root should be\n+considered as branches, as it tracks \"integrations\".\n+\n+In theory, anyway, it should even be possible to produce a git\n+repository with submodules when a similar thing has been done with\n+integrations in p4, by determining that there are integrations into a\n+subdirectory of the root.\n+\n+---\n+\n+Overview of operation:\n+\n+ - Allocate codeline\n+ - Import codeline\n+   - use p4_filelog to find the files and their revisions in the codeline\n+   - For each file,\n+\n+---\n+Saving processed state\n+\n+ - Record for each codeline\n+   - What are all the changesets?\n+\n+ - Record for each codeline/changeset\n+   - What's the commit\n+\ndiff --git a/p4client.c b/p4client.c\nnew file mode 100644\nindex 0000000..09adc47\n--- /dev/null\n+++ b/p4client.c\n@@ -0,0 +1,50 @@\n+#include \"p4client.h\"\n+\n+#include \"cache.h\"\n+#include \"run-command.h\"\n+\n+static const char *const *envp;\n+\n+void p4_init(const char *const *env)\n+{\n+\tenvp = env;\n+}\n+\n+static struct child_process child;\n+\n+int p4_call(int fds[], const char *arg0, int argc, const char **argv)\n+{\n+\tint i;\n+\tmemset(&child, 0, sizeof(child));\n+\tif (fds) {\n+\t\tchild.in = -1;\n+\t\tchild.out = -1;\n+\t} else {\n+\t\tchild.no_stdin = 1;\n+\t\tchild.no_stdout = 1;\n+\t}\n+\tchild.err = 0;\n+\tchild.argv = xcalloc(argc + 3, sizeof(*argv));\n+\tchild.argv[0] = \"p4\";\n+\tchild.argv[1] = arg0;\n+\tchild.env = envp;\n+\tfor (i = 0; i < argc; i++)\n+\t\tchild.argv[i + 2] = argv[i];\n+\tchild.argv[argc + 2] = NULL;\n+\tstart_command(&child);\n+\tif (fds) {\n+\t\tfds[0] = child.in;\n+\t\tfds[1] = child.out;\n+\t}\n+\treturn 0;\n+}\n+\n+int p4_complete(void)\n+{\n+\tif (!child.no_stdin)\n+\t\tclose(child.in);\n+\tif (!child.no_stdout)\n+\t\tclose(child.out);\n+\tfinish_command(&child);\n+\treturn 0;\n+}\ndiff --git a/p4client.h b/p4client.h\nnew file mode 100644\nindex 0000000..2fa2cc3\n--- /dev/null\n+++ b/p4client.h\n@@ -0,0 +1,10 @@\n+#ifndef P4CLIENT_H\n+#define P4CLIENT_H\n+\n+void p4_init(const char *const *env);\n+\n+int p4_call(int fds[], const char *arg0, int argc, const char **argv);\n+\n+int p4_complete();\n+\n+#endif\ndiff --git a/vcs-p4.c b/vcs-p4.c\nnew file mode 100644\nindex 0000000..3ac1e38\n--- /dev/null\n+++ b/vcs-p4.c\n@@ -0,0 +1,945 @@\n+#include \"cache.h\"\n+#include \"vcs-p4.h\"\n+#include \"strbuf.h\"\n+#include \"remote.h\"\n+#include \"commit.h\"\n+#include \"tree.h\"\n+#include \"tree-walk.h\"\n+#include \"diff.h\"\n+\n+#include \"p4client.h\"\n+\n+/** Should we try to find codelines that branch off of the relevant\n+ * ones, for future reference? This lets us find new things in\n+ * ls-remote without making the user tell us.\n+ **/\n+static int find_new_codelines;\n+\n+static regex_t *codeline_regex;\n+\n+#define CODELINE_TAG \"Codeline: \"\n+#define CHANGESET_TAG \"Changeset: \"\n+\n+/** List functions **/\n+\n+static void add_to_revision_list(struct p4_revision_list **list,\n+\t\t\t\t struct p4_revision *revision)\n+{\n+\twhile (*list)\n+\t\tlist = &(*list)->next;\n+\t*list = xcalloc(1, sizeof(**list));\n+\t(*list)->revision = revision;\n+}\n+\n+/** Functions to find or create representations **/\n+\n+static struct p4_depot *get_depot(void)\n+{\n+\tstruct p4_depot *depot = xcalloc(1, sizeof(*depot));\n+\tdepot->next_mark = 1;\n+\treturn depot;\n+}\n+\n+static struct p4_changeset *get_changeset(struct p4_codeline *codeline,\n+\t\t\t\t\t  long number);\n+\n+static char *codeline_to_refname(const char *path) {\n+\tstruct strbuf buf;\n+\tif (prefixcmp(path, \"//\"))\n+\t\treturn NULL;\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_addf(&buf, \"refs/p4/%s\", path + 2);\n+\treturn strbuf_detach(&buf, NULL);\n+}\n+\n+static char *refname_to_codeline(const char *refname) {\n+\tstruct strbuf buf;\n+\tif (prefixcmp(refname, \"refs/p4/\"))\n+\t\treturn NULL;\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_addf(&buf, \"//%s\", refname + strlen(\"refs/p4/\"));\n+\treturn strbuf_detach(&buf, NULL);\n+}\n+\n+static struct p4_codeline *get_codeline(struct p4_depot *depot, const char *path)\n+{\n+\tstruct p4_codeline **posn, *codeline;\n+\tunsigned char sha1[20];\n+\n+\tif (codeline_regex && regexec(codeline_regex, path, 0, NULL, 0))\n+\t\treturn NULL;\n+\n+\tfor (posn = &depot->codelines; *posn; posn = &(*posn)->next)\n+\t\tif (!strcmp(path, (*posn)->path))\n+\t\t\treturn *posn;\n+\tcodeline = xcalloc(1, sizeof(*codeline));\n+\tcodeline->depot = depot;\n+\tcodeline->path = xstrdup(path);\n+\n+\tcodeline->refname = codeline_to_refname(path);\n+\tif (!get_sha1(codeline->refname, sha1)) {\n+\t\tstruct commit *commit = lookup_commit(sha1);\n+\t\tchar *field;\n+\t\tparse_commit(commit);\n+\t\tprintf(\"progress found commit for %s\\n\", codeline->refname);\n+\t\tfield = strstr(commit->buffer, CHANGESET_TAG);\n+\t\tif (!field) {\n+\t\t\tfprintf(stderr, \"Couldn't find changeset line in commit\\n\");\n+\t\t} else {\n+\t\t\tstruct p4_changeset *changeset;\n+\t\t\tcodeline->finished_changeset =\n+\t\t\t\tatoi(field + strlen(CHANGESET_TAG));\n+\t\t\tprintf(\"progress for changeset %lu\\n\",\n+\t\t\t       codeline->finished_changeset);\n+\t\t\tchangeset = get_changeset(codeline, codeline->finished_changeset);\n+\t\t\tchangeset->commit = commit;\n+\t\t\tcodeline->history = changeset;\n+\t\t}\n+\t}\n+\t*posn = codeline;\n+\treturn codeline;\n+}\n+\n+static struct p4_codeline *find_codeline(struct p4_depot *depot, const char *path)\n+{\n+\tstruct p4_codeline **posn;\n+\tfor (posn = &depot->codelines; *posn; posn = &(*posn)->next)\n+\t\tif (!prefixcmp(path, (*posn)->path))\n+\t\t\treturn *posn;\n+\treturn NULL;\n+}\n+\n+/** Inserts the changeset at the right place in order for the codeline **/\n+static struct p4_changeset *get_changeset(struct p4_codeline *codeline,\n+\t\t\t\t\t  long number)\n+{\n+\tstruct p4_changeset **posn = &codeline->changesets;\n+\tstruct p4_changeset *changeset, *prev = NULL;\n+\twhile (*posn && (*posn)->number < number) {\n+\t\tprev = *posn;\n+\t\tposn = &(*posn)->next;\n+\t}\n+\tif (*posn && (*posn)->number == number)\n+\t\treturn *posn;\n+\tprintf(\"# add changeset %lu in %s\\n\", number, codeline->path);\n+\tchangeset = xcalloc(1, sizeof(*changeset));\n+\tchangeset->codeline = codeline;\n+\tchangeset->next = *posn;\n+\tchangeset->previous = prev;\n+\tif (changeset->next)\n+\t\tchangeset->next->previous = changeset;\n+\telse\n+\t\tcodeline->head = changeset;\n+\t*posn = changeset;\n+\tchangeset->number = number;\n+\tcodeline->num_changesets++;\n+\treturn changeset;\n+}\n+\n+static struct p4_changeset *changeset_from_commit(struct p4_depot *depot,\n+\t\t\t\t\t\t  struct commit *commit)\n+{\n+\tunsigned long number = 0;\n+\tchar *codeline = NULL, *field;\n+\tparse_commit(commit);\n+\tfield = strstr(commit->buffer, CHANGESET_TAG);\n+\tif (field)\n+\t\tnumber = atoi(field + strlen(CHANGESET_TAG));\n+\tfield = strstr(commit->buffer, CODELINE_TAG);\n+\tif (field) {\n+\t\tchar *end;\n+\t\tcodeline = field + strlen(CODELINE_TAG);\n+\t\tend = strchr(codeline, '\\n');\n+\t\tif (end)\n+\t\t\t*end = '\\0';\n+\t}\n+\tif (number && codeline)\n+\t\treturn get_changeset(get_codeline(depot, codeline), number);\n+\treturn NULL;\n+}\n+\n+static struct p4_file *get_file_by_full(struct p4_codeline *codeline,\n+\t\t\t\t\tconst char *fullpath)\n+{\n+\tconst char *rel = fullpath + strlen(codeline->path);\n+\tstruct p4_file **posn;\n+\tfor (posn = &codeline->files; *posn; posn = &(*posn)->next) {\n+\t\tif (!strcmp((*posn)->name, rel))\n+\t\t\treturn *posn;\n+\t}\n+\t*posn = xcalloc(1, sizeof(**posn));\n+\t(*posn)->codeline = codeline;\n+\t(*posn)->name = xstrdup(rel);\n+\treturn *posn;\n+}\n+\n+static struct p4_file *get_related_file(struct p4_file *base, const char *path)\n+{\n+\tint basenamelen = strlen(base->name);\n+\tint reldirlen = strlen(path) - basenamelen;\n+\tstruct p4_codeline *codeline;\n+\tif (reldirlen > 0 && !strcmp(path + reldirlen, base->name)) {\n+\t\t/* File with the same name in another codeline */\n+\t\tchar *other = xstrndup(path, reldirlen);\n+\t\tprintf(\"# find %s in %s\\n\", path, other);\n+\t\tcodeline = get_codeline(base->codeline->depot, other);\n+\t\tif (codeline)\n+\t\t\treturn get_file_by_full(codeline, path);\n+\t\treturn NULL;\n+\t}\n+\tcodeline = find_codeline(base->codeline->depot, path);\n+\tif (codeline) {\n+\t\t/* File with a different name in some known codeline */\n+\t\treturn get_file_by_full(codeline, path);\n+\t}\n+\t/* Not in any known codeline; need to recheck this after\n+\t * discovering codelines completes.\n+\t */\n+\treturn NULL;\n+}\n+\n+static struct p4_revision *get_revision(struct p4_file *file, unsigned number)\n+{\n+\tstruct p4_revision **posn;\n+\tstruct p4_revision *revision;\n+\tfor (posn = &file->revisions; *posn && (*posn)->number < number;\n+\t     posn = &(*posn)->next)\n+\t\t;\n+\tif (!*posn || (*posn)->number != number) {\n+\t\trevision = xcalloc(1, sizeof(*revision));\n+\t\trevision->next = *posn;\n+\t\t*posn = revision;\n+\t\trevision->number = number;\n+\t\trevision->file = file;\n+\t}\n+\treturn *posn;\n+}\n+\n+static int parse_p4_date(const char *date)\n+{\n+\tstruct tm tm;\n+\tmemset(&tm, 0, sizeof(tm));\n+\ttm.tm_year = strtol(date, NULL, 10) - 1900;\n+\ttm.tm_mon = strtol(date + 5, NULL, 10) - 1;\n+\ttm.tm_mday = strtol(date + 8, NULL, 10);\n+\ttm.tm_hour = strtol(date + 11, NULL, 10);\n+\ttm.tm_min = strtol(date + 14, NULL, 10);\n+\ttm.tm_sec = strtol(date + 17, NULL, 10);\n+\treturn mktime(&tm);\n+}\n+\n+static const char *get_file_type(char *text)\n+{\n+\tif (!prefixcmp(text, \"text\"))\n+\t\treturn \"text\";\n+\tif (!prefixcmp(text, \"ktext\"))\n+\t\treturn \"ktext\";\n+\tif (!prefixcmp(text, \"xtext\"))\n+\t\treturn \"xtext\";\n+\tif (!prefixcmp(text, \"kxtext\"))\n+\t\treturn \"kxtext\";\n+\treturn \"unknown\";\n+}\n+\n+static const char *get_file_mode(const char *type)\n+{\n+\tif (!strcmp(type, \"kxtext\") || !strcmp(type, \"xtext\"))\n+\t\treturn \"100755\";\n+\treturn \"100644\";\n+}\n+\n+static void output_data(struct strbuf *buf)\n+{\n+\tprintf(\"data %d\\n\", buf->len);\n+\tfwrite(buf->buf, 1, buf->len, stdout);\n+\tprintf(\"\\n\");\n+}\n+\n+static int write_blob(struct p4_codeline *codeline,\n+\t\t      const unsigned char *sha1,\n+\t\t      const char *path)\n+{\n+\tstruct strbuf buf;\n+\tvoid *content;\n+\tenum object_type type;\n+\tunsigned long size;\n+\tint fd;\n+\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_addf(&buf, \"%s/%s\", codeline->working, path);\n+\tcontent = read_sha1_file(sha1, &type, &size);\n+\tfd = open(buf.buf, O_WRONLY | O_CREAT, 0666);\n+\tif (fd < 0) {\n+\t\tdie(\"Got err %d\", errno);\n+\t}\n+\twrite_or_die(fd, content, size);\n+\treturn 0;\n+}\n+\n+/** P4 operations **/\n+\n+static int p4_where(struct p4_codeline *codeline)\n+{\n+\tint fds[2];\n+\tconst char *argv[1];\n+\tstruct strbuf buf;\n+\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_addstr(&buf, codeline->path);\n+\targv[0] = buf.buf;\n+\tp4_call(fds, \"where\", 1, argv);\n+\tFILE *input = fdopen(fds[1], \"r\");\n+\n+\twhile (!strbuf_getline(&buf, input, '\\n')) {\n+\t\tchar *working = strrchr(buf.buf, ' ');\n+\t\tif (working)\n+\t\t\tcodeline->working = xstrdup(working + 1);\n+\t}\n+\tp4_complete();\n+\treturn codeline->working ? 0 : -1;\n+}\n+\n+static void p4_sync(struct p4_codeline *codeline)\n+{\n+\tconst char *argv[1];\n+\tstruct strbuf buf;\n+\n+\tprintf(\"progress syncing %s/...\\n\", codeline->working);\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_addf(&buf, \"%s/...@%lu\",\n+\t\t    codeline->working, codeline->head->number);\n+\targv[0] = buf.buf;\n+\tp4_call(NULL, \"sync\", 1, argv);\n+\tp4_complete();\n+}\n+\n+static void p4_edit(struct p4_codeline *codeline, const char *path)\n+{\n+\tconst char *argv[1];\n+\tstruct strbuf buf;\n+\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_addf(&buf, \"%s/%s\", codeline->working, path);\n+\targv[0] = buf.buf;\n+\tp4_call(NULL, \"edit\", 1, argv);\n+\tstrbuf_release(&buf);\n+\tp4_complete();\n+}\n+\n+static void p4_add(struct p4_codeline *codeline, const char *path)\n+{\n+\tconst char *argv[1];\n+\tstruct strbuf buf;\n+\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_addf(&buf, \"%s/%s\", codeline->working, path);\n+\targv[0] = buf.buf;\n+\tp4_call(NULL, \"add\", 1, argv);\n+\tstrbuf_release(&buf);\n+\tp4_complete();\n+}\n+\n+static void p4_delete(struct p4_codeline *codeline, const char *path)\n+{\n+\tconst char *argv[1];\n+\tstruct strbuf buf;\n+\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_addf(&buf, \"%s/%s\", codeline->working, path);\n+\targv[0] = buf.buf;\n+\tp4_call(NULL, \"delete\", 1, argv);\n+\tstrbuf_release(&buf);\n+\tp4_complete();\n+}\n+\n+static void p4_submit(struct commit *commit)\n+{\n+\tint fds[2];\n+\tconst char *argv[1];\n+\tint skip = 0;\n+\targv[0] = \"-o\";\n+\tp4_call(fds, \"change\", 1, argv);\n+\n+\tstruct strbuf message;\n+\tstruct strbuf line;\n+\n+\tFILE *input = fdopen(fds[1], \"r\");\n+\n+\tstrbuf_init(&message, 0);\n+\tstrbuf_init(&line, 0);\n+\n+\twhile (!strbuf_getline(&line, input, '\\n')) {\n+\t\tif (!skip) {\n+\t\t\tstrbuf_addstr(&message, line.buf);\n+\t\t\tstrbuf_addch(&message, '\\n');\n+\t\t}\n+\t\tif (line.buf[0] != '\\t')\n+\t\t\tskip = 0;\n+\t\tif (!strcmp(line.buf, \"Description:\")) {\n+\t\t\tchar *posn;\n+\t\t\tparse_commit(commit);\n+\t\t\tposn = strstr(commit->buffer, \"\\n\\n\");\n+\t\t\tif (posn)\n+\t\t\t\tposn += 2;\n+\t\t\twhile (*posn) {\n+\t\t\t\tchar *eol = strchr(posn, '\\n');\n+\t\t\t\tstrbuf_addstr(&message, \"\\t\");\n+\t\t\t\tif (eol) {\n+\t\t\t\t\teol++;\n+\t\t\t\t\tstrbuf_add(&message, posn, eol - posn);\n+\t\t\t\t\tposn = eol;\n+\t\t\t\t} else {\n+\t\t\t\t\tstrbuf_addstr(&message, posn);\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tstrbuf_addstr(&message, \"\\n\");\n+\t\t\tskip = 1;\n+\t\t}\n+\t}\n+\n+\tfclose(input);\n+\tp4_complete();\n+\n+\tprintf(\"%s\\n\", message.buf);\n+\n+\targv[0] = \"-i\";\n+\tp4_call(fds, \"submit\", 1, argv);\n+\n+\twrite_or_die(fds[0], message.buf, message.len);\n+\tclose(fds[0]);\n+\n+\tinput = fdopen(fds[1], \"r\");\n+\twhile (!strbuf_getline(&line, input, '\\n'))\n+\t\tfprintf(stderr, \"%s\\n\", line.buf);\n+\tp4_complete();\n+}\n+\n+static void p4_print(struct p4_revision *revision)\n+{\n+\tint fds[2];\n+\tconst char *argv[2];\n+\tstruct strbuf line;\n+\tstrbuf_init(&line, 0);\n+\tstrbuf_addf(&line, \"%s%s#%lu\",\n+\t\t    revision->file->codeline->path,\n+\t\t    revision->file->name, revision->number);\n+\targv[1] = line.buf;\n+\targv[0] = \"-q\";\n+\tp4_call(fds, \"print\", 2, argv);\n+\n+\tstrbuf_reset(&line);\n+\tstrbuf_read(&line, fds[1], 0);\n+\tprintf(\"data %d\\n%s\\n\", line.len, line.buf);\n+\tclose(fds[1]);\n+\tp4_complete();\n+}\n+\n+static void p4_change(struct p4_changeset *changeset)\n+{\n+\tint fds[2];\n+\tconst char *argv[2];\n+\tstruct strbuf line;\n+\tstruct strbuf message;\n+\tint date = 0;\n+\tchar *user = NULL;\n+\n+\tstrbuf_init(&line, 0);\n+\tstrbuf_addf(&line, \"%lu\", changeset->number);\n+\targv[1] = line.buf;\n+\targv[0] = \"-o\";\n+\tp4_call(fds, \"change\", 2, argv);\n+\n+\tFILE *input = fdopen(fds[1], \"r\");\n+\n+\twhile (!strbuf_getline(&line, input, '\\n')) {\n+\t\tif (!prefixcmp(line.buf, \"User:\\t\"))\n+\t\t\tuser = xstrdup(line.buf + 6);\n+\t\telse if (!prefixcmp(line.buf, \"Date:\\t\"))\n+\t\t\tdate = parse_p4_date(line.buf + 6);\n+\t\telse if (!prefixcmp(line.buf, \"Description:\"))\n+\t\t\tbreak;\n+\t}\n+\tprintf(\"committer %s <%s> %d +0000\\n\", user, user, date);\n+\tfree(user);\n+\n+\tstrbuf_init(&message, 0);\n+\n+\twhile (!strbuf_getline(&line, input, '\\n')) {\n+\t\tstrbuf_addstr(&message, line.buf + (line.buf[0] == '\\t'));\n+\t\tstrbuf_addch(&message, '\\n');\n+\t}\n+\n+\tstrbuf_addf(&message, CODELINE_TAG \"%s\\n\" CHANGESET_TAG \"%lu\\n\",\n+\t\t    changeset->codeline->path, changeset->number);\n+\toutput_data(&message);\n+\tfclose(input);\n+\tp4_complete();\n+}\n+\n+/** Finds all files in the codeline, and all revisions of those files,\n+ * and all of the changesets they are from, and looks up the codelines\n+ * and files they integrate or branch.\n+ **/\n+static void p4_filelog(struct p4_codeline *codeline)\n+{\n+\tint fds[2];\n+\tstruct strbuf line;\n+\n+\tstruct p4_file *file = NULL;\n+\tstruct p4_revision *revision = NULL;\n+\tconst char *arg;\n+\n+\tif (codeline->filelog_done)\n+\t\treturn;\n+\n+\tprintf(\"progress looking at codeline %s\\n\", codeline->path);\n+\n+\tstrbuf_init(&line, 0);\n+\tstrbuf_addstr(&line, codeline->path);\n+\tstrbuf_addstr(&line, \"/...\");\n+\targ = line.buf;\n+\tp4_call(fds, \"filelog\", 1, &arg);\n+\n+\tFILE *input = fdopen(fds[1], \"r\");\n+\n+\twhile (!strbuf_getline(&line, input, '\\n')) {\n+\t\tif (prefixcmp(line.buf, \"...\")) {\n+\t\t\tif (file) {\n+\t\t\t\t// we're done with one; set HEAD number\n+\t\t\t\t// also need this at the end\n+\t\t\t}\n+\t\t\tfile = get_file_by_full(codeline, line.buf);\n+\t\t} else if (prefixcmp(line.buf, \"... ...\")) {\n+// ... #<rev> change <change> <op> on <date> by <client> (<type>) '<oneline>'\n+\t\t\tint rev, change;\n+\t\t\tchar *posn = line.buf + strlen(\"... #\");\n+\t\t\trev = strtoul(posn, &posn, 10);\n+\t\t\tposn += strlen(\" change \");\n+\t\t\tchange = strtoul(posn, &posn, 10);\n+\t\t\tposn = strchr(posn, '(') + 1;\n+\t\t\trevision = get_revision(file, rev);\n+\t\t\trevision->changeset = get_changeset(codeline, change);\n+\t\t\trevision->type = get_file_type(posn);\n+\t\t\tadd_to_revision_list(&revision->changeset->revisions,\n+\t\t\t\t\t     revision);\n+\t\t} else {\n+// ... ... <op> <direction> <path>#<rev>\n+\t\t\tconst char *path;\n+\t\t\tint rev, from = 0;\n+\t\t\tchar *type = line.buf + strlen(\"... ... \");\n+\t\t\tchar *posn = strrchr(type, ' ') + 1;\n+\n+\t\t\tfrom = (!prefixcmp(type, \"ignored\") &&\n+\t\t\t\tposn == type + strlen(\"ignored\") + 1) ||\n+\t\t\t\t!prefixcmp(strchr(type, ' '), \" from\");\n+\n+\t\t\tpath = posn;\n+\t\t\tposn = strchr(posn, '#');\n+\t\t\t*(posn++) = '\\0';\n+\t\t\tdo {\n+\t\t\t\t/* ???? What does a list of revisions mean? */\n+\t\t\t\trev = strtoul(posn, &posn, 10);\n+\t\t\t\tif (*posn != ',')\n+\t\t\t\t\tbreak;\n+\t\t\t\tposn += 2;\n+\t\t\t} while (1);\n+\t\t\tif (from) {\n+\t\t\t\tstruct p4_file *rel_file =\n+\t\t\t\t\tget_related_file(file, path);\n+\t\t\t\tif (!rel_file)\n+\t\t\t\t\tprintf(\"# Couldn't find %s related to %s %s\\n\",\n+\t\t\t\t\t    path, file->codeline->path,\n+\t\t\t\t\t    file->name);\n+\t\t\t\tif (rel_file && rel_file->codeline != codeline)\n+\t\t\t\t\tadd_to_revision_list(&revision->integrated,\n+\t\t\t\t\t\t\t     get_revision(rel_file, rev));\n+\t\t\t} else if (find_new_codelines) {\n+\t\t\t\t/* This is an \"<op> into <path>#<rev>\" line.\n+\t\t\t\t * We just want to try to create a codeline.\n+\t\t\t\t */\n+\t\t\t\tget_related_file(file, path);\n+\t\t\t}\n+\t\t}\n+\t}\n+\tfclose(input);\n+\tp4_complete();\n+\tif (codeline->history)\n+\t\tcodeline->unreported = codeline->history->next;\n+\telse\n+\t\tcodeline->unreported = codeline->changesets;\n+\tcodeline->filelog_done = 1;\n+}\n+\n+/** Functions to import things (i.e., fill out the representations) **/\n+\n+static struct p4_changeset_list *\n+find_codeline_changeset(struct p4_changeset_list **list,\n+\t\t\tstruct p4_codeline *codeline)\n+{\n+\twhile (*list) {\n+\t\tif ((*list)->changeset->codeline == codeline)\n+\t\t\treturn *list;\n+\t\tlist = &(*list)->next;\n+\t}\n+\t*list = xcalloc(1, sizeof(**list));\n+\treturn *list;\n+}\n+\n+static void resolve_changeset_integrates(struct p4_changeset *changeset)\n+{\n+\tstruct p4_revision_list *posn;\n+\tstruct p4_changeset_list *changesets = NULL;\n+\t/* For each codeline, we want the highest numbered changeset\n+\t * that introduced a revision that has been integrated.\n+\t */\n+\tfor (posn = changeset->revisions; posn; posn = posn->next) {\n+\t\tstruct p4_revision_list *rev_ints = posn->revision->integrated;\n+\t\twhile (rev_ints) {\n+\t\t\tstruct p4_changeset_list *item;\n+\t\t\tif (rev_ints->revision->file->codeline == changeset->codeline) {\n+\t\t\t\trev_ints = rev_ints->next;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\t/* The revision doesn't have the changeset\n+\t\t\t * filled out unless we call this.\n+\t\t\t */\n+\t\t\tp4_filelog(rev_ints->revision->file->codeline);\n+\t\t\titem = find_codeline_changeset(&changesets,\n+\t\t\t\t\t\t       rev_ints->revision->file->codeline);\n+\t\t\tif (!item->changeset ||\n+\t\t\t    item->changeset->number < rev_ints->revision->changeset->number) {\n+\t\t\t\tprintf(\"progress %lu integrates %s#%lu from %lu\\n\",\n+\t\t\t\t       changeset->number,\n+\t\t\t\t       rev_ints->revision->file->name,\n+\t\t\t\t       rev_ints->revision->number,\n+\t\t\t\t       rev_ints->revision->changeset->number);\n+\t\t\t\titem->changeset = rev_ints->revision->changeset;\n+\t\t\t}\n+\t\t\trev_ints = rev_ints->next;\n+\t\t}\n+\t}\n+\t/* We could issue a warning if the state of other files didn't\n+\t * match and yet didn't get integrated, but that's a lot of\n+\t * work and there's no good way to represent the case of a\n+\t * commit contributing to but not being completely obsoleted\n+\t * by another commit.\n+\t */\n+\tchangeset->integrated = changesets;\n+\twhile (changesets) {\n+\t\tprintf(\"# integrate %lu from %lu\\n\", changeset->number, changesets->changeset->number);\n+\t\tchangesets = changesets->next;\n+\t}\n+}\n+\n+static struct p4_codeline *import_depot(struct p4_depot *depot, const char *refname)\n+{\n+\tstruct p4_codeline *target, *posn;\n+\tchar *path = refname_to_codeline(refname);\n+\ttarget = get_codeline(depot, path);\n+\n+\tif (!target)\n+\t\tdie(\"Invalid codeline: %s\", path);\n+\n+\tfree(path);\n+\n+\tp4_filelog(target);\n+\n+\tprintf(\"progress resolving integrates\\n\");\n+\n+\t/* Now resolve all the integrates in changesets */\n+\tfor (posn = depot->codelines; posn; posn = posn->next) {\n+\t\tstruct p4_changeset *changeset;\n+\t\tfor (changeset = posn->unreported; changeset; changeset = changeset->next) {\n+\t\t\tresolve_changeset_integrates(changeset);\n+\t\t}\n+\t}\n+\n+\treturn target;\n+}\n+\n+static void name_changeset(struct p4_changeset *changeset)\n+{\n+\tif (changeset->commit)\n+\t\tprintf(\"%s\\n\", sha1_to_hex(changeset->commit->object.sha1));\n+\telse\n+\t\tprintf(\":%d\\n\", changeset->mark);\n+}\n+\n+static void lookup_git_changeset(struct p4_codeline *codeline,\n+\t\t\t\t struct p4_changeset *changeset)\n+{\n+\twhile (!changeset->commit) {\n+\t\tstruct commit *parent = codeline->history->commit->parents->item;\n+\t\tparse_commit(parent);\n+\t\tcodeline->history->previous->commit = parent;\n+\t\tcodeline->history = codeline->history->previous;\n+\t}\n+}\n+\n+static void report_codeline(struct p4_codeline *codeline,\n+\t\t\t    struct p4_changeset *until);\n+\n+static void identify_changeset(struct p4_changeset *changeset)\n+{\n+\tif (changeset->mark || changeset->commit)\n+\t\treturn;\n+\tif (changeset->codeline->finished_changeset >= changeset->number)\n+\t\tlookup_git_changeset(changeset->codeline, changeset);\n+\telse\n+\t\treport_codeline(changeset->codeline, changeset);\n+}\n+\n+static void report_codeline(struct p4_codeline *codeline, struct p4_changeset *until)\n+{\n+\tstruct p4_changeset *changeset;\n+\tstruct p4_revision_list *rev;\n+\n+\tprintf(\"progress importing content of codeline %s\", codeline->path);\n+\tif (until)\n+\t\tprintf(\" (up to changeset %lu)\", until->number);\n+\tprintf(\"\\n\");\n+\n+\tfor (changeset = codeline->unreported; changeset; changeset = changeset->next) {\n+\t\tstruct p4_changeset_list *integrated = changeset->integrated;\n+\t\tprintf(\"progress check %lu\\n\", changeset->number);\n+\n+\t\twhile (integrated) {\n+\t\t\tidentify_changeset(integrated->changeset);\n+\t\t\tintegrated = integrated->next;\n+\t\t}\n+\t\tprintf(\"progress import changeset %lu\\n\",\n+\t\t       changeset->number);\n+\t\tprintf(\"# changeset %lu\\n\", changeset->number);\n+\t\tprintf(\"commit %s\\n\", codeline->refname);\n+\t\tchangeset->mark = codeline->depot->next_mark++;\n+\t\tprintf(\"mark :%d\\n\", changeset->mark);\n+\t\tp4_change(changeset);\n+\t\tif (changeset->previous) {\n+\t\t\tprintf(\"from \");\n+\t\t\tname_changeset(changeset->previous);\n+\t\t}\n+\t\tintegrated = changeset->integrated;\n+\t\twhile (integrated) {\n+\t\t\tprintf(\"merge \");\n+\t\t\tname_changeset(integrated->changeset);\n+\t\t\tintegrated = integrated->next;\n+\t\t}\n+\n+\t\tfor (rev = changeset->revisions; rev; rev = rev->next) {\n+\t\t\tprintf(\"M %s inline %s\\n\",\n+\t\t\t       get_file_mode(rev->revision->type),\n+\t\t\t       rev->revision->file->name + 1);\n+\t\t\tp4_print(rev->revision);\n+\t\t}\n+\t\tprintf(\"\\n\");\n+\t\tcodeline->unreported = changeset->next;\n+\t\tif (changeset == until)\n+\t\t\tbreak;\n+\t}\n+\tprintf(\"checkpoint\\n\");\n+}\n+\n+static void import_p4(int ref_nr, const char **refs)\n+{\n+\tint i;\n+\tstruct p4_depot *depot = get_depot();\n+\tstruct p4_codeline *target;\n+\tsave_commit_buffer = 1;\n+\n+\tfor (i = 0; i < ref_nr; i++) {\n+\t\ttarget = import_depot(depot, refs[i]);\n+\n+\t\tidentify_changeset(target->head);\n+\t}\n+}\n+\n+static void export_change(struct diff_options *options,\n+\t\t\t  unsigned old_mode, unsigned new_mode,\n+\t\t\t  const unsigned char *old_sha1,\n+\t\t\t  const unsigned char *new_sha1,\n+\t\t\t  const char *path)\n+{\n+\tstruct p4_codeline *codeline = options->format_callback_data;\n+\tp4_edit(codeline, path);\n+\twrite_blob(codeline, new_sha1, path);\n+}\n+\n+static void export_add_remove(struct diff_options *options,\n+\t\t\t      int addremove, unsigned mode,\n+\t\t\t      const unsigned char *sha1,\n+\t\t\t      const char *path)\n+{\n+\tstruct p4_codeline *codeline = options->format_callback_data;\n+\tif (addremove == '+') {\n+\t\twrite_blob(codeline, sha1, path);\n+\t\tp4_add(codeline, path);\n+\t} else if (addremove == '-') {\n+\t\tp4_delete(codeline, path);\n+\t}\n+}\n+\n+static void export_commit(struct p4_codeline *codeline,\n+\t\t\t  struct commit *git_commit, struct commit *git_parent)\n+{\n+\tstruct tree_desc pre, post;\n+\tstruct diff_options opts;\n+\tmemset(&opts, 0, sizeof(opts));\n+\tparse_tree(git_commit->tree);\n+\tparse_tree(git_parent->tree);\n+\tinit_tree_desc(&pre, git_parent->tree->buffer, git_parent->tree->size);\n+\tinit_tree_desc(&post, git_commit->tree->buffer, git_commit->tree->size);\n+\topts.change = export_change;\n+\topts.add_remove = export_add_remove;\n+\topts.format_callback_data = codeline;\n+\topts.flags = DIFF_OPT_RECURSIVE;\n+\tdiff_tree(&pre, &post, \"/\", &opts);\n+\tp4_submit(git_commit);\n+}\n+\n+static void export_p4(struct remote *remote, const char *branch)\n+{\n+\tstruct p4_depot *depot = get_depot();\n+\tconst char *codeline = remote->url[0];\n+\tstruct p4_codeline *target;\n+\tstruct strbuf buf;\n+\n+\t// check client\n+\n+\ttarget = import_depot(depot, codeline);\n+\n+\tstrbuf_init(&buf, 0);\n+\n+\twhile (!strbuf_getline(&buf, stdin, '\\n')) {\n+\t\tstruct p4_changeset *parent = NULL, *integrate = NULL;\n+\t\tunsigned char sha1[20];\n+\t\tstruct commit *commit, *git_parent = NULL;\n+\t\tstruct commit_list *parents;\n+\t\tget_sha1(buf.buf, sha1);\n+\t\tcommit = lookup_commit(sha1);\n+\t\tparse_commit(commit);\n+\t\tfor (parents = commit->parents; parents; parents = parents->next) {\n+\t\t\tstruct p4_changeset *p4_parent =\n+\t\t\t\tchangeset_from_commit(depot, parents->item);\n+\t\t\tif (p4_parent) {\n+\t\t\t\tif (p4_parent->codeline == target) {\n+\t\t\t\t\tparent = p4_parent;\n+\t\t\t\t\tgit_parent = parents->item;\n+\t\t\t\t} else\n+\t\t\t\t\tintegrate = p4_parent;\n+\t\t\t}\n+\t\t}\n+\t\tif (target->head != parent) {\n+\t\t\tprintf(\"progress not up-to-date\\n\");\n+\t\t\treturn;\n+\t\t}\n+\t\tif (p4_where(target))\n+\t\t\tbreak;\n+\t\tp4_sync(target);\n+\n+\t\tif (!parent) {\n+\t\t\t// Need to start new codeline\n+\t\t}\n+\t\texport_commit(target, commit, git_parent);\n+\t}\n+}\n+\n+static const char **env;\n+static int env_nr;\n+static int env_alloc;\n+\n+static int handle_config(const char *key, const char *value, void *cb)\n+{\n+\tstruct strbuf buf;\n+\tconst char *subkey;\n+\tif (!prefixcmp(key, \"vcs-p4.\")) {\n+\t\tsubkey = key + 7;\n+\t\tif (!strcmp(subkey, \"port\")) {\n+\t\t\tstrbuf_init(&buf, 0);\n+\t\t\tstrbuf_addf(&buf, \"P4PORT=%s\", value);\n+\n+\t\t\tALLOC_GROW(env, env_nr + 1, env_alloc);\n+\t\t\tenv[env_nr++] = strbuf_detach(&buf, NULL);\n+\t\t}\n+\t\tif (!strcmp(subkey, \"client\")) {\n+\t\t\tstrbuf_init(&buf, 0);\n+\t\t\tstrbuf_addf(&buf, \"P4CLIENT=%s\", value);\n+\n+\t\t\tALLOC_GROW(env, env_nr + 1, env_alloc);\n+\t\t\tenv[env_nr++] = strbuf_detach(&buf, NULL);\n+\t\t}\n+\t\tif (!strcmp(subkey, \"codelineformat\")) {\n+\t\t\tcodeline_regex = (regex_t*)xmalloc(sizeof(regex_t));\n+\t\t\tif (regcomp(codeline_regex, value, REG_EXTENDED)) {\n+\t\t\t\tfree(codeline_regex);\n+\t\t\t\tfprintf(stderr, \"Invalid codeline pattern: %s\",\n+\t\t\t\t\tvalue);\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+int cmd_p4(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct remote *remote;\n+\n+\tgit_config(handle_config, NULL);\n+\n+\t//ALLOC_GROW(env, env_nr + 1, env_alloc);\n+\t//env[env_nr++] = \"P4PORT=localhost:1666\";\n+\n+\tALLOC_GROW(env, env_nr + 1, env_alloc);\n+\tenv[env_nr++] = NULL;\n+\n+\tp4_init(env);\n+\n+\tif (!strcmp(argv[1], \"capabilities\")) {\n+\t\tprintf(\"import\\n\");\n+\t\tprintf(\"find-new-branches\\n\");\n+\t\tprintf(\"export\\n\");\n+\t\tprintf(\"fork\\n\");\n+\t\tprintf(\"merge\\n\");\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(argv[1], \"import\")) {\n+\t\tprefix = setup_git_directory();\n+\t\tremote = remote_get(argv[2]);\n+\t\timport_p4(argc - 3, argv + 3);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(argv[1], \"list\")) {\n+\t\tint i;\n+\t\tprefix = setup_git_directory();\n+\t\tremote = remote_get(argv[2]);\n+\t\tfor (i = 0; i < remote->url_nr; i++) {\n+\t\t\tprintf(\"%s\\n\", codeline_to_refname(remote->url[i]));\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(argv[1], \"export\")) {\n+\t\tremote = remote_get(argv[2]);\n+\n+\t\texport_p4(remote, argv[3]);\n+\t\t// 1: check whether the import of the target location\n+\t\t//    is up-to-date\n+\n+\t\t// 2: find the target location in the client view\n+\n+\t\t// 3: bring the client view up-to-date with the target\n+\t\t//    location\n+\n+\t\t// 4: recheck that this matches the tree\n+\n+\t\t// 5: open the necessary files in the client\n+\n+\t\t// 6: replace the necessary files in the filesystem\n+\n+\t\t// 7: submit\n+\n+\t\t// 8: reimport\n+\n+\t\t// 9: go back to (3)\n+\t}\n+\treturn 1;\n+}\ndiff --git a/vcs-p4.h b/vcs-p4.h\nnew file mode 100644\nindex 0000000..55aa307\n--- /dev/null\n+++ b/vcs-p4.h\n@@ -0,0 +1,119 @@\n+#ifndef VCS_P4_H\n+#define VCS_P4_H\n+\n+struct p4_depot {\n+\tstruct p4_codeline *codelines;\n+\n+\tint next_mark;\n+};\n+\n+/** Note that multiple codelines can have changesets with the same\n+ * number.\n+ **/\n+struct p4_changeset {\n+\tstruct p4_codeline *codeline;\n+\n+\tunsigned long number;\n+\n+\t/** Used only if a previous import found this changeset **/\n+\tstruct commit *commit;\n+\n+\t/** Used only if this changeset is newly imported in this operation. **/\n+\tint mark;\n+\n+\tconst char *message;\n+\n+\tstruct p4_revision_list *revisions;\n+\n+\t/** Not explicit in p4 **/\n+\tstruct p4_changeset_list *integrated;\n+\n+\t/** Next and previous in codeline **/\n+\tstruct p4_changeset *next;\n+\tstruct p4_changeset *previous;\n+};\n+\n+struct p4_changeset_list {\n+\tstruct p4_changeset *changeset;\n+\tstruct p4_changeset_list *next;\n+};\n+\n+struct p4_revision {\n+\tunsigned long number;\n+\n+\tconst char *type;\n+\n+\tstruct p4_file *file;\n+\tstruct p4_changeset *changeset;\n+\n+\tstruct p4_revision_list *integrated;\n+\n+\t/** Next in file **/\n+\tstruct p4_revision *next;\n+};\n+\n+/** Represents a collection of revisions of different files\n+ **/\n+struct p4_revision_list {\n+\tstruct p4_revision *revision;\n+\tstruct p4_revision_list *next;\n+};\n+\n+struct p4_file {\n+\tstruct p4_codeline *codeline;\n+\tconst char *name;\n+\n+\tunsigned head_number;\n+\n+\tstruct p4_revision *revisions;\n+\n+\t/** Next file in codeline **/\n+\tstruct p4_file *next;\n+};\n+\n+/** perforce doesn't record codelines; we have to reverse-engineer\n+ * them from how people seem to be branching.\n+ **/\n+struct p4_codeline {\n+\tstruct p4_depot *depot;\n+\n+\t/** Base path of codeline **/\n+\tconst char *path;\n+\n+\t/** git refname to import into **/\n+\tconst char *refname;\n+\n+\tstruct p4_file *files;\n+\tstruct p4_changeset *changesets;\n+\n+\tint filelog_done;\n+\n+\t/* The incremental state is that we have some changeset that\n+\t * we previously imported up to, and we have git history going\n+\t * back from that point, of which we've looked up some and\n+\t * could look up more as needed. Also, there's p4-only history\n+\t * going forward after the common history, and we've imported\n+\t * some of that, and could import more as needed. Since\n+\t * codelines are sorted by changeset number, we can tell which\n+\t * way to go to get a name for a changeset.\n+\t */\n+\tstruct p4_changeset *history;\n+\tstruct p4_changeset *unreported;\n+\n+\tstruct p4_changeset *head;\n+\n+\tunsigned long finished_changeset;\n+\n+\t/** For reporting **/\n+\tunsigned long num_changesets;\n+\n+\t/** Next codeline in depot **/\n+\tstruct p4_codeline *next;\n+\n+\t/** Filesystem location of working directory for this codeline\n+\t * on the client.\n+\t **/\n+\tchar *working;\n+};\n+\n+#endif\n-- \n1.6.0.6\n"},{"id":"100058","messageId":"7vy6xhl1i7.fsf@gitster.siamese.dyndns.org","threadId":"17094","inReplyTo":"alpine.LNX.1.00.0901110336380.19665@iabervon.org","subject":"Re: [RFC PATCH 4/3] Add example git-vcs-p4","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-01-12T02:08:32Z","receivedAt":"2009-01-12T02:08:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Hmm...\n\n    CC vcs-p4.o\ncc1: warnings being treated as errors\nvcs-p4.c: In function 'output_data':\nvcs-p4.c:253: warning: format '%d' expects type 'int', but argument 2 has type 'size_t'\nvcs-p4.c: In function 'p4_where':\nvcs-p4.c:291: warning: ISO C90 forbids mixed declarations and code\nvcs-p4.c: In function 'p4_submit':\nvcs-p4.c:363: warning: ISO C90 forbids mixed declarations and code\nvcs-p4.c: In function 'p4_print':\nvcs-p4.c:433: warning: format '%d' expects type 'int', but argument 2 has type 'size_t'\nvcs-p4.c: In function 'p4_change':\nvcs-p4.c:453: warning: ISO C90 forbids mixed declarations and code\nvcs-p4.c: In function 'p4_filelog':\nvcs-p4.c:504: warning: ISO C90 forbids mixed declarations and code\nmake: *** [vcs-p4.o] Error 1\n"},{"id":"100073","messageId":"alpine.LNX.1.00.0901120026290.19665@iabervon.org","threadId":"17094","inReplyTo":"7vy6xhl1i7.fsf@gitster.siamese.dyndns.org","subject":"Re: [RFC PATCH 4/3] Add example git-vcs-p4","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2009-01-12T05:41:19Z","receivedAt":"2009-01-12T05:41:19Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Sun, 11 Jan 2009, Junio C Hamano wrote:\n\n> Hmm...\n> \n>     CC vcs-p4.o\n> cc1: warnings being treated as errors\n> vcs-p4.c: In function 'output_data':\n> vcs-p4.c:253: warning: format '%d' expects type 'int', but argument 2 has type 'size_t'\n> vcs-p4.c: In function 'p4_where':\n> vcs-p4.c:291: warning: ISO C90 forbids mixed declarations and code\n> vcs-p4.c: In function 'p4_submit':\n> vcs-p4.c:363: warning: ISO C90 forbids mixed declarations and code\n> vcs-p4.c: In function 'p4_print':\n> vcs-p4.c:433: warning: format '%d' expects type 'int', but argument 2 has type 'size_t'\n> vcs-p4.c: In function 'p4_change':\n> vcs-p4.c:453: warning: ISO C90 forbids mixed declarations and code\n> vcs-p4.c: In function 'p4_filelog':\n> vcs-p4.c:504: warning: ISO C90 forbids mixed declarations and code\n> make: *** [vcs-p4.o] Error 1\n\nI haven't been over the 4/3 stuff for coding style yet. But how do you get \nthese warnings? My gcc 4.1.2 doesn't seem to want to complain about \nnon-C90 usage except in strict C90 mode, where it gets more upset about \n\"inline\" all over the place.\n\n\t-Daniel\n*This .sig left intentionally blank*\n"},{"id":"100076","messageId":"7viqoljaub.fsf@gitster.siamese.dyndns.org","threadId":"17094","inReplyTo":"alpine.LNX.1.00.0901120026290.19665@iabervon.org","subject":"Re: [RFC PATCH 4/3] Add example git-vcs-p4","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-01-12T06:29:48Z","receivedAt":"2009-01-12T06:29:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Daniel Barkalow <barkalow@iabervon.org> writes:\n\n>> vcs-p4.c:504: warning: ISO C90 forbids mixed declarations and code\n>> make: *** [vcs-p4.o] Error 1\n>\n> I haven't been over the 4/3 stuff for coding style yet. But how do you get \n> these warnings?\n\nI thought I have mentioned this already, but...\n\nI compile with (at least) these:\n\n    -Wno-pointer-to-int-cast\n    -Wold-style-definition\n    -Wdeclaration-after-statement\n\nexplicitly specified.\n\nI also run kernel's checkpatch.pl script (but ignoring \"more than 80-col\nis too long\" rule) from time to time.\n\nI consider these are good ways to check style-discipline.\n"},{"id":"100109","messageId":"81b0412b0901120346x79772846p6e5097028fc9720c@mail.gmail.com","threadId":"17094","inReplyTo":"alpine.LNX.1.00.0901110336380.19665@iabervon.org","subject":"Re: [RFC PATCH 4/3] Add example git-vcs-p4","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2009-01-12T11:46:32Z","receivedAt":"2009-01-12T11:46:32Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"2009/1/11 Daniel Barkalow <barkalow@iabervon.org>:\n> diff --git a/Makefile b/Makefile\n>\n> +LIB_OBJS += p4client.o\n> +LIB_OBJS += vcs-p4.o\n> +\n>\n> +extern int cmd_p4(int argc, const char **argv, const char *prefix);\n> +\n\nWhy is your foreign VCS importer built-in? The majority wont ever need it,\nyet they have to pay the price for its text being in git executable.\n"},{"id":"100151","messageId":"alpine.LNX.1.00.0901121325080.19665@iabervon.org","threadId":"17094","inReplyTo":"81b0412b0901120346x79772846p6e5097028fc9720c@mail.gmail.com","subject":"Re: [RFC PATCH 4/3] Add example git-vcs-p4","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2009-01-12T18:36:03Z","receivedAt":"2009-01-12T18:36:03Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Mon, 12 Jan 2009, Alex Riesen wrote:\n\n> 2009/1/11 Daniel Barkalow <barkalow@iabervon.org>:\n> > diff --git a/Makefile b/Makefile\n> >\n> > +LIB_OBJS += p4client.o\n> > +LIB_OBJS += vcs-p4.o\n> > +\n> >\n> > +extern int cmd_p4(int argc, const char **argv, const char *prefix);\n> > +\n> \n> Why is your foreign VCS importer built-in? The majority wont ever need it,\n> yet they have to pay the price for its text being in git executable.\n\nPurely for convenience in the RFC version. The real one will probably need \nto be linked separately, since it will want to have the option of using \nthe terrible C++ API to the \nclosed-source-but-you-can-download-it-from-them library.\n\n\t-Daniel\n*This .sig left intentionally blank*\n"}]}