{"thread":{"id":"24915","subject":"[RFC PATCH] Introduce git-hive","startedAt":"2010-08-30T19:59:27Z","lastAt":"2010-09-06T02:28:06Z","messageCount":18,"participants":["cdahlin@redhat.com","Luke Kenneth Casson Leighton","Nguyen Thai Ngoc Duy","Casey Dahlin","Kevin Ballard","Ilari Liusvaara","Giuseppe Bilotta","Tomas Carnecky"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"149357","messageId":"1283198367-11440-1-git-send-email-cdahlin@redhat.com","threadId":"24915","inReplyTo":null,"subject":"[RFC PATCH] Introduce git-hive","fromName":"","fromEmail":"cdahlin@redhat.com","sentAt":"2010-08-30T19:59:27Z","receivedAt":"2010-08-30T19:59:27Z","isPatch":true,"sender":{"key":"cdahlin@redhat.com","avatar":null},"body":"From: Casey Dahlin <cdahlin@redhat.com>\n\nThis isn't really what I'd normally call ready for public consumption, but\nsince there's been some minimal list chatter on a similar subject I figured I'd\nput this out there early.\n\ngit-hive is a peer to peer service for exchanging branches. When started it\nruns a small daemon to publish your repository on a random (or specified) port.\nTwo hive daemons connected to oneanother can exchange branch lists and\nintroduce eachother to more hive daemons to widen the network.\n\nCurrently each hive daemon is associated with a project URI to identify what\nproject is being hosted.\n\nCommands introduced are:\n  git hive start - start the hive daemon and join a network\n  git hive stop - stop the hive daemon\n  git hive show - list peers and branches\n  git hive fetch - fetch a branch\n\nAs I've said, there's some rough edges around this, so here's some things that\nI'm very shortly going to be adding:\n\n* Mechanism to track when a peer was last heard about, and re-verify or flush\nold ones. Also don't forward information about a peer that hasn't been verified\nrecently. This will make things a LOT more robust, and prevent the flaming\nclusterfuck that happens during netsplits right now (once done netsplits should\nbe an almost uninteresting occurance).\n\n* Commit lines in branch listings. Fairly obvious UI thing that for some reason\nI only just thought of.\n\n* Improve tolerance of \\0 in pkt_line strings as the spec says I should (and\ntake advantage of it! the protocol could be much easier to parse if I used \\0\nas a separator).\n\n* Right now git-hive links against code from fetch-pack that clearly reaks of\nabandonment, though not specifically deprecated. Its a conflict of my two\nconcerns 1) make this change uninvasive until people have warmed up to the\npatch and 2) don't reimplement anything. I'm pretty sure there's a better way\nto grab fetch functionality.\n\n* Either some more commands for pull/etc. or some way to integrate better with\nthe normal versions of those commands (I thought of a uri helper but I'm not\nsure what sort of uri you'd use to refer to a hive location that would be clean\nfor the user)\n\n* Peer discovery. I'd like to use Avahi for this if there's no objections.\n\n* Tests :)\n\nSigned-off-by: Casey Dahlin <cdahlin@redhat.com>\n---\n .gitignore |    1 +\n Makefile   |    5 +\n hive.c     | 1825 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 1831 insertions(+), 0 deletions(-)\n create mode 100644 hive.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 14e2b6b..447b889 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -5,6 +5,7 @@\n /bin-wrappers/\n /git\n /git-add\n+/git-hive\n /git-add--interactive\n /git-am\n /git-annotate\ndiff --git a/Makefile b/Makefile\nindex e151516..190d540 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -399,6 +399,7 @@ PROGRAM_OBJS += shell.o\n PROGRAM_OBJS += show-index.o\n PROGRAM_OBJS += upload-pack.o\n PROGRAM_OBJS += http-backend.o\n+PROGRAM_OBJS += hive.o\n \n PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))\n \n@@ -449,6 +450,7 @@ BINDIR_PROGRAMS_NEED_X += git-upload-pack\n BINDIR_PROGRAMS_NEED_X += git-receive-pack\n BINDIR_PROGRAMS_NEED_X += git-upload-archive\n BINDIR_PROGRAMS_NEED_X += git-shell\n+BINDIR_PROGRAMS_NEED_X += git-hive\n \n BINDIR_PROGRAMS_NO_X += git-cvsserver\n \n@@ -1893,6 +1895,9 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)\n \n+git-hive$X: hive.o $(GITLIBS) builtin/fetch-pack.o\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)\n+\n $(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)\n \t$(QUIET_LNCP)$(RM) $@ && \\\n \tln $< $@ 2>/dev/null || \\\ndiff --git a/hive.c b/hive.c\nnew file mode 100644\nindex 0000000..63b3ee4\n--- /dev/null\n+++ b/hive.c\n@@ -0,0 +1,1825 @@\n+#include <sys/epoll.h>\n+#include <sys/socket.h>\n+#include <sys/un.h>\n+#include <sys/timerfd.h>\n+#include <arpa/inet.h>\n+#include <netinet/in.h>\n+\n+#include \"pkt-line.h\"\n+#include \"cache.h\"\n+#include \"parse-options.h\"\n+#include \"exec_cmd.h\"\n+#include \"run-command.h\"\n+#include \"strbuf.h\"\n+#include \"refs.h\"\n+#include \"fetch-pack.h\"\n+\n+extern int accept4 (int sockfd, struct sockaddr *addr, socklen_t *addrlen,\n+\t\t    int flags);\n+extern int dup3 (int oldfd, int newfd, int flags);\n+\n+struct peer {\n+\tchar *user_id;\n+\tstruct sockaddr *addr;\n+\tsize_t failures;\n+\tint peer_ok;\n+\tstruct sock_handler *handler;\n+\tstruct peer *next;\n+\tstruct peer *prev;\n+};\n+\n+static struct peer self;\n+static size_t active_peers;\n+\n+/* TODO: Options to change these */\n+static size_t min_peers = 1;\n+static size_t max_peers = 10; \n+static size_t max_alternates = 24;\n+\n+static size_t max_retries = 5;\n+\n+static char *proj_uri;\n+\n+typedef uint32_t (*sock_callback_t)(struct sock_handler *handler,\n+\t\t\t\t    uint32_t events);\n+struct sock_handler {\n+\tsock_callback_t function;\n+\tint fd;\n+\tstruct epoll_event event;\n+};\n+\n+struct timer;\n+typedef int (*timer_callback_t)(struct timer *timer);\n+struct timer {\n+\tstruct sock_handler handler;\n+\ttimer_callback_t function;\n+\tvoid *data;\n+};\n+\n+static size_t sock_handler_count = 0;\n+\n+static int poll_fd;\n+static int ctl_sock;\n+static struct sock_handler *ctl_handler;\n+static struct sock_handler *abort_timer = NULL;\n+\n+struct pkt_handler;\n+typedef enum {\n+\tPKT_DONE_CLOSE = 0,\n+\tPKT_DONE_REPURPOSED,\n+\tPKT_MORE,\n+} pkt_status_t;\n+typedef pkt_status_t (*pkt_callback_t)(struct pkt_handler *pkh);\n+struct pkt_handler {\n+\tstruct sock_handler handler;\n+\tsize_t line_ct;\n+\tsize_t sent;\n+\tsize_t got;\n+\tsize_t to_fill;\n+\tchar linelen[4];\n+\tchar *buf;\n+\tchar **lines;\n+\tvoid *data;\n+\tstruct strbuf output;\n+\tpkt_callback_t function;\n+\tint need_flush;\n+};\n+\n+struct listen_sock_handler {\n+\tstruct sock_handler handler;\n+\tpkt_callback_t sock_callback;\n+};\n+\n+static const char hive_usage[] =\n+\"git hive {start | show | fetch} [options...]\";\n+\n+static const char *const hive_start_usage[] = {\n+\t\"git hive start [options] <peer> <port>\",\n+\t\"git hive start [options] <peer>:<port>\",\n+\tNULL\n+};\n+\n+static const char hive_stop_usage[] = \"git hive stop\";\n+\n+static const char *const hive_show_usage[] = {\n+\t\"git hive show [--ip] [--[no-]branches] [regex]\",\n+\tNULL\n+};\n+\n+static const char *const hive_fetch_usage[] = {\n+\t\"git hive fetch <user regex> <branch> [target]\",\n+\tNULL\n+};\n+\n+#define PT_OPS_IP 1\n+#define PT_OPS_BRANCHES 2\n+#define PT_OPS_SKIPLINE 4\n+typedef uint32_t pt_ops_t;\n+\n+struct peer_list {\n+\tchar **entries;\n+\tsize_t len;\n+};\n+\n+static int shutting_down = 0;\n+\n+static void do_term(int sig)\n+{\n+\tif (! sig == SIGTERM)\n+\t\tdie(\"TERM handler called from strange place\");\n+\n+\tshutting_down = 1;\n+}\n+\n+struct sigaction term_action = {\n+\t.sa_handler = do_term,\n+\t.sa_flags = SA_RESTART,\n+};\n+\n+static void handler_register(struct sock_handler *handler);\n+#define pkt_handler_write(_pkh_i, ...) ({\t\t\\\n+\tstruct pkt_handler *_pkh = (void *)(_pkh_i);\t\\\n+\tpacket_buf_write(&_pkh->output, __VA_ARGS__);\t\\\n+\t_pkh->need_flush = 1;\t\t\t\t\\\n+\t_pkh->handler.event.events |= EPOLLOUT;\t\t\\\n+\thandler_register(&_pkh->handler);\t\t\\\n+})\n+\n+static void pkt_handler_flush(struct pkt_handler *pkh)\n+{\n+\tif (! pkh->need_flush)\n+\t\treturn;\n+\n+\tpacket_buf_flush(&pkh->output);\n+\tpkh->need_flush = 0;\n+}\n+\n+static char *getaddrstr(struct sockaddr *addr)\n+{\n+\tchar hostname[128];\n+\tchar port[8];\n+\tsize_t pos;\n+\tint gaierror;\n+\n+\tif ((gaierror = getnameinfo(addr, sizeof(struct sockaddr_in),\n+\t\t\t\t    hostname, 120, port, 8,\n+\t\t\t\t    NI_NUMERICHOST | NI_NUMERICSERV)))\n+\t\tdie(\"getnameinfo failed: %s\", gai_strerror(gaierror));\n+\n+\tpos = strlen(hostname);\n+\thostname[pos] = ':';\n+\n+\tmemcpy(hostname + pos + 1, port, strlen(port) + 1);\n+\n+\treturn xstrdup(hostname);\n+}\n+\n+static struct sockaddr *getaddrstruct(const char *str)\n+{\n+\tchar *ip;\n+\tint gairet;\n+\tconst char *port = rindex(str, ':');\n+\tstruct addrinfo *info;\n+\tstruct sockaddr *ret;\n+\tstruct addrinfo hint = {\n+\t\t.ai_family = AF_INET,\n+\t\t.ai_socktype = SOCK_STREAM,\n+\t};\n+\n+\tif (! port)\n+\t\tdie(\"invalid ip/port specification\");\n+\n+\tip = xstrndup(str, (port++) - str);\n+\n+\tif ((gairet = getaddrinfo(ip, port, &hint, &info)))\n+\t\tdie(\"could not interpret address: %s\", gai_strerror(gairet));\n+\n+\tfree(ip);\n+\n+\tret = memcpy(xmalloc(info->ai_addrlen), info->ai_addr,\n+\t\t     info->ai_addrlen);\n+\n+\tfreeaddrinfo(info);\n+\n+\treturn ret;\n+}\n+\n+static void peer_announce(struct pkt_handler *pkh, struct peer *peer,\n+\t\t\t  const char *ann_type)\n+{\n+\tchar *addr = getaddrstr(peer->addr);\n+\n+\tpkt_handler_write(pkh, \"%s %s %s\\n\", ann_type, addr, peer->user_id);\n+\n+\tfree(addr);\n+}\n+\n+static void handler_register(struct sock_handler *handler)\n+{\n+\tint op = EPOLL_CTL_MOD;\n+\n+\twhile (epoll_ctl(poll_fd, op, handler->fd, &handler->event)) {\n+\t\tif (op != EPOLL_CTL_MOD || errno != ENOENT)\n+\t\t\tdie_errno(\"epoll_ctl\");\n+\n+\t\top = EPOLL_CTL_ADD;\n+\t}\n+}\n+\n+static void broadcast_peer(struct peer *origin, struct peer *peer,\n+\t\t\t   const char *ann_type)\n+{\n+\tstruct peer *cur;\n+\n+\tfor (cur = self.next; cur != &self; cur = cur->next) {\n+\t\tstruct pkt_handler *pkh;\n+\n+\t\tif (! cur->handler)\n+\t\t\tcontinue;\n+\t\tif (cur == origin)\n+\t\t\tcontinue;\n+\t\tif (cur == peer)\n+\t\t\tcontinue;\n+\n+\t\tpkh = (struct pkt_handler *)cur->handler;\n+\n+\t\tif (ann_type)\n+\t\t\tpeer_announce(pkh, peer, ann_type);\n+\t\telse\n+\t\t\tpkt_handler_flush(pkh);\n+\t}\n+}\n+\n+static struct peer *lookup_peer(const char *user_id, struct sockaddr *addr,\n+\t\t\t\tstruct peer *start)\n+{\n+\tif (! start)\n+\t\tstart = &self;\n+\n+\tfor (start = start->next; start != &self; start = start->next) {\n+\t\tif (user_id && start->user_id &&\n+\t\t    strcmp(start->user_id, user_id))\n+\t\t\tcontinue;\n+\n+\t\tif (addr && memcmp(addr, start->addr,\n+\t\t\t\t   sizeof(struct sockaddr_in)))\n+\t\t\tcontinue;\n+\n+\t\treturn start;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static void destroy_sock_handler(struct sock_handler *handler)\n+{\n+\tfree(handler);\n+\tsock_handler_count--;\n+}\n+\n+static void init_sock_handler(struct sock_handler *handler, int fd,\n+\t\t\t      sock_callback_t function)\n+{\n+\thandler->fd = fd;\n+\thandler->function = function;\n+\thandler->event.events = EPOLLIN;\n+\thandler->event.data.ptr = handler;\n+\n+\tsock_handler_count++;\n+}\n+\n+static int pkt_handler_load_line_length(struct pkt_handler *pkh)\n+{\n+\tint i;\n+\n+\tfor(i = 0; i < 4; i++) {\n+\t\tunsigned char c = pkh->linelen[i];\n+\t\tpkh->to_fill <<= 4;\n+\t\tif (c >= '0' && c <= '9') {\n+\t\t\tpkh->to_fill += c - '0';\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (c >= 'a' && c <= 'f') {\n+\t\t\tpkh->to_fill += c - 'a' + 10;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (c >= 'A' && c <= 'F') {\n+\t\t\tpkh->to_fill += c - 'A' + 10;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void pkt_handler_push_buf(struct pkt_handler *pkh)\n+{\n+\t\tsize_t line_spc = pkh->line_ct - 1;\n+\t\tint i;\n+\n+\t\tif (! pkh->buf)\n+\t\t\tdie(\"unexpected empty line\");\n+\n+\t\tfor (i = 1; i < sizeof(line_spc) * 8; i *= 2)\n+\t\t\tline_spc |= line_spc >> i;\n+\t\tline_spc++;\n+\n+\t\tif (line_spc < 2);\n+\t\t\tline_spc = 2;\n+\n+\t\tif (line_spc == pkh->line_ct || !pkh->lines)\n+\t\t\tpkh->lines = xrealloc(pkh->lines,\n+\t\t\t\t\t      line_spc * sizeof(char *) * 2);\n+\n+\t\tpkh->lines[pkh->line_ct++] = pkh->buf;\n+\n+\t\tpkh->buf = NULL;\n+\t\tpkh->got = 0;\n+\t\tpkh->to_fill = 4;\n+}\n+\n+static void pkt_handler_drop_data(struct pkt_handler *pkh)\n+{\n+\tint i;\n+\n+\tif (pkh->buf)\n+\t\tfree(pkh->buf);\n+\tpkh->buf = NULL;\n+\n+\tfor (i = 0; i < pkh->line_ct; i++)\n+\t\tfree(pkh->lines[i]);\n+\n+\tfree(pkh->lines);\n+\tpkh->lines = NULL;\n+\tpkh->line_ct = 0;\n+\n+\tpkh->got = 0;\n+\tpkh->to_fill = 4;\n+}\n+\n+static uint32_t handle_timer(struct sock_handler *handler, uint32_t events)\n+{\n+\tstruct timer *timer = (struct timer *)handler;\n+\tuint64_t expiry;\n+\n+\tif (! (events & EPOLLIN))\n+\t\tdie(\"timerfd error\");\n+\n+\tswitch (read(handler->fd, &expiry, 8)) {\n+\tcase -1:\n+\t\tdie_errno(\"timerfd encountered error\");\n+\tcase 8:\n+\t\tbreak;\n+\tcase 0:\n+\t\tdie_errno(\"timerfd encountered unexpected EOF\");\n+\tdefault:\n+\t\tdie(\"timerfd underrun\");\n+\t}\n+\n+\tif (expiry > 1)\n+\t\twarning(\"lost timer events\");\n+\n+\tif (timer->function(timer))\n+\t\treturn EPOLLIN;\n+\n+\treturn 0;\n+}\n+\n+static uint32_t handle_pkt(struct sock_handler *handler, uint32_t events)\n+{\n+\tstruct pkt_handler *pkh = (struct pkt_handler *)handler;\n+\tchar *pos = pkh->buf;\n+\tssize_t got = -1;\n+\tpkt_status_t status;\n+\n+\tif (! (events & (EPOLLIN | EPOLLOUT))) {\n+\t\twarning(\"unexpected poll event\");\n+\t\tpkt_handler_drop_data(pkh);\n+\t\tgoto closeout;\n+\t}\n+\n+\tpkt_handler_flush(pkh);\n+\n+\tif ((events & EPOLLOUT) && pkh->output.len > 0) {\n+\t\tssize_t wrote = -1;\n+\n+\t\terrno = EINTR;\n+\t\twhile (wrote < 0 && errno == EINTR)\n+\t\t\twrote = write(handler->fd, pkh->output.buf,\n+\t\t\t\t      pkh->output.len);\n+\n+\t\tif (wrote < 0) {\n+\t\t\twarning(\"write error: %s\", strerror(errno));\n+\t\t\tpkt_handler_drop_data(pkh);\n+\t\t\tgoto closeout;\n+\t\t}\n+\n+\t\tstrbuf_remove(&pkh->output, 0, wrote);\n+\t}\n+\n+\t\n+\tif (! pkh->function) {\n+\t\tif (pkh->output.len)\n+\t\t\treturn EPOLLOUT;\n+\t\tgoto closeout;\n+\t}\n+\n+\tif (! (events & EPOLLIN))\n+\t\tgoto next_batch;\n+\n+\tif (! pos)\n+\t\tpos = pkh->linelen;\n+\n+\tpos += pkh->got;\n+\n+\terrno = EINTR;\n+\twhile (got < 0 && errno == EINTR)\n+\t\tgot = read(handler->fd, pos, pkh->to_fill);\n+\n+\tif (got == -1) {\n+\t\terror(\"read error: %s\", strerror(errno));\n+\t\tpkt_handler_drop_data(pkh);\n+\t\tgoto closeout;\n+\t}\n+\n+\tif (! got) {\n+\t\terror(\"Peer hung up unexpectedly\");\n+\t\t/* FIXME: this */\n+\t\tpkh->function(pkh);\n+\t\tpkt_handler_drop_data(pkh);\n+\t\tgoto closeout;\n+\t}\n+\n+\tpkh->got += got;\n+\tpkh->to_fill -= got;\n+\n+\tif (pkh->to_fill)\n+\t\tgoto next_batch;\n+\n+\tif (pkh->buf) {\n+\t\tpkt_handler_push_buf(pkh);\n+\t\tgoto next_batch;\n+\t}\n+\n+\tpkh->got = 0;\n+\tpkh->to_fill = 0;\n+\n+\tif (pkt_handler_load_line_length(pkh)) {\n+\t\terror(\"Peer gave invalid pkt line: invalid length\");\n+\t\tpkt_handler_drop_data(pkh);\n+\t\tgoto closeout;\n+\t}\n+\n+\tif (pkh->to_fill > 3) {\n+\t\tpkh->to_fill -= 4;\n+\t\tpkh->buf = xmallocz(pkh->to_fill);\n+\t\tgoto next_batch;\n+\t}\n+\n+\tif (pkh->to_fill > 0) {\n+\t\terror(\"Peer gave invalid pkt line: length too short\");\n+\t\tpkt_handler_drop_data(pkh);\n+\t\tgoto closeout;\n+\t}\n+\n+\tstatus = pkh->function(pkh);\n+\tpkt_handler_drop_data(pkh);\n+\n+\tif (status == PKT_MORE)\n+\t\tgoto next_batch;\n+\n+\tif (status == PKT_DONE_REPURPOSED) {\n+\t\tpkt_handler_drop_data(pkh);\n+\t\treturn 0;\n+\t}\n+\n+\tif (pkh->output.len) {\n+\t\tpkh->function = NULL;\n+\t\treturn EPOLLOUT;\n+\t}\n+\n+closeout:\n+\tclose(handler->fd);\n+\tstrbuf_release(&pkh->output);\n+\treturn 0;\n+\n+next_batch:\n+\tif (pkh->output.len)\n+\t\treturn EPOLLIN | EPOLLOUT;\n+\telse\n+\t\treturn EPOLLIN;\n+}\n+\n+static struct sock_handler *create_timer(struct timespec *time,\n+\t\t\t\t\t timer_callback_t function, void *data,\n+\t\t\t\t\t int is_absolute)\n+{\n+\tint fd = timerfd_create(CLOCK_MONOTONIC, O_CLOEXEC);\n+\tstruct timer *ret = xmalloc(sizeof(struct timer));\n+\tstruct itimerspec timerspec = {\n+\t\t.it_value = *time,\n+\t};\n+\n+\tis_absolute = is_absolute ? TFD_TIMER_ABSTIME : 0;\n+\n+\tif (fd < 0)\n+\t\tdie_errno(\"could not create timerfd\");\n+\n+\tif (timerfd_settime(fd, is_absolute, &timerspec, NULL))\n+\t\tdie_errno(\"could not set timer\");\n+\n+\tinit_sock_handler(&ret->handler, fd, handle_timer);\n+\tret->function = function;\n+\tret->data = data;\n+\n+\treturn &ret->handler;\n+}\n+\n+static void cancel_timer(struct timer *timer)\n+{\n+\tclose(timer->handler.fd);\n+\ttimer->handler.fd = -1;\n+\ttimer->function(timer);\n+\tfree(timer);\n+}\n+\n+static struct sock_handler *create_pkt_handler(int fd,\n+\t\t\t\t\t       pkt_callback_t function,\n+\t\t\t\t\t       void *data)\n+{\n+\tstruct pkt_handler *pkh = xmalloc(sizeof(struct pkt_handler));\n+\n+\tinit_sock_handler(&pkh->handler, fd, handle_pkt);\n+\n+\tpkh->got = 0;\n+\tpkh->to_fill = 4;\n+\tpkh->buf = NULL;\n+\tpkh->data = data;\n+\tpkh->function = function;\n+\tpkh->line_ct = 0;\n+\tpkh->lines = NULL;\n+\tpkh->sent = 0;\n+\tpkh->need_flush = 0;\n+\tstrbuf_init(&pkh->output, 80);\n+\n+\treturn &pkh->handler;\n+}\n+\n+static int begin_listen(void)\n+{\n+\tint fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);\n+\n+\tif (fd < 0)\n+\t\tdie_errno(\"Could not create socket\");\n+\n+\tif (bind(fd, (struct sockaddr *)self.addr,\n+\t\t sizeof (struct sockaddr_in))) {\n+\t\tswitch(errno) {\n+\t\tcase EACCES:\n+\t\tcase EADDRINUSE:\n+\t\t\tgoto fail;\n+\t\tdefault:\n+\t\t\tdie_errno(\"Could not bind to port\");\n+\t\t}\n+\t}\n+\n+\tif (!listen(fd, 5))\n+\t\treturn fd;\n+\n+\tif (errno != EADDRINUSE)\n+\t\tdie_errno(\"Could not listen on port\");\n+\n+fail:\n+\tclose(fd);\n+\treturn -1;\n+}\n+\n+struct sockaddr *parse_txt_peer(char *input, char **user_id_ret)\n+{\n+\tchar *user_id;\n+\n+\tuser_id = index(input, ' ');\n+\n+\tif (! user_id)\n+\t\treturn NULL;\n+\n+\t*(user_id++) = '\\0';\n+\n+\tuser_id[strlen(user_id) - 1] = '\\0';\n+\n+\tif (user_id_ret)\n+\t\t*user_id_ret = xstrdup(user_id);\n+\n+\treturn getaddrstruct(input);\n+}\n+\n+static pkt_status_t handle_peer(struct pkt_handler *pkh);\n+\n+static struct peer *create_peer(const char *user_id, struct sockaddr *addr,\n+\t\t\t\tstruct peer *broadcast_origin)\n+{\n+\tstruct peer *new;\n+\n+\tnew = xmalloc(sizeof(struct peer));\n+\tif (user_id)\n+\t\tnew->user_id = xstrdup(user_id);\n+\telse\n+\t\tnew->user_id = NULL;\n+\tnew->addr = addr;\n+\tnew->failures = 0;\n+\tnew->peer_ok = 0;\n+\tnew->handler = NULL;\n+\n+\tnew->next = self.next;\n+\tnew->prev = &self;\n+\tself.next = new;\n+\tnew->next->prev = new;\n+\n+\tif (broadcast_origin)\n+\t\tbroadcast_peer(broadcast_origin, new, \"remote-peer\");\n+\n+\treturn new;\n+}\n+\n+static struct peer *add_peer(int fd, const char *user_id,\n+\t\t\t     struct sockaddr *addr,\n+\t\t\t     struct peer *broadcast_origin)\n+{\n+\tstruct peer *new = NULL;\n+\tstruct sock_handler *handler;\n+\tint port = -1;\n+\n+\tif (fd < 0 && ! addr)\n+\t\tdie(\"Invalid arguments to add_peer: \"\n+\t\t    \"no file descriptor or address\");\n+\n+\tif (fd >= 0) {\n+\t\tif (addr)\n+\t\t\tport = ((struct sockaddr_in *)addr)->sin_port;\n+\t\tsocklen_t size = sizeof(struct sockaddr_in);\n+\t\taddr = xmalloc(size);\n+\n+\t\tif (getpeername(fd, addr, &size))\n+\t\t\tdie_errno(\"getpeername\");\n+\n+\t\tif (port >= 0)\n+\t\t\t((struct sockaddr_in *)addr)->sin_port = port;\n+\t}\n+\n+\tif (user_id)\n+\t\tnew = lookup_peer(user_id, addr, NULL);\n+\n+\tif (! new) {\n+\t\tnew = create_peer(user_id, addr, broadcast_origin);\n+\n+\t\tif (fd < 0)\n+\t\t\treturn new;\n+\t} else if (fd < 0) {\n+\t\treturn new;\n+\t} else if (new->handler) {\n+\t\treturn NULL;\n+\t}\n+\n+\thandler = create_pkt_handler(fd, handle_peer, new);\n+\thandler_register(handler);\n+\tnew->handler = handler;\n+\tactive_peers++;\n+\n+\treturn new;\n+}\n+\n+static int add_peer_txt(char *line, struct peer *broadcast_origin)\n+{\n+\tchar *user_id;\n+\tstruct sockaddr *addr;\n+\n+\taddr = parse_txt_peer(line, &user_id);\n+\n+\tif (! addr)\n+\t\treturn -1;\n+\n+\tadd_peer(-1, user_id, addr, broadcast_origin);\n+\treturn 0;\n+}\n+\n+static void lost_peer(struct peer *peer, struct peer *broadcast_origin)\n+{\n+\tpeer->prev->next = peer->next;\n+\tpeer->next->prev = peer->prev;\n+\n+\tif (broadcast_origin)\n+\t\tbroadcast_peer(broadcast_origin, peer, \"lost-peer\");\n+\n+\tif (peer->handler)\n+\t\tactive_peers--;\n+\n+\tfree(peer->user_id);\n+\tfree(peer->addr);\n+\tfree(peer);\n+}\n+\n+static size_t lost_peers(char *user_id, struct sockaddr *addr,\n+\t\t\t struct peer *broadcast_origin)\n+{\n+\tstruct peer *peer = NULL;\n+\tsize_t ret = 0;\n+\n+\tfor (peer = lookup_peer(user_id, addr, peer); peer;\n+\t     peer = lookup_peer(user_id, addr, peer)) {\n+\t\tstruct peer *old_peer = peer;\n+\t\t/* For active peers we don't care about third party\n+\t\t * announcements\n+\t\t */\n+\t\tif (peer->handler)\n+\t\t\tcontinue;\n+\n+\t\tpeer = peer->prev;\n+\n+\t\tlost_peer(old_peer, broadcast_origin);\n+\n+\t\tret++;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+static ssize_t lost_peer_txt(char *line, struct peer *broadcast_origin)\n+{\n+\tchar *user_id;\n+\tstruct sockaddr *addr;\n+\tsize_t ret = 0;\n+\n+\taddr = parse_txt_peer(line, &user_id);\n+\n+\tif (! addr)\n+\t\treturn -1;\n+\n+\tret = lost_peers(user_id, addr, broadcast_origin);\n+\n+\tfree(addr);\n+\n+\treturn ret;\n+}\n+\n+void write_hangup_message(struct pkt_handler *pkh)\n+{\n+\tif (active_peers == 1 &&\n+\t    (shutting_down || (self.next->handler && self.prev->handler)))\n+\t\tpkt_handler_write(pkh, \"hangup-bye\\n\");\n+\telse\n+\t\tpkt_handler_write(pkh, \"hangup\\n\");\n+}\n+\n+static void deactivate_peer(struct peer *peer, int failed)\n+{\n+\tif (! peer->handler)\n+\t\treturn;\n+\n+\tif (failed && ++peer->failures >= max_retries) {\n+\t\tlost_peer(peer, &self);\n+\t\treturn;\n+\t}\n+\n+\tpeer->peer_ok = 0;\n+\tpeer->handler = NULL;\n+\tactive_peers--;\n+}\n+\n+static pkt_status_t handle_peer(struct pkt_handler *pkh)\n+{\n+\tstruct peer *peer = pkh->data;\n+\tsize_t i;\n+\n+\tif (! pkh) {\n+\t\tlost_peer(peer, &self);\n+\t\tbroadcast_peer(&self, NULL, NULL);\n+\t\treturn PKT_DONE_CLOSE;\n+\t}\n+\n+\tfor (i = 0; i < pkh->line_ct; i++) {\n+\t\tchar *line = pkh->lines[i];\n+\n+\t\tif (! strncmp(line, \"peer-ok \", 8)) {\n+\t\t\tif (! peer->user_id) {\n+\t\t\t\tpeer->user_id = xstrdup(line + 8);\n+\t\t\t\tpeer->user_id[strlen(peer->user_id) - 1] = '\\0';\n+\t\t\t}\n+\t\t\tpeer->peer_ok = 1;\n+\t\t\tcontinue;\n+\t\t} else if (! strncmp(line, \"err-\", 4) && index(line, ' ')) {\n+\t\t\tint count_failure = 1;\n+\n+\t\t\tcount_failure = count_failure &&\n+\t\t\t\tstrncmp(line, \"err-too-many \", 13);\n+\t\t\t\t/* && strncmp(line, \"err-something-else\"...*/\n+\n+\t\t\terror(\"%s\", index(line, ' ') + 1);\n+\t\t\tdeactivate_peer(peer, count_failure);\n+\t\t} else if (! peer->peer_ok) {\n+\t\t\tpkt_handler_write(pkh, \"err-proto Protocol error\\n\");\n+\t\t\tdeactivate_peer(peer, 1);\n+\t\t} else if (! strncmp(line, \"remote-peer \", 12)) {\n+\t\t\tif (! add_peer_txt(line + 12, peer))\n+\t\t\t\tcontinue;\n+\t\t\tpkt_handler_write(pkh, \"err-proto Protocol error\\n\");\n+\t\t\tdeactivate_peer(peer, 1);\n+\t\t} else if (! strncmp(line, \"lost-peer \", 10)) {\n+\t\t\tif (lost_peer_txt(line + 10, peer) < 0)\n+\t\t\t\tcontinue;\n+\t\t\tpkt_handler_write(pkh, \"err-proto Protocol error\\n\");\n+\t\t\tdeactivate_peer(peer, 1);\n+\t\t} else if (! strcmp(line, \"hangup\\n\")) {\n+\t\t\tdeactivate_peer(peer, 0);\n+\t\t} else if (! strcmp(line, \"hangup-bye\\n\")) {\n+\t\t\tlost_peer(peer, &self);\n+\t\t} else {\n+\t\t\tpkt_handler_write(pkh, \"err-proto Protocol error\\n\");\n+\t\t\tdeactivate_peer(peer, 1);\n+\t\t}\n+\n+\t\twrite_hangup_message(pkh);\n+\n+\t\treturn PKT_DONE_CLOSE;\n+\t}\n+\n+\treturn PKT_MORE;\n+}\n+\n+static int ref_published(const char *refname, int is_tag)\n+{\n+\treturn 1;\n+}\n+\n+static int output_branch(const char *refname, const unsigned char *sha1,\n+\t\t\t int flags, void *cb_data)\n+{\n+\tint is_tag;\n+\n+\tif (! strncmp(refname, \"refs/heads/\", 11)) {\n+\t\trefname += 11;\n+\t\tis_tag = 0;\n+\t} else if (! strncmp(refname, \"refs/tags/\", 10)) {\n+\t\trefname += 10;\n+\t\tis_tag = 1;\n+\t} else {\n+\t\treturn 0;\n+\t}\n+\n+\tif (! ref_published(refname, 1))\n+\t\treturn 0;\n+\n+\tpkt_handler_write(cb_data, \"branch %s\\n\", refname);\n+\treturn 0;\n+}\n+\n+static pkt_status_t handle_initial_connect(struct pkt_handler *pkh)\n+{\n+\tchar *peer;\n+\tchar *ip;\n+\tstruct peer *new;\n+\tstruct peer *other;\n+\tstruct sockaddr *addr;\n+\n+\tif (! pkh->line_ct)\n+\t\treturn PKT_MORE;\n+\n+\tif (pkh->line_ct > 1) {\n+\t\tpkt_handler_write(pkh, \"err-proto Protocol error\\n\");\n+\t\tpkt_handler_write(pkh, \"hangup\\n\");\n+\t\treturn PKT_DONE_CLOSE;\n+\t}\n+\n+\tpeer = pkh->lines[0];\n+\n+\tif (! strcmp(peer, \"hangup\\n\")) {\n+\t\tpkt_handler_write(pkh, \"hangup\\n\");\n+\t\treturn PKT_DONE_CLOSE;\n+\t}\n+\n+\tif (! strncmp(peer, \"err-\", 4)) {\n+\t\tif (peer[strlen(peer) - 1] == '\\n')\n+\t\t\tpeer[strlen(peer) - 1] = '\\0';\n+\t\terror(\"%s\", peer);\n+\t\tpkt_handler_write(pkh, \"hangup\\n\");\n+\t\treturn PKT_DONE_CLOSE;\n+\t}\n+\n+\tif (strncmp(peer, \"1 \", 2)) {\n+\t\tpkt_handler_write(pkh, \"err-version Unsupported version\\n\");\n+\t\tpkt_handler_write(pkh, \"hangup\\n\");\n+\t\treturn PKT_DONE_CLOSE;\n+\t}\n+\n+\tpeer += 2;\n+\n+\tif (! strcmp(peer, \"branch-list\\n\")) {\n+\t\tfor_each_rawref(output_branch, pkh);\n+\t\tpkt_handler_write(pkh, \"hangup\\n\");\n+\t\treturn PKT_DONE_CLOSE;\n+\t}\n+\n+\tif (! strcmp(peer, \"upload-pack\\n\")) {\n+\t\tswitch(fork()) {\n+\t\tcase -1:\n+\t\t\tdie_errno(\"could not fork\");\n+\t\tcase 0:\n+\t\t\tif (dup2(pkh->handler.fd, 0) < 0)\n+\t\t\t\tdie_errno(\"dup2\");\n+\t\t\tif (dup2(pkh->handler.fd, 1) < 0)\n+\t\t\t\tdie_errno(\"dup2\");\n+\n+\t\t\tswitch(fork()) {\n+\t\t\tcase -1:\n+\t\t\t\tdie_errno(\"could not fork\");\n+\t\t\tcase 0:\n+\t\t\t\texecl_git_cmd(\"upload-pack\", get_git_dir(), NULL);\n+\t\t\t\tdie_errno(\"failed to run git upload-pack\");\n+\t\t\tdefault:\n+\t\t\t\texit(0);\n+\t\t\t}\n+\n+\t\tdefault:\n+\t\t\twait(NULL);\n+\t\t\treturn PKT_DONE_CLOSE;\n+\t\t}\n+\t}\n+\n+\tif (strncmp(peer, \"peer \", 5)) {\n+\t\tpkt_handler_write(pkh, \"err-proto Protocol error\\n\");\n+\t\tpkt_handler_write(pkh, \"hangup\\n\");\n+\t\treturn PKT_DONE_CLOSE;\n+\t}\n+\n+\tif (active_peers >= max_peers) {\n+\t\tsize_t announced = 0;\n+\t\tstruct peer *peer = self.next;\n+\n+\t\tpkt_handler_write(pkh, \"err-load We have too many peers already\\n\");\n+\n+\t\twhile (peer != &self && announced < max_alternates) {\n+\t\t\tpeer_announce(pkh, peer, \"remote-peer\");\n+\t\t\tpeer = peer->next;\n+\t\t\tannounced++;\n+\t\t}\n+\n+\t\tpkt_handler_write(pkh, \"hangup\\n\");\n+\n+\t\treturn PKT_DONE_CLOSE;\n+\t}\n+\n+\tpeer += 5;\n+\n+\tif (strncmp(peer, proj_uri, strlen(proj_uri))) {\n+\t\tpkt_handler_write(pkh, \"err-proj Unknown project\\n\");\n+\t\tpkt_handler_write(pkh, \"hangup\\n\");\n+\t\treturn PKT_DONE_CLOSE;\n+\t}\n+\n+\tpeer += strlen(proj_uri);\n+\n+\tif (*peer != ' ') {\n+\t\tpkt_handler_write(pkh, \"err-proto Protocol error\\n\");\n+\t\tpkt_handler_write(pkh, \"hangup\\n\");\n+\t\treturn PKT_DONE_CLOSE;\n+\t}\n+\n+\tpeer++;\n+\n+\tip = peer;\n+\n+\tpeer = index(peer, ' ');\n+\n+\tif (! peer) {\n+\t\tpkt_handler_write(pkh, \"err-proto Protocol error\\n\");\n+\t\tpkt_handler_write(pkh, \"hangup\\n\");\n+\t\treturn PKT_DONE_CLOSE;\n+\t}\n+\n+\tip = xstrndup(ip, peer - ip);\n+\n+\tpeer++;\n+\n+\t/* Kill the \\n */\n+\tpeer[strlen(peer) - 1] = '\\0';\n+\n+\taddr = getaddrstruct(ip);\n+\tnew = add_peer(pkh->handler.fd, peer, addr, &self);\n+\tfree(addr);\n+\n+\tif (! new) {\n+\t\tpkt_handler_write(pkh, \"err-too-many Too many connections\\n\");\n+\t\tpkt_handler_write(pkh, \"hangup\\n\");\n+\t\treturn PKT_DONE_CLOSE;\n+\t}\n+\n+\tbroadcast_peer(new, NULL, NULL);\n+\n+\tnew->peer_ok = 1;\n+\tpkt_handler_write(new->handler, \"peer-ok %s\\n\", self.user_id);\n+\tfor (other = self.next; other != &self; other = other->next)\n+\t\tif (other != new)\n+\t\t\tpeer_announce((struct pkt_handler *)new->handler,\n+\t\t\t\t      other, \"remote-peer\");\n+\n+\treturn PKT_DONE_REPURPOSED;\n+}\n+\n+static uint32_t handle_listen_sock(struct sock_handler *handler,\n+\t\t\t\t   uint32_t events)\n+{\n+\tint newfd = -1;\n+\tsocklen_t slen = sizeof(struct sockaddr_in);\n+\tstruct sockaddr *addr = xmalloc(slen);\n+\tstruct sock_handler *new;\n+\tstruct listen_sock_handler *lhand =\n+\t\t(struct listen_sock_handler *)handler;\n+\n+\tif (! (events & EPOLLIN))\n+\t\tdie(\"Listen socket got unexpected epoll event\");\n+\n+\tnewfd = accept4(handler->fd, addr, &slen, O_CLOEXEC);\n+\n+\tif (newfd == -1) {\n+\t\tfree(addr);\n+\t\terror(\"Failed to accept new peer: %s\", strerror(errno));\n+\t\treturn EPOLLIN;\n+\t}\n+\n+\tnew = create_pkt_handler(newfd, lhand->sock_callback, addr);\n+\n+\thandler_register(new);\n+\treturn EPOLLIN;\n+}\n+\n+static struct sock_handler *create_listen_handler(int fd,\n+\t\t\t\t\t\t  pkt_callback_t function)\n+{\n+\tstruct listen_sock_handler *ret =\n+\t\txmalloc(sizeof(struct listen_sock_handler));\n+\n+\tinit_sock_handler(&ret->handler, fd, handle_listen_sock);\n+\tret->sock_callback = function;\n+\n+\treturn &ret->handler;\n+}\n+\n+static void activate_peer(struct peer *peer)\n+{\n+\tstruct sock_handler *handler;\n+\tchar *ip;\n+\tint fd;\n+\n+\tif (peer->handler)\n+\t\treturn;\n+\n+\tfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);\n+\n+\tif (fd < 0) {\n+\t\terror(\"Failed to activate peer: %s\", strerror(errno));\n+\t\treturn;\n+\t}\n+\n+\tif (connect(fd, peer->addr, sizeof(struct sockaddr_in))) {\n+\t\tclose(fd);\n+\t\terror(\"Could not reach peer: %s\", strerror(errno));\n+\n+\t\tif (++peer->failures >= max_retries)\n+\t\t\tlost_peer(peer, &self);\n+\n+\t\treturn;\n+\t}\n+\n+\thandler = create_pkt_handler(fd, handle_peer, peer);\n+\thandler->event.events |= EPOLLOUT;\n+\thandler_register(handler);\n+\tpeer->handler = handler;\n+\tactive_peers++;\n+\n+\tip = getaddrstr(self.addr);\n+\tpkt_handler_write(handler, \"1 peer %s %s %s\\n\", proj_uri, ip,\n+\t\t\t  self.user_id);\n+\tpkt_handler_flush((struct pkt_handler *)handler);\n+\tfree(ip);\n+}\n+\n+static pkt_status_t handle_ctl_sock(struct pkt_handler *pkh)\n+{\n+\tsize_t i;\n+\n+\tif (! pkh->line_ct)\n+\t\treturn PKT_MORE;\n+\n+\tfor (i = 0; i < pkh->line_ct; i++) {\n+\t\tchar *line = pkh->lines[i];\n+\n+\t\tif (! strcmp(line, \"show\\n\")) {\n+\t\t\tstruct peer *peer;\n+\n+\t\t\tfor (peer = self.next; peer != &self;\n+\t\t\t     peer = peer->next) {\n+\t\t\t\tchar *addr;\n+\n+\t\t\t\tif (! peer->user_id)\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\taddr = getaddrstr(peer->addr);\n+\n+\t\t\t\tpkt_handler_write(pkh, \"have-peer %s %s\\n\",\n+\t\t\t\t\t\t  addr, peer->user_id);\n+\n+\t\t\t\tfree(addr);\n+\t\t\t}\n+\n+\t\t\tpkt_handler_write(pkh, \"end-show\\n\");\n+\t\t} else if (! strcmp(line, \"hangup\\n\")) {\n+\t\t\tpkt_handler_flush(pkh);\n+\t\t\treturn PKT_DONE_CLOSE;\n+\t\t} else if (! strcmp(line, \"pid\\n\")) {\n+\t\t\tpkt_handler_write(pkh, \"pid %u\\n\", getpid());\n+\t\t} else {\n+\t\t\tpkt_handler_write(pkh, \"err-did-not-understand %s\\n\", line);\n+\t\t}\n+\t}\n+\n+\treturn PKT_MORE;\n+}\n+\n+static int handle_abort_expire(struct timer *timer)\n+{\n+\tdie(\"clean shutdown took too long\");\n+}\n+\n+void do_shutdown(void)\n+{\n+\tchar *ctl_path;\n+\tstruct peer *peer;\n+\tstruct timespec onemin = {\n+\t\t.tv_sec = 60,\n+\t};\n+\n+\tclose(self.handler->fd);\n+\tdestroy_sock_handler(self.handler);\n+\tself.handler = NULL;\n+\n+\tctl_path = git_pathdup(\"hive_ctl\");\n+\tunlink(ctl_path);\n+\tfree(ctl_path);\n+\n+\tclose(ctl_sock);\n+\tdestroy_sock_handler(ctl_handler);\n+\n+\tfor (peer = self.next; peer != &self; peer = peer->next) {\n+\t\tstruct peer *old;\n+\n+\t\tif (peer->handler) {\n+\t\t\tstruct pkt_handler *pkh =\n+\t\t\t\t(struct pkt_handler *)peer->handler;\n+\t\t\twrite_hangup_message(pkh);\n+\n+\t\t\tpkt_handler_drop_data(pkh);\n+\t\t\tpkh->function = NULL;\n+\t\t}\n+\n+\t\told = peer->prev;\n+\t\tlost_peer(peer, NULL);\n+\t\tpeer = old;\n+\t}\n+\n+\tabort_timer = create_timer(&onemin, handle_abort_expire, NULL, 0);\n+\thandler_register(abort_timer);\n+}\n+\n+static int run_server(int min_listen_port, int max_listen_port)\n+{\n+\tint i;\n+\tint log_fd;\n+\tint listen_fd = -1;\n+\tchar *log_path;\n+\tchar *ctl_path;\n+\tstruct sock_handler *handler;\n+\tstruct sockaddr_in *addr = xmalloc(sizeof(struct sockaddr_in));\n+\tstruct epoll_event events[32];\n+\tstruct sockaddr_un ctl_addr = { .sun_family = AF_UNIX };\n+\n+\tint port_delta = max_listen_port - min_listen_port + 1;\n+\tint start_offset;\n+\n+\tlog_path = git_pathdup(\"hive_log\");\n+\tctl_path = git_pathdup(\"hive_ctl\");\n+\tmemcpy(ctl_addr.sun_path, ctl_path, strlen(ctl_path) + 1);\n+\tfree(ctl_path);\n+\t\n+\tctl_sock = socket(AF_UNIX, SOCK_STREAM, 0);\n+\n+\tif (ctl_sock < 0)\n+\t\tdie_errno(\"could not create control socket\");\n+\n+\twhile (bind(ctl_sock, (struct sockaddr *)&ctl_addr,\n+\t\t    sizeof(struct sockaddr_un))) {\n+\t\tif (errno != EADDRINUSE)\n+\t\t\tdie_errno(\"could not bind control socket\");\n+\n+\t\tif (! connect(ctl_sock, (struct sockaddr *)&ctl_addr,\n+\t\t\t    sizeof(struct sockaddr_un))) {\n+\t\t\tpacket_write(ctl_sock, \"hangup\\n\");\n+\t\t\tpacket_flush(ctl_sock);\n+\n+\t\t\tdie(\"hive server already running\");\n+\t\t}\n+\n+\t\tif (errno == ECONNREFUSED) {\n+\t\t\tunlink(ctl_addr.sun_path);\n+\t\t} else if (errno != ENOENT) {\n+\t\t\tdie_errno(\"could not connect to running instance\");\n+\t\t}\n+\t}\n+\n+\tif (listen(ctl_sock, 20))\n+\t\tdie_errno(\"could not listen on control socket\");\n+\n+\tlog_fd = open(log_path, O_APPEND | O_CREAT | O_WRONLY, 0644);\n+\tfree(log_path);\n+\n+\tif (log_fd < 0)\n+\t\tdie_errno(\"could not open log file\");\n+\n+\tsrand(time(NULL));\n+\n+\tstart_offset = rand() % port_delta;\n+\n+\t*addr = (struct sockaddr_in) { .sin_family = AF_INET };\n+\tself.addr = (struct sockaddr *)addr;\n+\n+\tfor (i = 0; i < port_delta && listen_fd < 0; i++) {\n+\t\tint port = (i + start_offset) % port_delta + min_listen_port;\n+\t\taddr->sin_port = htons(port);\n+\t\tlisten_fd = begin_listen();\n+\t}\n+\n+\tif (listen_fd < 0) {\n+\t\tif (min_listen_port != max_listen_port)\n+\t\t\tdie(\"could not find available listen port\");\n+\t\telse\n+\t\t\tdie(\"port %d unavailable\", min_listen_port);\n+\t}\n+\n+\thandler = create_listen_handler(listen_fd, handle_initial_connect);\n+\tself.handler = handler;\n+\n+\tctl_handler = create_listen_handler(ctl_sock, handle_ctl_sock);\n+\n+\tpoll_fd = epoll_create1(EPOLL_CLOEXEC);\n+\n+\tif (poll_fd < 0)\n+\t\tdie_errno(\"epoll_create1\");\n+\n+\thandler_register(handler);\n+\thandler_register(ctl_handler);\n+\n+\tswitch (fork()) {\n+\tcase -1:\n+\t\tdie_errno(\"could not fork\");\n+\tcase 0:\n+\t\tbreak;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+\n+\tsetsid();\n+\n+\tif (dup3(log_fd, 1, O_CLOEXEC) < 0)\n+\t\tdie_errno(\"could not set stdout\");\n+\tif (dup2(log_fd, 2) < 0)\n+\t\tdie_errno(\"could not set stderr\");\n+\n+\tclose(0);\n+\tclose(log_fd);\n+\n+\tswitch (open(\"/dev/null\", O_RDONLY | O_CLOEXEC) != 0) {\n+\tcase -1:\n+\t\tdie_errno(\"could not set stdin\");\n+\tcase 0:\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"could not set stdin\");\n+\t}\n+\n+\tif (sigaction (SIGTERM, &term_action, NULL))\n+\t\tdie_errno(\"could not set TERM action\");\n+\n+\ti = 0;\n+\tdo {\n+\t\tstruct peer *peer;\n+\n+\t\twhile (i-- >= 1) {\n+\t\t\tstruct sock_handler *hand = events[i].data.ptr;\n+\t\t\tuint32_t next_events =\n+\t\t\t\thand->function(hand, events[i].events);\n+\n+\t\t\tif (! next_events) {\n+\t\t\t\t/* The file descriptor should have been removed\n+\t\t\t\t * from poll_fd or repurposed therein at this\n+\t\t\t\t * point.\n+\t\t\t\t */\n+\t\t\t\tdestroy_sock_handler(hand);\n+\t\t\t\tcontinue;\n+\t\t\t} else if (next_events == hand->event.events) {\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\thand->event.events = next_events;\n+\n+\t\t\thandler_register(hand);\n+\t\t}\n+\n+\n+\t\tif (shutting_down) {\n+\t\t\tif (self.handler)\n+\t\t\t\tdo_shutdown();\n+\n+\t\t\tif (sock_handler_count == 1) {\n+\t\t\t\tclose(abort_timer->fd);\n+\t\t\t\tdestroy_sock_handler(abort_timer);\n+\t\t\t}\n+\n+\t\t\tif (! sock_handler_count)\n+\t\t\t\treturn 0;\n+\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tfor (peer = self.next; peer != &self &&\n+\t\t     active_peers < min_peers; peer = peer->next)\n+\t\t\tif (! peer->handler)\n+\t\t\t\tactivate_peer(peer);\n+\n+\t\tif (! sock_handler_count)\n+\t\t\tdie(\"file descriptor listen queue depleted\");\n+\t} while ((i = epoll_wait(poll_fd, events, 32, -1)) >= 0 || errno == EINTR);\n+\n+\tdie_errno(\"epoll_wait\");\n+}\n+\n+static int read_repo_config(const char *key_, const char *value_, void *cb)\n+{\n+\tif (!strcmp(key_, \"hive.uri\"))\n+\t\tproj_uri = xstrdup(value_);\n+\n+\treturn 0;\n+}\n+\n+static void init_self(char *user_id)\n+{\n+\tself.user_id = user_id;\n+\tself.addr = NULL;\n+\tself.handler = NULL;\n+\tself.failures = 0;\n+\tself.next = &self;\n+\tself.prev = &self;\n+\n+\tactive_peers = 0;\n+}\n+\n+static int start(int argc, const char **argv)\n+{\n+\tint nongit;\n+\tchar *user_name = NULL;\n+\tchar *user_email = NULL;\n+\tchar *user_id;\n+\tchar *repo_config;\n+\tconst char *prefix = setup_git_directory_gently(&nongit);\n+\tchar *addr_str;\n+\tint min_listen_port = -1;\n+\tint max_listen_port = -1;\n+\tint listen_port = -1;\n+\n+\tstruct sockaddr *sockaddr;\n+\n+\tproj_uri = NULL;\n+\trepo_config = git_pathdup(\"config\");\n+\tgit_config_from_file(read_repo_config, repo_config, NULL);\n+\tfree(repo_config);\n+\n+\tstruct option options[] = {\n+\t\tOPT_GROUP(\"Identification options\"),\n+\t\tOPT_STRING(0, \"proj-uri\", &proj_uri, \"URI\", \"Upstream URI to identify project\"),\n+\t\tOPT_STRING(0, \"name\", &user_name, \"NAME\", \"Published name of the owner of this repository\"),\n+\t\tOPT_STRING(0, \"email\", &user_email, \"EMAIL\", \"Published email of the owner of this repository\"),\n+\n+\t\tOPT_GROUP(\"Communication options\"),\n+\t\tOPT_INTEGER(0, \"listen-port\", &listen_port, \"Port to listen for peers on. Defaults to random\"),\n+\t\tOPT_INTEGER(0, \"min-port\", &min_listen_port, \"Mininmum port to select as a listen port. Defaults to 10000\"),\n+\t\tOPT_INTEGER(0, \"max-port\", &max_listen_port, \"Maximum port to select as a listen port. Defaults to 65535\"),\n+\t\tOPT_END(),\n+\t};\n+\n+\tif (nongit)\n+\t\tdie(\"not a git repository\");\n+\n+\targc = parse_options(argc, argv, prefix, options, hive_start_usage,\n+\t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n+\n+\tuser_id = xstrdup(fmt_name(user_name, user_email));\n+\n+\tif (! proj_uri) {\n+\t\terror(\"You must use --proj-uri or set hive.uri in the \"\n+\t\t      \"repository configuration\");\n+\t\tusage_with_options(hive_start_usage, options);\n+\t}\n+\n+\tif (index(proj_uri, ' ')) {\n+\t\terror(\"project URI cannot contain space\");\n+\t\tusage_with_options(hive_start_usage, options);\n+\t}\n+\n+\tif (listen_port != -1) {\n+\t\tif (min_listen_port != -1) {\n+\t\t\terror(\"cannot specify --listen-port with --min-port\");\n+\t\t\tusage_with_options(hive_start_usage, options);\n+\t\t}\n+\n+\t\tif (max_listen_port != -1) {\n+\t\t\terror(\"cannot specify --listen-port with --max-port\");\n+\t\t\tusage_with_options(hive_start_usage, options);\n+\t\t}\n+\n+\t\tmin_listen_port = max_listen_port = listen_port;\n+\t}\n+\n+\tif (max_listen_port < min_listen_port) {\n+\t\terror(\"maximum port value must be greater than minimum\");\n+\t\tusage_with_options(hive_start_usage, options);\n+\t}\n+\n+\tif (min_listen_port == -1 && max_listen_port == -1) {\n+\t\tmin_listen_port = 10000;\n+\t\tmax_listen_port = 65535;\n+\t}\n+\n+\tif (min_listen_port < 0 || max_listen_port > 65535) {\n+\t\terror(\"port values must be in the range of 0..65535\");\n+\t\tusage_with_options(hive_start_usage, options);\n+\t}\n+\n+\tif (argc > 2) {\n+\t\terror(\"wrong number of arguments\");\n+\t\tusage_with_options(hive_start_usage, options);\n+\t}\n+\n+\tinit_self(user_id);\n+\n+\tif (argc == 0)\n+\t\treturn run_server(min_listen_port, max_listen_port);\n+\n+\tif (argc == 2) {\n+\t\taddr_str = xmallocz(strlen(argv[0]) + strlen(argv[1]) + 1);\n+\t\tsprintf(addr_str, \"%s:%s\", argv[0], argv[1]);\n+\t} else {\n+\t\taddr_str = xstrdup(argv[0]);\n+\t}\n+\n+\tsockaddr = getaddrstruct(addr_str);\n+\tfree(addr_str);\n+\n+\tadd_peer(-1, NULL, sockaddr, NULL);\n+\n+\treturn run_server(min_listen_port, max_listen_port);\n+}\n+\n+void select_peer(const char *peerstr, void *data)\n+{\n+\tconst char *user_id_brk = index(peerstr, ' ');\n+\tstruct peer_list *list = data;\n+\n+\tprintf(\"%lu) %s\", ++list->len, peerstr);\n+\n+\tlist->entries = xrealloc(list->entries,\n+\t\t\t\t sizeof(char *) * (list->len + 1));\n+\tlist->entries[list->len] = NULL;\n+\tlist->entries[list->len - 1] =\n+\t\txstrndup(peerstr, user_id_brk - peerstr);\n+}\n+\n+void print_peer(const char *peerstr, void *data)\n+{\n+\tconst char *user_id_brk = index(peerstr, ' ');\n+\tchar *ip;\n+\tpt_ops_t flags = *(pt_ops_t *)data;\n+\tstruct sockaddr *sockaddr;\n+\tint peer_sock;\n+\tchar buf[1024];\n+\n+\tif (! (flags & PT_OPS_IP))\n+\t\tprintf(\"%s\", user_id_brk + 1);\n+\telse\n+\t\tprintf(\"%s\", peerstr);\n+\n+\tif (! (flags & PT_OPS_BRANCHES))\n+\t\treturn;\n+\n+\tip = xstrndup(peerstr, user_id_brk - peerstr);\n+\n+\tsockaddr = getaddrstruct(ip);\n+\n+\tfree(ip);\n+\n+\tpeer_sock = socket(AF_INET, SOCK_STREAM, 0);\n+\n+\tif (peer_sock < 0)\n+\t\tdie_errno(\"could not create socket\");\n+\n+\tif (connect(peer_sock, sockaddr, sizeof(struct sockaddr_in))) {\n+\t\twarning(\"could not connect to peer: %s\", strerror(errno));\n+\t\tfree(sockaddr);\n+\t\tclose(peer_sock);\n+\t\treturn;\n+\t}\n+\n+\tfree(sockaddr);\n+\n+\tpacket_write(peer_sock, \"1 branch-list\\n\");\n+\tpacket_flush(peer_sock);\n+\n+\tif (flags & PT_OPS_SKIPLINE)\n+\t\tprintf(\"\\n\");\n+\n+\t(*(pt_ops_t *)data) |= PT_OPS_SKIPLINE;\n+\n+\twhile (packet_read_line(peer_sock, buf, 1024)) {\n+\t\tif (! strcmp(buf, \"hangup\\n\"))\n+\t\t\tbreak;\n+\t\tif (strncmp(buf, \"branch \", 7)) {\n+\t\t\twarning(\"protocol error from peer\");\n+\t\t\tbreak;\n+\t\t}\n+\t\tprintf(\"\\t%s\", buf + 7);\n+\t}\n+\n+\tpacket_write(peer_sock, \"hangup\\n\");\n+\tpacket_flush(peer_sock);\n+\tclose(peer_sock);\n+\treturn;\n+}\n+\n+int get_ctl_socket(void)\n+{\n+\tint ctl_fd;\n+\tchar *ctl_path;\n+\tstruct sockaddr_un addr = { .sun_family = AF_UNIX };\n+\n+\tctl_path = git_pathdup(\"hive_ctl\");\n+\tctl_fd = socket(AF_UNIX, SOCK_STREAM, 0);\n+\n+\tif (ctl_fd < 0)\n+\t\tdie_errno(\"could not create control client socket\");\n+\n+\tmemcpy(addr.sun_path, ctl_path, strlen(ctl_path) + 1);\n+\tfree(ctl_path);\n+\n+\tif (connect(ctl_fd, (struct sockaddr *)&addr,\n+\t\t    sizeof(struct sockaddr_un)))\n+\t\tdie_errno(\"could not connect to control socket\");\n+\n+\treturn ctl_fd;\n+}\n+\n+int do_for_peers(void (*func)(const char *, void *), regex_t *pat, void *data)\n+{\n+\tint ctl_fd;\n+\tchar buf[1024];\n+\n+\tctl_fd = get_ctl_socket();\n+\n+\tpacket_write(ctl_fd, \"show\\n\");\n+\tpacket_write(ctl_fd, \"hangup\\n\");\n+\tpacket_flush(ctl_fd);\n+\n+\twhile (packet_read_line(ctl_fd, buf, 1024)) {\n+\t\tchar *user_id;\n+\t\tif (! strcmp(buf, \"end-show\\n\")) {\n+\t\t\tclose(ctl_fd);\n+\t\t\treturn 0;\n+\t\t}\n+\n+\t\tif (strncmp(buf, \"have-peer \", 10))\n+\t\t\tdie(\"malformed response from daemon\");\n+\n+\t\tuser_id = index(buf + 10, ' ');\n+\n+\t\tif (! user_id)\n+\t\t\tdie(\"malformed response from daemon\");\n+\n+\t\tuser_id++;\n+\n+\t\tif (pat && regexec(pat, user_id, 0, NULL, 0))\n+\t\t\tcontinue;\n+\n+\t\tfunc(buf + 10, data);\n+\t}\n+\n+\tdie_errno(\"daemon hung up unexpectedly\");\n+}\n+\n+int fetch(int argc, const char **argv)\n+{\n+\tint nongit;\n+\tregex_t regex;\n+\tconst char *prefix = setup_git_directory_gently(&nongit);\n+\tconst char *newbranch;\n+\tint ret;\n+\tint peer_sock[2];\n+\tchar *heads[] = { NULL, NULL };\n+\tstruct peer_list peer_list= { NULL, 0 };\n+\tsize_t peer_idx = 0;\n+\tstruct sockaddr *addr;\n+\tstruct fetch_pack_args fpa = { .uploadpack = \"git-upload-pack\" };\n+\tstruct ref *ref = NULL;\n+\n+\tstruct option options[] = {\n+\t\tOPT_GROUP(\"Generic options\"),\n+\t\tOPT_END(),\n+\t};\n+\n+\tif (nongit)\n+\t\tdie(\"not a git repository\");\n+\n+\targc = parse_options(argc, argv, prefix, options, hive_show_usage,\n+\t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n+\n+\tif (argc < 2 || argc > 3)\n+\t\tusage_with_options(hive_show_usage, options);\n+\n+\tret = regcomp(&regex, argv[0], REG_EXTENDED | REG_ICASE | REG_NOSUB);\n+\n+\tif (ret) {\n+\t\tchar errbuf[256];\n+\t\tregerror(ret, &regex, errbuf, 256);\n+\t\terror(\"Invalid regex: %s\", errbuf);\n+\t\tusage_with_options(hive_show_usage, options);\n+\t}\n+\n+\tdo_for_peers(select_peer, &regex, &peer_list);\n+\n+\tregfree(&regex);\n+\n+\tif (! peer_list.len)\n+\t\tdie(\"No such peer\");\n+\n+\tif (peer_list.len > 1) {\n+\t\twhile (peer_idx < 1 || peer_idx > peer_list.len) {\n+\t\t\tprintf(\"Choose peer: \");\n+\t\t\tscanf(\"%lu\", &peer_idx);\n+\t\t}\n+\n+\t\tpeer_idx--;\n+\t}\n+\n+\taddr = getaddrstruct(peer_list.entries[peer_idx]);\n+\tfree(peer_list.entries);\n+\n+\tpeer_sock[0] = peer_sock[1] = socket(AF_INET, SOCK_STREAM, 0);\n+\n+\tif (peer_sock[0] < 0)\n+\t\tdie_errno(\"could not create socket\");\n+\n+\tif (connect(peer_sock[0], addr, sizeof(struct sockaddr_in)))\n+\t\tdie_errno(\"could not connect to peer\");\n+\n+\tfree(addr);\n+\n+\tpacket_write(peer_sock[0], \"1 upload-pack\\n\");\n+\tpacket_flush(peer_sock[0]);\n+\n+\theads[0] = (char *)argv[1];\n+\n+\tget_remote_heads(peer_sock[0], &ref, 0, NULL, 0, NULL);\n+\tref = fetch_pack(&fpa, peer_sock, NULL, ref, NULL, 1, heads, NULL);\n+\n+\tclose(peer_sock[0]);\n+\n+\tif (heads[0] && heads[0][0])\n+\t\tdie(\"No such remote ref\");\n+\n+\tif (argc == 3)\n+\t\tnewbranch = argv[2];\n+\telse\n+\t\tnewbranch = rindex(ref->name, '/') + 1;\n+\n+\texecl_git_cmd(\"branch\", newbranch, sha1_to_hex(ref->old_sha1), NULL);\n+\tdie_errno(\"Could not create branch\");\n+}\n+\n+int show(int argc, const char **argv)\n+{\n+\tint branches = 0;\n+\tint ip = 0;\n+\tint ret;\n+\tint nongit;\n+\tpt_ops_t opts = 0;\n+\tregex_t regex;\n+\tconst char *prefix = setup_git_directory_gently(&nongit);\n+\n+\tstruct option options[] = {\n+\t\tOPT_GROUP(\"Generic options\"),\n+\t\tOPT_BOOLEAN('i', \"ip\", &ip, \"Print IP and port with each peer\"),\n+\t\tOPT_UYN(0, \"branches\", &branches, \"Show branches for each peer. This is default if a regex is given\"),\n+\t\tOPT_END(),\n+\t};\n+\n+\tif (nongit)\n+\t\tdie(\"not a git repository\");\n+\n+\targc = parse_options(argc, argv, prefix, options, hive_show_usage,\n+\t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n+\n+\tif (argc > 1)\n+\t\tusage_with_options(hive_show_usage, options);\n+\n+\tif (ip)\n+\t\topts |= PT_OPS_IP;\n+\n+\tif (argc == 0) {\n+\t\tif (branches)\n+\t\t\topts |= PT_OPS_BRANCHES;\n+\n+\t\treturn do_for_peers(print_peer, NULL, &opts);\n+\t}\n+\n+\tif (! branches)\n+\t\tbranches = 1;\n+\n+\tif (branches != 2)\n+\t\topts |= PT_OPS_BRANCHES;\n+\n+\tret = regcomp(&regex, argv[0], REG_EXTENDED | REG_ICASE | REG_NOSUB);\n+\n+\tif (ret) {\n+\t\tchar errbuf[256];\n+\t\tregerror(ret, &regex, errbuf, 256);\n+\t\terror(\"Invalid regex: %s\", errbuf);\n+\t\tusage_with_options(hive_show_usage, options);\n+\t}\n+\n+\tret = do_for_peers(print_peer, &regex, &opts);\n+\n+\tregfree(&regex);\n+\n+\treturn ret;\n+}\n+\n+int stop(int argc, const char **argv)\n+{\n+\tint ctl_fd;\n+\tpid_t pid;\n+\tchar buf[1024];\n+\tchar *pid_txt;\n+\n+\tif (argc != 1)\n+\t\tusage(hive_stop_usage);\n+\n+\tctl_fd = get_ctl_socket();\n+\n+\tpacket_write(ctl_fd, \"pid\\n\");\n+\tpacket_write(ctl_fd, \"hangup\\n\");\n+\tpacket_flush(ctl_fd);\n+\n+\tif (! packet_read_line(ctl_fd, buf, 1024))\n+\t\tdie(\"daemon hung up unexpectedly\");\n+\n+\tif (strncmp(buf, \"pid \", 4))\n+\t\tdie(\"unknown response from daemon\");\n+\n+\tpid_txt = buf;\n+\tpid_txt += 4;\n+\n+\tif (! sscanf(pid_txt, \"%u\", &pid))\n+\t\tdie(\"malformed response from daemon\");\n+\n+\tkill(pid, SIGTERM);\n+\treturn 0;\n+}\n+\n+int main(int argc, const char **argv)\n+{\n+\tif (argc < 2)\n+\t\tusage(hive_usage);\n+\n+\targv++;\n+\targc--;\n+\n+\tif (!strcmp(*argv, \"start\"))\n+\t\treturn start(argc, argv);\n+\n+\tif (!strcmp(*argv, \"fetch\"))\n+\t\treturn fetch(argc, argv);\n+\n+\tif (!strcmp(*argv, \"show\"))\n+\t\treturn show(argc, argv);\n+\n+\tif (!strcmp(*argv, \"stop\"))\n+\t\treturn stop(argc, argv);\n+\n+\tusage(hive_usage);\n+}\n-- \n1.7.2.1\n"},{"id":"149361","messageId":"AANLkTikcV6f=bUBa-F44JCEFOT0dHrvgrLY-j9hvvOyX@mail.gmail.com","threadId":"24915","inReplyTo":"1283198367-11440-1-git-send-email-cdahlin@redhat.com","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Luke Kenneth Casson Leighton","fromEmail":"luke.leighton@gmail.com","sentAt":"2010-08-30T21:17:07Z","receivedAt":"2010-08-30T21:17:07Z","isPatch":true,"sender":{"key":"luke.leighton@gmail.com","avatar":null},"body":"On Mon, Aug 30, 2010 at 8:59 PM,  <cdahlin@redhat.com> wrote:\n> From: Casey Dahlin <cdahlin@redhat.com>\n>\n> This isn't really what I'd normally call ready for public consumption, but\n> since there's been some minimal list chatter on a similar subject I figured I'd\n> put this out there early.\n>\n> git-hive is a peer to peer service for exchanging branches. When started it\n> runs a small daemon to publish your repository on a random (or specified) port.\n> Two hive daemons connected to oneanother can exchange branch lists and\n> introduce eachother to more hive daemons to widen the network.\n\n p.s. is there any location where, in diagrammatic form, the\nprotocol's described?\n"},{"id":"149390","messageId":"AANLkTinx_Y1iUt9tj4io=GskwRyvK2EdWv9cO9gAbxBS@mail.gmail.com","threadId":"24915","inReplyTo":"AANLkTikcV6f=bUBa-F44JCEFOT0dHrvgrLY-j9hvvOyX@mail.gmail.com","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-08-31T01:29:02Z","receivedAt":"2010-08-31T01:29:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Aug 31, 2010 at 7:17 AM, Luke Kenneth Casson Leighton\n<luke.leighton@gmail.com> wrote:\n> On Mon, Aug 30, 2010 at 8:59 PM,  <cdahlin@redhat.com> wrote:\n>> From: Casey Dahlin <cdahlin@redhat.com>\n>>\n>> This isn't really what I'd normally call ready for public consumption, but\n>> since there's been some minimal list chatter on a similar subject I figured I'd\n>> put this out there early.\n>>\n>> git-hive is a peer to peer service for exchanging branches. When started it\n>> runs a small daemon to publish your repository on a random (or specified) port.\n>> Two hive daemons connected to oneanother can exchange branch lists and\n>> introduce eachother to more hive daemons to widen the network.\n>\n>  p.s. is there any location where, in diagrammatic form, the\n> protocol's described?\n\nAlso a short tutorial how to use it would be nice.\n\nI take it I can put different repositories (say wine.git and git.git)\nin the hive. When I fetch git.git from the hive, it won't fetch\nwine.git?\n-- \nDuy\n"},{"id":"149417","messageId":"20100831143839.GC16034@foucault.redhat.com","threadId":"24915","inReplyTo":"AANLkTinx_Y1iUt9tj4io=GskwRyvK2EdWv9cO9gAbxBS@mail.gmail.com","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Casey Dahlin","fromEmail":"cdahlin@redhat.com","sentAt":"2010-08-31T14:38:39Z","receivedAt":"2010-08-31T14:38:39Z","isPatch":true,"sender":{"key":"cdahlin@redhat.com","avatar":null},"body":"On Tue, Aug 31, 2010 at 11:29:02AM +1000, Nguyen Thai Ngoc Duy wrote:\n> On Tue, Aug 31, 2010 at 7:17 AM, Luke Kenneth Casson Leighton\n> <luke.leighton@gmail.com> wrote:\n> > On Mon, Aug 30, 2010 at 8:59 PM,  <cdahlin@redhat.com> wrote:\n> >> From: Casey Dahlin <cdahlin@redhat.com>\n> >>\n> >> This isn't really what I'd normally call ready for public consumption, but\n> >> since there's been some minimal list chatter on a similar subject I figured I'd\n> >> put this out there early.\n> >>\n> >> git-hive is a peer to peer service for exchanging branches. When started it\n> >> runs a small daemon to publish your repository on a random (or specified) port.\n> >> Two hive daemons connected to oneanother can exchange branch lists and\n> >> introduce eachother to more hive daemons to widen the network.\n> >\n> >  p.s. is there any location where, in diagrammatic form, the\n> > protocol's described?\n> \n> Also a short tutorial how to use it would be nice.\n> \n> I take it I can put different repositories (say wine.git and git.git)\n> in the hive. When I fetch git.git from the hive, it won't fetch\n> wine.git?\n\nFirst step to using hive is to set the project uri for your repo (may change\nthis to a different mechanism later).\n\n\tcasey@host_a$ git config --add hive.uri http://myproject.org\n\nThis is what prevents the scenario you describe; repos with the same project\nURI are assumed to be compatible. It can be given as a command line argument to\n'git hive start', but this way is much more usable.\n\nNext we start the service for our repo.\n\n\tcasey@host_a$ git hive start --listen-port 21121\n\nNormally the listen port is selected randomly. We specify it here so that other\nhive users can explicitly connect to us and \"bootstrap\" themselves into the\nhive network.\n\nNow to have someone join.\n\n\tnguyen@host_b$ git config --add hive.uri http://myproject.org\n\tnguyen@host_b$ git hive start host_a.com:21121\n\nSo from host_b we specify host_a's address and listen port, and we join the\nnetwork. From here on out anyone who also connects to host_a will get host_b's\n(randomly selected) listen port automatically and be able to connect to it as\nwell.\n\nSo now our two peers can see each other.\n\n\tcasey@host_a$ git hive show --branches\n\tNguyen Thai Ngoc Duy <pclouds@gmail.com>\n\t\tmaster\n\t\tfor_casey\n\t\n\t---\n\n\tnguyen@host_b$ git hive show --branches\n\tCasey Dahlin <cdahlin@redhat.com>\n\t\tmaster\n\t\tstable\n\t\tv2.1\n\nAnd we can exchange them\n\n\tcasey@host_a$ git hive fetch nguyen for_casey\n\tcasey@host_a$ git branch\n\t* master\n\t  stable\n\t  for_casey\n\nNote that the two arguments in fetch are a regex which searches through user\nIDs and a branch name, which is why I can abbreviate to just \"nguyen\" in all\nlower case.\n\n--CJD\n"},{"id":"149423","messageId":"AANLkTinF1o0RZSKYEL9Qc=uwXx6fBBXh6wRx2CTULBSE@mail.gmail.com","threadId":"24915","inReplyTo":"20100831143839.GC16034@foucault.redhat.com","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Luke Kenneth Casson Leighton","fromEmail":"luke.leighton@gmail.com","sentAt":"2010-08-31T15:08:03Z","receivedAt":"2010-08-31T15:08:03Z","isPatch":true,"sender":{"key":"luke.leighton@gmail.com","avatar":null},"body":"On Tue, Aug 31, 2010 at 3:38 PM, Casey Dahlin <cdahlin@redhat.com> wrote:\n\n>        nguyen@host_b$ git config --add hive.uri http://myproject.org\n>        nguyen@host_b$ git hive start host_a.com:21121\n>\n> So from host_b we specify host_a's address and listen port, and we join the\n> network. From here on out anyone who also connects to host_a will get host_b's\n> (randomly selected) listen port automatically and be able to connect to it as\n> well.\n>\n> So now our two peers can see each other.\n\n ok - this only works if the two peers can see each other's ip\naddresses.  i.e. if the two machines are either on a local subnet or\nif the two machines are directly on the public internet.  ( or if\nyou've forced people to set up a firewall rule and/or UPnP rule, but\neven then UPnP doesn't solve the entire problem - only one part of it)\n\n ... unless (and i haven't reviewed the code closely, i admit) you're\nusing the following protocol:\n\n * make tcp connection\n * send dedicated specific message \"please tell me my public IP and port\"\n * far end does sockaddr lookup of the incoming socket\n * far end returns IP and port as response to requestor\n\nin this way, requestors can determine what the \"apparent\" IP address\nis as far as having been NAT'd through half a ton of ISP layers\nperforming NAT, local routers performing NAT, laptops such as mine\ndoing NAT sharing of a 3G connection over a netgear router and so on.\n\nso it entirely depends on the scope / scale you're thinking of.  if\nyou are aiming hive at \"local subnets\", it's pretty much perfect, and,\nmuch as i hate to say it and you should not _remotely_ consider using\nit because of the vast and horrific dependencies, i can understand why\nyou were considering avahi, because it is a good \"local subnets\"\nsolution.\n\nhowever for \"global networking\" - for putting hive out onto the public\ninternet - there is one hell of a lot more involved, and all these\n\"filesharing\" applications have solved near as damnit every single one\nof the issues.\n\nso, answering the question you were asking earlier: i believe that you\nreally do need to consider taking the closest c-based bittorrent\nlibrary/application apart, and use it as the basis for git-hive.  if\nyou don't, you will be here forever, reinventing everything that these\nfileshare-app-writers have spent nearly a decade perfecting.\n\nl.\n"},{"id":"149435","messageId":"20100831155159.GD16034@foucault.redhat.com","threadId":"24915","inReplyTo":"AANLkTinF1o0RZSKYEL9Qc=uwXx6fBBXh6wRx2CTULBSE@mail.gmail.com","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Casey Dahlin","fromEmail":"cdahlin@redhat.com","sentAt":"2010-08-31T15:52:00Z","receivedAt":"2010-08-31T15:52:00Z","isPatch":true,"sender":{"key":"cdahlin@redhat.com","avatar":null},"body":"On Tue, Aug 31, 2010 at 04:08:03PM +0100, Luke Kenneth Casson Leighton wrote:\n> On Tue, Aug 31, 2010 at 3:38 PM, Casey Dahlin <cdahlin@redhat.com> wrote:\n> \n> >        nguyen@host_b$ git config --add hive.uri http://myproject.org\n> >        nguyen@host_b$ git hive start host_a.com:21121\n> >\n> > So from host_b we specify host_a's address and listen port, and we join the\n> > network. From here on out anyone who also connects to host_a will get host_b's\n> > (randomly selected) listen port automatically and be able to connect to it as\n> > well.\n> >\n> > So now our two peers can see each other.\n> \n>  ok - this only works if the two peers can see each other's ip\n> addresses.  i.e. if the two machines are either on a local subnet or\n> if the two machines are directly on the public internet.  ( or if\n> you've forced people to set up a firewall rule and/or UPnP rule, but\n> even then UPnP doesn't solve the entire problem - only one part of it)\n> \n>  ... unless (and i haven't reviewed the code closely, i admit) you're\n> using the following protocol:\n> \n>  * make tcp connection\n>  * send dedicated specific message \"please tell me my public IP and port\"\n>  * far end does sockaddr lookup of the incoming socket\n>  * far end returns IP and port as response to requestor\n> \n\nIts a bit more primitive right now (a bit broken even) but that's the eventual\nplan.\n\nPiece you missed though is that the port on the connect end isn't the same as\nthe listen port. Connecting back to it would do no good.\n\n> in this way, requestors can determine what the \"apparent\" IP address\n> is as far as having been NAT'd through half a ton of ISP layers\n> performing NAT, local routers performing NAT, laptops such as mine\n> doing NAT sharing of a 3G connection over a netgear router and so on.\n> \n\nYou can't get back through all those anyway unless they've all been set up to\nallow it. I don't know what magic you've seen torrent clients do, but the\nprocedure for the ones I always used was:\n\n1) Check to see if it can receive connections on the port it wants.\n2) Bitch at user if it can't.\n\nBittorrent has the luxury of being able to proxy for the poor firewall-bound\nusers since as long as there's one peer exposed to the internet you can have\nany two other peers connect to him and give him the data they want to exchange,\nto the benefit of all 3. Git won't work that way because not everyone in the\nswarm wants all chunks of data, so if you found a proxy node, you might have to\nmake him carry data (possibly lots of data) that he has no personal interest\nin.\n\n> so, answering the question you were asking earlier: i believe that you\n> really do need to consider taking the closest c-based bittorrent\n> library/application apart, and use it as the basis for git-hive.  if\n> you don't, you will be here forever, reinventing everything that these\n> fileshare-app-writers have spent nearly a decade perfecting.\n> \n\nThe thing avahi was going to provide was bootstrapping. Bittorrent DHT handles\nthis by having a list of known-good peers stashed away somewhere\n(bittorrent.org hosts one). Essentially a non-p2p solution to joining the p2p\nnetwork. That's pretty much the only WAN solution. Local networks on the same\nsubnet have the option of UDP multicast. Avahi can do that. I'd be willing to\ndo it manually or with something else. But yes, for your WAN case its useless.\n\n--CJD\n"},{"id":"149452","messageId":"AANLkTikrQhzyPckN2t3+FzaJFzYboDRVfW3bPPZOGpJQ@mail.gmail.com","threadId":"24915","inReplyTo":"20100831155159.GD16034@foucault.redhat.com","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Luke Kenneth Casson Leighton","fromEmail":"luke.leighton@gmail.com","sentAt":"2010-08-31T16:47:42Z","receivedAt":"2010-08-31T16:47:42Z","isPatch":true,"sender":{"key":"luke.leighton@gmail.com","avatar":null},"body":"On Tue, Aug 31, 2010 at 4:52 PM, Casey Dahlin <cdahlin@redhat.com> wrote:\n> Bittorrent has the luxury of being able to proxy for the poor firewall-bound\n> users since as long as there's one peer exposed to the internet you can have\n> any two other peers connect to him and give him the data they want to exchange,\n> to the benefit of all 3. Git won't work that way because not everyone in the\n> swarm wants all chunks of data, so if you found a proxy node, you might have to\n> make him carry data (possibly lots of data) that he has no personal interest\n> in.\n\n ah, no you don't.\n\n but - think about it: even if they don't, if they don't want the set\nof commits that get you up to a particular HEAD or other tag or\nbranch, what the hell are they doing?? :)  from what i can gather, git\nsimply doesn't operate in a way where you can \"pick and choose\" which\ncommits you are and are not going to keep around, in order to\nreconstruct the repository.\n\n i hope that's right, because i'm counting on it.  i.e i'm counting on\nthe following being true:\n\n \"all copies of all git repositories have exactly the same objects\nsuch that git pack-object on the exact same ref and the exact same\nobject ref will return exactly the same information\".\n\n if anyone knows a reason why that is NOT the case, please could you tell me!\n\nl.\n"},{"id":"149459","messageId":"20100831172008.GE16034@foucault.redhat.com","threadId":"24915","inReplyTo":"AANLkTikrQhzyPckN2t3+FzaJFzYboDRVfW3bPPZOGpJQ@mail.gmail.com","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Casey Dahlin","fromEmail":"cdahlin@redhat.com","sentAt":"2010-08-31T17:20:09Z","receivedAt":"2010-08-31T17:20:09Z","isPatch":true,"sender":{"key":"cdahlin@redhat.com","avatar":null},"body":"On Tue, Aug 31, 2010 at 05:47:42PM +0100, Luke Kenneth Casson Leighton wrote:\n> On Tue, Aug 31, 2010 at 4:52 PM, Casey Dahlin <cdahlin@redhat.com> wrote:\n> > Bittorrent has the luxury of being able to proxy for the poor firewall-bound\n> > users since as long as there's one peer exposed to the internet you can have\n> > any two other peers connect to him and give him the data they want to exchange,\n> > to the benefit of all 3. Git won't work that way because not everyone in the\n> > swarm wants all chunks of data, so if you found a proxy node, you might have to\n> > make him carry data (possibly lots of data) that he has no personal interest\n> > in.\n> \n>  ah, no you don't.\n> \n>  but - think about it: even if they don't, if they don't want the set\n> of commits that get you up to a particular HEAD or other tag or\n> branch, what the hell are they doing?? :)  from what i can gather, git\n> simply doesn't operate in a way where you can \"pick and choose\" which\n> commits you are and are not going to keep around, in order to\n> reconstruct the repository.\n> \n>  i hope that's right, because i'm counting on it.  i.e i'm counting on\n> the following being true:\n> \n>  \"all copies of all git repositories have exactly the same objects\n> such that git pack-object on the exact same ref and the exact same\n> object ref will return exactly the same information\".\n> \n>  if anyone knows a reason why that is NOT the case, please could you tell me!\n> \n\nCommits are always the same for everyone (though two commits you might think of\nas \"the same commit\" may not be in git terms). Branches are pretty much always\nsubsets or supersets of oneanother. Repositories? Essentially snowflakes.\n\nTry the kernel: Linus has a branch. Most individuals' repos are going to have\nsome subset of that branch checked out under various names. Not everyone will\nhave all of the commits though, nor will they necessarily want them just now.\nMost people will have no ref pointing to the top commit of the Linus branch; it\nwill be a couple of commits down due to new things they've added on top. Refs\nare NOT resource identifiers, and certainly not global resource identifiers.\n\nNow consider the networking people. They have their own branch. It attaches to\nthe linus branch somewhere below the tip linus has published (probably at a\nrevision tag) and includes several unique commits. All the networking people\nwant those commits. None of the rest of people do. There's a btrfs branch too.\nSome people want that, some don't. Some will have the networking and btrfs\nbranches. Most will have added commits on top privately.\n\nNow consider share by email, where the same commit may appear in several\nslightly different forms with different SHA 1s.\n\nIn summary, no. Repositories are balls of independently produced content. A\ngood peer to peer network needs to let any one client individually address\neverything out there, but still take advantage of those instances where lots of\npeople do have copies of the same object.\n\n--CJD\n"},{"id":"149483","messageId":"20100831191909.GF16034@foucault.redhat.com","threadId":"24915","inReplyTo":"815C806E-E7DC-4B7D-9B45-4C9B289DFEEF@sb.org","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Casey Dahlin","fromEmail":"cdahlin@redhat.com","sentAt":"2010-08-31T19:19:09Z","receivedAt":"2010-08-31T19:19:09Z","isPatch":true,"sender":{"key":"cdahlin@redhat.com","avatar":null},"body":"On Tue, Aug 31, 2010 at 12:15:39PM -0700, Kevin Ballard wrote:\n> What about using Bonjour (a.k.a. DNS-SD) on OSes that provide this functionality? A lot of us already have local networks that are designed to let Bonjour propagate across the entire network, but hasn't been tested on simple UDP multicast. And with Bonjour you can even get wide-area Bonjour domains, so for example I could set up git hive on my desktop at home, and then get at it via my MobileMe wide-area Bonjour domain from anywhere else in the world.\n> \n> -Kevin Ballard\n> \n\nAvahi is Linux's Bonjour implementation ;)\n\n--CJD\n"},{"id":"149484","messageId":"9CA6AC3D-5C51-4770-BF78-077DE5CD80C5@sb.org","threadId":"24915","inReplyTo":"20100831191909.GF16034@foucault.redhat.com","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Kevin Ballard","fromEmail":"kevin@sb.org","sentAt":"2010-08-31T19:21:35Z","receivedAt":"2010-08-31T19:21:35Z","isPatch":true,"sender":{"key":"kevin@sb.org","avatar":"https://avatars.githubusercontent.com/u/714?v=4"},"body":"On Aug 31, 2010, at 12:19 PM, Casey Dahlin wrote:\n\n> On Tue, Aug 31, 2010 at 12:15:39PM -0700, Kevin Ballard wrote:\n>> What about using Bonjour (a.k.a. DNS-SD) on OSes that provide this functionality? A lot of us already have local networks that are designed to let Bonjour propagate across the entire network, but hasn't been tested on simple UDP multicast. And with Bonjour you can even get wide-area Bonjour domains, so for example I could set up git hive on my desktop at home, and then get at it via my MobileMe wide-area Bonjour domain from anywhere else in the world.\n>> \n>> -Kevin Ballard\n>> \n> \n> Avahi is Linux's Bonjour implementation ;)\n> \n> --CJD\n\nAh hah, now it makes sense. I guess I should have looked up Avahi before just assuming it was some other P2P doohickey. Now, if git-hive is running on OS X, will it still be using a bundled version of Avahi, or will it default to using the system-provided mDNSResponder daemon (e.g. OS X's Bonjour)?\n\n-Kevin Ballard"},{"id":"149485","messageId":"F50884C0-3C80-450C-A799-3940D8B68B3D@sb.org","threadId":"24915","inReplyTo":"20100831155159.GD16034@foucault.redhat.com","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Kevin Ballard","fromEmail":"kevin@sb.org","sentAt":"2010-08-31T19:23:30Z","receivedAt":"2010-08-31T19:23:30Z","isPatch":true,"sender":{"key":"kevin@sb.org","avatar":"https://avatars.githubusercontent.com/u/714?v=4"},"body":"This message was originally rejected from the list due to containing an HTML subpart. Resending as plain text.\n\n---\n\nWhat about using Bonjour (a.k.a. DNS-SD) on OSes that provide this functionality? A lot of us already have local networks that are designed to let Bonjour propagate across the entire network, but hasn't been tested on simple UDP multicast. And with Bonjour you can even get wide-area Bonjour domains, so for example I could set up git hive on my desktop at home, and then get at it via my MobileMe wide-area Bonjour domain from anywhere else in the world.\n\n-Kevin Ballard\n\nOn Aug 31, 2010, at 8:52 AM, Casey Dahlin wrote:\n\n> Local networks on the same\n> subnet have the option of UDP multicast. Avahi can do that. I'd be willing to\n> do it manually or with something else. But yes, for your WAN case its useless.\n"},{"id":"149487","messageId":"20100831192539.GG16034@foucault.redhat.com","threadId":"24915","inReplyTo":"9CA6AC3D-5C51-4770-BF78-077DE5CD80C5@sb.org","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Casey Dahlin","fromEmail":"cdahlin@redhat.com","sentAt":"2010-08-31T19:25:39Z","receivedAt":"2010-08-31T19:25:39Z","isPatch":true,"sender":{"key":"cdahlin@redhat.com","avatar":null},"body":"> > \n> > Avahi is Linux's Bonjour implementation ;)\n> > \n> > --CJD\n> \n> Ah hah, now it makes sense. I guess I should have looked up Avahi before just assuming it was some other P2P doohickey. Now, if git-hive is running on OS X, will it still be using a bundled version of Avahi, or will it default to using the system-provided mDNSResponder daemon (e.g. OS X's Bonjour)?\n> \n\nNot sure Avahi will be used at all at this point given present objections. Its\nhard because although its the same protocol, I believe the api is different, so\nwe'd need additional code. I don't think bundling Avahi on OSX is the right\nanswer to anything regardless.\n\n--CJD\n"},{"id":"149527","messageId":"20100901035623.GA8775@LK-Perkele-V2.elisa-laajakaista.fi","threadId":"24915","inReplyTo":"20100831143839.GC16034@foucault.redhat.com","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Ilari Liusvaara","fromEmail":"ilari.liusvaara@elisanet.fi","sentAt":"2010-09-01T03:56:24Z","receivedAt":"2010-09-01T03:56:24Z","isPatch":true,"sender":{"key":"ilari.liusvaara@elisanet.fi","avatar":null},"body":"On Tue, Aug 31, 2010 at 10:38:39AM -0400, Casey Dahlin wrote:\n> \n> And we can exchange them\n> \n> \tcasey@host_a$ git hive fetch nguyen for_casey\n> \tcasey@host_a$ git branch\n> \t* master\n> \t  stable\n> \t  for_casey\n> \n> Note that the two arguments in fetch are a regex which searches through user\n> IDs and a branch name, which is why I can abbreviate to just \"nguyen\" in all\n> lower case.\n\nAny possibilty for adding remote helper for fetching/pulling directly using\nfetch? You have full-duplex connectivity and Git running on both ends,\nright[1]?\n\n[1] remote-helper connect command is designed for just these sort of cases.\n\n-Ilari\n"},{"id":"149528","messageId":"20100901040702.GH16034@foucault.redhat.com","threadId":"24915","inReplyTo":"20100901035623.GA8775@LK-Perkele-V2.elisa-laajakaista.fi","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Casey Dahlin","fromEmail":"cdahlin@redhat.com","sentAt":"2010-09-01T04:07:03Z","receivedAt":"2010-09-01T04:07:03Z","isPatch":true,"sender":{"key":"cdahlin@redhat.com","avatar":null},"body":"On Wed, Sep 01, 2010 at 06:56:24AM +0300, Ilari Liusvaara wrote:\n> On Tue, Aug 31, 2010 at 10:38:39AM -0400, Casey Dahlin wrote:\n> > \n> > And we can exchange them\n> > \n> > \tcasey@host_a$ git hive fetch nguyen for_casey\n> > \tcasey@host_a$ git branch\n> > \t* master\n> > \t  stable\n> > \t  for_casey\n> > \n> > Note that the two arguments in fetch are a regex which searches through user\n> > IDs and a branch name, which is why I can abbreviate to just \"nguyen\" in all\n> > lower case.\n> \n> Any possibilty for adding remote helper for fetching/pulling directly using\n> fetch? You have full-duplex connectivity and Git running on both ends,\n> right[1]?\n> \n> [1] remote-helper connect command is designed for just these sort of cases.\n> \n> -Ilari\n\nYep. This will be patch v2 :)\n"},{"id":"149990","messageId":"i60s5p$vii$1@dough.gmane.org","threadId":"24915","inReplyTo":"20100831143839.GC16034@foucault.redhat.com","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-09-05T19:48:06Z","receivedAt":"2010-09-05T19:48:06Z","isPatch":true,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"Casey Dahlin wrote:\n> \n> So now our two peers can see each other.\n> \n> casey@host_a$ git hive show --branches\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com>\n> master\n> for_casey\n> \n> ---\n> \n> nguyen@host_b$ git hive show --branches\n> Casey Dahlin <cdahlin@redhat.com>\n> master\n> stable\n> v2.1\n> \n> And we can exchange them\n> \n> casey@host_a$ git hive fetch nguyen for_casey\n> casey@host_a$ git branch\n> * master\n> stable\n> for_casey\n> \n> Note that the two arguments in fetch are a regex which searches through\n> user IDs and a branch name, which is why I can abbreviate to just \"nguyen\"\n> in all lower case.\n\nI may be a little late into the discussion, but I must say I very much like the\nidea. I realize that this is mostly intended for local repo sharing (typically\nbetween coworkers), but I suspect the idea could be extended to more general\ndistributed repository, ehrm, distribution.\n\nThe only thing I would object to, so far, is hive fetch bringing stuff\ndirectly into my local repository. I typically prefer content to remain\nfenced in its appropriate namespace (e.g. I have issues even with the way tags\nand notes are imported from remotes). For hives, it might be a better idea to\nhave the hive-fetched branches plop into either refs/remotes or maybe even a\nnew dedicated namespace like refs/hive/ ?\n"},{"id":"149994","messageId":"4C83F8CE.2060403@dbservice.com","threadId":"24915","inReplyTo":"20100831192539.GG16034@foucault.redhat.com","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Tomas Carnecky","fromEmail":"tom@dbservice.com","sentAt":"2010-09-05T20:08:46Z","receivedAt":"2010-09-05T20:08:46Z","isPatch":true,"sender":{"key":"tom@dbservice.com","avatar":"https://gravatar.com/avatar/900a300bdd1a8bbe086008ad78210bbee2ad2803b7d50a5cba04c1e9404bd6d2?d=mp&s=160"},"body":"On 8/31/10 9:25 PM, Casey Dahlin wrote:\n> Not sure Avahi will be used at all at this point given present objections. Its\n\nWhich objections?\n\n> hard because although its the same protocol, I believe the api is different, so\n> we'd need additional code. I don't think bundling Avahi on OSX is the right\n> answer to anything regardless.\n\nYou can code against the mDNSResponder API and on Linux use the Avahi\nmDNSResponder compatibility layer.\n\ntom\n"},{"id":"150025","messageId":"20100906022532.GA15150@foucault.redhat.com","threadId":"24915","inReplyTo":"20100905194810.5940F384096@mbox.dmi.unict.it","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Casey Dahlin","fromEmail":"cdahlin@redhat.com","sentAt":"2010-09-06T02:25:33Z","receivedAt":"2010-09-06T02:25:33Z","isPatch":true,"sender":{"key":"cdahlin@redhat.com","avatar":null},"body":"On Sun, Sep 05, 2010 at 09:48:06PM +0200, Giuseppe Bilotta wrote:\n> Casey Dahlin wrote:\n> > \n> > So now our two peers can see each other.\n> > \n> > casey@host_a$ git hive show --branches\n> > Nguyen Thai Ngoc Duy <pclouds@gmail.com>\n> > master\n> > for_casey\n> > \n> > ---\n> > \n> > nguyen@host_b$ git hive show --branches\n> > Casey Dahlin <cdahlin@redhat.com>\n> > master\n> > stable\n> > v2.1\n> > \n> > And we can exchange them\n> > \n> > casey@host_a$ git hive fetch nguyen for_casey\n> > casey@host_a$ git branch\n> > * master\n> > stable\n> > for_casey\n> > \n> > Note that the two arguments in fetch are a regex which searches through\n> > user IDs and a branch name, which is why I can abbreviate to just \"nguyen\"\n> > in all lower case.\n> \n> I may be a little late into the discussion, but I must say I very much like the\n> idea. I realize that this is mostly intended for local repo sharing (typically\n> between coworkers), but I suspect the idea could be extended to more general\n> distributed repository, ehrm, distribution.\n> \n> The only thing I would object to, so far, is hive fetch bringing stuff\n> directly into my local repository. I typically prefer content to remain\n> fenced in its appropriate namespace (e.g. I have issues even with the way tags\n> and notes are imported from remotes). For hives, it might be a better idea to\n> have the hive-fetched branches plop into either refs/remotes or maybe even a\n> new dedicated namespace like refs/hive/ ?\n\nPlan is to kill hive-fetch in favor of a fetch helper so the ordinary git\nretrieval commands may be used.\n\n--CJD\n"},{"id":"150026","messageId":"20100906022805.GB15150@foucault.redhat.com","threadId":"24915","inReplyTo":"4C83F8CE.2060403@dbservice.com","subject":"Re: [RFC PATCH] Introduce git-hive","fromName":"Casey Dahlin","fromEmail":"cdahlin@redhat.com","sentAt":"2010-09-06T02:28:06Z","receivedAt":"2010-09-06T02:28:06Z","isPatch":true,"sender":{"key":"cdahlin@redhat.com","avatar":null},"body":"On Sun, Sep 05, 2010 at 10:08:46PM +0200, Tomas Carnecky wrote:\n> On 8/31/10 9:25 PM, Casey Dahlin wrote:\n> > Not sure Avahi will be used at all at this point given present objections. Its\n> \n> Which objections?\n> \n\nI believe Luke outlined some issues earlier, particularly the difficulty of\ngetting avahi on Windows. If others would like to continue that flame without\nme I'll step in and decide the winner when everyone's tired out :)\n\n> > hard because although its the same protocol, I believe the api is different, so\n> > we'd need additional code. I don't think bundling Avahi on OSX is the right\n> > answer to anything regardless.\n> \n> You can code against the mDNSResponder API and on Linux use the Avahi\n> mDNSResponder compatibility layer.\n> \n\nGood to know :)\n\n--CJD\n"}]}