From 960deccb26a5bee6c6cd63d50e8272f540a27b19 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 19 Oct 2005 14:27:01 -0700 Subject: [PATCH 01/11] git-daemon: timeout, eliminate double DWIM It turns out that not only did git-daemon do DWIM, but git-upload-pack does as well. This is bad; security checks have to be performed *after* canonicalization, not before. Additionally, the current git-daemon can be trivially DoSed by spewing SYNs at the target port. This patch adds a --strict option to git-upload-pack to disable all DWIM, a --timeout option to git-daemon and git-upload-pack, and an --init-timeout option to git-daemon (which is typically set to a much lower value, since the initial request should come immediately from the client.) Signed-off-by: H. Peter Anvin Signed-off-by: Junio C Hamano --- daemon.c | 21 +++++++++++++++++++-- upload-pack.c | 45 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/daemon.c b/daemon.c index 9ea6c31cd1..c3381b344c 100644 --- a/daemon.c +++ b/daemon.c @@ -13,7 +13,9 @@ static int log_syslog; static int verbose; -static const char daemon_usage[] = "git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all] [directory...]"; +static const char daemon_usage[] = +"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n" +" [--timeout=n] [--init-timeout=n] [directory...]"; /* List of acceptable pathname prefixes */ static char **ok_paths = NULL; @@ -21,6 +23,9 @@ static char **ok_paths = NULL; /* If this is set, git-daemon-export-ok is not required */ static int export_all_trees = 0; +/* Timeout, and initial timeout */ +static unsigned int timeout = 0; +static unsigned int init_timeout = 0; static void logreport(int priority, const char *err, va_list params) { @@ -170,6 +175,8 @@ static int upload(char *dir) /* Enough for the longest path above including final null */ int buflen = strlen(dir)+10; char *dirbuf = xmalloc(buflen); + /* Timeout as string */ + char timeout_buf[64]; loginfo("Request for '%s'", dir); @@ -190,8 +197,10 @@ static int upload(char *dir) */ signal(SIGTERM, SIG_IGN); + snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout); + /* git-upload-pack only ever reads stuff, so this is safe */ - execlp("git-upload-pack", "git-upload-pack", ".", NULL); + execlp("git-upload-pack", "git-upload-pack", "--strict", timeout_buf, ".", NULL); return -1; } @@ -200,7 +209,9 @@ static int execute(void) static char line[1000]; int len; + alarm(init_timeout ? init_timeout : timeout); len = packet_read_line(0, line, sizeof(line)); + alarm(0); if (len && line[len-1] == '\n') line[--len] = 0; @@ -598,6 +609,12 @@ int main(int argc, char **argv) export_all_trees = 1; continue; } + if (!strncmp(arg, "--timeout=", 10)) { + timeout = atoi(arg+10); + } + if (!strncmp(arg, "--init-timeout=", 10)) { + init_timeout = atoi(arg+15); + } if (!strcmp(arg, "--")) { ok_paths = &argv[i+1]; break; diff --git a/upload-pack.c b/upload-pack.c index 21b4b8b757..8a41cafaaa 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -4,13 +4,19 @@ #include "tag.h" #include "object.h" -static const char upload_pack_usage[] = "git-upload-pack "; +static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] "; #define MAX_HAS (16) #define MAX_NEEDS (256) static int nr_has = 0, nr_needs = 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) { @@ -100,6 +106,7 @@ static int get_common_commits(void) for(;;) { len = packet_read_line(0, line, sizeof(line)); + reset_timeout(); if (!len) { packet_write(1, "NAK\n"); @@ -122,6 +129,7 @@ static int get_common_commits(void) for (;;) { len = packet_read_line(0, line, sizeof(line)); + reset_timeout(); if (!len) continue; len = strip(line, len); @@ -145,6 +153,7 @@ static int receive_needs(void) for (;;) { unsigned char dummy[20], *sha1_buf; len = packet_read_line(0, line, sizeof(line)); + reset_timeout(); if (!len) return needs; @@ -179,6 +188,7 @@ static int send_ref(const char *refname, const unsigned char *sha1) static int upload_pack(void) { + reset_timeout(); head_ref(send_ref); for_each_ref(send_ref); packet_flush(1); @@ -193,18 +203,43 @@ static int upload_pack(void) int main(int argc, char **argv) { const char *dir; - if (argc != 2) + 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[1]; + dir = argv[i]; /* chdir to the directory. If that fails, try appending ".git" */ if (chdir(dir) < 0) { - if (chdir(mkpath("%s.git", dir)) < 0) + if (strict || chdir(mkpath("%s.git", dir)) < 0) die("git-upload-pack unable to chdir to %s", dir); } - chdir(".git"); + if (!strict) + chdir(".git"); + if (access("objects", X_OK) || access("refs", X_OK)) die("git-upload-pack: %s doesn't seem to be a git archive", dir); + putenv("GIT_DIR=."); upload_pack(); return 0; From e0004e286c50364f134c951754c34e815489ead0 Mon Sep 17 00:00:00 2001 From: Nick Hengeveld Date: Wed, 19 Oct 2005 14:27:01 -0700 Subject: [PATCH 02/11] Support for HTTP transfer timeouts based on transfer speed Add configuration settings to abort HTTP requests if the transfer rate drops below a threshold for a specified length of time. Environment variables override config file settings. Signed-off-by: Nick Hengeveld Signed-off-by: Junio C Hamano --- http-fetch.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/http-fetch.c b/http-fetch.c index efa6e82329..a7dc2cc3bd 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -100,6 +100,8 @@ static char *ssl_key = NULL; static char *ssl_capath = NULL; #endif static char *ssl_cainfo = NULL; +static long curl_low_speed_limit = -1; +static long curl_low_speed_time = -1; struct buffer { @@ -158,6 +160,17 @@ static int http_options(const char *var, const char *value) } #endif + if (!strcmp("http.lowspeedlimit", var)) { + if (curl_low_speed_limit == -1) + curl_low_speed_limit = (long)git_config_int(var, value); + return 0; + } + if (!strcmp("http.lowspeedtime", var)) { + if (curl_low_speed_time == -1) + curl_low_speed_time = (long)git_config_int(var, value); + return 0; + } + /* Fall back on the default ones */ return git_default_config(var, value); } @@ -246,6 +259,13 @@ static CURL* get_curl_handle(void) curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo); curl_easy_setopt(result, CURLOPT_FAILONERROR, 1); + if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) { + curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT, + curl_low_speed_limit); + curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME, + curl_low_speed_time); + } + return result; } @@ -1177,6 +1197,8 @@ int main(int argc, char **argv) char *url; int arg = 1; struct active_request_slot *slot; + char *low_speed_limit; + char *low_speed_time; while (arg < argc && argv[arg][0] == '-') { if (argv[arg][1] == 't') { @@ -1232,6 +1254,13 @@ int main(int argc, char **argv) #endif ssl_cainfo = getenv("GIT_SSL_CAINFO"); + low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT"); + if (low_speed_limit != NULL) + curl_low_speed_limit = strtol(low_speed_limit, NULL, 10); + low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME"); + if (low_speed_time != NULL) + curl_low_speed_time = strtol(low_speed_time, NULL, 10); + git_config(http_options); if (curl_ssl_verify == -1) From 49bb805e69f97e75472e54a68e9eb24e08dee011 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Oct 2005 14:27:02 -0700 Subject: [PATCH 03/11] Do not ask for objects known to be complete. On top of optimization by Linus not to ask refs that already match, we can walk our refs and not issue "want" for things that are known to be reachable from them. Signed-off-by: Junio C Hamano --- fetch-pack.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/fetch-pack.c b/fetch-pack.c index 459736909c..969e72a781 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -1,6 +1,9 @@ #include "cache.h" #include "refs.h" #include "pkt-line.h" +#include "commit.h" +#include "tag.h" +#include #include static int quiet; @@ -78,16 +81,73 @@ static int find_common(int fd[2], unsigned char *result_sha1, return retval; } +#define COMPLETE (1U << 0) +static struct commit_list *complete = NULL; + +static int mark_complete(const char *path, const unsigned char *sha1) +{ + struct object *o = parse_object(sha1); + + while (o && o->type == tag_type) { + o->flags |= COMPLETE; + o = parse_object(((struct tag *)o)->tagged->sha1); + } + if (o->type == commit_type) { + struct commit *commit = (struct commit *)o; + commit->object.flags |= COMPLETE; + insert_by_date(commit, &complete); + } + return 0; +} + +static void mark_recent_complete_commits(unsigned long cutoff) +{ + while (complete && cutoff <= complete->item->date) { + if (verbose) + fprintf(stderr, "Marking %s as complete\n", + sha1_to_hex(complete->item->object.sha1)); + pop_most_recent_commit(&complete, COMPLETE); + } +} + static int everything_local(struct ref *refs) { + struct ref *ref; int retval; + unsigned long cutoff = 0; + + track_object_refs = 0; + save_commit_buffer = 0; + + for (ref = refs; ref; ref = ref->next) { + struct object *o; + + o = parse_object(ref->old_sha1); + if (!o) + continue; + + /* We already have it -- which may mean that we were + * in sync with the other side at some time after + * that (it is OK if we guess wrong here). + */ + if (o->type == commit_type) { + struct commit *commit = (struct commit *)o; + if (!cutoff || cutoff < commit->date) + cutoff = commit->date; + } + } + + for_each_ref(mark_complete); + if (cutoff) + mark_recent_complete_commits(cutoff); for (retval = 1; refs ; refs = refs->next) { const unsigned char *remote = refs->old_sha1; unsigned char local[20]; + struct object *o; - if (read_ref(git_path("%s", refs->name), local) < 0 || - memcmp(remote, local, 20)) { + o = parse_object(remote); + if (!o || !(o->flags & COMPLETE)) { retval = 0; if (!verbose) continue; From e1c7ada6dd1fdf249d0bb84f3293d3be768b9239 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Oct 2005 14:43:43 -0700 Subject: [PATCH 04/11] git-clone: always keep pack sent from remote. This deprecates --keep and -q flags and always keeps the pack sent from the remote site. Corresponding configuration variables are also removed. Signed-off-by: Junio C Hamano --- clone-pack.c | 72 +++------------------------------------------------- 1 file changed, 4 insertions(+), 68 deletions(-) diff --git a/clone-pack.c b/clone-pack.c index f9b263a441..4f4975b4ab 100644 --- a/clone-pack.c +++ b/clone-pack.c @@ -3,10 +3,8 @@ #include "pkt-line.h" #include -static int quiet; -static int keep_pack; static const char clone_pack_usage[] = -"git-clone-pack [-q] [--keep] [--exec=] [:] []*"; +"git-clone-pack [--exec=] [:] []*"; static const char *exec = "git-upload-pack"; static void clone_handshake(int fd[2], struct ref *ref) @@ -114,41 +112,6 @@ static void write_refs(struct ref *ref) free(head_path); } -static int clone_by_unpack(int fd[2]) -{ - int status; - pid_t pid; - - pid = fork(); - if (pid < 0) - die("git-clone-pack: unable to fork off git-unpack-objects"); - if (!pid) { - dup2(fd[0], 0); - close(fd[0]); - close(fd[1]); - execlp("git-unpack-objects", "git-unpack-objects", - quiet ? "-q" : NULL, NULL); - die("git-unpack-objects exec failed"); - } - close(fd[0]); - close(fd[1]); - while (waitpid(pid, &status, 0) < 0) { - if (errno != EINTR) - die("waiting for git-unpack-objects: %s", strerror(errno)); - } - if (WIFEXITED(status)) { - int code = WEXITSTATUS(status); - if (code) - die("git-unpack-objects died with error code %d", code); - return 0; - } - if (WIFSIGNALED(status)) { - int sig = WTERMSIG(status); - die("git-unpack-objects died of signal %d", sig); - } - die("Sherlock Holmes! git-unpack-objects died of unnatural causes %d!", status); -} - static int finish_pack(const char *pack_tmp_name) { int pipe_fd[2]; @@ -294,35 +257,13 @@ static int clone_pack(int fd[2], int nr_match, char **match) } clone_handshake(fd, refs); - if (keep_pack) - status = clone_without_unpack(fd); - else - status = clone_by_unpack(fd); + status = clone_without_unpack(fd); if (!status) write_refs(refs); return status; } -static int clone_options(const char *var, const char *value) -{ - if (!strcmp("clone.keeppack", var)) { - keep_pack = git_config_bool(var, value); - return 0; - } - if (!strcmp("clone.quiet", var)) { - quiet = git_config_bool(var, value); - return 0; - } - /* - * Put other local option parsing for this program - * here ... - */ - - /* Fall back on the default ones */ - return git_default_config(var, value); -} - int main(int argc, char **argv) { int i, ret, nr_heads; @@ -330,25 +271,20 @@ int main(int argc, char **argv) int fd[2]; pid_t pid; - git_config(clone_options); nr_heads = 0; heads = NULL; for (i = 1; i < argc; i++) { char *arg = argv[i]; if (*arg == '-') { - if (!strcmp("-q", arg)) { - quiet = 1; + if (!strcmp("-q", arg)) continue; - } if (!strncmp("--exec=", arg, 7)) { exec = arg + 7; continue; } - if (!strcmp("--keep", arg)) { - keep_pack = 1; + if (!strcmp("--keep", arg)) continue; - } usage(clone_pack_usage); } dest = arg; From 76e712f1b38c6bfa744ee55fb46dfdb45ad3646e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Oct 2005 14:43:43 -0700 Subject: [PATCH 05/11] git-clone: always keep pack sent from remote (documentation). This adjusts the documentation. Signed-off-by: Junio C Hamano --- Documentation/git-clone-pack.txt | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/Documentation/git-clone-pack.txt b/Documentation/git-clone-pack.txt index b58165a5fc..cfc7b62f31 100644 --- a/Documentation/git-clone-pack.txt +++ b/Documentation/git-clone-pack.txt @@ -8,26 +8,17 @@ git-clone-pack - Clones a repository by receiving packed objects. SYNOPSIS -------- -'git-clone-pack' [-q] [--keep] [--exec=] [:] [...] +'git-clone-pack' [--exec=] [:] [...] DESCRIPTION ----------- Clones a repository into the current repository by invoking 'git-upload-pack', possibly on the remote host via ssh, in -the named repository, and invoking 'git-unpack-objects' locally -to receive the pack. +the named repository, and stores the sent pack in the local +repository. OPTIONS ------- --q:: - Pass '-q' flag to 'git-unpack-objects'; this makes the - cloning process less verbose. - ---keep:: - Do not invoke 'git-unpack-objects' on received data, but - create a single packfile out of it instead, and store it - in the object database. - --exec=:: Use this to specify the path to 'git-upload-pack' on the remote side, if it is not found on your $PATH. From d6a461e17758f369cd7b76e78262b1717cc5c39c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Oct 2005 15:01:50 -0700 Subject: [PATCH 06/11] count-objects: squelch error from find on sparse object directory. Signed-off-by: Junio C Hamano --- git-count-objects.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-count-objects.sh b/git-count-objects.sh index 74ee4f371f..843d2fd9f2 100755 --- a/git-count-objects.sh +++ b/git-count-objects.sh @@ -2,7 +2,7 @@ . git-sh-setup -echo $(find "$GIT_DIR/objects"/?? -type f -print | wc -l) objects, \ +echo $(find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null | wc -l) objects, \ $({ echo 0 # "no-such" is to help Darwin folks by not using xargs -r. From 0a8944dd48b97d258240af967dfdfcea4f203b85 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 19 Oct 2005 16:14:34 -0700 Subject: [PATCH 07/11] [PATCH] Do not send "want" lines for complete objects It was all good and well to check if all remote refs are complete (local refs or descendants thereof), but we can just as easily use the same information to avoid sending "want" lines just for the complete objects in the case that not all remote refs are complete (or their names differ). Also, git-fetch-pack does not have to ask for descendants of remote refs which are complete (for now, git-rev-list is told to ignore only the first parent). That change also eliminates a code path where a popen()ed handle was not pclose()ed. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- fetch-pack.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/fetch-pack.c b/fetch-pack.c index 969e72a781..9dfd072db7 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -12,31 +12,49 @@ static const char fetch_pack_usage[] = "git-fetch-pack [-q] [-v] [--exec=upload-pack] [host:]directory ..."; static const char *exec = "git-upload-pack"; +#define COMPLETE (1U << 0) + static int find_common(int fd[2], unsigned char *result_sha1, struct ref *refs) { int fetching; static char line[1000]; - int count = 0, flushes = 0, retval; + static char rev_command[1024]; + int count = 0, flushes = 0, retval, rev_command_len; FILE *revs; - revs = popen("git-rev-list $(git-rev-parse --all)", "r"); - if (!revs) - die("unable to run 'git-rev-list'"); - + strcpy(rev_command, "git-rev-list $(git-rev-parse --all)"); + rev_command_len = strlen(rev_command); fetching = 0; for ( ; refs ; refs = refs->next) { unsigned char *remote = refs->old_sha1; - unsigned char *local = refs->new_sha1; - if (!memcmp(remote, local, 20)) + /* + If that object is complete (i.e. it is a descendant of a + local ref), we don't want it, nor its descendants. + */ + if (has_sha1_file(remote) + && parse_object(remote)->flags & COMPLETE) { + if (rev_command_len + 44 < sizeof(rev_command)) { + snprintf(rev_command + rev_command_len, 44, + " ^%s^", sha1_to_hex(remote)); + rev_command_len += 43; + } + continue; + } + packet_write(fd[1], "want %s\n", sha1_to_hex(remote)); fetching++; } packet_flush(fd[1]); if (!fetching) return 1; + + revs = popen(rev_command, "r"); + if (!revs) + die("unable to run 'git-rev-list'"); + flushes = 1; retval = -1; while (fgets(line, sizeof(line), revs) != NULL) { @@ -81,7 +99,6 @@ static int find_common(int fd[2], unsigned char *result_sha1, return retval; } -#define COMPLETE (1U << 0) static struct commit_list *complete = NULL; static int mark_complete(const char *path, const unsigned char *sha1) From 4dab94d52e25b6a53a715613ec5b06b42b240106 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Oct 2005 18:28:17 -0700 Subject: [PATCH 08/11] Do not feed rev-list an invalid SHA1 expression. The previous round to optimize fetch-pack has a small bug that feeds SHA1^ ("parent commit") before making sure SHA1 is actually a commit (or a tag that eventually dereferences to a commit). Also it did not help culling the known-to-be-common parents if the common one was a merge. Signed-off-by: Junio C Hamano --- fetch-pack.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/fetch-pack.c b/fetch-pack.c index 9dfd072db7..4ee91f2ca2 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -28,22 +28,36 @@ static int find_common(int fd[2], unsigned char *result_sha1, fetching = 0; for ( ; refs ; refs = refs->next) { unsigned char *remote = refs->old_sha1; + struct object *o; /* - If that object is complete (i.e. it is a descendant of a - local ref), we don't want it, nor its descendants. - */ - if (has_sha1_file(remote) - && parse_object(remote)->flags & COMPLETE) { - if (rev_command_len + 44 < sizeof(rev_command)) { + * If that object is complete (i.e. it is an ancestor of a + * local ref), we tell them we have it but do not have to + * tell them about its ancestors, which they already know + * about. + */ + if (has_sha1_file(remote) && + ((o = parse_object(remote)) != NULL) && + (o->flags & COMPLETE)) { + struct commit_list *p; + struct commit *commit = + (struct commit *) (o = deref_tag(o)); + if (!o) + goto repair; + if (o->type != commit_type) + continue; + p = commit->parents; + while (p && + rev_command_len + 44 < sizeof(rev_command)) { snprintf(rev_command + rev_command_len, 44, - " ^%s^", sha1_to_hex(remote)); + " ^%s", + sha1_to_hex(p->item->object.sha1)); rev_command_len += 43; + p = p->next; } - continue; } - + repair: packet_write(fd[1], "want %s\n", sha1_to_hex(remote)); fetching++; } From d6a73596e766b934e25182e7cdd8e6c2dbcc671e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Oct 2005 21:25:39 -0700 Subject: [PATCH 09/11] git-fetch/push/pull: documentation. The documentation was lazily sharing the argument description across these commands. Lazy may be a way of life, but that does not justify confusing others ;-). Signed-off-by: Junio C Hamano --- Documentation/git-fetch.txt | 5 +++++ Documentation/git-pull.txt | 4 ++++ Documentation/git-push.txt | 9 +++++++++ Documentation/pull-fetch-param.txt | 10 ---------- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt index c0b5aac5f2..71693650c4 100644 --- a/Documentation/git-fetch.txt +++ b/Documentation/git-fetch.txt @@ -25,6 +25,11 @@ OPTIONS ------- include::pull-fetch-param.txt[] +-a, \--append:: + Append ref names and object names of fetched refs to the + existing contents of $GIT_DIR/FETCH_HEAD. Without this + option old data in $GIT_DIR/FETCH_HEAD will be overwritten. + -u, \--update-head-ok:: By default 'git-fetch' refuses to update the head which corresponds to the current branch. This flag disables the diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index 952779292b..bae05dee99 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -24,6 +24,10 @@ OPTIONS ------- include::pull-fetch-param.txt[] +-a, \--append:: + Append ref names and object names of fetched refs to the + existing contents of $GIT_DIR/FETCH_HEAD. Without this + option old data in $GIT_DIR/FETCH_HEAD will be overwritten. Author ------ diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 809ac8ba06..f45ac5ee49 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -21,6 +21,15 @@ OPTIONS ------- include::pull-fetch-param.txt[] +\--all:: + Instead of naming each ref to push, specifies all refs + to be pushed. + +-f, \--force:: + Usually, the command refuses to update a local ref that is + not an ancestor of the remote ref used to overwrite it. + This flag disables the check. What this means is that the + local repository can lose commits; use it with care. Author ------ diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt index 51222b6f3f..e8db9d7ca5 100644 --- a/Documentation/pull-fetch-param.txt +++ b/Documentation/pull-fetch-param.txt @@ -75,13 +75,3 @@ Some short-cut notations are also supported. pushing. That is, do not store it locally if fetching, and update the same name if pushing. --a, \--append:: - Append ref names and object names of fetched refs to the - existing contents of $GIT_DIR/FETCH_HEAD. Without this - option old data in $GIT_DIR/FETCH_HEAD will be overwritten. - --f, \--force:: - Usually, the command refuses to update a local ref that is - not an ancestor of the remote ref used to overwrite it. - This flag disables the check. What this means is that the - local repository can lose commits; use it with care. From f1f0a2be9fe6e6734d900381de189c56e023be7e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Oct 2005 21:55:49 -0700 Subject: [PATCH 10/11] Be more careful tangling object chains while marking commits. Also Johannes noticed we use parse_object to look up if we know that object already -- we should just ask the in-core object registry with lookup_object() for that. Signed-off-by: Junio C Hamano --- fetch-pack.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/fetch-pack.c b/fetch-pack.c index 4ee91f2ca2..8566ab1744 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -35,9 +35,12 @@ static int find_common(int fd[2], unsigned char *result_sha1, * local ref), we tell them we have it but do not have to * tell them about its ancestors, which they already know * about. + * + * We use lookup_object here because we are only + * interested in the case we *know* the object is + * reachable and we have already scanned it. */ - if (has_sha1_file(remote) && - ((o = parse_object(remote)) != NULL) && + if (((o = lookup_object(remote)) != NULL) && (o->flags & COMPLETE)) { struct commit_list *p; struct commit *commit = @@ -120,10 +123,13 @@ static int mark_complete(const char *path, const unsigned char *sha1) struct object *o = parse_object(sha1); while (o && o->type == tag_type) { + struct tag *t = (struct tag *) o; + if (!t->tagged) + break; /* broken repository */ o->flags |= COMPLETE; - o = parse_object(((struct tag *)o)->tagged->sha1); + o = parse_object(t->tagged->sha1); } - if (o->type == commit_type) { + if (o && o->type == commit_type) { struct commit *commit = (struct commit *)o; commit->object.flags |= COMPLETE; insert_by_date(commit, &complete); From 6e1c6c103c522d01829f3a63992a023ff031e851 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Oct 2005 22:48:16 -0700 Subject: [PATCH 11/11] Make sure we barf on ref^{type} failure. Martin Langhoff noticed that ref^0 barfed correctly when we did not have the commit in a broken repository, but ref^{commit} didn't. Signed-off-by: Junio C Hamano --- sha1_name.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sha1_name.c b/sha1_name.c index 75c688ecf2..cc320d3d7f 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -323,6 +323,8 @@ static int peel_onion(const char *name, int len, unsigned char *sha1) return -1; if (!type_string) { o = deref_tag(o); + if (!o || (!o->parsed && !parse_object(o->sha1))) + return -1; memcpy(sha1, o->sha1, 20); } else { @@ -332,7 +334,7 @@ static int peel_onion(const char *name, int len, unsigned char *sha1) */ while (1) { - if (!o) + if (!o || (!o->parsed && !parse_object(o->sha1))) return -1; if (o->type == type_string) { memcpy(sha1, o->sha1, 20);