Merge branch 'jc/archive'
* jc/archive: git-tar-tree: devolve git-tar-tree into a wrapper for git-archive git-archive: inline default_parse_extra() builtin-archive.c: rename remote_request() to extract_remote_arg() upload-archive: monitor child communication more carefully. Add sideband status report to git-archive protocol Prepare larger packet buffer for upload-pack protocol. Teach --exec to git-archive --remote Add --verbose to git-archive archive: force line buffered output to stderr Use xstrdup instead of strdup in builtin-{tar,zip}-tree.c Move sideband server side support into reusable form. Move sideband client side support into reusable form. archive: allow remote to have more formats than we understand. git-archive: make compression level of ZIP archives configurable Add git-upload-archive git-archive: wire up ZIP format. git-archive: wire up TAR format. Add git-archive
This commit is contained in:
commit
4d69065d3a
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,6 +8,7 @@ git-apply
|
|||||||
git-applymbox
|
git-applymbox
|
||||||
git-applypatch
|
git-applypatch
|
||||||
git-archimport
|
git-archimport
|
||||||
|
git-archive
|
||||||
git-bisect
|
git-bisect
|
||||||
git-branch
|
git-branch
|
||||||
git-cat-file
|
git-cat-file
|
||||||
@ -118,6 +119,7 @@ git-unpack-objects
|
|||||||
git-update-index
|
git-update-index
|
||||||
git-update-ref
|
git-update-ref
|
||||||
git-update-server-info
|
git-update-server-info
|
||||||
|
git-upload-archive
|
||||||
git-upload-pack
|
git-upload-pack
|
||||||
git-upload-tar
|
git-upload-tar
|
||||||
git-var
|
git-var
|
||||||
|
100
Documentation/git-archive.txt
Normal file
100
Documentation/git-archive.txt
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
git-archive(1)
|
||||||
|
==============
|
||||||
|
|
||||||
|
NAME
|
||||||
|
----
|
||||||
|
git-archive - Creates a archive of the files in the named tree
|
||||||
|
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
--------
|
||||||
|
'git-archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]
|
||||||
|
[--remote=<repo>] <tree-ish> [path...]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
-----------
|
||||||
|
Creates an archive of the specified format containing the tree
|
||||||
|
structure for the named tree. If <prefix> is specified it is
|
||||||
|
prepended to the filenames in the archive.
|
||||||
|
|
||||||
|
'git-archive' behaves differently when given a tree ID versus when
|
||||||
|
given a commit ID or tag ID. In the first case the current time is
|
||||||
|
used as modification time of each file in the archive. In the latter
|
||||||
|
case the commit time as recorded in the referenced commit object is
|
||||||
|
used instead. Additionally the commit ID is stored in a global
|
||||||
|
extended pax header if the tar format is used; it can be extracted
|
||||||
|
using 'git-get-tar-commit-id'. In ZIP files it is stored as a file
|
||||||
|
comment.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-------
|
||||||
|
|
||||||
|
--format=<fmt>::
|
||||||
|
Format of the resulting archive: 'tar', 'zip'...
|
||||||
|
|
||||||
|
--list::
|
||||||
|
Show all available formats.
|
||||||
|
|
||||||
|
--prefix=<prefix>/::
|
||||||
|
Prepend <prefix>/ to each filename in the archive.
|
||||||
|
|
||||||
|
<extra>::
|
||||||
|
This can be any options that the archiver backend understand.
|
||||||
|
|
||||||
|
--remote=<repo>::
|
||||||
|
Instead of making a tar archive from local repository,
|
||||||
|
retrieve a tar archive from a remote repository.
|
||||||
|
|
||||||
|
<tree-ish>::
|
||||||
|
The tree or commit to produce an archive for.
|
||||||
|
|
||||||
|
path::
|
||||||
|
If one or more paths are specified, include only these in the
|
||||||
|
archive, otherwise include all files and subdirectories.
|
||||||
|
|
||||||
|
CONFIGURATION
|
||||||
|
-------------
|
||||||
|
By default, file and directories modes are set to 0666 or 0777 in tar
|
||||||
|
archives. It is possible to change this by setting the "umask" variable
|
||||||
|
in the repository configuration as follows :
|
||||||
|
|
||||||
|
[tar]
|
||||||
|
umask = 002 ;# group friendly
|
||||||
|
|
||||||
|
The special umask value "user" indicates that the user's current umask
|
||||||
|
will be used instead. The default value remains 0, which means world
|
||||||
|
readable/writable files and directories.
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
--------
|
||||||
|
git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -)::
|
||||||
|
|
||||||
|
Create a tar archive that contains the contents of the
|
||||||
|
latest commit on the current branch, and extracts it in
|
||||||
|
`/var/tmp/junk` directory.
|
||||||
|
|
||||||
|
git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz::
|
||||||
|
|
||||||
|
Create a compressed tarball for v1.4.0 release.
|
||||||
|
|
||||||
|
git archive --format=tar --prefix=git-1.4.0/ v1.4.0{caret}\{tree\} | gzip >git-1.4.0.tar.gz::
|
||||||
|
|
||||||
|
Create a compressed tarball for v1.4.0 release, but without a
|
||||||
|
global extended pax header.
|
||||||
|
|
||||||
|
git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs.zip::
|
||||||
|
|
||||||
|
Put everything in the current head's Documentation/ directory
|
||||||
|
into 'git-1.4.0-docs.zip', with the prefix 'git-docs/'.
|
||||||
|
|
||||||
|
Author
|
||||||
|
------
|
||||||
|
Written by Franck Bui-Huu and Rene Scharfe.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
--------------
|
||||||
|
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
|
||||||
|
|
||||||
|
GIT
|
||||||
|
---
|
||||||
|
Part of the gitlink:git[7] suite
|
37
Documentation/git-upload-archive.txt
Normal file
37
Documentation/git-upload-archive.txt
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
git-upload-archive(1)
|
||||||
|
====================
|
||||||
|
|
||||||
|
NAME
|
||||||
|
----
|
||||||
|
git-upload-archive - Send archive
|
||||||
|
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
--------
|
||||||
|
'git-upload-archive' <directory>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
-----------
|
||||||
|
Invoked by 'git-archive --remote' and sends a generated archive to the
|
||||||
|
other end over the git protocol.
|
||||||
|
|
||||||
|
This command is usually not invoked directly by the end user. The UI
|
||||||
|
for the protocol is on the 'git-archive' side, and the program pair
|
||||||
|
is meant to be used to get an archive from a remote repository.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-------
|
||||||
|
<directory>::
|
||||||
|
The repository to get a tar archive from.
|
||||||
|
|
||||||
|
Author
|
||||||
|
------
|
||||||
|
Written by Franck Bui-Huu.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
--------------
|
||||||
|
Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
|
||||||
|
|
||||||
|
GIT
|
||||||
|
---
|
||||||
|
Part of the gitlink:git[7] suite
|
8
Makefile
8
Makefile
@ -234,8 +234,8 @@ LIB_FILE=libgit.a
|
|||||||
XDIFF_LIB=xdiff/lib.a
|
XDIFF_LIB=xdiff/lib.a
|
||||||
|
|
||||||
LIB_H = \
|
LIB_H = \
|
||||||
blob.h cache.h commit.h csum-file.h delta.h \
|
archive.h blob.h cache.h commit.h csum-file.h delta.h \
|
||||||
diff.h object.h pack.h pkt-line.h quote.h refs.h \
|
diff.h object.h pack.h pkt-line.h quote.h refs.h sideband.h \
|
||||||
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
|
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
|
||||||
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h
|
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h
|
||||||
|
|
||||||
@ -247,7 +247,7 @@ DIFF_OBJS = \
|
|||||||
LIB_OBJS = \
|
LIB_OBJS = \
|
||||||
blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
|
blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
|
||||||
date.o diff-delta.o entry.o exec_cmd.o ident.o lockfile.o \
|
date.o diff-delta.o entry.o exec_cmd.o ident.o lockfile.o \
|
||||||
object.o pack-check.o patch-delta.o path.o pkt-line.o \
|
object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \
|
||||||
quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
|
quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
|
||||||
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
|
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
|
||||||
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
|
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
|
||||||
@ -258,6 +258,7 @@ LIB_OBJS = \
|
|||||||
BUILTIN_OBJS = \
|
BUILTIN_OBJS = \
|
||||||
builtin-add.o \
|
builtin-add.o \
|
||||||
builtin-apply.o \
|
builtin-apply.o \
|
||||||
|
builtin-archive.o \
|
||||||
builtin-cat-file.o \
|
builtin-cat-file.o \
|
||||||
builtin-checkout-index.o \
|
builtin-checkout-index.o \
|
||||||
builtin-check-ref-format.o \
|
builtin-check-ref-format.o \
|
||||||
@ -294,6 +295,7 @@ BUILTIN_OBJS = \
|
|||||||
builtin-unpack-objects.o \
|
builtin-unpack-objects.o \
|
||||||
builtin-update-index.o \
|
builtin-update-index.o \
|
||||||
builtin-update-ref.o \
|
builtin-update-ref.o \
|
||||||
|
builtin-upload-archive.o \
|
||||||
builtin-upload-tar.o \
|
builtin-upload-tar.o \
|
||||||
builtin-verify-pack.o \
|
builtin-verify-pack.o \
|
||||||
builtin-write-tree.o \
|
builtin-write-tree.o \
|
||||||
|
47
archive.h
Normal file
47
archive.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#ifndef ARCHIVE_H
|
||||||
|
#define ARCHIVE_H
|
||||||
|
|
||||||
|
#define MAX_EXTRA_ARGS 32
|
||||||
|
#define MAX_ARGS (MAX_EXTRA_ARGS + 32)
|
||||||
|
|
||||||
|
struct archiver_args {
|
||||||
|
const char *base;
|
||||||
|
struct tree *tree;
|
||||||
|
const unsigned char *commit_sha1;
|
||||||
|
time_t time;
|
||||||
|
const char **pathspec;
|
||||||
|
unsigned int verbose : 1;
|
||||||
|
void *extra;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int (*write_archive_fn_t)(struct archiver_args *);
|
||||||
|
|
||||||
|
typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv);
|
||||||
|
|
||||||
|
struct archiver {
|
||||||
|
const char *name;
|
||||||
|
struct archiver_args args;
|
||||||
|
write_archive_fn_t write_archive;
|
||||||
|
parse_extra_args_fn_t parse_extra;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct archiver archivers[];
|
||||||
|
|
||||||
|
extern int parse_archive_args(int argc,
|
||||||
|
const char **argv,
|
||||||
|
struct archiver *ar);
|
||||||
|
|
||||||
|
extern void parse_treeish_arg(const char **treeish,
|
||||||
|
struct archiver_args *ar_args,
|
||||||
|
const char *prefix);
|
||||||
|
|
||||||
|
extern void parse_pathspec_arg(const char **pathspec,
|
||||||
|
struct archiver_args *args);
|
||||||
|
/*
|
||||||
|
* Archive-format specific backends.
|
||||||
|
*/
|
||||||
|
extern int write_tar_archive(struct archiver_args *);
|
||||||
|
extern int write_zip_archive(struct archiver_args *);
|
||||||
|
extern void *parse_extra_zip_args(int argc, const char **argv);
|
||||||
|
|
||||||
|
#endif /* ARCHIVE_H */
|
263
builtin-archive.c
Normal file
263
builtin-archive.c
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2006 Franck Bui-Huu
|
||||||
|
* Copyright (c) 2006 Rene Scharfe
|
||||||
|
*/
|
||||||
|
#include <time.h>
|
||||||
|
#include "cache.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
#include "archive.h"
|
||||||
|
#include "commit.h"
|
||||||
|
#include "tree-walk.h"
|
||||||
|
#include "exec_cmd.h"
|
||||||
|
#include "pkt-line.h"
|
||||||
|
#include "sideband.h"
|
||||||
|
|
||||||
|
static const char archive_usage[] = \
|
||||||
|
"git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]";
|
||||||
|
|
||||||
|
struct archiver archivers[] = {
|
||||||
|
{
|
||||||
|
.name = "tar",
|
||||||
|
.write_archive = write_tar_archive,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "zip",
|
||||||
|
.write_archive = write_zip_archive,
|
||||||
|
.parse_extra = parse_extra_zip_args,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int run_remote_archiver(const char *remote, int argc,
|
||||||
|
const char **argv)
|
||||||
|
{
|
||||||
|
char *url, buf[LARGE_PACKET_MAX];
|
||||||
|
int fd[2], i, len, rv;
|
||||||
|
pid_t pid;
|
||||||
|
const char *exec = "git-upload-archive";
|
||||||
|
int exec_at = 0;
|
||||||
|
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
const char *arg = argv[i];
|
||||||
|
if (!strncmp("--exec=", arg, 7)) {
|
||||||
|
if (exec_at)
|
||||||
|
die("multiple --exec specified");
|
||||||
|
exec = arg + 7;
|
||||||
|
exec_at = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
url = xstrdup(remote);
|
||||||
|
pid = git_connect(fd, url, exec);
|
||||||
|
if (pid < 0)
|
||||||
|
return pid;
|
||||||
|
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (i == exec_at)
|
||||||
|
continue;
|
||||||
|
packet_write(fd[1], "argument %s\n", argv[i]);
|
||||||
|
}
|
||||||
|
packet_flush(fd[1]);
|
||||||
|
|
||||||
|
len = packet_read_line(fd[0], buf, sizeof(buf));
|
||||||
|
if (!len)
|
||||||
|
die("git-archive: expected ACK/NAK, got EOF");
|
||||||
|
if (buf[len-1] == '\n')
|
||||||
|
buf[--len] = 0;
|
||||||
|
if (strcmp(buf, "ACK")) {
|
||||||
|
if (len > 5 && !strncmp(buf, "NACK ", 5))
|
||||||
|
die("git-archive: NACK %s", buf + 5);
|
||||||
|
die("git-archive: protocol error");
|
||||||
|
}
|
||||||
|
|
||||||
|
len = packet_read_line(fd[0], buf, sizeof(buf));
|
||||||
|
if (len)
|
||||||
|
die("git-archive: expected a flush");
|
||||||
|
|
||||||
|
/* Now, start reading from fd[0] and spit it out to stdout */
|
||||||
|
rv = recv_sideband("archive", fd[0], 1, 2, buf, sizeof(buf));
|
||||||
|
close(fd[0]);
|
||||||
|
rv |= finish_connect(pid);
|
||||||
|
|
||||||
|
return !!rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init_archiver(const char *name, struct archiver *ar)
|
||||||
|
{
|
||||||
|
int rv = -1, i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(archivers); i++) {
|
||||||
|
if (!strcmp(name, archivers[i].name)) {
|
||||||
|
memcpy(ar, &archivers[i], sizeof(struct archiver));
|
||||||
|
rv = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args)
|
||||||
|
{
|
||||||
|
ar_args->pathspec = get_pathspec(ar_args->base, pathspec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
|
||||||
|
const char *prefix)
|
||||||
|
{
|
||||||
|
const char *name = argv[0];
|
||||||
|
const unsigned char *commit_sha1;
|
||||||
|
time_t archive_time;
|
||||||
|
struct tree *tree;
|
||||||
|
struct commit *commit;
|
||||||
|
unsigned char sha1[20];
|
||||||
|
|
||||||
|
if (get_sha1(name, sha1))
|
||||||
|
die("Not a valid object name");
|
||||||
|
|
||||||
|
commit = lookup_commit_reference_gently(sha1, 1);
|
||||||
|
if (commit) {
|
||||||
|
commit_sha1 = commit->object.sha1;
|
||||||
|
archive_time = commit->date;
|
||||||
|
} else {
|
||||||
|
commit_sha1 = NULL;
|
||||||
|
archive_time = time(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
tree = parse_tree_indirect(sha1);
|
||||||
|
if (tree == NULL)
|
||||||
|
die("not a tree object");
|
||||||
|
|
||||||
|
if (prefix) {
|
||||||
|
unsigned char tree_sha1[20];
|
||||||
|
unsigned int mode;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = get_tree_entry(tree->object.sha1, prefix,
|
||||||
|
tree_sha1, &mode);
|
||||||
|
if (err || !S_ISDIR(mode))
|
||||||
|
die("current working directory is untracked");
|
||||||
|
|
||||||
|
free(tree);
|
||||||
|
tree = parse_tree_indirect(tree_sha1);
|
||||||
|
}
|
||||||
|
ar_args->tree = tree;
|
||||||
|
ar_args->commit_sha1 = commit_sha1;
|
||||||
|
ar_args->time = archive_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_archive_args(int argc, const char **argv, struct archiver *ar)
|
||||||
|
{
|
||||||
|
const char *extra_argv[MAX_EXTRA_ARGS];
|
||||||
|
int extra_argc = 0;
|
||||||
|
const char *format = NULL; /* might want to default to "tar" */
|
||||||
|
const char *base = "";
|
||||||
|
int verbose = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
const char *arg = argv[i];
|
||||||
|
|
||||||
|
if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) {
|
||||||
|
for (i = 0; i < ARRAY_SIZE(archivers); i++)
|
||||||
|
printf("%s\n", archivers[i].name);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) {
|
||||||
|
verbose = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(arg, "--format=", 9)) {
|
||||||
|
format = arg + 9;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(arg, "--prefix=", 9)) {
|
||||||
|
base = arg + 9;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "--")) {
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (arg[0] == '-') {
|
||||||
|
if (extra_argc > MAX_EXTRA_ARGS - 1)
|
||||||
|
die("Too many extra options");
|
||||||
|
extra_argv[extra_argc++] = arg;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need at least one parameter -- tree-ish */
|
||||||
|
if (argc - 1 < i)
|
||||||
|
usage(archive_usage);
|
||||||
|
if (!format)
|
||||||
|
die("You must specify an archive format");
|
||||||
|
if (init_archiver(format, ar) < 0)
|
||||||
|
die("Unknown archive format '%s'", format);
|
||||||
|
|
||||||
|
if (extra_argc) {
|
||||||
|
if (!ar->parse_extra)
|
||||||
|
die("'%s' format does not handle %s",
|
||||||
|
ar->name, extra_argv[0]);
|
||||||
|
ar->args.extra = ar->parse_extra(extra_argc, extra_argv);
|
||||||
|
}
|
||||||
|
ar->args.verbose = verbose;
|
||||||
|
ar->args.base = base;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *extract_remote_arg(int *ac, const char **av)
|
||||||
|
{
|
||||||
|
int ix, iy, cnt = *ac;
|
||||||
|
int no_more_options = 0;
|
||||||
|
const char *remote = NULL;
|
||||||
|
|
||||||
|
for (ix = iy = 1; ix < cnt; ix++) {
|
||||||
|
const char *arg = av[ix];
|
||||||
|
if (!strcmp(arg, "--"))
|
||||||
|
no_more_options = 1;
|
||||||
|
if (!no_more_options) {
|
||||||
|
if (!strncmp(arg, "--remote=", 9)) {
|
||||||
|
if (remote)
|
||||||
|
die("Multiple --remote specified");
|
||||||
|
remote = arg + 9;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg[0] != '-')
|
||||||
|
no_more_options = 1;
|
||||||
|
}
|
||||||
|
if (ix != iy)
|
||||||
|
av[iy] = arg;
|
||||||
|
iy++;
|
||||||
|
}
|
||||||
|
if (remote) {
|
||||||
|
av[--cnt] = NULL;
|
||||||
|
*ac = cnt;
|
||||||
|
}
|
||||||
|
return remote;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmd_archive(int argc, const char **argv, const char *prefix)
|
||||||
|
{
|
||||||
|
struct archiver ar;
|
||||||
|
int tree_idx;
|
||||||
|
const char *remote = NULL;
|
||||||
|
|
||||||
|
remote = extract_remote_arg(&argc, argv);
|
||||||
|
if (remote)
|
||||||
|
return run_remote_archiver(remote, argc, argv);
|
||||||
|
|
||||||
|
setlinebuf(stderr);
|
||||||
|
|
||||||
|
memset(&ar, 0, sizeof(ar));
|
||||||
|
tree_idx = parse_archive_args(argc, argv, &ar);
|
||||||
|
if (prefix == NULL)
|
||||||
|
prefix = setup_git_directory();
|
||||||
|
|
||||||
|
argv += tree_idx;
|
||||||
|
parse_treeish_arg(argv, &ar.args, prefix);
|
||||||
|
parse_pathspec_arg(argv + 1, &ar.args);
|
||||||
|
|
||||||
|
return ar.write_archive(&ar.args);
|
||||||
|
}
|
@ -3,12 +3,12 @@
|
|||||||
*/
|
*/
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "tree-walk.h"
|
|
||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "strbuf.h"
|
#include "strbuf.h"
|
||||||
#include "tar.h"
|
#include "tar.h"
|
||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
#include "pkt-line.h"
|
#include "pkt-line.h"
|
||||||
|
#include "archive.h"
|
||||||
|
|
||||||
#define RECORDSIZE (512)
|
#define RECORDSIZE (512)
|
||||||
#define BLOCKSIZE (RECORDSIZE * 20)
|
#define BLOCKSIZE (RECORDSIZE * 20)
|
||||||
@ -21,6 +21,7 @@ static unsigned long offset;
|
|||||||
|
|
||||||
static time_t archive_time;
|
static time_t archive_time;
|
||||||
static int tar_umask;
|
static int tar_umask;
|
||||||
|
static int verbose;
|
||||||
|
|
||||||
/* writes out the whole block, but only if it is full */
|
/* writes out the whole block, but only if it is full */
|
||||||
static void write_if_needed(void)
|
static void write_if_needed(void)
|
||||||
@ -168,6 +169,8 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
|
|||||||
mode = 0100666;
|
mode = 0100666;
|
||||||
sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
|
sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
|
||||||
} else {
|
} else {
|
||||||
|
if (verbose)
|
||||||
|
fprintf(stderr, "%.*s\n", path->len, path->buf);
|
||||||
if (S_ISDIR(mode)) {
|
if (S_ISDIR(mode)) {
|
||||||
*header.typeflag = TYPEFLAG_DIR;
|
*header.typeflag = TYPEFLAG_DIR;
|
||||||
mode = (mode | 0777) & ~tar_umask;
|
mode = (mode | 0777) & ~tar_umask;
|
||||||
@ -244,37 +247,6 @@ static void write_global_extended_header(const unsigned char *sha1)
|
|||||||
free(ext_header.buf);
|
free(ext_header.buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void traverse_tree(struct tree_desc *tree, struct strbuf *path)
|
|
||||||
{
|
|
||||||
int pathlen = path->len;
|
|
||||||
struct name_entry entry;
|
|
||||||
|
|
||||||
while (tree_entry(tree, &entry)) {
|
|
||||||
void *eltbuf;
|
|
||||||
char elttype[20];
|
|
||||||
unsigned long eltsize;
|
|
||||||
|
|
||||||
eltbuf = read_sha1_file(entry.sha1, elttype, &eltsize);
|
|
||||||
if (!eltbuf)
|
|
||||||
die("cannot read %s", sha1_to_hex(entry.sha1));
|
|
||||||
|
|
||||||
path->len = pathlen;
|
|
||||||
strbuf_append_string(path, entry.path);
|
|
||||||
if (S_ISDIR(entry.mode))
|
|
||||||
strbuf_append_string(path, "/");
|
|
||||||
|
|
||||||
write_entry(entry.sha1, path, entry.mode, eltbuf, eltsize);
|
|
||||||
|
|
||||||
if (S_ISDIR(entry.mode)) {
|
|
||||||
struct tree_desc subtree;
|
|
||||||
subtree.buf = eltbuf;
|
|
||||||
subtree.size = eltsize;
|
|
||||||
traverse_tree(&subtree, path);
|
|
||||||
}
|
|
||||||
free(eltbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int git_tar_config(const char *var, const char *value)
|
static int git_tar_config(const char *var, const char *value)
|
||||||
{
|
{
|
||||||
if (!strcmp(var, "tar.umask")) {
|
if (!strcmp(var, "tar.umask")) {
|
||||||
@ -291,50 +263,95 @@ static int git_tar_config(const char *var, const char *value)
|
|||||||
|
|
||||||
static int generate_tar(int argc, const char **argv, const char *prefix)
|
static int generate_tar(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
unsigned char sha1[20], tree_sha1[20];
|
struct archiver_args args;
|
||||||
struct commit *commit;
|
int result;
|
||||||
struct tree_desc tree;
|
char *base = NULL;
|
||||||
struct strbuf current_path;
|
|
||||||
void *buffer;
|
|
||||||
|
|
||||||
current_path.buf = xmalloc(PATH_MAX);
|
|
||||||
current_path.alloc = PATH_MAX;
|
|
||||||
current_path.len = current_path.eof = 0;
|
|
||||||
|
|
||||||
git_config(git_tar_config);
|
git_config(git_tar_config);
|
||||||
|
|
||||||
switch (argc) {
|
memset(&args, 0, sizeof(args));
|
||||||
case 3:
|
if (argc != 2 && argc != 3)
|
||||||
strbuf_append_string(¤t_path, argv[2]);
|
|
||||||
strbuf_append_string(¤t_path, "/");
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
case 2:
|
|
||||||
if (get_sha1(argv[1], sha1))
|
|
||||||
die("Not a valid object name %s", argv[1]);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
usage(tar_tree_usage);
|
usage(tar_tree_usage);
|
||||||
|
if (argc == 3) {
|
||||||
|
int baselen = strlen(argv[2]);
|
||||||
|
base = xmalloc(baselen + 2);
|
||||||
|
memcpy(base, argv[2], baselen);
|
||||||
|
base[baselen] = '/';
|
||||||
|
base[baselen + 1] = '\0';
|
||||||
|
}
|
||||||
|
args.base = base;
|
||||||
|
parse_treeish_arg(argv + 1, &args, NULL);
|
||||||
|
|
||||||
|
result = write_tar_archive(&args);
|
||||||
|
free(base);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
commit = lookup_commit_reference_gently(sha1, 1);
|
static int write_tar_entry(const unsigned char *sha1,
|
||||||
if (commit) {
|
const char *base, int baselen,
|
||||||
write_global_extended_header(commit->object.sha1);
|
const char *filename, unsigned mode, int stage)
|
||||||
archive_time = commit->date;
|
{
|
||||||
} else
|
static struct strbuf path;
|
||||||
archive_time = time(NULL);
|
int filenamelen = strlen(filename);
|
||||||
|
void *buffer;
|
||||||
|
char type[20];
|
||||||
|
unsigned long size;
|
||||||
|
|
||||||
tree.buf = buffer = read_object_with_reference(sha1, tree_type,
|
if (!path.alloc) {
|
||||||
&tree.size, tree_sha1);
|
path.buf = xmalloc(PATH_MAX);
|
||||||
if (!tree.buf)
|
path.alloc = PATH_MAX;
|
||||||
die("not a reference to a tag, commit or tree object: %s",
|
path.len = path.eof = 0;
|
||||||
sha1_to_hex(sha1));
|
}
|
||||||
|
if (path.alloc < baselen + filenamelen) {
|
||||||
|
free(path.buf);
|
||||||
|
path.buf = xmalloc(baselen + filenamelen);
|
||||||
|
path.alloc = baselen + filenamelen;
|
||||||
|
}
|
||||||
|
memcpy(path.buf, base, baselen);
|
||||||
|
memcpy(path.buf + baselen, filename, filenamelen);
|
||||||
|
path.len = baselen + filenamelen;
|
||||||
|
if (S_ISDIR(mode)) {
|
||||||
|
strbuf_append_string(&path, "/");
|
||||||
|
buffer = NULL;
|
||||||
|
size = 0;
|
||||||
|
} else {
|
||||||
|
buffer = read_sha1_file(sha1, type, &size);
|
||||||
|
if (!buffer)
|
||||||
|
die("cannot read %s", sha1_to_hex(sha1));
|
||||||
|
}
|
||||||
|
|
||||||
if (current_path.len > 0)
|
write_entry(sha1, &path, mode, buffer, size);
|
||||||
write_entry(tree_sha1, ¤t_path, 040777, NULL, 0);
|
|
||||||
traverse_tree(&tree, ¤t_path);
|
|
||||||
write_trailer();
|
|
||||||
free(buffer);
|
free(buffer);
|
||||||
free(current_path.buf);
|
|
||||||
|
return READ_TREE_RECURSIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int write_tar_archive(struct archiver_args *args)
|
||||||
|
{
|
||||||
|
int plen = args->base ? strlen(args->base) : 0;
|
||||||
|
|
||||||
|
git_config(git_tar_config);
|
||||||
|
|
||||||
|
archive_time = args->time;
|
||||||
|
verbose = args->verbose;
|
||||||
|
|
||||||
|
if (args->commit_sha1)
|
||||||
|
write_global_extended_header(args->commit_sha1);
|
||||||
|
|
||||||
|
if (args->base && plen > 0 && args->base[plen - 1] == '/') {
|
||||||
|
char *base = xstrdup(args->base);
|
||||||
|
int baselen = strlen(base);
|
||||||
|
|
||||||
|
while (baselen > 0 && base[baselen - 1] == '/')
|
||||||
|
base[--baselen] = '\0';
|
||||||
|
write_tar_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
|
||||||
|
free(base);
|
||||||
|
}
|
||||||
|
read_tree_recursive(args->tree, args->base, plen, 0,
|
||||||
|
args->pathspec, write_tar_entry);
|
||||||
|
write_trailer();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
174
builtin-upload-archive.c
Normal file
174
builtin-upload-archive.c
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2006 Franck Bui-Huu
|
||||||
|
*/
|
||||||
|
#include <time.h>
|
||||||
|
#include "cache.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
#include "archive.h"
|
||||||
|
#include "pkt-line.h"
|
||||||
|
#include "sideband.h"
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
|
||||||
|
static const char upload_archive_usage[] =
|
||||||
|
"git-upload-archive <repo>";
|
||||||
|
|
||||||
|
static const char deadchild[] =
|
||||||
|
"git-upload-archive: archiver died with error";
|
||||||
|
|
||||||
|
static const char lostchild[] =
|
||||||
|
"git-upload-archive: archiver process was lost";
|
||||||
|
|
||||||
|
|
||||||
|
static int run_upload_archive(int argc, const char **argv, const char *prefix)
|
||||||
|
{
|
||||||
|
struct archiver ar;
|
||||||
|
const char *sent_argv[MAX_ARGS];
|
||||||
|
const char *arg_cmd = "argument ";
|
||||||
|
char *p, buf[4096];
|
||||||
|
int treeish_idx;
|
||||||
|
int sent_argc;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (argc != 2)
|
||||||
|
usage(upload_archive_usage);
|
||||||
|
|
||||||
|
if (strlen(argv[1]) > sizeof(buf))
|
||||||
|
die("insanely long repository name");
|
||||||
|
|
||||||
|
strcpy(buf, argv[1]); /* enter-repo smudges its argument */
|
||||||
|
|
||||||
|
if (!enter_repo(buf, 0))
|
||||||
|
die("not a git archive");
|
||||||
|
|
||||||
|
/* put received options in sent_argv[] */
|
||||||
|
sent_argc = 1;
|
||||||
|
sent_argv[0] = "git-upload-archive";
|
||||||
|
for (p = buf;;) {
|
||||||
|
/* This will die if not enough free space in buf */
|
||||||
|
len = packet_read_line(0, p, (buf + sizeof buf) - p);
|
||||||
|
if (len == 0)
|
||||||
|
break; /* got a flush */
|
||||||
|
if (sent_argc > MAX_ARGS - 2)
|
||||||
|
die("Too many options (>29)");
|
||||||
|
|
||||||
|
if (p[len-1] == '\n') {
|
||||||
|
p[--len] = 0;
|
||||||
|
}
|
||||||
|
if (len < strlen(arg_cmd) ||
|
||||||
|
strncmp(arg_cmd, p, strlen(arg_cmd)))
|
||||||
|
die("'argument' token or flush expected");
|
||||||
|
|
||||||
|
len -= strlen(arg_cmd);
|
||||||
|
memmove(p, p + strlen(arg_cmd), len);
|
||||||
|
sent_argv[sent_argc++] = p;
|
||||||
|
p += len;
|
||||||
|
*p++ = 0;
|
||||||
|
}
|
||||||
|
sent_argv[sent_argc] = NULL;
|
||||||
|
|
||||||
|
/* parse all options sent by the client */
|
||||||
|
treeish_idx = parse_archive_args(sent_argc, sent_argv, &ar);
|
||||||
|
|
||||||
|
parse_treeish_arg(sent_argv + treeish_idx, &ar.args, prefix);
|
||||||
|
parse_pathspec_arg(sent_argv + treeish_idx + 1, &ar.args);
|
||||||
|
|
||||||
|
return ar.write_archive(&ar.args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void error_clnt(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
va_list params;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
va_start(params, fmt);
|
||||||
|
len = vsprintf(buf, fmt, params);
|
||||||
|
va_end(params);
|
||||||
|
send_sideband(1, 3, buf, len, LARGE_PACKET_MAX);
|
||||||
|
die("sent error to the client: %s", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_input(int child_fd, int band)
|
||||||
|
{
|
||||||
|
char buf[16384];
|
||||||
|
ssize_t sz = read(child_fd, buf, sizeof(buf));
|
||||||
|
if (sz < 0) {
|
||||||
|
if (errno != EINTR)
|
||||||
|
error_clnt("read error: %s\n", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
send_sideband(1, band, buf, sz, LARGE_PACKET_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmd_upload_archive(int argc, const char **argv, const char *prefix)
|
||||||
|
{
|
||||||
|
pid_t writer;
|
||||||
|
int fd1[2], fd2[2];
|
||||||
|
/*
|
||||||
|
* Set up sideband subprocess.
|
||||||
|
*
|
||||||
|
* We (parent) monitor and read from child, sending its fd#1 and fd#2
|
||||||
|
* multiplexed out to our fd#1. If the child dies, we tell the other
|
||||||
|
* end over channel #3.
|
||||||
|
*/
|
||||||
|
if (pipe(fd1) < 0 || pipe(fd2) < 0) {
|
||||||
|
int err = errno;
|
||||||
|
packet_write(1, "NACK pipe failed on the remote side\n");
|
||||||
|
die("upload-archive: %s", strerror(err));
|
||||||
|
}
|
||||||
|
writer = fork();
|
||||||
|
if (writer < 0) {
|
||||||
|
int err = errno;
|
||||||
|
packet_write(1, "NACK fork failed on the remote side\n");
|
||||||
|
die("upload-archive: %s", strerror(err));
|
||||||
|
}
|
||||||
|
if (!writer) {
|
||||||
|
/* child - connect fd#1 and fd#2 to the pipe */
|
||||||
|
dup2(fd1[1], 1);
|
||||||
|
dup2(fd2[1], 2);
|
||||||
|
close(fd1[1]); close(fd2[1]);
|
||||||
|
close(fd1[0]); close(fd2[0]); /* we do not read from pipe */
|
||||||
|
|
||||||
|
exit(run_upload_archive(argc, argv, prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parent - read from child, multiplex and send out to fd#1 */
|
||||||
|
close(fd1[1]); close(fd2[1]); /* we do not write to pipe */
|
||||||
|
packet_write(1, "ACK\n");
|
||||||
|
packet_flush(1);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
struct pollfd pfd[2];
|
||||||
|
int status;
|
||||||
|
|
||||||
|
pfd[0].fd = fd1[0];
|
||||||
|
pfd[0].events = POLLIN;
|
||||||
|
pfd[1].fd = fd2[0];
|
||||||
|
pfd[1].events = POLLIN;
|
||||||
|
if (poll(pfd, 2, -1) < 0) {
|
||||||
|
if (errno != EINTR) {
|
||||||
|
error("poll failed resuming: %s",
|
||||||
|
strerror(errno));
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pfd[0].revents & POLLIN)
|
||||||
|
/* Data stream ready */
|
||||||
|
process_input(pfd[0].fd, 1);
|
||||||
|
if (pfd[1].revents & POLLIN)
|
||||||
|
/* Status stream ready */
|
||||||
|
process_input(pfd[1].fd, 2);
|
||||||
|
if ((pfd[0].revents | pfd[1].revents) == POLLIN)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (waitpid(writer, &status, 0) < 0)
|
||||||
|
error_clnt("%s", lostchild);
|
||||||
|
else if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
|
||||||
|
error_clnt("%s", deadchild);
|
||||||
|
packet_flush(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
@ -8,10 +8,12 @@
|
|||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
#include "quote.h"
|
#include "quote.h"
|
||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
|
#include "archive.h"
|
||||||
|
|
||||||
static const char zip_tree_usage[] =
|
static const char zip_tree_usage[] =
|
||||||
"git-zip-tree [-0|...|-9] <tree-ish> [ <base> ]";
|
"git-zip-tree [-0|...|-9] <tree-ish> [ <base> ]";
|
||||||
|
|
||||||
|
static int verbose;
|
||||||
static int zip_date;
|
static int zip_date;
|
||||||
static int zip_time;
|
static int zip_time;
|
||||||
|
|
||||||
@ -163,6 +165,8 @@ static int write_zip_entry(const unsigned char *sha1,
|
|||||||
crc = crc32(0, Z_NULL, 0);
|
crc = crc32(0, Z_NULL, 0);
|
||||||
|
|
||||||
path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen);
|
path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen);
|
||||||
|
if (verbose)
|
||||||
|
fprintf(stderr, "%s\n", path);
|
||||||
if (pathlen > 0xffff) {
|
if (pathlen > 0xffff) {
|
||||||
error("path too long (%d chars, SHA1: %s): %s", pathlen,
|
error("path too long (%d chars, SHA1: %s): %s", pathlen,
|
||||||
sha1_to_hex(sha1), path);
|
sha1_to_hex(sha1), path);
|
||||||
@ -351,3 +355,44 @@ int cmd_zip_tree(int argc, const char **argv, const char *prefix)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int write_zip_archive(struct archiver_args *args)
|
||||||
|
{
|
||||||
|
int plen = strlen(args->base);
|
||||||
|
|
||||||
|
dos_time(&args->time, &zip_date, &zip_time);
|
||||||
|
|
||||||
|
zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
|
||||||
|
zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
|
||||||
|
verbose = args->verbose;
|
||||||
|
|
||||||
|
if (args->base && plen > 0 && args->base[plen - 1] == '/') {
|
||||||
|
char *base = xstrdup(args->base);
|
||||||
|
int baselen = strlen(base);
|
||||||
|
|
||||||
|
while (baselen > 0 && base[baselen - 1] == '/')
|
||||||
|
base[--baselen] = '\0';
|
||||||
|
write_zip_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
|
||||||
|
free(base);
|
||||||
|
}
|
||||||
|
read_tree_recursive(args->tree, args->base, plen, 0,
|
||||||
|
args->pathspec, write_zip_entry);
|
||||||
|
write_zip_trailer(args->commit_sha1);
|
||||||
|
|
||||||
|
free(zip_dir);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *parse_extra_zip_args(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
for (; argc > 0; argc--, argv++) {
|
||||||
|
const char *arg = argv[0];
|
||||||
|
|
||||||
|
if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0')
|
||||||
|
zlib_compression_level = arg[1] - '0';
|
||||||
|
else
|
||||||
|
die("Unknown argument for zip format: %s", arg);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@ extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
|
|||||||
|
|
||||||
extern int cmd_add(int argc, const char **argv, const char *prefix);
|
extern int cmd_add(int argc, const char **argv, const char *prefix);
|
||||||
extern int cmd_apply(int argc, const char **argv, const char *prefix);
|
extern int cmd_apply(int argc, const char **argv, const char *prefix);
|
||||||
|
extern int cmd_archive(int argc, const char **argv, const char *prefix);
|
||||||
extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
|
extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
|
||||||
extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
|
extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
|
||||||
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
|
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
|
||||||
@ -55,6 +56,7 @@ extern int cmd_zip_tree(int argc, const char **argv, const char *prefix);
|
|||||||
extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
|
extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
|
||||||
extern int cmd_update_index(int argc, const char **argv, const char *prefix);
|
extern int cmd_update_index(int argc, const char **argv, const char *prefix);
|
||||||
extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
|
extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
|
||||||
|
extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
|
||||||
extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
|
extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
|
||||||
extern int cmd_version(int argc, const char **argv, const char *prefix);
|
extern int cmd_version(int argc, const char **argv, const char *prefix);
|
||||||
extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
|
extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
|
||||||
|
7
daemon.c
7
daemon.c
@ -325,7 +325,14 @@ static int upload_pack(void)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int upload_archive(void)
|
||||||
|
{
|
||||||
|
execl_git_cmd("upload-archive", ".", NULL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static struct daemon_service daemon_service[] = {
|
static struct daemon_service daemon_service[] = {
|
||||||
|
{ "upload-archive", "uploadarch", upload_archive, 0, 1 },
|
||||||
{ "upload-pack", "uploadpack", upload_pack, 1, 1 },
|
{ "upload-pack", "uploadpack", upload_pack, 1, 1 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "exec_cmd.h"
|
#include "exec_cmd.h"
|
||||||
#include "pkt-line.h"
|
#include "pkt-line.h"
|
||||||
|
#include "sideband.h"
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
@ -114,36 +115,13 @@ static pid_t setup_sideband(int sideband, const char *me, int fd[2], int xd[2])
|
|||||||
die("%s: unable to fork off sideband demultiplexer", me);
|
die("%s: unable to fork off sideband demultiplexer", me);
|
||||||
if (!side_pid) {
|
if (!side_pid) {
|
||||||
/* subprocess */
|
/* subprocess */
|
||||||
|
char buf[LARGE_PACKET_MAX];
|
||||||
|
|
||||||
close(fd[0]);
|
close(fd[0]);
|
||||||
if (xd[0] != xd[1])
|
if (xd[0] != xd[1])
|
||||||
close(xd[1]);
|
close(xd[1]);
|
||||||
while (1) {
|
if (recv_sideband(me, xd[0], fd[1], 2, buf, sizeof(buf)))
|
||||||
char buf[1024];
|
|
||||||
int len = packet_read_line(xd[0], buf, sizeof(buf));
|
|
||||||
if (len == 0)
|
|
||||||
break;
|
|
||||||
if (len < 1)
|
|
||||||
die("%s: protocol error: no band designator",
|
|
||||||
me);
|
|
||||||
len--;
|
|
||||||
switch (buf[0] & 0xFF) {
|
|
||||||
case 3:
|
|
||||||
safe_write(2, "remote: ", 8);
|
|
||||||
safe_write(2, buf+1, len);
|
|
||||||
safe_write(2, "\n", 1);
|
|
||||||
exit(1);
|
exit(1);
|
||||||
case 2:
|
|
||||||
safe_write(2, "remote: ", 8);
|
|
||||||
safe_write(2, buf+1, len);
|
|
||||||
continue;
|
|
||||||
case 1:
|
|
||||||
safe_write(fd[1], buf+1, len);
|
|
||||||
continue;
|
|
||||||
default:
|
|
||||||
die("%s: protocol error: bad band #%d",
|
|
||||||
me, (buf[0] & 0xFF));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
close(xd[0]);
|
close(xd[0]);
|
||||||
|
12
fetch-pack.c
12
fetch-pack.c
@ -166,10 +166,11 @@ static int find_common(int fd[2], unsigned char *result_sha1,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!fetching)
|
if (!fetching)
|
||||||
packet_write(fd[1], "want %s%s%s%s\n",
|
packet_write(fd[1], "want %s%s%s%s%s\n",
|
||||||
sha1_to_hex(remote),
|
sha1_to_hex(remote),
|
||||||
(multi_ack ? " multi_ack" : ""),
|
(multi_ack ? " multi_ack" : ""),
|
||||||
(use_sideband ? " side-band" : ""),
|
(use_sideband == 2 ? " side-band-64k" : ""),
|
||||||
|
(use_sideband == 1 ? " side-band" : ""),
|
||||||
(use_thin_pack ? " thin-pack" : ""));
|
(use_thin_pack ? " thin-pack" : ""));
|
||||||
else
|
else
|
||||||
packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
|
packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
|
||||||
@ -426,7 +427,12 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
|
|||||||
fprintf(stderr, "Server supports multi_ack\n");
|
fprintf(stderr, "Server supports multi_ack\n");
|
||||||
multi_ack = 1;
|
multi_ack = 1;
|
||||||
}
|
}
|
||||||
if (server_supports("side-band")) {
|
if (server_supports("side-band-64k")) {
|
||||||
|
if (verbose)
|
||||||
|
fprintf(stderr, "Server supports side-band-64k\n");
|
||||||
|
use_sideband = 2;
|
||||||
|
}
|
||||||
|
else if (server_supports("side-band")) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
fprintf(stderr, "Server supports side-band\n");
|
fprintf(stderr, "Server supports side-band\n");
|
||||||
use_sideband = 1;
|
use_sideband = 1;
|
||||||
|
@ -12,6 +12,7 @@ struct cmdname_help common_cmds[] = {"
|
|||||||
sort <<\EOF |
|
sort <<\EOF |
|
||||||
add
|
add
|
||||||
apply
|
apply
|
||||||
|
archive
|
||||||
bisect
|
bisect
|
||||||
branch
|
branch
|
||||||
checkout
|
checkout
|
||||||
|
2
git.c
2
git.c
@ -220,6 +220,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
|
|||||||
} commands[] = {
|
} commands[] = {
|
||||||
{ "add", cmd_add, RUN_SETUP },
|
{ "add", cmd_add, RUN_SETUP },
|
||||||
{ "apply", cmd_apply },
|
{ "apply", cmd_apply },
|
||||||
|
{ "archive", cmd_archive },
|
||||||
{ "cat-file", cmd_cat_file, RUN_SETUP },
|
{ "cat-file", cmd_cat_file, RUN_SETUP },
|
||||||
{ "checkout-index", cmd_checkout_index, RUN_SETUP },
|
{ "checkout-index", cmd_checkout_index, RUN_SETUP },
|
||||||
{ "check-ref-format", cmd_check_ref_format },
|
{ "check-ref-format", cmd_check_ref_format },
|
||||||
@ -261,6 +262,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
|
|||||||
{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
|
{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
|
||||||
{ "update-index", cmd_update_index, RUN_SETUP },
|
{ "update-index", cmd_update_index, RUN_SETUP },
|
||||||
{ "update-ref", cmd_update_ref, RUN_SETUP },
|
{ "update-ref", cmd_update_ref, RUN_SETUP },
|
||||||
|
{ "upload-archive", cmd_upload_archive },
|
||||||
{ "upload-tar", cmd_upload_tar },
|
{ "upload-tar", cmd_upload_tar },
|
||||||
{ "version", cmd_version },
|
{ "version", cmd_version },
|
||||||
{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
|
{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
|
||||||
|
74
sideband.c
Normal file
74
sideband.c
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#include "pkt-line.h"
|
||||||
|
#include "sideband.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Receive multiplexed output stream over git native protocol.
|
||||||
|
* in_stream is the input stream from the remote, which carries data
|
||||||
|
* in pkt_line format with band designator. Demultiplex it into out
|
||||||
|
* and err and return error appropriately. Band #1 carries the
|
||||||
|
* primary payload. Things coming over band #2 is not necessarily
|
||||||
|
* error; they are usually informative message on the standard error
|
||||||
|
* stream, aka "verbose"). A message over band #3 is a signal that
|
||||||
|
* the remote died unexpectedly. A flush() concludes the stream.
|
||||||
|
*/
|
||||||
|
int recv_sideband(const char *me, int in_stream, int out, int err, char *buf, int bufsz)
|
||||||
|
{
|
||||||
|
while (1) {
|
||||||
|
int len = packet_read_line(in_stream, buf, bufsz);
|
||||||
|
if (len == 0)
|
||||||
|
break;
|
||||||
|
if (len < 1) {
|
||||||
|
len = sprintf(buf, "%s: protocol error: no band designator\n", me);
|
||||||
|
safe_write(err, buf, len);
|
||||||
|
return SIDEBAND_PROTOCOL_ERROR;
|
||||||
|
}
|
||||||
|
len--;
|
||||||
|
switch (buf[0] & 0xFF) {
|
||||||
|
case 3:
|
||||||
|
safe_write(err, "remote: ", 8);
|
||||||
|
safe_write(err, buf+1, len);
|
||||||
|
safe_write(err, "\n", 1);
|
||||||
|
return SIDEBAND_REMOTE_ERROR;
|
||||||
|
case 2:
|
||||||
|
safe_write(err, "remote: ", 8);
|
||||||
|
safe_write(err, buf+1, len);
|
||||||
|
continue;
|
||||||
|
case 1:
|
||||||
|
safe_write(out, buf+1, len);
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
len = sprintf(buf + 1,
|
||||||
|
"%s: protocol error: bad band #%d\n",
|
||||||
|
me, buf[0] & 0xFF);
|
||||||
|
safe_write(err, buf+1, len);
|
||||||
|
return SIDEBAND_PROTOCOL_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fd is connected to the remote side; send the sideband data
|
||||||
|
* over multiplexed packet stream.
|
||||||
|
*/
|
||||||
|
ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max)
|
||||||
|
{
|
||||||
|
ssize_t ssz = sz;
|
||||||
|
const char *p = data;
|
||||||
|
|
||||||
|
while (sz) {
|
||||||
|
unsigned n;
|
||||||
|
char hdr[5];
|
||||||
|
|
||||||
|
n = sz;
|
||||||
|
if (packet_max - 5 < n)
|
||||||
|
n = packet_max - 5;
|
||||||
|
sprintf(hdr, "%04x", n + 5);
|
||||||
|
hdr[4] = band;
|
||||||
|
safe_write(fd, hdr, 5);
|
||||||
|
safe_write(fd, p, n);
|
||||||
|
p += n;
|
||||||
|
sz -= n;
|
||||||
|
}
|
||||||
|
return ssz;
|
||||||
|
}
|
13
sideband.h
Normal file
13
sideband.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#ifndef SIDEBAND_H
|
||||||
|
#define SIDEBAND_H
|
||||||
|
|
||||||
|
#define SIDEBAND_PROTOCOL_ERROR -2
|
||||||
|
#define SIDEBAND_REMOTE_ERROR -1
|
||||||
|
|
||||||
|
#define DEFAULT_PACKET_MAX 1000
|
||||||
|
#define LARGE_PACKET_MAX 65520
|
||||||
|
|
||||||
|
int recv_sideband(const char *me, int in_stream, int out, int err, char *, int);
|
||||||
|
ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
|
||||||
|
|
||||||
|
#endif
|
@ -4,6 +4,7 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
#include "pkt-line.h"
|
#include "pkt-line.h"
|
||||||
|
#include "sideband.h"
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
@ -19,6 +20,9 @@ static int use_thin_pack;
|
|||||||
static struct object_array have_obj;
|
static struct object_array have_obj;
|
||||||
static struct object_array want_obj;
|
static struct object_array want_obj;
|
||||||
static unsigned int timeout;
|
static unsigned int timeout;
|
||||||
|
/* 0 for no sideband,
|
||||||
|
* otherwise maximum packet size (up to 65520 bytes).
|
||||||
|
*/
|
||||||
static int use_sideband;
|
static int use_sideband;
|
||||||
|
|
||||||
static void reset_timeout(void)
|
static void reset_timeout(void)
|
||||||
@ -33,19 +37,10 @@ static int strip(char *line, int len)
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PACKET_MAX 1000
|
|
||||||
static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
|
static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
|
||||||
{
|
{
|
||||||
ssize_t ssz;
|
if (use_sideband)
|
||||||
const char *p;
|
return send_sideband(1, fd, data, sz, use_sideband);
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
if (!use_sideband)
|
|
||||||
return 0;
|
|
||||||
packet_flush(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!use_sideband) {
|
|
||||||
if (fd == 3)
|
if (fd == 3)
|
||||||
/* emergency quit */
|
/* emergency quit */
|
||||||
fd = 2;
|
fd = 2;
|
||||||
@ -55,24 +50,6 @@ static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
|
|||||||
}
|
}
|
||||||
return safe_write(fd, data, sz);
|
return safe_write(fd, data, sz);
|
||||||
}
|
}
|
||||||
p = data;
|
|
||||||
ssz = sz;
|
|
||||||
while (sz) {
|
|
||||||
unsigned n;
|
|
||||||
char hdr[5];
|
|
||||||
|
|
||||||
n = sz;
|
|
||||||
if (PACKET_MAX - 5 < n)
|
|
||||||
n = PACKET_MAX - 5;
|
|
||||||
sprintf(hdr, "%04x", n + 5);
|
|
||||||
hdr[4] = fd;
|
|
||||||
safe_write(1, hdr, 5);
|
|
||||||
safe_write(1, p, n);
|
|
||||||
p += n;
|
|
||||||
sz -= n;
|
|
||||||
}
|
|
||||||
return ssz;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void create_pack_file(void)
|
static void create_pack_file(void)
|
||||||
{
|
{
|
||||||
@ -308,7 +285,8 @@ static void create_pack_file(void)
|
|||||||
goto fail;
|
goto fail;
|
||||||
fprintf(stderr, "flushed.\n");
|
fprintf(stderr, "flushed.\n");
|
||||||
}
|
}
|
||||||
send_client_data(1, NULL, 0);
|
if (use_sideband)
|
||||||
|
packet_flush(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fail:
|
fail:
|
||||||
@ -413,8 +391,10 @@ static void receive_needs(void)
|
|||||||
multi_ack = 1;
|
multi_ack = 1;
|
||||||
if (strstr(line+45, "thin-pack"))
|
if (strstr(line+45, "thin-pack"))
|
||||||
use_thin_pack = 1;
|
use_thin_pack = 1;
|
||||||
if (strstr(line+45, "side-band"))
|
if (strstr(line+45, "side-band-64k"))
|
||||||
use_sideband = 1;
|
use_sideband = LARGE_PACKET_MAX;
|
||||||
|
else if (strstr(line+45, "side-band"))
|
||||||
|
use_sideband = DEFAULT_PACKET_MAX;
|
||||||
|
|
||||||
/* We have sent all our refs already, and the other end
|
/* We have sent all our refs already, and the other end
|
||||||
* should have chosen out of them; otherwise they are
|
* should have chosen out of them; otherwise they are
|
||||||
@ -436,7 +416,7 @@ static void receive_needs(void)
|
|||||||
|
|
||||||
static int send_ref(const char *refname, const unsigned char *sha1)
|
static int send_ref(const char *refname, const unsigned char *sha1)
|
||||||
{
|
{
|
||||||
static const char *capabilities = "multi_ack thin-pack side-band";
|
static const char *capabilities = "multi_ack thin-pack side-band side-band-64k";
|
||||||
struct object *o = parse_object(sha1);
|
struct object *o = parse_object(sha1);
|
||||||
|
|
||||||
if (!o)
|
if (!o)
|
||||||
|
Loading…
Reference in New Issue
Block a user