git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Ramkumar Ramachandra <artagnon@gmail.com>
To: Git List <git@vger.kernel.org>
Cc: Jonathan Nieder <jrnieder@gmail.com>,
	David Barr <david.barr@cordelta.com>,
	Sverre Rabbelier <srabbelier@gmail.com>
Subject: [PATCH 5/5] vcs-svn: Add dir_cache for svnload
Date: Wed, 19 Jan 2011 11:14:59 +0530	[thread overview]
Message-ID: <1295415899-1192-6-git-send-email-artagnon@gmail.com> (raw)
In-Reply-To: <1295415899-1192-1-git-send-email-artagnon@gmail.com>

The logic for creating directories recursively is now
implemented. Unfortunately, dir_cache is currently implemented
inefficiently using string_list- should probably use a treap or trie
in future.

Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 Makefile              |    2 +-
 vcs-svn/dir_cache.c   |   40 +++++++++++++++
 vcs-svn/dir_cache.h   |   12 +++++
 vcs-svn/dump_export.c |  104 ++++++++++++++++++++++++++++++++-------
 vcs-svn/dump_export.h |   33 ++++++++++++
 vcs-svn/svnload.c     |  130 +++++++++++++++++++++++--------------------------
 vcs-svn/svnload.h     |   10 ++++
 7 files changed, 243 insertions(+), 88 deletions(-)
 create mode 100644 vcs-svn/dir_cache.c
 create mode 100644 vcs-svn/dir_cache.h
 create mode 100644 vcs-svn/dump_export.h
 create mode 100644 vcs-svn/svnload.h

diff --git a/Makefile b/Makefile
index 40f6691..d9c2442 100644
--- a/Makefile
+++ b/Makefile
@@ -1836,7 +1836,7 @@ XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
 	xdiff/xmerge.o xdiff/xpatience.o
 VCSSVN_OBJS = vcs-svn/line_buffer.o vcs-svn/svnload.o vcs-svn/dump_export.o \
 	vcs-svn/repo_tree.o vcs-svn/fast_export.o vcs-svn/sliding_window.o \
-	vcs-svn/svndiff.o vcs-svn/svndump.o
+	vcs-svn/svndiff.o vcs-svn/svndump.o vcs-svn/dir_cache.o
 VCSSVN_TEST_OBJS = test-obj-pool.o \
 	test-line-buffer.o test-treap.o test-svn-fe.o
 OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS) $(VCSSVN_OBJS)
diff --git a/vcs-svn/dir_cache.c b/vcs-svn/dir_cache.c
new file mode 100644
index 0000000..9a608ce
--- /dev/null
+++ b/vcs-svn/dir_cache.c
@@ -0,0 +1,40 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+#include "string-list.h"
+#include "line_buffer.h"
+#include "dump_export.h"
+
+static struct string_list dirents = STRING_LIST_INIT_DUP;
+static struct string_list_item *dir = NULL;
+
+void dir_cache_add(const char *path, enum node_kind kind) {
+	dir = string_list_insert(&dirents, path);
+	dir->util = malloc(sizeof(enum node_kind));
+	*((enum node_kind *)(dir->util)) = kind;
+}
+
+void dir_cache_remove(const char *path) {
+	dir = string_list_lookup(&dirents, path);
+	if (dir)
+		*((enum node_kind *)(dir->util)) = NODE_KIND_UNKNOWN;
+}
+
+enum node_kind dir_cache_lookup(const char *path) {
+	dir = string_list_lookup(&dirents, path);
+	if (dir)
+		return *((enum node_kind *)(dir->util));
+	else
+		return NODE_KIND_UNKNOWN;
+}
+
+void dir_cache_init() {
+	return;
+}
+
+void dir_cache_deinit() {
+	string_list_clear(&dirents, 1);
+}
diff --git a/vcs-svn/dir_cache.h b/vcs-svn/dir_cache.h
new file mode 100644
index 0000000..43c3797
--- /dev/null
+++ b/vcs-svn/dir_cache.h
@@ -0,0 +1,12 @@
+#ifndef DIR_CACHE_H_
+#define DIR_CACHE_H_
+
+#include "dump_export.h"
+
+void dir_cache_add(const char *path, enum node_kind kind);
+void dir_cache_remove(const char *path);
+enum node_kind dir_cache_lookup(const char *path);
+void dir_cache_init();
+void dir_cache_deinit();
+
+#endif
diff --git a/vcs-svn/dump_export.c b/vcs-svn/dump_export.c
index 04ede06..6f2a9d7 100644
--- a/vcs-svn/dump_export.c
+++ b/vcs-svn/dump_export.c
@@ -7,6 +7,9 @@
 #include "strbuf.h"
 #include "line_buffer.h"
 #include "dump_export.h"
