"[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
"[--no-progress] [-v] [<host>:]<directory> [<refs>...]";
+static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc,
+ const char *name, int namelen)
+{
+ struct ref *ref = xcalloc(1, sizeof(*ref) + namelen + 1);
+
+ memcpy(ref->name, name, namelen);
+ ref->name[namelen] = '\0';
+ (*nr)++;
+ ALLOC_GROW(*sought, *nr, *alloc);
+ (*sought)[*nr - 1] = ref;
+}
+
+static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
+ const char *string)
+{
+ add_sought_entry_mem(sought, nr, alloc, string, strlen(string));
+}
+
int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
{
int i, ret;
struct ref *ref = NULL;
const char *dest = NULL;
- struct string_list sought = STRING_LIST_INIT_DUP;
+ struct ref **sought = NULL;
+ int nr_sought = 0, alloc_sought = 0;
int fd[2];
char *pack_lockfile = NULL;
char **pack_lockfile_ptr = NULL;
* refs from the standard input:
*/
for (; i < argc; i++)
- string_list_append(&sought, xstrdup(argv[i]));
+ add_sought_entry(&sought, &nr_sought, &alloc_sought, argv[i]);
if (args.stdin_refs) {
if (args.stateless_rpc) {
/* in stateless RPC mode we use pkt-line to read
* from stdin, until we get a flush packet
*/
- static char line[1000];
for (;;) {
- int n = packet_read_line(0, line, sizeof(line));
- if (!n)
+ char *line = packet_read_line(0, NULL);
+ if (!line)
break;
- if (line[n-1] == '\n')
- n--;
- add_sought_entry_mem(&sought, &nr_sought, &alloc_sought, line, n);
- string_list_append(&sought, xstrdup(line));
++ add_sought_entry(&sought, &nr_sought, &alloc_sought, line);
}
}
else {
/* read from stdin one ref per line, until EOF */
struct strbuf line = STRBUF_INIT;
while (strbuf_getline(&line, stdin, '\n') != EOF)
- string_list_append(&sought, strbuf_detach(&line, NULL));
+ add_sought_entry(&sought, &nr_sought, &alloc_sought, line.buf);
strbuf_release(&line);
}
}
args.verbose ? CONNECT_VERBOSE : 0);
}
- get_remote_heads(fd[0], &ref, 0, NULL);
+ get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL);
ref = fetch_pack(&args, fd, conn, ref, dest,
- &sought, pack_lockfile_ptr);
+ sought, nr_sought, pack_lockfile_ptr);
if (pack_lockfile) {
printf("lock %s\n", pack_lockfile);
fflush(stdout);
if (finish_connect(conn))
return 1;
- ret = !ref || sought.nr;
+ ret = !ref;
/*
* If the heads to pull were given, we should have consumed
* remote no-such-ref' would silently succeed without issuing
* an error.
*/
- for (i = 0; i < sought.nr; i++)
- error("no such remote ref %s", sought.items[i].string);
+ for (i = 0; i < nr_sought; i++) {
+ if (!sought[i] || sought[i]->matched)
+ continue;
+ error("no such remote ref %s", sought[i]->name);
+ ret = 1;
+ }
+
while (ref) {
printf("%s %s\n",
sha1_to_hex(ref->old_sha1), ref->name);
void git_deflate_init(git_zstream *, int level);
void git_deflate_init_gzip(git_zstream *, int level);
+void git_deflate_init_raw(git_zstream *, int level);
void git_deflate_end(git_zstream *);
int git_deflate_abort(git_zstream *);
int git_deflate_end_gently(git_zstream *);
OBJ_BLOB;
}
+/* Double-check local_repo_env below if you add to this list. */
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
+#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
#define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
#define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
/*
- * Repository-local GIT_* environment variables
- * The array is NULL-terminated to simplify its usage in contexts such
- * environment creation or simple walk of the list.
- * The number of non-NULL entries is available as a macro.
+ * This environment variable is expected to contain a boolean indicating
+ * whether we should or should not treat:
+ *
+ * GIT_DIR=foo.git git ...
+ *
+ * as if GIT_WORK_TREE=. was given. It's not expected that users will make use
+ * of this, but we use it internally to communicate to sub-processes that we
+ * are in a bare repo. If not set, defaults to true.
+ */
+#define GIT_IMPLICIT_WORK_TREE_ENVIRONMENT "GIT_IMPLICIT_WORK_TREE"
+
+/*
+ * Repository-local GIT_* environment variables; these will be cleared
+ * when git spawns a sub-process that runs inside another repository.
+ * The array is NULL-terminated, which makes it easy to pass in the "env"
+ * parameter of a run-command invocation, or to do a simple walk.
*/
-#define LOCAL_REPO_ENV_SIZE 9
-extern const char *const local_repo_env[LOCAL_REPO_ENV_SIZE + 1];
+extern const char * const local_repo_env[];
extern int is_bare_repository_cfg;
extern int is_bare_repository(void);
force:1,
forced_update:1,
merge:1,
- deletion:1;
+ deletion:1,
+ matched:1;
enum {
REF_STATUS_NONE = 0,
REF_STATUS_OK,
int nr, alloc;
unsigned char (*array)[20];
};
- extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *);
+ extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
+ struct ref **list, unsigned int flags,
+ struct extra_have_objects *);
extern int server_supports(const char *feature);
extern int parse_feature_request(const char *features, const char *feature);
extern const char *server_feature_value(const char *feature, int *len_ret);
extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
+/* A hook for count-objects to report invalid files in pack directory */
+extern void (*report_garbage)(const char *desc, const char *path);
+
extern void prepare_packed_git(void);
extern void reprepare_packed_git(void);
extern void install_packed_git(struct packed_git *pack);
#define HOST_NAME_MAX 256
#endif
-#ifndef NI_MAXSERV
-#define NI_MAXSERV 32
-#endif
-
#ifdef NO_INITGROUPS
#define initgroups(x, y) (0) /* nothing */
#endif
static int execute(void)
{
- static char line[1000];
+ char *line = packet_buffer;
int pktlen, len, i;
char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
loginfo("Connection from %s:%s", addr, port);
alarm(init_timeout ? init_timeout : timeout);
- pktlen = packet_read_line(0, line, sizeof(line));
+ pktlen = packet_read(0, NULL, NULL, packet_buffer, sizeof(packet_buffer), 0);
alarm(0);
len = strlen(line);
#define MAX_IN_VAIN 256
static struct commit_list *rev_list;
-static int non_common_revs, multi_ack, use_sideband;
+static int non_common_revs, multi_ack, use_sideband, allow_tip_sha1_in_want;
static void rev_list_push(struct commit *commit, int mark)
{
* shallow and unshallow commands every time there
* is a block of have lines exchanged.
*/
- char line[1000];
- while (packet_read_line(fd, line, sizeof(line))) {
+ char *line;
+ while ((line = packet_read_line(fd, NULL))) {
if (!prefixcmp(line, "shallow "))
continue;
if (!prefixcmp(line, "unshallow "))
static enum ack_type get_ack(int fd, unsigned char *result_sha1)
{
- static char line[1000];
- int len = packet_read_line(fd, line, sizeof(line));
+ int len;
+ char *line = packet_read_line(fd, &len);
if (!len)
die("git fetch-pack: expected ACK/NAK, got EOF");
- if (line[len-1] == '\n')
- line[--len] = 0;
if (!strcmp(line, "NAK"))
return NAK;
if (!prefixcmp(line, "ACK ")) {
if (!get_sha1_hex(line+4, result_sha1)) {
+ if (len < 45)
+ return ACK;
if (strstr(line+45, "continue"))
return ACK_continue;
if (strstr(line+45, "common"))
send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
packet_flush(fd);
} else
- safe_write(fd, buf->buf, buf->len);
+ write_or_die(fd, buf->buf, buf->len);
}
static void insert_one_alternate_ref(const struct ref *ref, void *unused)
state_len = req_buf.len;
if (args->depth > 0) {
- char line[1024];
+ char *line;
unsigned char sha1[20];
send_request(args, fd[1], &req_buf);
- while (packet_read_line(fd[0], line, sizeof(line))) {
+ while ((line = packet_read_line(fd[0], NULL))) {
if (!prefixcmp(line, "shallow ")) {
if (get_sha1_hex(line + 8, sha1))
die("invalid shallow line: %s", line);
}
}
-static int non_matching_ref(struct string_list_item *item, void *unused)
-{
- if (item->util) {
- item->util = NULL;
- return 0;
- }
- else
- return 1;
-}
-
static void filter_refs(struct fetch_pack_args *args,
- struct ref **refs, struct string_list *sought)
+ struct ref **refs,
+ struct ref **sought, int nr_sought)
{
struct ref *newlist = NULL;
struct ref **newtail = &newlist;
struct ref *ref, *next;
- int sought_pos;
+ int i;
- sought_pos = 0;
+ i = 0;
for (ref = *refs; ref; ref = next) {
int keep = 0;
next = ref->next;
+
if (!memcmp(ref->name, "refs/", 5) &&
check_refname_format(ref->name + 5, 0))
; /* trash */
else {
- while (sought_pos < sought->nr) {
- int cmp = strcmp(ref->name, sought->items[sought_pos].string);
+ while (i < nr_sought) {
+ int cmp = strcmp(ref->name, sought[i]->name);
if (cmp < 0)
break; /* definitely do not have it */
else if (cmp == 0) {
keep = 1; /* definitely have it */
- sought->items[sought_pos++].util = "matched";
- break;
+ sought[i]->matched = 1;
}
- else
- sought_pos++; /* might have it; keep looking */
+ i++;
}
}
- if (! keep && args->fetch_all &&
+ if (!keep && args->fetch_all &&
(!args->depth || prefixcmp(ref->name, "refs/tags/")))
keep = 1;
}
}
- filter_string_list(sought, 0, non_matching_ref, NULL);
+ /* Append unmatched requests to the list */
+ if (allow_tip_sha1_in_want) {
+ for (i = 0; i < nr_sought; i++) {
+ ref = sought[i];
+ if (ref->matched)
+ continue;
+ if (get_sha1_hex(ref->name, ref->old_sha1))
+ continue;
+
+ ref->matched = 1;
+ *newtail = ref;
+ ref->next = NULL;
+ newtail = &ref->next;
+ }
+ }
*refs = newlist;
}
}
static int everything_local(struct fetch_pack_args *args,
- struct ref **refs, struct string_list *sought)
+ struct ref **refs,
+ struct ref **sought, int nr_sought)
{
struct ref *ref;
int retval;
}
}
- filter_refs(args, refs, sought);
+ filter_refs(args, refs, sought, nr_sought);
for (retval = 1, ref = *refs; ref ; ref = ref->next) {
const unsigned char *remote = ref->old_sha1;
return 0;
}
+static int cmp_ref_by_name(const void *a_, const void *b_)
+{
+ const struct ref *a = *((const struct ref **)a_);
+ const struct ref *b = *((const struct ref **)b_);
+ return strcmp(a->name, b->name);
+}
+
static struct ref *do_fetch_pack(struct fetch_pack_args *args,
int fd[2],
const struct ref *orig_ref,
- struct string_list *sought,
+ struct ref **sought, int nr_sought,
char **pack_lockfile)
{
struct ref *ref = copy_ref_list(orig_ref);
int agent_len;
sort_ref_list(&ref, ref_compare_name);
+ qsort(sought, nr_sought, sizeof(*sought), cmp_ref_by_name);
if (is_repository_shallow() && !server_supports("shallow"))
die("Server does not support shallow clients");
fprintf(stderr, "Server supports side-band\n");
use_sideband = 1;
}
+ if (server_supports("allow-tip-sha1-in-want")) {
+ if (args->verbose)
+ fprintf(stderr, "Server supports allow-tip-sha1-in-want\n");
+ allow_tip_sha1_in_want = 1;
+ }
if (!server_supports("thin-pack"))
args->use_thin_pack = 0;
if (!server_supports("no-progress"))
agent_len, agent_feature);
}
- if (everything_local(args, &ref, sought)) {
+ if (everything_local(args, &ref, sought, nr_sought)) {
packet_flush(fd[1]);
goto all_done;
}
did_setup = 1;
}
+static int remove_duplicates_in_refs(struct ref **ref, int nr)
+{
+ struct string_list names = STRING_LIST_INIT_NODUP;
+ int src, dst;
+
+ for (src = dst = 0; src < nr; src++) {
+ struct string_list_item *item;
+ item = string_list_insert(&names, ref[src]->name);
+ if (item->util)
+ continue; /* already have it */
+ item->util = ref[src];
+ if (src != dst)
+ ref[dst] = ref[src];
+ dst++;
+ }
+ for (src = dst; src < nr; src++)
+ ref[src] = NULL;
+ string_list_clear(&names, 0);
+ return dst;
+}
+
struct ref *fetch_pack(struct fetch_pack_args *args,
int fd[], struct child_process *conn,
const struct ref *ref,
const char *dest,
- struct string_list *sought,
+ struct ref **sought, int nr_sought,
char **pack_lockfile)
{
struct stat st;
st.st_mtime = 0;
}
- if (sought->nr) {
- sort_string_list(sought);
- string_list_remove_duplicates(sought, 0);
- }
+ if (nr_sought)
+ nr_sought = remove_duplicates_in_refs(sought, nr_sought);
if (!ref) {
packet_flush(fd[1]);
die("no matching remote head");
}
- ref_cpy = do_fetch_pack(args, fd, ref, sought, pack_lockfile);
+ ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, pack_lockfile);
if (args->depth > 0) {
static struct lock_file lock;
return;
for (;;) {
- int cmp = cmp, len;
+ int cmp = 0; /* assigned before used */
+ int len;
if (!fgets(buffer, sizeof(buffer), f)) {
fclose(f);
struct ref *refs;
connect_setup(transport, for_push, 0);
- get_remote_heads(data->fd[0], &refs,
+ get_remote_heads(data->fd[0], NULL, 0, &refs,
for_push ? REF_NORMAL : 0, &data->extra_have);
data->got_remote_heads = 1;
int nr_heads, struct ref **to_fetch)
{
struct git_transport_data *data = transport->data;
- struct string_list sought = STRING_LIST_INIT_DUP;
const struct ref *refs;
char *dest = xstrdup(transport->url);
struct fetch_pack_args args;
- int i;
struct ref *refs_tmp = NULL;
memset(&args, 0, sizeof(args));
args.no_progress = !transport->progress;
args.depth = data->options.depth;
- for (i = 0; i < nr_heads; i++)
- string_list_append(&sought, to_fetch[i]->name);
-
if (!data->got_remote_heads) {
connect_setup(transport, 0, 0);
- get_remote_heads(data->fd[0], &refs_tmp, 0, NULL);
+ get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0, NULL);
data->got_remote_heads = 1;
}
refs = fetch_pack(&args, data->fd, data->conn,
refs_tmp ? refs_tmp : transport->remote_refs,
- dest, &sought, &transport->pack_lockfile);
+ dest, to_fetch, nr_heads,
+ &transport->pack_lockfile);
close(data->fd[0]);
close(data->fd[1]);
if (finish_connect(data->conn))
free_refs(refs_tmp);
- string_list_clear(&sought, 0);
free(dest);
return (refs ? 0 : -1);
}
struct ref *tmp_refs;
connect_setup(transport, 1, 0);
- get_remote_heads(data->fd[0], &tmp_refs, REF_NORMAL, NULL);
+ get_remote_heads(data->fd[0], NULL, 0, &tmp_refs, REF_NORMAL, NULL);
data->got_remote_heads = 1;
}
match_flags |= MATCH_REFS_MIRROR;
if (flags & TRANSPORT_PUSH_PRUNE)
match_flags |= MATCH_REFS_PRUNE;
+ if (flags & TRANSPORT_PUSH_FOLLOW_TAGS)
+ match_flags |= MATCH_REFS_FOLLOW_TAGS;
if (match_push_refs(local_refs, &remote_refs,
refspec_nr, refspec, match_flags)) {
#define SHALLOW (1u << 16)
#define NOT_SHALLOW (1u << 17)
#define CLIENT_SHALLOW (1u << 18)
+#define HIDDEN_REF (1u << 19)
static unsigned long oldest_have;
static int no_done;
static int use_thin_pack, use_ofs_delta, use_include_tag;
static int no_progress, daemon_mode;
+static int allow_tip_sha1_in_want;
static int shallow_nr;
static struct object_array have_obj;
static struct object_array want_obj;
* otherwise maximum packet size (up to 65520 bytes).
*/
static int use_sideband;
- static int debug_fd;
static int advertise_refs;
static int stateless_rpc;
alarm(timeout);
}
- static int strip(char *line, int len)
- {
- if (len && line[len-1] == '\n')
- line[--len] = 0;
- return len;
- }
-
static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
{
if (use_sideband)
xwrite(fd, data, sz);
return sz;
}
- return safe_write(fd, data, sz);
+ write_or_die(fd, data, sz);
+ return sz;
}
static FILE *pack_pipe = NULL;
if (!has_sha1_file(sha1))
return -1;
- o = lookup_object(sha1);
- if (!(o && o->parsed))
- o = parse_object(sha1);
+ o = parse_object(sha1);
if (!o)
die("oops (%s)", sha1_to_hex(sha1));
if (o->type == OBJ_COMMIT) {
static int get_common_commits(void)
{
- static char line[1000];
unsigned char sha1[20];
char last_hex[41];
int got_common = 0;
save_commit_buffer = 0;
for (;;) {
- int len = packet_read_line(0, line, sizeof(line));
+ char *line = packet_read_line(0, NULL);
reset_timeout();
- if (!len) {
+ if (!line) {
if (multi_ack == 2 && got_common
&& !got_other && ok_to_give_up()) {
sent_ready = 1;
got_other = 0;
continue;
}
- strip(line, len);
if (!prefixcmp(line, "have ")) {
switch (got_sha1(line+5, sha1)) {
case -1: /* they have what we do not */
}
}
+static int is_our_ref(struct object *o)
+{
+ return o->flags &
+ ((allow_tip_sha1_in_want ? HIDDEN_REF : 0) | OUR_REF);
+}
+
static void check_non_tip(void)
{
static const char *argv[] = {
o = get_indexed_object(--i);
if (!o)
continue;
- if (!(o->flags & OUR_REF))
+ if (!is_our_ref(o))
continue;
memcpy(namebuf + 1, sha1_to_hex(o->sha1), 40);
if (write_in_full(cmd.in, namebuf, 42) < 0)
namebuf[40] = '\n';
for (i = 0; i < want_obj.nr; i++) {
o = want_obj.objects[i].item;
- if (o->flags & OUR_REF)
+ if (is_our_ref(o))
continue;
memcpy(namebuf, sha1_to_hex(o->sha1), 40);
if (write_in_full(cmd.in, namebuf, 41) < 0)
/* Pick one of them (we know there at least is one) */
for (i = 0; i < want_obj.nr; i++) {
o = want_obj.objects[i].item;
- if (!(o->flags & OUR_REF))
+ if (!is_our_ref(o))
die("git upload-pack: not our ref %s",
sha1_to_hex(o->sha1));
}
static void receive_needs(void)
{
struct object_array shallows = OBJECT_ARRAY_INIT;
- static char line[1000];
- int len, depth = 0;
+ int depth = 0;
int has_non_tip = 0;
shallow_nr = 0;
- if (debug_fd)
- write_str_in_full(debug_fd, "#S\n");
for (;;) {
struct object *o;
const char *features;
unsigned char sha1_buf[20];
- len = packet_read_line(0, line, sizeof(line));
+ char *line = packet_read_line(0, NULL);
reset_timeout();
- if (!len)
+ if (!line)
break;
- if (debug_fd)
- write_in_full(debug_fd, line, len);
if (!prefixcmp(line, "shallow ")) {
unsigned char sha1[20];
struct object *object;
- if (get_sha1(line + 8, sha1))
+ if (get_sha1_hex(line + 8, sha1))
die("invalid shallow line: %s", line);
object = parse_object(sha1);
if (!object)
die("did not find object for %s", line);
if (object->type != OBJ_COMMIT)
die("invalid shallow object %s", sha1_to_hex(sha1));
- object->flags |= CLIENT_SHALLOW;
- add_object_array(object, NULL, &shallows);
+ if (!(object->flags & CLIENT_SHALLOW)) {
+ object->flags |= CLIENT_SHALLOW;
+ add_object_array(object, NULL, &shallows);
+ }
continue;
}
if (!prefixcmp(line, "deepen ")) {
if (parse_feature_request(features, "include-tag"))
use_include_tag = 1;
- o = lookup_object(sha1_buf);
+ o = parse_object(sha1_buf);
if (!o)
die("git upload-pack: not our ref %s",
sha1_to_hex(sha1_buf));
if (!(o->flags & WANTED)) {
o->flags |= WANTED;
- if (!(o->flags & OUR_REF))
+ if (!is_our_ref(o))
has_non_tip = 1;
add_object_array(o, NULL, &want_obj);
}
}
- if (debug_fd)
- write_str_in_full(debug_fd, "#E\n");
/*
* We have sent all our refs already, and the other end
{
struct object *o = lookup_unknown_object(sha1);
- if (ref_is_hidden(refname))
+ if (ref_is_hidden(refname)) {
+ o->flags |= HIDDEN_REF;
return 1;
+ }
if (!o)
die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
o->flags |= OUR_REF;
return 0;
if (capabilities)
- packet_write(1, "%s %s%c%s%s agent=%s\n",
+ packet_write(1, "%s %s%c%s%s%s agent=%s\n",
sha1_to_hex(sha1), refname_nons,
0, capabilities,
+ allow_tip_sha1_in_want ? " allow-tip-sha1-in-want" : "",
stateless_rpc ? " no-done" : "",
git_user_agent_sanitized());
else
static int upload_pack_config(const char *var, const char *value, void *unused)
{
+ if (!strcmp("uploadpack.allowtipsha1inwant", var))
+ allow_tip_sha1_in_want = git_config_bool(var, value);
return parse_hide_refs_config(var, value, "uploadpack");
}
if (is_repository_shallow())
die("attempt to fetch/clone from a shallow repository");
git_config(upload_pack_config, NULL);
- if (getenv("GIT_DEBUG_SEND_PACK"))
- debug_fd = atoi(getenv("GIT_DEBUG_SEND_PACK"));
upload_pack();
return 0;
}