|
|
|
#include "cache.h"
|
|
|
|
#include "refs.h"
|
|
|
|
#include "pkt-line.h"
|
|
|
|
#include "tag.h"
|
|
|
|
#include "object.h"
|
|
|
|
#include "commit.h"
|
|
|
|
#include "exec_cmd.h"
|
|
|
|
#include <signal.h>
|
|
|
|
#include <sys/poll.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
|
|
|
static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
|
|
|
|
|
|
|
|
#define THEY_HAVE (1U << 0)
|
|
|
|
#define OUR_REF (1U << 1)
|
|
|
|
#define WANTED (1U << 2)
|
|
|
|
#define MAX_HAS 256
|
|
|
|
#define MAX_NEEDS 256
|
|
|
|
static int nr_has = 0, nr_needs = 0, multi_ack = 0, nr_our_refs = 0;
|
|
|
|
static int use_thin_pack = 0;
|
|
|
|
static unsigned char has_sha1[MAX_HAS][20];
|
|
|
|
static unsigned char needs_sha1[MAX_NEEDS][20];
|
|
|
|
static unsigned int timeout = 0;
|
|
|
|
|
|
|
|
static void reset_timeout(void)
|
|
|
|
{
|
|
|
|
alarm(timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int strip(char *line, int len)
|
|
|
|
{
|
|
|
|
if (len && line[len-1] == '\n')
|
|
|
|
line[--len] = 0;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void create_pack_file(void)
|
|
|
|
{
|
|
|
|
/* Pipes between rev-list to pack-objects, pack-objects to us
|
|
|
|
* and pack-objects error stream for progress bar.
|
|
|
|
*/
|
|
|
|
int lp_pipe[2], pu_pipe[2], pe_pipe[2];
|
|
|
|
pid_t pid_rev_list, pid_pack_objects;
|
|
|
|
int create_full_pack = (nr_our_refs == nr_needs && !nr_has);
|
|
|
|
char data[8193], progress[128];
|
|
|
|
int buffered = -1;
|
|
|
|
|
|
|
|
if (pipe(lp_pipe) < 0)
|
|
|
|
die("git-upload-pack: unable to create pipe");
|
|
|
|
pid_rev_list = fork();
|
|
|
|
if (pid_rev_list < 0)
|
|
|
|
die("git-upload-pack: unable to fork git-rev-list");
|
|
|
|
|
|
|
|
if (!pid_rev_list) {
|
|
|
|
int i;
|
|
|
|
int args;
|
|
|
|
const char **argv;
|
|
|
|
char *buf;
|
|
|
|
char **p;
|
|
|
|
|
|
|
|
if (create_full_pack) {
|
|
|
|
args = 10;
|
|
|
|
use_thin_pack = 0; /* no point doing it */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
args = nr_has + nr_needs + 5;
|
|
|
|
p = xmalloc(args * sizeof(char *));
|
|
|
|
argv = (const char **) p;
|
|
|
|
buf = xmalloc(args * 45);
|
|
|
|
|
|
|
|
dup2(lp_pipe[1], 1);
|
|
|
|
close(0);
|
|
|
|
close(lp_pipe[0]);
|
|
|
|
close(lp_pipe[1]);
|
|
|
|
*p++ = "rev-list";
|
|
|
|
*p++ = use_thin_pack ? "--objects-edge" : "--objects";
|
|
|
|
if (create_full_pack || MAX_NEEDS <= nr_needs)
|
|
|
|
*p++ = "--all";
|
|
|
|
else {
|
|
|
|
for (i = 0; i < nr_needs; i++) {
|
|
|
|
*p++ = buf;
|
|
|
|
memcpy(buf, sha1_to_hex(needs_sha1[i]), 41);
|
|
|
|
buf += 41;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!create_full_pack)
|
|
|
|
for (i = 0; i < nr_has; i++) {
|
|
|
|
*p++ = buf;
|
|
|
|
*buf++ = '^';
|
|
|
|
memcpy(buf, sha1_to_hex(has_sha1[i]), 41);
|
|
|
|
buf += 41;
|
|
|
|
}
|
|
|
|
*p++ = NULL;
|
|
|
|
execv_git_cmd(argv);
|
|
|
|
die("git-upload-pack: unable to exec git-rev-list");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pipe(pu_pipe) < 0)
|
|
|
|
die("git-upload-pack: unable to create pipe");
|
|
|
|
if (pipe(pe_pipe) < 0)
|
|
|
|
die("git-upload-pack: unable to create pipe");
|
|
|
|
pid_pack_objects = fork();
|
|
|
|
if (pid_pack_objects < 0) {
|
|
|
|
/* daemon sets things up to ignore TERM */
|
|
|
|
kill(pid_rev_list, SIGKILL);
|
|
|
|
die("git-upload-pack: unable to fork git-pack-objects");
|
|
|
|
}
|
|
|
|
if (!pid_pack_objects) {
|
|
|
|
dup2(lp_pipe[0], 0);
|
|
|
|
dup2(pu_pipe[1], 1);
|
|
|
|
dup2(pe_pipe[1], 2);
|
|
|
|
|
|
|
|
close(lp_pipe[0]);
|
|
|
|
close(lp_pipe[1]);
|
|
|
|
close(pu_pipe[0]);
|
|
|
|
close(pu_pipe[1]);
|
|
|
|
close(pe_pipe[0]);
|
|
|
|
close(pe_pipe[1]);
|
|
|
|
execl_git_cmd("pack-objects", "--stdout", "--progress", NULL);
|
|
|
|
kill(pid_rev_list, SIGKILL);
|
|
|
|
die("git-upload-pack: unable to exec git-pack-objects");
|
|
|
|
}
|
|
|
|
|
|
|
|
close(lp_pipe[0]);
|
|
|
|
close(lp_pipe[1]);
|
|
|
|
|
|
|
|
/* We read from pe_pipe[0] to capture stderr output for
|
|
|
|
* progress bar, and pu_pipe[0] to capture the pack data.
|
|
|
|
*/
|
|
|
|
close(pe_pipe[1]);
|
|
|
|
close(pu_pipe[1]);
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
const char *who;
|
|
|
|
char *cp;
|
|
|
|
struct pollfd pfd[2];
|
|
|
|
pid_t pid;
|
|
|
|
int status;
|
|
|
|
ssize_t sz;
|
|
|
|
int pe, pu, pollsize;
|
|
|
|
|
|
|
|
pollsize = 0;
|
|
|
|
pe = pu = -1;
|
|
|
|
|
|
|
|
if (0 <= pu_pipe[0]) {
|
|
|
|
pfd[pollsize].fd = pu_pipe[0];
|
|
|
|
pfd[pollsize].events = POLLIN;
|
|
|
|
pu = pollsize;
|
|
|
|
pollsize++;
|
|
|
|
}
|
|
|
|
if (0 <= pe_pipe[0]) {
|
|
|
|
pfd[pollsize].fd = pe_pipe[0];
|
|
|
|
pfd[pollsize].events = POLLIN;
|
|
|
|
pe = pollsize;
|
|
|
|
pollsize++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pollsize) {
|
|
|
|
if (poll(pfd, pollsize, -1) < 0) {
|
|
|
|
if (errno != EINTR) {
|
|
|
|
error("poll failed, resuming: %s",
|
|
|
|
strerror(errno));
|
|
|
|
sleep(1);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
|
|
|
|
/* Data ready; we keep the last byte
|
|
|
|
* to ourselves in case we detect
|
|
|
|
* broken rev-list, so that we can
|
|
|
|
* leave the stream corrupted. This
|
|
|
|
* is unfortunate -- unpack-objects
|
|
|
|
* would happily accept a valid pack
|
|
|
|
* data with trailing garbage, so
|
|
|
|
* appending garbage after we pass all
|
|
|
|
* the pack data is not good enough to
|
|
|
|
* signal breakage to downstream.
|
|
|
|
*/
|
|
|
|
char *cp = data;
|
|
|
|
ssize_t outsz = 0;
|
|
|
|
if (0 <= buffered) {
|
|
|
|
*cp++ = buffered;
|
|
|
|
outsz++;
|
|
|
|
}
|
|
|
|
sz = read(pu_pipe[0], cp,
|
|
|
|
sizeof(data) - outsz);
|
|
|
|
if (0 < sz)
|
|
|
|
;
|
|
|
|
else if (sz == 0) {
|
|
|
|
close(pu_pipe[0]);
|
|
|
|
pu_pipe[0] = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
goto fail;
|
|
|
|
sz += outsz;
|
|
|
|
if (1 < sz) {
|
|
|
|
buffered = data[sz-1] & 0xFF;
|
|
|
|
sz--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
buffered = -1;
|
|
|
|
sz = xwrite(1, data, sz);
|
|
|
|
if (sz < 0)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
|
|
|
|
/* Status ready; we do not use it for now,
|
|
|
|
* but later we will add side-band to send it
|
|
|
|
* to the other side.
|
|
|
|
*/
|
|
|
|
sz = read(pe_pipe[0], progress,
|
|
|
|
sizeof(progress));
|
|
|
|
if (0 < sz)
|
|
|
|
write(2, progress, sz);
|
|
|
|
else if (sz == 0) {
|
|
|
|
close(pe_pipe[0]);
|
|
|
|
pe_pipe[0] = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* See if the children are still there */
|
|
|
|
if (pid_rev_list || pid_pack_objects) {
|
|
|
|
pid = waitpid(-1, &status, WNOHANG);
|
|
|
|
if (!pid)
|
|
|
|
continue;
|
|
|
|
who = ((pid == pid_rev_list) ? "git-rev-list" :
|
|
|
|
(pid == pid_pack_objects) ? "git-pack-objects" :
|
|
|
|
NULL);
|
|
|
|
if (!who) {
|
|
|
|
if (pid < 0) {
|
|
|
|
error("git-upload-pack: %s",
|
|
|
|
strerror(errno));
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
error("git-upload-pack: we weren't "
|
|
|
|
"waiting for %d", pid);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) {
|
|
|
|
error("git-upload-pack: %s died with error.",
|
|
|
|
who);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (pid == pid_rev_list)
|
|
|
|
pid_rev_list = 0;
|
|
|
|
if (pid == pid_pack_objects)
|
|
|
|
pid_pack_objects = 0;
|
|
|
|
if (pid_rev_list || pid_pack_objects)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* both died happily */
|
|
|
|
if (pollsize)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* flush the data */
|
|
|
|
if (0 <= buffered) {
|
|
|
|
data[0] = buffered;
|
|
|
|
sz = xwrite(1, data, 1);
|
|
|
|
if (sz < 0)
|
|
|
|
goto fail;
|
|
|
|
fprintf(stderr, "flushed.\n");
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fail:
|
|
|
|
if (pid_pack_objects)
|
|
|
|
kill(pid_pack_objects, SIGKILL);
|
|
|
|
if (pid_rev_list)
|
|
|
|
kill(pid_rev_list, SIGKILL);
|
|
|
|
die("git-upload-pack: aborting due to possible repository corruption on the remote side.");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int got_sha1(char *hex, unsigned char *sha1)
|
|
|
|
{
|
|
|
|
if (get_sha1_hex(hex, sha1))
|
|
|
|
die("git-upload-pack: expected SHA1 object, got '%s'", hex);
|
|
|
|
if (!has_sha1_file(sha1))
|
|
|
|
return 0;
|
|
|
|
if (nr_has < MAX_HAS) {
|
|
|
|
struct object *o = lookup_object(sha1);
|
|
|
|
if (!(o && o->parsed))
|
|
|
|
o = parse_object(sha1);
|
|
|
|
if (!o)
|
|
|
|
die("oops (%s)", sha1_to_hex(sha1));
|
Shrink "struct object" a bit
This shrinks "struct object" by a small amount, by getting rid of the
"struct type *" pointer and replacing it with a 3-bit bitfield instead.
In addition, we merge the bitfields and the "flags" field, which
incidentally should also remove a useless 4-byte padding from the object
when in 64-bit mode.
Now, our "struct object" is still too damn large, but it's now less
obviously bloated, and of the remaining fields, only the "util" (which is
not used by most things) is clearly something that should be eventually
discarded.
This shrinks the "git-rev-list --all" memory use by about 2.5% on the
kernel archive (and, perhaps more importantly, on the larger mozilla
archive). That may not sound like much, but I suspect it's more on a
64-bit platform.
There are other remaining inefficiencies (the parent lists, for example,
probably have horrible malloc overhead), but this was pretty obvious.
Most of the patch is just changing the comparison of the "type" pointer
from one of the constant string pointers to the appropriate new TYPE_xxx
small integer constant.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
19 years ago
|
|
|
if (o->type == TYPE_COMMIT) {
|
|
|
|
struct commit_list *parents;
|
|
|
|
if (o->flags & THEY_HAVE)
|
|
|
|
return 0;
|
|
|
|
o->flags |= THEY_HAVE;
|
|
|
|
for (parents = ((struct commit*)o)->parents;
|
|
|
|
parents;
|
|
|
|
parents = parents->next)
|
|
|
|
parents->item->object.flags |= THEY_HAVE;
|
|
|
|
}
|
|
|
|
memcpy(has_sha1[nr_has++], sha1, 20);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_common_commits(void)
|
|
|
|
{
|
|
|
|
static char line[1000];
|
|
|
|
unsigned char sha1[20], last_sha1[20];
|
|
|
|
int len;
|
|
|
|
|
|
|
|
track_object_refs = 0;
|
|
|
|
save_commit_buffer = 0;
|
|
|
|
|
|
|
|
for(;;) {
|
|
|
|
len = packet_read_line(0, line, sizeof(line));
|
|
|
|
reset_timeout();
|
|
|
|
|
|
|
|
if (!len) {
|
|
|
|
if (nr_has == 0 || multi_ack)
|
|
|
|
packet_write(1, "NAK\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
len = strip(line, len);
|
|
|
|
if (!strncmp(line, "have ", 5)) {
|
|
|
|
if (got_sha1(line+5, sha1) &&
|
|
|
|
(multi_ack || nr_has == 1)) {
|
|
|
|
if (nr_has >= MAX_HAS)
|
|
|
|
multi_ack = 0;
|
|
|
|
packet_write(1, "ACK %s%s\n",
|
|
|
|
sha1_to_hex(sha1),
|
|
|
|
multi_ack ? " continue" : "");
|
|
|
|
if (multi_ack)
|
|
|
|
memcpy(last_sha1, sha1, 20);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(line, "done")) {
|
|
|
|
if (nr_has > 0) {
|
|
|
|
if (multi_ack)
|
|
|
|
packet_write(1, "ACK %s\n",
|
|
|
|
sha1_to_hex(last_sha1));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
packet_write(1, "NAK\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
die("git-upload-pack: expected SHA1 list, got '%s'", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int receive_needs(void)
|
|
|
|
{
|
|
|
|
static char line[1000];
|
|
|
|
int len, needs;
|
|
|
|
|
|
|
|
needs = 0;
|
|
|
|
for (;;) {
|
|
|
|
struct object *o;
|
|
|
|
unsigned char dummy[20], *sha1_buf;
|
|
|
|
len = packet_read_line(0, line, sizeof(line));
|
|
|
|
reset_timeout();
|
|
|
|
if (!len)
|
|
|
|
return needs;
|
|
|
|
|
|
|
|
sha1_buf = dummy;
|
|
|
|
if (needs == MAX_NEEDS) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"warning: supporting only a max of %d requests. "
|
|
|
|
"sending everything instead.\n",
|
|
|
|
MAX_NEEDS);
|
|
|
|
}
|
|
|
|
else if (needs < MAX_NEEDS)
|
|
|
|
sha1_buf = needs_sha1[needs];
|
|
|
|
|
|
|
|
if (strncmp("want ", line, 5) || get_sha1_hex(line+5, sha1_buf))
|
|
|
|
die("git-upload-pack: protocol error, "
|
|
|
|
"expected to get sha, not '%s'", line);
|
|
|
|
if (strstr(line+45, "multi_ack"))
|
|
|
|
multi_ack = 1;
|
|
|
|
if (strstr(line+45, "thin-pack"))
|
|
|
|
use_thin_pack = 1;
|
|
|
|
|
|
|
|
/* We have sent all our refs already, and the other end
|
|
|
|
* should have chosen out of them; otherwise they are
|
|
|
|
* asking for nonsense.
|
|
|
|
*
|
|
|
|
* Hmph. We may later want to allow "want" line that
|
|
|
|
* asks for something like "master~10" (symbolic)...
|
|
|
|
* would it make sense? I don't know.
|
|
|
|
*/
|
|
|
|
o = lookup_object(sha1_buf);
|
|
|
|
if (!o || !(o->flags & OUR_REF))
|
|
|
|
die("git-upload-pack: not our ref %s", line+5);
|
|
|
|
if (!(o->flags & WANTED)) {
|
|
|
|
o->flags |= WANTED;
|
|
|
|
needs++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int send_ref(const char *refname, const unsigned char *sha1)
|
|
|
|
{
|
|
|
|
static char *capabilities = "multi_ack thin-pack";
|
|
|
|
struct object *o = parse_object(sha1);
|
|
|
|
|
|
|
|
if (!o)
|
|
|
|
die("git-upload-pack: cannot find object %s:", sha1_to_hex(sha1));
|
|
|
|
|
|
|
|
if (capabilities)
|
|
|
|
packet_write(1, "%s %s%c%s\n", sha1_to_hex(sha1), refname,
|
|
|
|
0, capabilities);
|
|
|
|
else
|
|
|
|
packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname);
|
|
|
|
capabilities = NULL;
|
|
|
|
if (!(o->flags & OUR_REF)) {
|
|
|
|
o->flags |= OUR_REF;
|
|
|
|
nr_our_refs++;
|
|
|
|
}
|
Shrink "struct object" a bit
This shrinks "struct object" by a small amount, by getting rid of the
"struct type *" pointer and replacing it with a 3-bit bitfield instead.
In addition, we merge the bitfields and the "flags" field, which
incidentally should also remove a useless 4-byte padding from the object
when in 64-bit mode.
Now, our "struct object" is still too damn large, but it's now less
obviously bloated, and of the remaining fields, only the "util" (which is
not used by most things) is clearly something that should be eventually
discarded.
This shrinks the "git-rev-list --all" memory use by about 2.5% on the
kernel archive (and, perhaps more importantly, on the larger mozilla
archive). That may not sound like much, but I suspect it's more on a
64-bit platform.
There are other remaining inefficiencies (the parent lists, for example,
probably have horrible malloc overhead), but this was pretty obvious.
Most of the patch is just changing the comparison of the "type" pointer
from one of the constant string pointers to the appropriate new TYPE_xxx
small integer constant.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
19 years ago
|
|
|
if (o->type == TYPE_TAG) {
|
|
|
|
o = deref_tag(o, refname, 0);
|
|
|
|
packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int upload_pack(void)
|
|
|
|
{
|
|
|
|
reset_timeout();
|
|
|
|
head_ref(send_ref);
|
|
|
|
for_each_ref(send_ref);
|
|
|
|
packet_flush(1);
|
|
|
|
nr_needs = receive_needs();
|
|
|
|
if (!nr_needs)
|
|
|
|
return 0;
|
|
|
|
get_common_commits();
|
|
|
|
create_pack_file();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
char *dir;
|
|
|
|
int i;
|
|
|
|
int strict = 0;
|
|
|
|
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
char *arg = argv[i];
|
|
|
|
|
|
|
|
if (arg[0] != '-')
|
|
|
|
break;
|
|
|
|
if (!strcmp(arg, "--strict")) {
|
|
|
|
strict = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strncmp(arg, "--timeout=", 10)) {
|
|
|
|
timeout = atoi(arg+10);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--")) {
|
|
|
|
i++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i != argc-1)
|
|
|
|
usage(upload_pack_usage);
|
|
|
|
dir = argv[i];
|
|
|
|
|
|
|
|
if (!enter_repo(dir, strict))
|
|
|
|
die("'%s': unable to chdir or not a git archive", dir);
|
|
|
|
|
|
|
|
upload_pack();
|
|
|
|
return 0;
|
|
|
|
}
|