+#include "dir_cache.h"
+
+static struct strbuf props;
 
 void dump_export_begin_rev(int revision, const char *revprops,
 			int prop_len) {
@@ -19,39 +22,47 @@ void dump_export_begin_rev(int revision, const char *revprops,
 void dump_export_node(const char *path, enum node_kind kind,
 		enum node_action action, unsigned long text_len,
 		unsigned long copyfrom_rev, const char *copyfrom_path) {
+	int dump_props = 1; /* Boolean */
+	strbuf_reset(&props);
 	printf("Node-path: %s\n", path);
-	printf("Node-kind: ");
-	switch (action) {
+	switch (kind) {
 	case NODE_KIND_NORMAL:
-		printf("file\n");
+		printf("Node-kind: file\n");
 		break;
 	case NODE_KIND_EXECUTABLE:
-		printf("file\n");
+		printf("Node-kind: file\n");
+		strbuf_addf(&props, "K\nsvn:executable\nV\n1\n");
 		break;
 	case NODE_KIND_SYMLINK:
-		printf("file\n");
+		printf("Node-kind: file\n");
+		strbuf_addf(&props, "K\nsvn:special\nV\n1\n");
 		break;
 	case NODE_KIND_GITLINK:
-		printf("file\n");
+		printf("Node-kind: file\n");
+		break;
+	case NODE_KIND_DIR:
+		printf("Node-kind: dir\n");
 		break;
 	case NODE_KIND_SUBDIR:
 		die("Unsupported: subdirectory");
 	default:
-		break;
+		break; /* When a node is being removed, nothing is printed */
 	}
-	printf("Node-action: ");
+	strbuf_add(&props, "PROPS-END\n", 10);
+
 	switch (action) {
 	case NODE_ACTION_CHANGE:
-		printf("change\n");
+		printf("Node-action: change\n");
 		break;
 	case NODE_ACTION_ADD:
-		printf("add\n");
+		printf("Node-action: add\n");
 		break;
 	case NODE_ACTION_REPLACE:
-		printf("replace\n");
+		printf("Node-action: replace\n");
 		break;
 	case NODE_ACTION_DELETE:
-		printf("delete\n");
+		printf("Node-action: delete\n");
+		dump_props = 0;
 		break;
 	default:
 		break;
@@ -60,14 +71,71 @@ void dump_export_node(const char *path, enum node_kind kind,
 		printf("Node-copyfrom-rev: %lu\n", copyfrom_rev);
 		printf("Node-copyfrom-path: %s\n", copyfrom_path);
 	}
-	printf("Prop-delta: false\n");
-	printf("Prop-content-length: 10\n"); /* Constant 10 for "PROPS-END" */
-	printf("Text-delta: false\n");
-	printf("Text-content-length: %lu\n", text_len);
-	printf("Content-length: %lu\n\n", text_len + 10);
-	printf("PROPS-END\n\n");
+	if (dump_props) {
+		printf("Prop-delta: false\n");
+		printf("Prop-content-length: %lu\n", props.len);
+	}
+	if (text_len) {
+		printf("Text-delta: false\n");		
+		printf("Text-content-length: %lu\n", text_len);
+	}
+	if (text_len || dump_props) {
+		printf("Content-length: %lu\n\n", text_len + props.len);
+		printf("%s", props.buf);
+	}
+	if (!text_len)
+		printf("\n");
+}
+
+void dump_export_node_r(const char *path, enum node_kind kind,
+			enum node_action action, unsigned long text_len,
+			unsigned long copyfrom_rev, const char *copyfrom_path) {
+	char *start, *t;
+	start = (char *) path;
+
+	while((t = strchr(start, '/'))) {
+			*t = '\0';
+			if (dir_cache_lookup(path) == NODE_KIND_UNKNOWN) {
+				dir_cache_add(path, NODE_KIND_NORMAL);
+				dump_export_node(path, NODE_KIND_DIR,
+						NODE_ACTION_ADD, 0,
+						SVN_INVALID_REV, NULL);
+			}
+			*t = '/';   /* Change it back */
+			start = t + 1;
+	}
+	switch (dir_cache_lookup(path)) {
+	case NODE_KIND_UNKNOWN:
+		action = NODE_ACTION_ADD;
+		break;
+	case NODE_KIND_SYMLINK:
+		dir_cache_remove(path);
+		dump_export_node(path, NODE_KIND_UNKNOWN,
+				NODE_ACTION_DELETE,
+				0, SVN_INVALID_REV, NULL);
+		break;
+	case NODE_KIND_DIR:
+		die("File was previously a directory?");
+		break;
+	case NODE_KIND_SUBDIR:
+		die("Subdirectories unsupported");
+		break;
+	default:
+		action = NODE_ACTION_CHANGE;
+		break;
+	}
+	dir_cache_add(path, kind);
+	dump_export_node(path, kind, action, text_len, copyfrom_rev, copyfrom_path);
 }
 
 void dump_export_text(struct line_buffer *data, off_t len) {
 	buffer_copy_bytes(data, len);
 }
+
+void dump_export_init() {
+	strbuf_init(&props, MAX_GITSVN_LINE_LEN);
+}
+
+void dump_export_deinit() {
+	strbuf_release(&props);
+}
diff --git a/vcs-svn/dump_export.h b/vcs-svn/dump_export.h
new file mode 100644
index 0000000..e9f51a3
--- /dev/null
+++ b/vcs-svn/dump_export.h
@@ -0,0 +1,33 @@
+#ifndef DUMP_EXPORT_H_
+#define DUMP_EXPORT_H_
+
+#define MAX_GITSVN_LINE_LEN 4096
+#define SVN_INVALID_REV 0
+
+enum node_action {
+	NODE_ACTION_UNKNOWN,
+	NODE_ACTION_CHANGE,
+	NODE_ACTION_ADD,
+	NODE_ACTION_DELETE,
+	NODE_ACTION_REPLACE
+};
+
+enum node_kind {
+	NODE_KIND_UNKNOWN,       /* Missing node */
+	NODE_KIND_NORMAL,
+	NODE_KIND_EXECUTABLE,
+	NODE_KIND_SYMLINK,
+	NODE_KIND_GITLINK,
+	NODE_KIND_DIR,           /* SVN-specific */
+	NODE_KIND_SUBDIR
+};
+
+void dump_export_begin_rev(int revision, const char *revprops, int prop_len);
+void dump_export_text(struct line_buffer *data, off_t len);
+void dump_export_node_r(const char *path, enum node_kind kind,
+			enum node_action action, unsigned long text_len,
+			unsigned long copyfrom_rev, const char *copyfrom_path);
+void dump_export_init();
+void dump_export_deinit();
+
+#endif
diff --git a/vcs-svn/svnload.c b/vcs-svn/svnload.c
index 7043ae7..19c4689 100644
--- a/vcs-svn/svnload.c
+++ b/vcs-svn/svnload.c
@@ -11,17 +11,16 @@
 #include "git-compat-util.h"
 #include "line_buffer.h"
 #include "dump_export.h"
-#include "strbuf.h"
+#include "dir_cache.h"
 
 #define SVN_DATE_FORMAT "%Y-%m-%dT%H:%M:%S.000000Z"
 #define SVN_DATE_LEN 28
 #define LENGTH_UNKNOWN (~0)
 
 static struct line_buffer input = LINE_BUFFER_INIT;
-static struct strbuf blobs[100];
-	
+
 static struct {
-	unsigned long prop_len, text_len, copyfrom_rev, mark;
+	unsigned long prop_len, text_len, copyfrom_rev;
 	int text_delta, prop_delta; /* Boolean */
 	enum node_action action;
 	enum node_kind kind;
@@ -60,15 +59,14 @@ static void reset_node_ctx(void)
 {
 	node_ctx.prop_len = LENGTH_UNKNOWN;
 	node_ctx.text_len = LENGTH_UNKNOWN;
-	node_ctx.mark = 0;
-	node_ctx.copyfrom_rev = 0;
+	node_ctx.copyfrom_rev = SVN_INVALID_REV;
 	node_ctx.text_delta = -1;
 	node_ctx.prop_delta = -1;
 	strbuf_reset(&node_ctx.copyfrom_path);
 	strbuf_reset(&node_ctx.path);
 }
 
-static void populate_props(struct strbuf *props, const char *author,
+static void populate_revprops(struct strbuf *props, const char *author,
 			const char *log, const char *date) {
 	strbuf_reset(props);	
 	strbuf_addf(props, "K\nsvn:author\nV\n%s\n", author);
@@ -83,7 +81,7 @@ static void parse_author_line(char *val, struct strbuf *name,
 	char time_buf[SVN_DATE_LEN];
 	const struct tm *tm_time;
 
-	/* Simon Hausmann <shausman@trolltech.com> 1170199019 +0100 */
+	/* Author Name <author@email.com> 1170199019 +0530 */
 	strbuf_reset(name);
 	strbuf_reset(email);
 	strbuf_reset(date);
@@ -104,46 +102,42 @@ static void parse_author_line(char *val, struct strbuf *name,
 
 void svnload_read(void) {
 	char *t, *val;
-	int mode_incr;
-	struct strbuf *to_dump;
+	int len, mode_incr;
 
 	while ((t = buffer_read_line(&input))) {
 		val = strchr(t, ' ');
-		if (!val) {
-			if (!memcmp(t, "blob", 4))
-				active_ctx = BLOB_CTX;
-			else if (!memcmp(t, "deleteall", 9))
-				;
-			continue;
+		if (!val)
+			len = strlen(t);
+		else {
+			*val++ = '\0';
+			len = val - t - 1;
 		}
-		*val++ = '\0';
 
-		/* strlen(key) */
-		switch (val - t - 1) {
+		switch (len) {
 		case 1:
 			if (!memcmp(t, "D", 1)) {
 				node_ctx.action = NODE_ACTION_DELETE;
-			}
-			else if (!memcmp(t, "C", 1)) {
+			} else if (!memcmp(t, "C", 1)) {
 				node_ctx.action = NODE_ACTION_ADD;
-			}
-			else if (!memcmp(t, "R", 1)) {
+			} else if (!memcmp(t, "R", 1)) {
 				node_ctx.action = NODE_ACTION_REPLACE;
-			}
-			else if (!memcmp(t, "M", 1)) {
+			} else if (!memcmp(t, "M", 1)) {
 				node_ctx.action = NODE_ACTION_CHANGE;
-				mode_incr = 7;
-				if (!memcmp(val, "100644", 6))
-					node_ctx.kind = NODE_KIND_NORMAL;
-				else if (!memcmp(val, "100755", 6))
-					node_ctx.kind = NODE_KIND_EXECUTABLE;
-				else if (!memcmp(val, "120000", 6))
-					node_ctx.kind = NODE_KIND_SYMLINK;
-				else if (!memcmp(val, "160000", 6))
-					node_ctx.kind = NODE_KIND_GITLINK;
-				else if (!memcmp(val, "040000", 6))
-					node_ctx.kind = NODE_KIND_SUBDIR;
-				else {
+				if (strlen(val) >= 6) {
+					if (!memcmp(val, "100644", 6))
+						node_ctx.kind = NODE_KIND_NORMAL;
+					else if (!memcmp(val, "100755", 6))
+						node_ctx.kind = NODE_KIND_EXECUTABLE;
+					else if (!memcmp(val, "120000", 6))
+						node_ctx.kind = NODE_KIND_SYMLINK;
+					else if (!memcmp(val, "160000", 6))
+						node_ctx.kind = NODE_KIND_GITLINK;
+					else if (!memcmp(val, "040000", 6))
+						node_ctx.kind = NODE_KIND_SUBDIR;
+					else
+						die("Unrecognized mode: %s", val);
+					mode_incr = 7;
+				} else if (strlen(val) >= 3) {
 					if (!memcmp(val, "755", 3))
 						node_ctx.kind = NODE_KIND_EXECUTABLE;
 					else if(!memcmp(val, "644", 3))
@@ -151,40 +145,35 @@ void svnload_read(void) {
 					else
 						die("Unrecognized mode: %s", val);
 					mode_incr = 4;
-				}
+				} else
+					die ("Malformed filemodify: Missing mode");
 				val += mode_incr;
 				t = strchr(val, ' ');
+				if (!t)
+					die("Malformed filemodify: Missing dataref");
 				*t++ = '\0';
 				strbuf_reset(&node_ctx.path);
 				strbuf_add(&node_ctx.path, t, strlen(t));
-				if (!memcmp(val + 1, "inline", 6))
-					die("Unsupported dataref: inline");
+				if (!strncmp(val, "inline", 6))
+					active_ctx = BLOB_CTX;
 				else if (*val == ':')
-					to_dump = &blobs[strtoul(val + 1, NULL, 10)];
+					die("Unsupported dataref: marks");
 				else
 					die("Unsupported dataref: sha1");
-				dump_export_node(node_ctx.path.buf, node_ctx.kind,
-						node_ctx.action, to_dump->len,
-						0, NULL);
-				printf("%s", to_dump->buf);
 			}
 			break;
+		case 2:
+			if (!memcmp(t, "ls", 2))
+				die("ls not supported");
 		case 3:
 			if (!memcmp(t, "tag", 3))
 				continue;
 			break;
 		case 4:
-			if (!memcmp(t, "mark", 4))
-				switch(active_ctx) {
-				case COMMIT_CTX:
-					/* What do we do with commit marks? */
-					continue;
-				case BLOB_CTX:
-					node_ctx.mark = strtoul(val + 1, NULL, 10);
-					break;
-				default:
-					break;
-				}
+			if (!memcmp(t, "blob", 4))
+				die("Please inline blobs in the fast-import stream");
+			else if (!memcmp(t, "mark", 4))
+				continue;
 			else if (!memcmp(t, "from", 4))
 				continue;
 			else if (!memcmp(t, "data", 4)) {
@@ -194,7 +183,7 @@ void svnload_read(void) {
 					buffer_read_binary(&input,
 							&rev_ctx.log,
 							strtoul(val, NULL, 10));
-					populate_props(&rev_ctx.props,
+					populate_revprops(&rev_ctx.props,
 						rev_ctx.svn_author.buf,
 						rev_ctx.log.buf,
 						rev_ctx.author_date.buf);
@@ -204,9 +193,10 @@ void svnload_read(void) {
 					break;
 				case BLOB_CTX:
 					node_ctx.text_len = strtoul(val, NULL, 10);
-					buffer_read_binary(&input,
-							&blobs[node_ctx.mark],
-							node_ctx.text_len);
+					dump_export_node_r(node_ctx.path.buf, node_ctx.kind,
+							node_ctx.action, node_ctx.text_len,
+							SVN_INVALID_REV, NULL);
+					buffer_copy_bytes(&input, node_ctx.text_len);
 					break;
 				default:
 					break;
@@ -231,14 +221,18 @@ void svnload_read(void) {
 					rev_ctx.author_email.buf,
 					t - rev_ctx.author_email.buf);
 
-			}
-			else if (!memcmp(t, "commit", 6)) {
+			} else if (!memcmp(t, "commit", 6)) {
 				rev_ctx.rev ++;
 				active_ctx = COMMIT_CTX;
 			}
 			break;
+		case 8:
+			if (!memcmp(t, "cat-blob", 8))
+				die("cat-blob unsupported");
 		case 9:
-			if (!memcmp(t, "committer", 9))
+			if (!memcmp(t, "deleteall", 9))
+				continue;
+			else if (!memcmp(t, "committer", 9))
 				parse_author_line(val, &rev_ctx.committer,
 						&rev_ctx.committer_email,
 						&rev_ctx.committer_date);
@@ -251,7 +245,6 @@ void svnload_read(void) {
 
 int svnload_init(const char *filename)
 {
-	int i;
 	if (buffer_init(&input, filename))
 		return error("cannot open %s: %s", filename, strerror(errno));
 	active_ctx = UNKNOWN_CTX;
@@ -265,14 +258,13 @@ int svnload_init(const char *filename)
 	strbuf_init(&rev_ctx.committer_email, MAX_GITSVN_LINE_LEN);
 	strbuf_init(&node_ctx.path, MAX_GITSVN_LINE_LEN);
 	strbuf_init(&node_ctx.copyfrom_path, MAX_GITSVN_LINE_LEN);
-	for (i = 0; i < 100; i ++)
-		strbuf_init(&blobs[i], 10000);
+	dump_export_init();
+	dir_cache_init();
 	return 0;
 }
 
 void svnload_deinit(void)
 {
-	int i;
 	reset_rev_ctx(0);
 	reset_node_ctx();
 	strbuf_release(&rev_ctx.props);
@@ -285,8 +277,8 @@ void svnload_deinit(void)
 	strbuf_release(&rev_ctx.committer_email);
 	strbuf_release(&node_ctx.path);
 	strbuf_release(&node_ctx.copyfrom_path);
-	for (i = 0; i < 100; i ++)
-		strbuf_release(&blobs[i]);
+	dump_export_deinit();
+	dir_cache_deinit();
 	if (buffer_deinit(&input))
 		fprintf(stderr, "Input error\n");
 	if (ferror(stdout))
diff --git a/vcs-svn/svnload.h b/vcs-svn/svnload.h
new file mode 100644
index 0000000..0c8fe8b
--- /dev/null
+++ b/vcs-svn/svnload.h
@@ -0,0 +1,10 @@
+#ifndef SVNLOAD_H_
+#define SVNLOAD_H_
+
+#define SVN_INVALID_REV -1
+
+int svnload_init(const char *filename);
+void svnload_deinit(void);
+void svnload_read(void);
+
+#endif
-- 
1.7.4.rc1.7.g2cf08.dirty

      parent reply	other threads:[~2011-01-19  5:44 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-01-19  5:44 [RFC PATCH v2 0/5] Towards a Git-to-SVN bridge Ramkumar Ramachandra
2011-01-19  5:44 ` [PATCH 1/5] date: Expose the time_to_tm function Ramkumar Ramachandra
2011-01-19  5:44 ` [PATCH 2/5] vcs-svn: Start working on the dumpfile producer Ramkumar Ramachandra
2011-01-22  0:30   ` Junio C Hamano
2011-01-22  9:45     ` Ramkumar Ramachandra
2011-01-19  5:44 ` [PATCH 3/5] Build an svn-fi target in contrib/svn-fe Ramkumar Ramachandra
2011-01-19  5:44 ` [PATCH 4/5] fast-export: Introduce --inline-blobs Ramkumar Ramachandra
2011-01-19 19:50   ` Junio C Hamano
2011-01-19 21:48     ` Jonathan Nieder
2011-01-20  4:50       ` Ramkumar Ramachandra
2011-01-20  5:48         ` Jonathan Nieder
2011-01-20  6:28           ` Ramkumar Ramachandra
2011-01-20 13:53         ` Drew Northup
2011-01-22  9:24           ` Ramkumar Ramachandra
2011-01-22 19:18             ` Jonathan Nieder
2011-01-20  5:41     ` Jonathan Nieder
2011-01-22  0:30   ` Junio C Hamano
2011-01-19  5:44 ` Ramkumar Ramachandra [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1295415899-1192-6-git-send-email-artagnon@gmail.com \
    --to=artagnon@gmail.com \
    --cc=david.barr@cordelta.com \
    --cc=git@vger.kernel.org \
    --cc=jrnieder@gmail.com \
    --cc=srabbelier@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).