|
|
|
#include "cache.h"
|
|
|
|
#include "refs.h"
|
|
|
|
#include "pkt-line.h"
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
|
|
|
static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
|
|
|
|
|
|
|
|
static const char *unpacker = "git-unpack-objects";
|
|
|
|
|
|
|
|
static int show_ref(const char *path, const unsigned char *sha1)
|
|
|
|
{
|
|
|
|
packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void write_head_info(void)
|
|
|
|
{
|
|
|
|
for_each_ref(show_ref);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct command {
|
|
|
|
struct command *next;
|
|
|
|
unsigned char old_sha1[20];
|
|
|
|
unsigned char new_sha1[20];
|
|
|
|
char ref_name[0];
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct command *commands = NULL;
|
|
|
|
|
|
|
|
static int is_all_zeroes(const char *hex)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < 40; i++)
|
|
|
|
if (*hex++ != '0')
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int verify_old_ref(const char *name, char *hex_contents)
|
|
|
|
{
|
|
|
|
int fd, ret;
|
|
|
|
char buffer[60];
|
|
|
|
|
|
|
|
if (is_all_zeroes(hex_contents))
|
|
|
|
return 0;
|
|
|
|
fd = open(name, O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
ret = read(fd, buffer, 40);
|
|
|
|
close(fd);
|
|
|
|
if (ret != 40)
|
|
|
|
return -1;
|
|
|
|
if (memcmp(buffer, hex_contents, 40))
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void update(const char *name, unsigned char *old_sha1, unsigned char *new_sha1)
|
|
|
|
{
|
|
|
|
char new_hex[60], *old_hex, *lock_name;
|
|
|
|
int newfd, namelen, written;
|
|
|
|
|
|
|
|
namelen = strlen(name);
|
|
|
|
lock_name = xmalloc(namelen + 10);
|
|
|
|
memcpy(lock_name, name, namelen);
|
|
|
|
memcpy(lock_name + namelen, ".lock", 6);
|
|
|
|
|
|
|
|
strcpy(new_hex, sha1_to_hex(new_sha1));
|
|
|
|
old_hex = sha1_to_hex(old_sha1);
|
|
|
|
if (!has_sha1_file(new_sha1))
|
|
|
|
die("unpack should have generated %s, but I can't find it!", new_hex);
|
|
|
|
|
|
|
|
newfd = open(lock_name, O_CREAT | O_EXCL | O_WRONLY, 0666);
|
|
|
|
if (newfd < 0)
|
|
|
|
die("unable to create %s (%s)", lock_name, strerror(errno));
|
|
|
|
|
|
|
|
/* Write the ref with an ending '\n' */
|
|
|
|
new_hex[40] = '\n';
|
|
|
|
new_hex[41] = 0;
|
|
|
|
written = write(newfd, new_hex, 41);
|
|
|
|
/* Remove the '\n' again */
|
|
|
|
new_hex[40] = 0;
|
|
|
|
|
|
|
|
close(newfd);
|
|
|
|
if (written != 41) {
|
|
|
|
unlink(lock_name);
|
|
|
|
die("unable to write %s", lock_name);
|
|
|
|
}
|
|
|
|
if (verify_old_ref(name, old_hex) < 0) {
|
|
|
|
unlink(lock_name);
|
|
|
|
die("%s changed during push", name);
|
|
|
|
}
|
|
|
|
if (rename(lock_name, name) < 0) {
|
|
|
|
unlink(lock_name);
|
|
|
|
die("unable to replace %s", name);
|
|
|
|
}
|
|
|
|
fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This gets called after(if) we've successfully
|
|
|
|
* unpacked the data payload.
|
|
|
|
*/
|
|
|
|
static void execute_commands(void)
|
|
|
|
{
|
|
|
|
struct command *cmd = commands;
|
|
|
|
|
|
|
|
while (cmd) {
|
|
|
|
update(cmd->ref_name, cmd->old_sha1, cmd->new_sha1);
|
|
|
|
cmd = cmd->next;
|
|
|
|
}
|
[PATCH] Add update-server-info.
The git-update-server-info command prepares informational files
to help clients discover the contents of a repository, and pull
from it via a dumb transport protocols. Currently, the
following files are produced.
- The $repo/info/refs file lists the name of heads and tags
available in the $repo/refs/ directory, along with their
SHA1. This can be used by git-ls-remote command running on
the client side.
- The $repo/info/rev-cache file describes the commit ancestry
reachable from references in the $repo/refs/ directory. This
file is in an append-only binary format to make the server
side friendly to rsync mirroring scheme, and can be read by
git-show-rev-cache command.
- The $repo/objects/info/pack file lists the name of the packs
available, the interdependencies among them, and the head
commits and tags contained in them. Along with the other two
files, this is designed to help clients to make smart pull
decisions.
The git-receive-pack command is changed to invoke it at the end,
so just after a push to a public repository finishes via "git
push", the server info is automatically updated.
In addition, building of the rev-cache file can be done by a
standalone git-build-rev-cache command separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
20 years ago
|
|
|
update_server_info(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void read_head_info(void)
|
|
|
|
{
|
|
|
|
struct command **p = &commands;
|
|
|
|
for (;;) {
|
|
|
|
static char line[1000];
|
|
|
|
unsigned char old_sha1[20], new_sha1[20];
|
|
|
|
struct command *cmd;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = packet_read_line(0, line, sizeof(line));
|
|
|
|
if (!len)
|
|
|
|
break;
|
|
|
|
if (line[len-1] == '\n')
|
|
|
|
line[--len] = 0;
|
|
|
|
if (len < 83 ||
|
|
|
|
line[40] != ' ' ||
|
|
|
|
line[81] != ' ' ||
|
|
|
|
get_sha1_hex(line, old_sha1) ||
|
|
|
|
get_sha1_hex(line + 41, new_sha1))
|
|
|
|
die("protocol error: expected old/new/ref, got '%s'", line);
|
|
|
|
cmd = xmalloc(sizeof(struct command) + len - 80);
|
|
|
|
memcpy(cmd->old_sha1, old_sha1, 20);
|
|
|
|
memcpy(cmd->new_sha1, new_sha1, 20);
|
|
|
|
memcpy(cmd->ref_name, line + 82, len - 81);
|
|
|
|
cmd->next = NULL;
|
|
|
|
*p = cmd;
|
|
|
|
p = &cmd->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void unpack(void)
|
|
|
|
{
|
|
|
|
pid_t pid = fork();
|
|
|
|
|
|
|
|
if (pid < 0)
|
|
|
|
die("unpack fork failed");
|
|
|
|
if (!pid) {
|
|
|
|
execlp(unpacker, unpacker, NULL);
|
|
|
|
die("unpack execute failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
int status, code;
|
|
|
|
int retval = waitpid(pid, &status, 0);
|
|
|
|
|
|
|
|
if (retval < 0) {
|
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
|
|
|
die("waitpid failed (%s)", strerror(retval));
|
|
|
|
}
|
|
|
|
if (retval != pid)
|
|
|
|
die("waitpid is confused");
|
|
|
|
if (WIFSIGNALED(status))
|
|
|
|
die("%s died of signal %d", unpacker, WTERMSIG(status));
|
|
|
|
if (!WIFEXITED(status))
|
|
|
|
die("%s died out of really strange complications", unpacker);
|
|
|
|
code = WEXITSTATUS(status);
|
|
|
|
if (code)
|
|
|
|
die("%s exited with error code %d", unpacker, code);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
const char *dir = NULL;
|
|
|
|
|
|
|
|
argv++;
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
const char *arg = *argv++;
|
|
|
|
|
|
|
|
if (*arg == '-') {
|
|
|
|
/* Do flag handling here */
|
|
|
|
usage(receive_pack_usage);
|
|
|
|
}
|
|
|
|
if (dir)
|
|
|
|
usage(receive_pack_usage);
|
|
|
|
dir = arg;
|
|
|
|
}
|
|
|
|
if (!dir)
|
|
|
|
usage(receive_pack_usage);
|
|
|
|
|
|
|
|
/* chdir to the directory. If that fails, try appending ".git" */
|
|
|
|
if (chdir(dir) < 0) {
|
|
|
|
if (chdir(mkpath("%s.git", dir)) < 0)
|
|
|
|
die("unable to cd to %s", dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we have a ".git" directory, chdir to it */
|
|
|
|
chdir(".git");
|
|
|
|
setenv("GIT_DIR", ".", 1);
|
|
|
|
|
|
|
|
if (access("objects", X_OK) < 0 || access("refs/heads", X_OK) < 0)
|
|
|
|
die("%s doesn't appear to be a git directory", dir);
|
|
|
|
write_head_info();
|
|
|
|
|
|
|
|
/* EOF */
|
|
|
|
packet_flush(1);
|
|
|
|
|
|
|
|
read_head_info();
|
|
|
|
if (commands) {
|
|
|
|
unpack();
|
|
|
|
execute_commands();
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|