author | Junio C Hamano <junkio@cox.net> | |
Fri, 21 Oct 2005 06:19:47 +0000 (23:19 -0700) | ||
committer | Junio C Hamano <junkio@cox.net> | |
Fri, 21 Oct 2005 06:19:47 +0000 (23:19 -0700) |
27 files changed:
index 6ecb089c5cc4cf8dcaa8005c0ee6e4208bced346..36f42e051c610a89c5bbebb56c52817815c81adf 100644 (file)
inspect that with `ls`. For your new empty project, it should show you
three entries, among other things:
- - a symlink called `HEAD`, pointing to `refs/heads/master` (if your
- platform does not have native symlinks, it is a file containing the
- line "ref: refs/heads/master")
+ - a symlink called `HEAD`, pointing to `refs/heads/master`
+
Don't worry about the fact that the file that the `HEAD` link points to
doesn't even exist yet -- you haven't created the commit that will
------------
diff --git a/hello b/hello
-index 557db03..263414f 100644
--- a/hello
+++ b/hello
@@ -1 +1,2 @@
on its standard input, and it will write out the resulting object name for the
commit to its standard output.
-And this is where we create the `.git/refs/heads/master` file
-which is pointed at by `HEAD`. This file is supposed to contain
-the reference to the top-of-tree of the master branch, and since
-that's exactly what `git-commit-tree` spits out, we can do this
-all with a sequence of simple shell commands:
+And this is where we start using the `.git/HEAD` file. The `HEAD` file is
+supposed to contain the reference to the top-of-tree, and since that's
+exactly what `git-commit-tree` spits out, we can do this all with a simple
+shell pipeline:
------------------------------------------------
-tree=$(git-write-tree)
-commit=$(echo 'Initial commit' | git-commit-tree $tree)
-git-update-ref HEAD $(commit)
+echo "Initial commit" | git-commit-tree $(git-write-tree) > .git/HEAD
------------------------------------------------
which will say:
just telling `git checkout` what the base of the checkout would be.
In other words, if you have an earlier tag or branch, you'd just do
-------------
-git checkout -b mybranch earlier-commit
-------------
+ git checkout -b mybranch earlier-commit
and it would create the new branch `mybranch` at the earlier commit,
and check out the state at that time.
You can always just jump back to your original `master` branch by doing
-------------
-git checkout master
-------------
+ git checkout master
(or any other branch-name, for that matter) and if you forget which
branch you happen to be on, a simple
-------------
-ls -l .git/HEAD
-------------
+ ls -l .git/HEAD
-will tell you where it's pointing (Note that on platforms with bad or no
-symlink support, you have to execute
+will tell you where it's pointing. To get the list of branches
+you have, you can say
-------------
-cat .git/HEAD
-------------
-
-instead). To get the list of branches you have, you can say
-
-------------
-git branch
-------------
+ git branch
which is nothing more than a simple script around `ls .git/refs/heads`.
There will be asterisk in front of the branch you are currently on.
Sometimes you may wish to create a new branch _without_ actually
checking it out and switching to it. If so, just use the command
-------------
-git branch <branchname> [startingpoint]
-------------
+ git branch <branchname> [startingpoint]
which will simply _create_ the branch, but will not do anything further.
You can then later -- once you decide that you want to actually develop
! [mybranch] Some work.
--
+ [master] Merged "mybranch" changes.
++ [master~1] Some fun.
++ [mybranch] Some work.
------------------------------------------------
to the `master` branch. Let's go back to `mybranch`, and run
resolve to get the "upstream changes" back to your branch.
-------------
-git checkout mybranch
-git resolve HEAD master "Merge upstream changes."
-------------
+ git checkout mybranch
+ git resolve HEAD master "Merge upstream changes."
This outputs something like this (the actual commit object names
would be different)
project `my-git`. After logging into the remote machine, create
an empty directory:
-------------
-mkdir my-git.git
-------------
+ mkdir my-git.git
Then, make that directory into a GIT repository by running
`git init-db`, but this time, since its name is not the usual
`.git`, we do things slightly differently:
-------------
-GIT_DIR=my-git.git git-init-db
-------------
+ GIT_DIR=my-git.git git-init-db
Make sure this directory is available for others you want your
changes to be pulled by via the transport of your choice. Also
Come back to the machine you have your private repository. From
there, run this command:
-------------
-git push <public-host>:/path/to/my-git.git master
-------------
+ git push <public-host>:/path/to/my-git.git master
This synchronizes your public repository to match the named
branch head (i.e. `master` in this case) and objects reachable
repository. Kernel.org mirror network takes care of the
propagation to other publicly visible machines:
-------------
-git push master.kernel.org:/pub/scm/git/git.git/
-------------
+ git push master.kernel.org:/pub/scm/git/git.git/
Packing your repository
immutable once they are created, there is a way to optimize the
storage by "packing them together". The command
-------------
-git repack
-------------
+ git repack
will do it for you. If you followed the tutorial examples, you
would have accumulated about 17 objects in `.git/objects/??/`
Once you have packed objects, you do not need to leave the
unpacked objects that are contained in the pack file anymore.
-------------
-git prune-packed
-------------
+ git prune-packed
would remove them for you.
diff --git a/Makefile b/Makefile
index e67d0e78228d186a4358ede07bb686bbac53678b..588e5b0015375d24726e04a953da80878427dad4 100644 (file)
--- a/Makefile
+++ b/Makefile
# DEFINES += -DUSE_STDEV
-GIT_VERSION = 0.99.8
+GIT_VERSION = 0.99.8f
CFLAGS = -g -O2 -Wall
ALL_CFLAGS = $(CFLAGS) $(PLATFORM_DEFINES) $(DEFINES)
git-ssh-upload git-tar-tree git-unpack-file \
git-unpack-objects git-update-index git-update-server-info \
git-upload-pack git-verify-pack git-write-tree \
- git-update-ref git-symbolic-ref \
+ git-update-ref git-symbolic-ref git-check-ref-format \
$(SIMPLE_PROGRAMS)
# Backward compatibility -- to be removed after 1.0
index f8862722fdba204d6e15e4d012f8891bcc36475c..c58d9a249135872c7df13e2b99c24fde355367ca 100644 (file)
--- a/apply.c
+++ b/apply.c
#include <ctype.h>
#include <fnmatch.h>
#include "cache.h"
+#include "quote.h"
// We default to the merge behaviour, since that's what most people would
// expect.
@@ -142,6 +143,35 @@ static char * find_name(const char *line, char *def, int p_value, int terminate)
const char *start = line;
char *name;
+ if (*line == '"') {
+ /* Proposed "new-style" GNU patch/diff format; see
+ * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
+ */
+ name = unquote_c_style(line, NULL);
+ if (name) {
+ char *cp = name;
+ while (p_value) {
+ cp = strchr(name, '/');
+ if (!cp)
+ break;
+ cp++;
+ p_value--;
+ }
+ if (cp) {
+ /* name can later be freed, so we need
+ * to memmove, not just return cp
+ */
+ memmove(name, cp, strlen(cp) + 1);
+ free(def);
+ return name;
+ }
+ else {
+ free(name);
+ name = NULL;
+ }
+ }
+ }
+
for (;;) {
char c = *line;
*/
static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
{
- int len;
- const char *name;
-
if (!orig_name && !isnull)
return find_name(line, NULL, 1, 0);
- name = "/dev/null";
- len = 9;
if (orig_name) {
+ int len;
+ const char *name;
+ char *another;
name = orig_name;
len = strlen(name);
if (isnull)
die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
- }
-
- if (*name == '/')
- goto absolute_path;
-
- for (;;) {
- char c = *line++;
- if (c == '\n')
- break;
- if (c != '/')
- continue;
-absolute_path:
- if (memcmp(line, name, len) || line[len] != '\n')
- break;
+ another = find_name(line, NULL, 1, 0);
+ if (!another || memcmp(another, name, len))
+ die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
+ free(another);
return orig_name;
}
- die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
- return NULL;
+ else {
+ /* expect "/dev/null" */
+ if (memcmp("/dev/null", line, 9) || line[9] != '\n')
+ die("git-apply: bad git-diff - expected /dev/null on line %d", linenr);
+ return NULL;
+ }
}
static int gitdiff_oldname(const char *line, struct patch *patch)
return -1;
}
-static char *git_header_name(char *line)
+static const char *stop_at_slash(const char *line, int llen)
+{
+ int i;
+
+ for (i = 0; i < llen; i++) {
+ int ch = line[i];
+ if (ch == '/')
+ return line + i;
+ }
+ return NULL;
+}
+
+/* This is to extract the same name that appears on "diff --git"
+ * line. We do not find and return anything if it is a rename
+ * patch, and it is OK because we will find the name elsewhere.
+ * We need to reliably find name only when it is mode-change only,
+ * creation or deletion of an empty file. In any of these cases,
+ * both sides are the same name under a/ and b/ respectively.
+ */
+static char *git_header_name(char *line, int llen)
{
int len;
- char *name, *second;
+ const char *name;
+ const char *second = NULL;
- /*
- * Find the first '/'
- */
- name = line;
- for (;;) {
- char c = *name++;
- if (c == '\n')
+ line += strlen("diff --git ");
+ llen -= strlen("diff --git ");
+
+ if (*line == '"') {
+ const char *cp;
+ char *first = unquote_c_style(line, &second);
+ if (!first)
return NULL;
- if (c == '/')
- break;
+
+ /* advance to the first slash */
+ cp = stop_at_slash(first, strlen(first));
+ if (!cp || cp == first) {
+ /* we do not accept absolute paths */
+ free_first_and_fail:
+ free(first);
+ return NULL;
+ }
+ len = strlen(cp+1);
+ memmove(first, cp+1, len+1); /* including NUL */
+
+ /* second points at one past closing dq of name.
+ * find the second name.
+ */
+ while ((second < line + llen) && isspace(*second))
+ second++;
+
+ if (line + llen <= second)
+ goto free_first_and_fail;
+ if (*second == '"') {
+ char *sp = unquote_c_style(second, NULL);
+ if (!sp)
+ goto free_first_and_fail;
+ cp = stop_at_slash(sp, strlen(sp));
+ if (!cp || cp == sp) {
+ free_both_and_fail:
+ free(sp);
+ goto free_first_and_fail;
+ }
+ /* They must match, otherwise ignore */
+ if (strcmp(cp+1, first))
+ goto free_both_and_fail;
+ free(sp);
+ return first;
+ }
+
+ /* unquoted second */
+ cp = stop_at_slash(second, line + llen - second);
+ if (!cp || cp == second)
+ goto free_first_and_fail;
+ cp++;
+ if (line + llen - cp != len + 1 ||
+ memcmp(first, cp, len))
+ goto free_first_and_fail;
+ return first;
}
- /*
- * We don't accept absolute paths (/dev/null) as possibly valid
- */
- if (name == line+1)
+ /* unquoted first name */
+ name = stop_at_slash(line, llen);
+ if (!name || name == line)
return NULL;
+ name++;
+
+ /* since the first name is unquoted, a dq if exists must be
+ * the beginning of the second name.
+ */
+ for (second = name; second < line + llen; second++) {
+ if (*second == '"') {
+ const char *cp = second;
+ const char *np;
+ char *sp = unquote_c_style(second, NULL);
+
+ if (!sp)
+ return NULL;
+ np = stop_at_slash(sp, strlen(sp));
+ if (!np || np == sp) {
+ free_second_and_fail:
+ free(sp);
+ return NULL;
+ }
+ np++;
+ len = strlen(np);
+ if (len < cp - name &&
+ !strncmp(np, name, len) &&
+ isspace(name[len])) {
+ /* Good */
+ memmove(sp, np, len + 1);
+ return sp;
+ }
+ goto free_second_and_fail;
+ }
+ }
+
/*
* Accept a name only if it shows up twice, exactly the same
* form.
@@ -423,7 +540,7 @@ static int parse_git_header(char *line, int len, unsigned int size, struct patch
* or removing or adding empty files), so we get
* the default name from the header.
*/
- patch->def_name = git_header_name(line + strlen("diff --git "));
+ patch->def_name = git_header_name(line, len);
line += len;
size -= len;
{
const char *prefix = "";
char *name = patch->new_name;
+ char *qname = NULL;
int len, max, add, del, total;
if (!name)
name = patch->old_name;
+ if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
+ qname = xmalloc(len + 1);
+ quote_c_style(name, qname, NULL, 0);
+ name = qname;
+ }
+
/*
* "scale" the filename
*/
printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
len, name, patch->lines_added + patch->lines_deleted,
add, pluses, del, minuses);
+ if (qname)
+ free(qname);
}
static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size)
if (lines > max_change)
max_change = lines;
if (patch->old_name) {
- int len = strlen(patch->old_name);
+ int len = quote_c_style(patch->old_name, NULL, NULL, 0);
+ if (!len)
+ len = strlen(patch->old_name);
if (len > max_len)
max_len = len;
}
if (patch->new_name) {
- int len = strlen(patch->new_name);
+ int len = quote_c_style(patch->new_name, NULL, NULL, 0);
+ if (!len)
+ len = strlen(patch->new_name);
if (len > max_len)
max_len = len;
}
index ec2a1610b2fd6edec6c95847d4377f9c0241b738..201ce990897aaa49779fb45d7656d963739bd85e 100644 (file)
--- a/cache.h
+++ b/cache.h
extern int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
int nr_refspec, char **refspec, int all);
extern int get_ack(int fd, unsigned char *result_sha1);
-extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match);
+extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, int ignore_funny);
extern struct packed_git *parse_pack_index(unsigned char *sha1);
extern struct packed_git *parse_pack_index_file(const unsigned char *sha1,
diff --git a/check-ref-format.c b/check-ref-format.c
--- /dev/null
+++ b/check-ref-format.c
@@ -0,0 +1,17 @@
+/*
+ * GIT - The information manager from hell
+ */
+
+#include "cache.h"
+#include "refs.h"
+
+#include <stdio.h>
+
+int main(int ac, char **av)
+{
+ if (ac != 2)
+ usage("git-check-ref-format refname");
+ if (check_ref_format(av[1]))
+ exit(1);
+ return 0;
+}
diff --git a/clone-pack.c b/clone-pack.c
index c102ca885bc3d6700bea82fba5d4c97ab6a93db7..2ac35f6825fa84e1f18ba191d943b367744ba9a0 100644 (file)
--- a/clone-pack.c
+++ b/clone-pack.c
int fd;
char *hex;
+ if (!strncmp(ref->name, "refs/", 5) &&
+ check_ref_format(ref->name + 5)) {
+ error("refusing to create funny ref '%s' locally", ref->name);
+ return;
+ }
+
if (safe_create_leading_directories(path))
die("unable to create leading directory for %s", ref->name);
fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0666);
int status;
pid_t pid;
- get_remote_heads(fd[0], &refs, nr_match, match);
+ get_remote_heads(fd[0], &refs, nr_match, match, 1);
if (!refs) {
packet_flush(fd[1]);
die("no matching remote head");
diff --git a/connect.c b/connect.c
index 825c439accfbb4be4daa21a4d8b33addca0cd8b2..e21d39a3574ff0815520187d21624970b19da92f 100644 (file)
--- a/connect.c
+++ b/connect.c
#include "cache.h"
#include "pkt-line.h"
#include "quote.h"
+#include "refs.h"
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
/*
* Read all the refs from the other end
*/
-struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match)
+struct ref **get_remote_heads(int in, struct ref **list,
+ int nr_match, char **match, int ignore_funny)
{
*list = NULL;
for (;;) {
if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ')
die("protocol error: expected sha/ref, got '%s'", buffer);
name = buffer + 41;
+
+ if (ignore_funny && 45 < len && !memcmp(name, "refs/", 5) &&
+ check_ref_format(name + 5))
+ continue;
+
if (nr_match && !path_match(name, nr_match, match))
continue;
ref = xcalloc(1, sizeof(*ref) + len - 40);
diff --git a/daemon.c b/daemon.c
index cec7e75d5ec6be6f14572592024f2461194b7369..bd278b01c11ed831623e3c6db111f53c2e08d050 100644 (file)
--- a/daemon.c
+++ b/daemon.c
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;
/* 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)
{
{
const char *p = dir;
char **pp;
- int sl = 1, ndot = 0;
+ int sl, ndot;
+
+ /* The pathname here should be an absolute path. */
+ if ( *p++ != '/' )
+ return 0;
+
+ sl = 1; ndot = 0;
for (;;) {
if ( *p == '.' ) {
ndot++;
- } else if ( *p == '/' || *p == '\0' ) {
+ } else if ( *p == '\0' ) {
+ /* Reject "." and ".." at the end of the path */
if ( sl && ndot > 0 && ndot < 3 )
- return 0; /* . or .. in path */
+ return 0;
+
+ /* Otherwise OK */
+ break;
+ } else if ( *p == '/' ) {
+ /* Refuse "", "." or ".." */
+ if ( sl && ndot < 3 )
+ return 0;
sl = 1;
- if ( *p == '\0' )
- break; /* End of string and all is good */
+ ndot = 0;
} else {
sl = ndot = 0;
}
if ( ok_paths && *ok_paths ) {
int ok = 0;
- int dirlen = strlen(dir); /* read_packet_line can return embedded \0 */
+ int dirlen = strlen(dir);
for ( pp = ok_paths ; *pp ; pp++ ) {
int len = strlen(*pp);
return 1; /* Path acceptable */
}
-static int upload(char *dir, int dirlen)
+static int set_dir(const char *dir)
{
- loginfo("Request for '%s'", dir);
-
if (!path_ok(dir)) {
- logerror("Forbidden directory: %s\n", dir);
+ errno = EACCES;
return -1;
}
- if (chdir(dir) < 0) {
- logerror("Cannot chdir('%s'): %s", dir, strerror(errno));
+ if ( chdir(dir) )
return -1;
- }
-
- chdir(".git");
-
+
/*
* Security on the cheap.
*
* a "git-daemon-export-ok" flag that says that the other side
* is ok with us doing this.
*/
- if ((!export_all_trees && access("git-daemon-export-ok", F_OK)) ||
- access("objects/00", X_OK) ||
- access("HEAD", R_OK)) {
- logerror("Not a valid git-daemon-enabled repository: '%s'", dir);
+ if (!export_all_trees && access("git-daemon-export-ok", F_OK)) {
+ errno = EACCES;
+ return -1;
+ }
+
+ if (access("objects/", X_OK) || access("HEAD", R_OK)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* If all this passed, we're OK */
+ return 0;
+}
+
+static int upload(char *dir)
+{
+ /* Try paths in this order */
+ static const char *paths[] = { "%s", "%s/.git", "%s.git", "%s.git/.git", NULL };
+ const char **pp;
+ /* 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);
+
+ for ( pp = paths ; *pp ; pp++ ) {
+ snprintf(dirbuf, buflen, *pp, dir);
+ if ( !set_dir(dirbuf) )
+ break;
+ }
+
+ if ( !*pp ) {
+ logerror("Cannot set directory '%s': %s", dir, strerror(errno));
return -1;
}
*/
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;
}
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;
if (!strncmp("git-upload-pack /", line, 17))
- return upload(line + 16, len - 16);
+ return upload(line+16);
logerror("Protocol error: '%s'", line);
return -1;
export_all_trees = 1;
continue;
}
+ if (!strncmp(arg, "--timeout=", 10)) {
+ timeout = atoi(arg+10);
+ }
+ if (!strncmp(arg, "--init-timeout=", 15)) {
+ init_timeout = atoi(arg+15);
+ }
if (!strcmp(arg, "--")) {
ok_paths = &argv[i+1];
break;
diff --git a/debian/changelog b/debian/changelog
index bebc1919ba70ce71d015e71a572cc583641362c5..0df374767df5099f461db61b933d418de9006a15 100644 (file)
--- a/debian/changelog
+++ b/debian/changelog
+git-core (0.99.8f-0) unstable; urgency=low
+
+ * GIT 0.99.8f
+
+ -- Junio C Hamano <junkio@cox.net> Wed, 19 Oct 2005 02:29:24 -0700
+
+git-core (0.99.8e-0) unstable; urgency=low
+
+ * GIT 0.99.8e
+
+ -- Junio C Hamano <junkio@cox.net> Mon, 17 Oct 2005 17:45:08 -0700
+
+git-core (0.99.8d-0) unstable; urgency=low
+
+ * GIT 0.99.8d
+
+ -- Junio C Hamano <junkio@cox.net> Sat, 15 Oct 2005 17:22:58 -0700
+
+git-core (0.99.8c-0) unstable; urgency=low
+
+ * GIT 0.99.8c
+
+ -- Junio C Hamano <junkio@cox.net> Sun, 9 Oct 2005 19:19:16 -0700
+
+git-core (0.99.8b-0) unstable; urgency=low
+
+ * GIT 0.99.8b
+
+ -- Junio C Hamano <junkio@cox.net> Wed, 5 Oct 2005 15:41:24 -0700
+
+git-core (0.99.8a-0) unstable; urgency=low
+
+ * GIT 0.99.8a
+
+ -- Junio C Hamano <junkio@cox.net> Mon, 3 Oct 2005 16:27:32 -0700
+
git-core (0.99.8-0) unstable; urgency=low
* GIT 0.99.8
diff --git a/fetch-pack.c b/fetch-pack.c
index 582f967a7af68670a6bb174745e43df35a2d0eef..969e72a7816701ed3f86ab42db1472346cab991e 100644 (file)
--- a/fetch-pack.c
+++ b/fetch-pack.c
#include "cache.h"
#include "refs.h"
#include "pkt-line.h"
+#include "commit.h"
+#include "tag.h"
+#include <time.h>
#include <sys/wait.h>
static int quiet;
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;
FILE *revs;
if (!revs)
die("unable to run 'git-rev-list'");
- while (refs) {
+ fetching = 0;
+ for ( ; refs ; refs = refs->next) {
unsigned char *remote = refs->old_sha1;
- if (verbose)
- fprintf(stderr,
- "want %s (%s)\n", sha1_to_hex(remote),
- refs->name);
+ unsigned char *local = refs->new_sha1;
+
+ if (!memcmp(remote, local, 20))
+ continue;
packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
- refs = refs->next;
+ fetching++;
}
packet_flush(fd[1]);
+ if (!fetching)
+ return 1;
flushes = 1;
retval = -1;
while (fgets(line, sizeof(line), revs) != NULL) {
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;
+
+ o = parse_object(remote);
+ if (!o || !(o->flags & COMPLETE)) {
+ retval = 0;
+ if (!verbose)
+ continue;
+ fprintf(stderr,
+ "want %s (%s)\n", sha1_to_hex(remote),
+ refs->name);
+ continue;
+ }
+
+ memcpy(refs->new_sha1, local, 20);
+ if (!verbose)
+ continue;
+ fprintf(stderr,
+ "already have %s (%s)\n", sha1_to_hex(remote),
+ refs->name);
+ }
+ return retval;
+}
+
static int fetch_pack(int fd[2], int nr_match, char **match)
{
struct ref *ref;
int status;
pid_t pid;
- get_remote_heads(fd[0], &ref, nr_match, match);
+ get_remote_heads(fd[0], &ref, nr_match, match, 1);
if (!ref) {
packet_flush(fd[1]);
die("no matching remote head");
}
+ if (everything_local(ref)) {
+ packet_flush(fd[1]);
+ goto all_done;
+ }
if (find_common(fd, sha1, ref) < 0)
fprintf(stderr, "warning: no common commits\n");
pid = fork();
int code = WEXITSTATUS(status);
if (code)
die("git-unpack-objects died with error code %d", code);
+all_done:
while (ref) {
printf("%s %s\n",
sha1_to_hex(ref->old_sha1), ref->name);
diff --git a/git-branch.sh b/git-branch.sh
index 074229c206d01b8ca097b4ced902e9bf40987a06..e2db9063d4c63288c5834046962290ff9dfca127 100755 (executable)
--- a/git-branch.sh
+++ b/git-branch.sh
}
delete_branch () {
- option="$1" branch_name="$2"
+ option="$1"
+ shift
headref=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD |
sed -e 's|^refs/heads/||')
- case ",$headref," in
- ",$branch_name,")
- die "Cannot delete the branch you are on." ;;
- ,,)
- die "What branch are you on anyway?" ;;
- esac
- branch=$(cat "$GIT_DIR/refs/heads/$branch_name") &&
- branch=$(git-rev-parse --verify "$branch^0") ||
- die "Seriously, what branch are you talking about?"
- case "$option" in
- -D)
- ;;
- *)
- mbs=$(git-merge-base -a "$branch" HEAD | tr '\012' ' ')
- case " $mbs " in
- *' '$branch' '*)
- # the merge base of branch and HEAD contains branch --
- # which means that the HEAD contains everything in the HEAD.
+ for branch_name
+ do
+ case ",$headref," in
+ ",$branch_name,")
+ die "Cannot delete the branch you are on." ;;
+ ,,)
+ die "What branch are you on anyway?" ;;
+ esac
+ branch=$(cat "$GIT_DIR/refs/heads/$branch_name") &&
+ branch=$(git-rev-parse --verify "$branch^0") ||
+ die "Seriously, what branch are you talking about?"
+ case "$option" in
+ -D)
;;
*)
- echo >&2 "The branch '$branch_name' is not a strict subset of your current HEAD.
-If you are sure you want to delete it, run 'git branch -D $branch_name'."
- exit 1
+ mbs=$(git-merge-base -a "$branch" HEAD | tr '\012' ' ')
+ case " $mbs " in
+ *' '$branch' '*)
+ # the merge base of branch and HEAD contains branch --
+ # which means that the HEAD contains everything in the HEAD.
+ ;;
+ *)
+ echo >&2 "The branch '$branch_name' is not a strict subset of your current HEAD.
+ If you are sure you want to delete it, run 'git branch -D $branch_name'."
+ exit 1
+ ;;
+ esac
;;
esac
- ;;
- esac
- rm -f "$GIT_DIR/refs/heads/$branch_name"
- echo "Deleted branch $branch_name."
+ rm -f "$GIT_DIR/refs/heads/$branch_name"
+ echo "Deleted branch $branch_name."
+ done
exit 0
}
do
case "$1" in
-d | -D)
- delete_branch "$1" "$2"
+ delete_branch "$@"
exit
;;
--)
rev=$(git-rev-parse --verify "$head") || exit
-[ -e "$GIT_DIR/refs/heads/$branchname" ] && die "$branchname already exists"
+[ -e "$GIT_DIR/refs/heads/$branchname" ] &&
+ die "$branchname already exists."
+git-check-ref-format "heads/$branchname" ||
+ die "we do not like '$branchname' as a branch name."
echo $rev > "$GIT_DIR/refs/heads/$branchname"
diff --git a/git-checkout.sh b/git-checkout.sh
index c3825904b60d36edca96506d7f451cf0349b262d..2c053a33c3c78f27150cdd397ad28641787abb3a 100755 (executable)
--- a/git-checkout.sh
+++ b/git-checkout.sh
die "git checkout: -b needs a branch name"
[ -e "$GIT_DIR/refs/heads/$newbranch" ] &&
die "git checkout: branch $newbranch already exists"
+ git-check-ref-format "heads/$newbranch" ||
+ die "we do not like '$newbranch' as a branch name."
;;
"-f")
force=1
diff --git a/git-clone.sh b/git-clone.sh
index 71431319c008fbc5771d97c4229e7054efd76395..18e692a67b1515c57a20c8574ed53836fb53f2bd 100755 (executable)
--- a/git-clone.sh
+++ b/git-clone.sh
while read sha1 refname
do
name=`expr "$refname" : 'refs/\(.*\)'` &&
- git-http-fetch -v -a -w "$name" "$name" "$1/" || exit 1
+ case "$name" in
+ *^*) ;;
+ *)
+ git-http-fetch -v -a -w "$name" "$name" "$1/" || exit 1
+ esac
done <"$clone_tmp/refs"
rm -fr "$clone_tmp"
}
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index f7c3a515936ac33320c1df3a5f2db71cf3ae3a92..f00f7596b75753c23a8b9dd6092b1a7d8b72835e 100755 (executable)
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
my @opt;
@opt = split(/,/,$opt_p) if defined $opt_p;
unshift @opt, '-z', $opt_z if defined $opt_z;
- unless ($opt_p =~ m/--no-cvs-direct/) {
+ unless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {
push @opt, '--cvs-direct';
}
exec("cvsps",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
diff --git a/git-fetch.sh b/git-fetch.sh
index d3988660ff8cf214cfb1f5903a4bab5ab63b6cea..360fecdd4ea6d59d52274fa397fd6af895e0f1f1 100755 (executable)
--- a/git-fetch.sh
+++ b/git-fetch.sh
reflist=$(get_remote_refs_for_fetch "$@")
if test "$tags"
then
- taglist=$(git-ls-remote --tags "$remote" | awk '{ print "."$2":"$2 }')
+ taglist=$(git-ls-remote --tags "$remote" |
+ sed -e '
+ /\^/d
+ s/^[^ ]* //
+ s/.*/&:&/')
if test "$#" -gt 1
then
# remote URL plus explicit refspecs; we need to merge them.
diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index 4d8a572a9903179494c7cc4f8a72729a47f1c12b..32f10855b6e94ad891417a476bafd3092135bec7 100755 (executable)
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
heads/* | tags/* ) local="refs/$local" ;;
*) local="refs/heads/$local" ;;
esac
+
+ if local_ref_name=$(expr "$local" : 'refs/\(.*\)')
+ then
+ git-check-ref-format "$local_ref_name" ||
+ die "* refusing to create funny ref '$local_ref_name' locally"
+ fi
echo "${dot_prefix}${force}${remote}:${local}"
dot_prefix=.
done
diff --git a/git-tag.sh b/git-tag.sh
index 400bdb9843c4ea19d17b2450f1efd16ba93b0fb8..11b0492ba83d23155e460d0a805eedd7bf556de1 100755 (executable)
--- a/git-tag.sh
+++ b/git-tag.sh
die "tag '$name' already exists"
fi
shift
+git-check-ref-format "tags/$name" ||
+ die "we do not like '$name' as a tag name."
object=$(git-rev-parse --verify --default HEAD "$@") || exit 1
type=$(git-cat-file -t $object) || exit 1
diff --git a/peek-remote.c b/peek-remote.c
index 4b1d0d5ba8ebb61c50a8766c73c6ba19e6efdef9..ee49bf3b7b123359153a961c56f5aa3d58515864 100644 (file)
--- a/peek-remote.c
+++ b/peek-remote.c
{
struct ref *ref;
- get_remote_heads(fd[0], &ref, 0, NULL);
+ get_remote_heads(fd[0], &ref, 0, NULL, 0);
packet_flush(fd[1]);
while (ref) {
index 5e6fda311c5f5fdaa20b7e9d64e46c13b7d9cce8..92e07f070ff54be3f5d0ca02582a7c9e1fd1fa53 100644 (file)
--- a/quote.c
+++ b/quote.c
return buf;
}
+/*
+ * C-style name quoting.
+ *
+ * Does one of three things:
+ *
+ * (1) if outbuf and outfp are both NULL, inspect the input name and
+ * counts the number of bytes that are needed to hold c_style
+ * quoted version of name, counting the double quotes around
+ * it but not terminating NUL, and returns it. However, if name
+ * does not need c_style quoting, it returns 0.
+ *
+ * (2) if outbuf is not NULL, it must point at a buffer large enough
+ * to hold the c_style quoted version of name, enclosing double
+ * quotes, and terminating NUL. Fills outbuf with c_style quoted
+ * version of name enclosed in double-quote pair. Return value
+ * is undefined.
+ *
+ * (3) if outfp is not NULL, outputs c_style quoted version of name,
+ * but not enclosed in double-quote pair. Return value is undefined.
+ */
+
+int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
+{
+#undef EMIT
+#define EMIT(c) \
+ (outbuf ? (*outbuf++ = (c)) : outfp ? fputc(c, outfp) : (count++))
+
+#define EMITQ() EMIT('\\')
+
+ const char *sp;
+ int ch, count = 0, needquote = 0;
+
+ if (!no_dq)
+ EMIT('"');
+ for (sp = name; (ch = *sp++); ) {
+
+ if ((ch < ' ') || (ch == '"') || (ch == '\\') ||
+ (ch == 0177)) {
+ needquote = 1;
+ switch (ch) {
+ case '\a': EMITQ(); ch = 'a'; break;
+ case '\b': EMITQ(); ch = 'b'; break;
+ case '\f': EMITQ(); ch = 'f'; break;
+ case '\n': EMITQ(); ch = 'n'; break;
+ case '\r': EMITQ(); ch = 'r'; break;
+ case '\t': EMITQ(); ch = 't'; break;
+ case '\v': EMITQ(); ch = 'v'; break;
+
+ case '\\': /* fallthru */
+ case '"': EMITQ(); break;
+ case ' ':
+ break;
+ default:
+ /* octal */
+ EMITQ();
+ EMIT(((ch >> 6) & 03) + '0');
+ EMIT(((ch >> 3) & 07) + '0');
+ ch = (ch & 07) + '0';
+ break;
+ }
+ }
+ EMIT(ch);
+ }
+ if (!no_dq)
+ EMIT('"');
+ if (outbuf)
+ *outbuf = 0;
+
+ return needquote ? count : 0;
+}
+
+/*
+ * C-style name unquoting.
+ *
+ * Quoted should point at the opening double quote. Returns
+ * an allocated memory that holds unquoted name, which the caller
+ * should free when done. Updates endp pointer to point at
+ * one past the ending double quote if given.
+ */
+
+char *unquote_c_style(const char *quoted, const char **endp)
+{
+ const char *sp;
+ char *name = NULL, *outp = NULL;
+ int count = 0, ch, ac;
+
+#undef EMIT
+#define EMIT(c) (outp ? (*outp++ = (c)) : (count++))
+
+ if (*quoted++ != '"')
+ return NULL;
+
+ while (1) {
+ /* first pass counts and allocates, second pass fills */
+ for (sp = quoted; (ch = *sp++) != '"'; ) {
+ if (ch == '\\') {
+ switch (ch = *sp++) {
+ case 'a': ch = '\a'; break;
+ case 'b': ch = '\b'; break;
+ case 'f': ch = '\f'; break;
+ case 'n': ch = '\n'; break;
+ case 'r': ch = '\r'; break;
+ case 't': ch = '\t'; break;
+ case 'v': ch = '\v'; break;
+
+ case '\\': case '"':
+ break; /* verbatim */
+
+ case '0'...'7':
+ /* octal */
+ ac = ((ch - '0') << 6);
+ if ((ch = *sp++) < '0' || '7' < ch)
+ return NULL;
+ ac |= ((ch - '0') << 3);
+ if ((ch = *sp++) < '0' || '7' < ch)
+ return NULL;
+ ac |= (ch - '0');
+ ch = ac;
+ break;
+ default:
+ return NULL; /* malformed */
+ }
+ }
+ EMIT(ch);
+ }
+
+ if (name) {
+ *outp = 0;
+ if (endp)
+ *endp = sp;
+ return name;
+ }
+ outp = name = xmalloc(count + 1);
+ }
+}
+
+void write_name_quoted(const char *prefix, const char *name,
+ int quote, FILE *out)
+{
+ int needquote;
+
+ if (!quote) {
+ no_quote:
+ if (prefix && prefix[0])
+ fputs(prefix, out);
+ fputs(name, out);
+ return;
+ }
+
+ needquote = 0;
+ if (prefix && prefix[0])
+ needquote = quote_c_style(prefix, NULL, NULL, 0);
+ if (!needquote)
+ needquote = quote_c_style(name, NULL, NULL, 0);
+ if (needquote) {
+ fputc('"', out);
+ if (prefix && prefix[0])
+ quote_c_style(prefix, NULL, out, 1);
+ quote_c_style(name, NULL, out, 1);
+ fputc('"', out);
+ }
+ else
+ goto no_quote;
+}
index c8cfb3a12482b0a2398d7d9e164c37e5473b44b8..ea227bb7d44b3a37990ce0d00ab4eea34a30065a 100644 (file)
--- a/quote.h
+++ b/quote.h
#ifndef QUOTE_H
#define QUOTE_H
+#include <stdio.h>
/* Help to copy the thing properly quoted for the shell safety.
* any single quote is replaced with '\'', and the whole thing
* sq_quote() in a real application.
*/
-char *sq_quote(const char *src);
+extern char *sq_quote(const char *src);
+extern int quote_c_style(const char *name, char *outbuf, FILE *outfp,
+ int nodq);
+extern char *unquote_c_style(const char *quoted, const char **endp);
+
+extern void write_name_quoted(const char *prefix, const char *name,
+ int quote, FILE *out);
#endif
diff --git a/receive-pack.c b/receive-pack.c
index 06857eb77fef93788f54d17cca68924f5213c9db..8f157bc3f0ef1f9d96bca18fc81854fc24bff936 100644 (file)
--- a/receive-pack.c
+++ b/receive-pack.c
char new_hex[60], *old_hex, *lock_name;
int newfd, namelen, written;
+ if (!strncmp(name, "refs/", 5) && check_ref_format(name + 5))
+ return error("refusing to create funny ref '%s' locally",
+ name);
+
namelen = strlen(name);
lock_name = xmalloc(namelen + 10);
memcpy(lock_name, name, namelen);
index 5a8cbd4ef386da60d3f7a2cac4563fc993e38b94..d7f8dfddf4979c0dda2b888435e6c343b29409ad 100644 (file)
--- a/refs.c
+++ b/refs.c
return retval;
}
+/*
+ * Make sure "ref" is something reasonable to have under ".git/refs/";
+ * We do not like it if:
+ *
+ * - any path component of it begins with ".", or
+ * - it has double dots "..", or
+ * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
+ * - it ends with a "/".
+ */
+
+static inline int bad_ref_char(int ch)
+{
+ return (((unsigned) ch) <= ' ' ||
+ ch == '~' || ch == '^' || ch == ':');
+}
+
int check_ref_format(const char *ref)
{
- char *middle;
- if (ref[0] == '.' || ref[0] == '/')
- return -1;
- middle = strchr(ref, '/');
- if (!middle || !middle[1])
- return -1;
- if (strchr(middle + 1, '/'))
- return -1;
- return 0;
+ int ch, level;
+ const char *cp = ref;
+
+ level = 0;
+ while (1) {
+ while ((ch = *cp++) == '/')
+ ; /* tolerate duplicated slashes */
+ if (!ch)
+ return -1; /* should not end with slashes */
+
+ /* we are at the beginning of the path component */
+ if (ch == '.' || bad_ref_char(ch))
+ return -1;
+
+ /* scan the rest of the path component */
+ while ((ch = *cp++) != 0) {
+ if (bad_ref_char(ch))
+ return -1;
+ if (ch == '/')
+ break;
+ if (ch == '.' && *cp == '.')
+ return -1;
+ }
+ level++;
+ if (!ch) {
+ if (level < 2)
+ return -1; /* at least of form "heads/blah" */
+ return 0;
+ }
+ }
}
int write_ref_sha1_unlocked(const char *ref, const unsigned char *sha1)
diff --git a/rev-list.c b/rev-list.c
index c60aa72957cb65501266911aefd8aaaa2f2e4e16..3a32e405a38faebe0d4b7ec3b743fe3e15cfd3cf 100644 (file)
--- a/rev-list.c
+++ b/rev-list.c
if (!merge_order) {
sort_by_date(&list);
+ if (list && !limited && max_count == 1 &&
+ !tag_objects && !tree_objects && !blob_objects) {
+ show_commit(list->item);
+ return 0;
+ }
if (limited)
list = limit_list(list);
if (topo_order)
diff --git a/send-pack.c b/send-pack.c
index 55d8ff7e102fa6cc369c993035c0579c2d33b775..9f9a6e70b8789f63891c5b6e07822c546af8eec4 100644 (file)
--- a/send-pack.c
+++ b/send-pack.c
int new_refs;
/* No funny business with the matcher */
- remote_tail = get_remote_heads(in, &remote_refs, 0, NULL);
+ remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, 1);
get_local_heads();
/* match them up */
diff --git a/sha1_name.c b/sha1_name.c
index f64755fbceea617c424b9b83999eeaa91bf76af1..d0896f8183d87df53d80c6e5e72ebdb5932c29a8 100644 (file)
--- a/sha1_name.c
+++ b/sha1_name.c
#include "cache.h"
+#include "tag.h"
#include "commit.h"
+#include "tree.h"
+#include "blob.h"
static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
{
return 0;
}
+static int peel_onion(const char *name, int len, unsigned char *sha1)
+{
+ unsigned char outer[20];
+ const char *sp;
+ const char *type_string = NULL;
+ struct object *o;
+
+ /*
+ * "ref^{type}" dereferences ref repeatedly until you cannot
+ * dereference anymore, or you get an object of given type,
+ * whichever comes first. "ref^{}" means just dereference
+ * tags until you get a non-tag. "ref^0" is a shorthand for
+ * "ref^{commit}". "commit^{tree}" could be used to find the
+ * top-level tree of the given commit.
+ */
+ if (len < 4 || name[len-1] != '}')
+ return -1;
+
+ for (sp = name + len - 1; name <= sp; sp--) {
+ int ch = *sp;
+ if (ch == '{' && name < sp && sp[-1] == '^')
+ break;
+ }
+ if (sp <= name)
+ return -1;
+
+ sp++; /* beginning of type name, or closing brace for empty */
+ if (!strncmp(commit_type, sp, 6) && sp[6] == '}')
+ type_string = commit_type;
+ else if (!strncmp(tree_type, sp, 4) && sp[4] == '}')
+ type_string = tree_type;
+ else if (!strncmp(blob_type, sp, 4) && sp[4] == '}')
+ type_string = blob_type;
+ else if (sp[0] == '}')
+ type_string = NULL;
+ else
+ return -1;
+
+ if (get_sha1_1(name, sp - name - 2, outer))
+ return -1;
+
+ o = parse_object(outer);
+ if (!o)
+ return -1;
+ if (!type_string) {
+ o = deref_tag(o);
+ memcpy(sha1, o->sha1, 20);
+ }
+ else {
+ /* At this point, the syntax look correct, so
+ * if we do not get the needed object, we should
+ * barf.
+ */
+
+ while (1) {
+ if (!o)
+ return -1;
+ if (o->type == type_string) {
+ memcpy(sha1, o->sha1, 20);
+ return 0;
+ }
+ if (o->type == tag_type)
+ o = ((struct tag*) o)->tagged;
+ else if (o->type == commit_type)
+ o = &(((struct commit *) o)->tree->object);
+ else
+ return error("%.*s: expected %s type, but the object dereferences to %s type",
+ len, name, type_string,
+ o->type);
+ if (!o->parsed)
+ parse_object(o->sha1);
+ }
+ }
+ return 0;
+}
+
static int get_sha1_1(const char *name, int len, unsigned char *sha1)
{
int parent, ret;
return get_nth_ancestor(name, len1, sha1, parent);
}
+ ret = peel_onion(name, len, sha1);
+ if (!ret)
+ return 0;
+
ret = get_sha1_basic(name, len, sha1);
if (!ret)
return 0;
diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
--- a/t/t1200-tutorial.sh
+++ /dev/null
@@ -1,160 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Johannes Schindelin
-#
-
-test_description='Test git-rev-parse with different parent options'
-
-. ./test-lib.sh
-
-echo "Hello World" > hello
-echo "Silly example" > example
-
-git-update-index --add hello example
-
-test_expect_success 'blob' "test blob = \"$(git-cat-file -t 557db03)\""
-
-test_expect_success 'blob 557db03' "test \"Hello World\" = \"$(git-cat-file blob 557db03)\""
-
-echo "It's a new day for git" >>hello
-cat > diff.expect << EOF
-diff --git a/hello b/hello
-index 557db03..263414f 100644
---- a/hello
-+++ b/hello
-@@ -1 +1,2 @@
- Hello World
-+It's a new day for git
-EOF
-git-diff-files -p > diff.output
-test_expect_success 'git-diff-files -p' 'cmp diff.expect diff.output'
-git diff > diff.output
-test_expect_success 'git diff' 'cmp diff.expect diff.output'
-
-tree=$(git-write-tree 2>/dev/null)
-
-test_expect_success 'tree' "test 8988da15d077d4829fc51d8544c097def6644dbb = $tree"
-
-output="$(echo "Initial commit" | git-commit-tree $(git-write-tree) 2>&1 > .git/refs/heads/master)"
-
-test_expect_success 'commit' "test 'Committing initial tree 8988da15d077d4829fc51d8544c097def6644dbb' = \"$output\""
-
-git-diff-index -p HEAD > diff.output
-test_expect_success 'git-diff-index -p HEAD' 'cmp diff.expect diff.output'
-
-git diff HEAD > diff.output
-test_expect_success 'git diff HEAD' 'cmp diff.expect diff.output'
-
-#rm hello
-#test_expect_success 'git-read-tree --reset HEAD' "git-read-tree --reset HEAD ; test \"hello: needs update\" = \"$(git-update-index --refresh)\""
-
-cat > whatchanged.expect << EOF
-diff-tree VARIABLE (from root)
-Author: VARIABLE
-Date: VARIABLE
-
- Initial commit
-
-diff --git a/example b/example
-new file mode 100644
-index 0000000..f24c74a
---- /dev/null
-+++ b/example
-@@ -0,0 +1 @@
-+Silly example
-diff --git a/hello b/hello
-new file mode 100644
-index 0000000..557db03
---- /dev/null
-+++ b/hello
-@@ -0,0 +1 @@
-+Hello World
-EOF
-
-git-whatchanged -p --root | \
- sed -e "1s/^\(.\{10\}\).\{40\}/\1VARIABLE/" \
- -e "2,3s/^\(.\{8\}\).*$/\1VARIABLE/" \
-> whatchanged.output
-test_expect_success 'git-whatchanged -p --root' 'cmp whatchanged.expect whatchanged.output'
-
-git tag my-first-tag
-test_expect_success 'git tag my-first-tag' 'cmp .git/refs/heads/master .git/refs/tags/my-first-tag'
-
-# TODO: test git-clone
-
-git checkout -b mybranch
-test_expect_success 'git checkout -b mybranch' 'cmp .git/refs/heads/master .git/refs/heads/mybranch'
-
-cat > branch.expect <<EOF
- master
-* mybranch
-EOF
-
-git branch > branch.output
-test_expect_success 'git branch' 'cmp branch.expect branch.output'
-
-git checkout mybranch
-echo "Work, work, work" >>hello
-git commit -m 'Some work.' hello
-
-git checkout master
-
-echo "Play, play, play" >>hello
-echo "Lots of fun" >>example
-git commit -m 'Some fun.' hello example
-
-test_expect_failure 'git resolve now fails' 'git resolve HEAD mybranch "Merge work in mybranch"'
-
-cat > hello << EOF
-Hello World
-It's a new day for git
-Play, play, play
-Work, work, work
-EOF
-
-git commit -m 'Merged "mybranch" changes.' hello
-
-cat > show-branch.expect << EOF
-* [master] Merged "mybranch" changes.
- ! [mybranch] Some work.
---
-+ [master] Merged "mybranch" changes.
-++ [mybranch] Some work.
-EOF
-
-git show-branch master mybranch > show-branch.output
-test_expect_success 'git show-branch' 'cmp show-branch.expect show-branch.output'
-
-git checkout mybranch
-
-cat > resolve.expect << EOF
-Updating from VARIABLE to VARIABLE.
- example | 1 +
- hello | 1 +
- 2 files changed, 2 insertions(+), 0 deletions(-)
-EOF
-
-git resolve HEAD master "Merge upstream changes." | \
- sed -e "1s/[0-9a-f]\{40\}/VARIABLE/g" > resolve.output
-test_expect_success 'git resolve' 'cmp resolve.expect resolve.output'
-
-cat > show-branch2.expect << EOF
-! [master] Merged "mybranch" changes.
- * [mybranch] Merged "mybranch" changes.
---
-++ [master] Merged "mybranch" changes.
-EOF
-
-git show-branch master mybranch > show-branch2.output
-test_expect_success 'git show-branch' 'cmp show-branch2.expect show-branch2.output'
-
-# TODO: test git fetch
-
-# TODO: test git push
-
-test_expect_success 'git repack' 'git repack'
-test_expect_success 'git prune-packed' 'git prune-packed'
-test_expect_failure '-> only packed objects' 'find -type f .git/objects/[0-9a-f][0-9a-f]'
-
-test_done
-
diff --git a/upload-pack.c b/upload-pack.c
index 83f5a35d2686401e9a70d7aafae07c88c7b6e936..80a5d0925a751fc3f2002db68dc019ecc01fffd7 100644 (file)
--- a/upload-pack.c
+++ b/upload-pack.c
#include "refs.h"
#include "pkt-line.h"
-static const char upload_pack_usage[] = "git-upload-pack <dir>";
+static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
#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)
{
for(;;) {
len = packet_read_line(0, line, sizeof(line));
+ reset_timeout();
if (!len) {
packet_write(1, "NAK\n");
for (;;) {
len = packet_read_line(0, line, sizeof(line));
+ reset_timeout();
if (!len)
continue;
len = strip(line, len);
for (;;) {
unsigned char dummy[20], *sha1_buf;
len = packet_read_line(0, line, sizeof(line));
+ reset_timeout();
if (!len)
return needs;
static int upload_pack(void)
{
+ reset_timeout();
head_ref(send_ref);
for_each_ref(send_ref);
packet_flush(1);
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;