* Error : git svn fetch
From: chongyc @ 2008-12-23 12:25 UTC (permalink / raw)
To: git
Hi
I found that 'git svn fetch' failed in cloning the hudson svn reposotory.
I want to git-clone the svn repository
svn repository URL : https://svn.dev.java.net/svn/hudson/
username : guest
password :
So I run followings to git-clone
[root@localhost hudson]# git --version
git version 1.6.0.6
[root@localhost hudson]# git svn init -T trunk -t tags -b branches
https://svn.dev.java.net/svn/hudson/
[root@localhost hudson]# git svn fetch
Found possible branch point:
https://svn.dev.java.net/svn/hudson/tags/hudson-1_230 =>
https://svn.dev.java.net/svn/hudson/branches/buildnav-1636, 10490
Initializing parent: buildnav-1636@10490
Found possible branch point:
https://svn.dev.java.net/svn/hudson/trunk/hudson/main =>
https://svn.dev.java.net/svn/hudson/tags/hudson-1_230, 10450
Initializing parent: buildnav-1636@10450
Found branch parent: (buildnav-1636@10490) a1c395e5db063ca1ffbbe008e309c5
11d56219e0
Following parent with do_switch
remoting/pom.xml was not found in commit
a1c395e5db063ca1ffbbe008e309c511d56219e0 (r10447)
[root@localhost hudson]#
What shall I do to git-clone it ?
Please help me
>From chongyc
^ permalink raw reply
* Error : git svn fetch
From: chongyc @ 2008-12-23 12:13 UTC (permalink / raw)
To: git
Hi
I found that 'git svn fetch' failed in cloning the hudson svn reposotory.
I want to git-clone the svn repository
svn repository URL : https://svn.dev.java.net/svn/hudson/
username : guest
password :
So I run followings to git-clone
[root@localhost hudson]# git --version
git version 1.6.0.6
[root@localhost hudson]# git svn init -T trunk -t tags -b branches
https://svn.dev.java.net/svn/hudson/
[root@localhost hudson]# git svn fetch
Found possible branch point:
https://svn.dev.java.net/svn/hudson/tags/hudson-1_230 =>
https://svn.dev.java.net/svn/hudson/branches/buildnav-1636, 10490
Initializing parent: buildnav-1636@10490
Found possible branch point:
https://svn.dev.java.net/svn/hudson/trunk/hudson/main =>
https://svn.dev.java.net/svn/hudson/tags/hudson-1_230, 10450
Initializing parent: buildnav-1636@10450
Found branch parent: (buildnav-1636@10490) a1c395e5db063ca1ffbbe008e309c5
11d56219e0
Following parent with do_switch
remoting/pom.xml was not found in commit
a1c395e5db063ca1ffbbe008e309c511d56219e0 (r10447)
[root@localhost hudson]#
What shall I do to git-clone it ?
Please help me
>From chongyc
^ permalink raw reply
* Error : git svn fetch
From: chongyc @ 2008-12-23 12:12 UTC (permalink / raw)
To: git
Hi
I found that 'git svn fetch' failed in cloning the hudson svn reposotory.
I want to git-clone the svn repository
svn repository URL : https://svn.dev.java.net/svn/hudson/
username : guest
password :
So I run followings to git-clone
[root@localhost hudson]# git --version
git version 1.6.0.6
[root@localhost hudson]# git svn init -T trunk -t tags -b branches
https://svn.dev.java.net/svn/hudson/
[root@localhost hudson]# git svn fetch
Found possible branch point:
https://svn.dev.java.net/svn/hudson/tags/hudson-1_230 =>
https://svn.dev.java.net/svn/hudson/branches/buildnav-1636, 10490
Initializing parent: buildnav-1636@10490
Found possible branch point:
https://svn.dev.java.net/svn/hudson/trunk/hudson/main =>
https://svn.dev.java.net/svn/hudson/tags/hudson-1_230, 10450
Initializing parent: buildnav-1636@10450
Found branch parent: (buildnav-1636@10490) a1c395e5db063ca1ffbbe008e309c5
11d56219e0
Following parent with do_switch
remoting/pom.xml was not found in commit
a1c395e5db063ca1ffbbe008e309c511d56219e0 (r10447)
[root@localhost hudson]#
What shall I do to git-clone it ?
Please help me
>From chongyc
^ permalink raw reply
* Can `git config` override entries in .gitconfig?
From: Nicholas LaRoche @ 2008-12-23 12:05 UTC (permalink / raw)
To: git
Is there a direct way to change the user.email entry for a git
repository for one user (applied to all previous commits)?
I tried `git config --unset user.email` followed by `git config
user.email email2` but it just sets a second field called user.email
that shows up in `git config -l` as a duplicate. My ~/.gitconfig file
contains email1 for the user.email entry.
Also, when the repository is created can I specify a second set of
contact information (i.e. using a project specific email) which isn't a
part of ~/.gitconfig?
output of `git config -l`:
user.email=email1
..
..
user.email=email2
Regards,
Nick
^ permalink raw reply
* Re: What's cooking in git.git (Dec 2008, #03; Sun, 21)
From: Jeff King @ 2008-12-23 12:05 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Johannes Schindelin
In-Reply-To: <7vr641pvid.fsf@gitster.siamese.dyndns.org>
On Sun, Dec 21, 2008 at 04:23:22AM -0800, Junio C Hamano wrote:
> * js/notes (Sat Dec 20 13:06:03 2008 +0100) 4 commits
> - Add an expensive test for git-notes
> - Speed up git notes lookup
> - Add a script to edit/inspect notes
> - Introduce commit notes
I haven't had much time to really look at this closely, and I probably
won't for another week or so due to the holidays. But from my cursory
examination, I think I want to propose something that is a bit
different. So if nobody objects (and I think Dscho already said he was
going to be out of touch for two weeks due to the holidays) I'd like
this to remain in pu for the time being.
-Peff
^ permalink raw reply
* Re: Applying patches from a patch set
From: Mark Ryden @ 2008-12-23 11:25 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vabaqy5kx.fsf@gitster.siamese.dyndns.org>
Hello,
I tried usuing slrn with mutt; mutt seems to me better.
I saved 15 git patches from one patch set into a mailbox
using mutt.
Now I tried:
git am maibox
and got this error:
Patch is empty. Was it split wrong?
This error occurred after "git am maibox" applied the first 6 patches
in this patch set.
looking at that seventh patch shows that it seems to be perfectly
ok.
Any ideas what can be wrong here?
Regards,
Mark
On Sat, Dec 20, 2008 at 10:06 PM, Junio C Hamano <gitster@pobox.com> wrote:
> "Mark Ryden" <markryde@gmail.com> writes:
>
>> Hello,
>> I am subscribed to some linux kernel subsystem mailing list; in this
>> list there are sometimes patchsets with more than
>> 30-40 patches.
>> I am using gmail web interface client.
>>
>> In order to apply a patch set I copy and paste each patch from the
>> patchset into a file, and then apply that patch.
>> I assume that there is a better way.
>> Recently I encountered a fatal error when doing so (and I am not sure
>> what caused it).
>>
>> So my question is: does anyone know a more elegant way of applying a
>> large patchset ?
>> Maybe there is better mail client with which this process can be done easily ?
>
> First mistake(?) is you seem to be doing copy&paste from browser. Don't.
> It can easily damage whitespaces. Find out how your webmail interface
> allows you to save selected messages in a mbox and let you download it.
>
> Then
>
> $ git am that-mbox-file
>
>
>
>
>
^ permalink raw reply
* Re: git-cvsimport fuzzy commit log matching?
From: Pierre Habouzit @ 2008-12-23 11:06 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Matthias Urlichs, git
In-Reply-To: <20081223110302.GA9376@lst.de>
[-- Attachment #1: Type: text/plain, Size: 2186 bytes --]
On Tue, Dec 23, 2008 at 11:03:02AM +0000, Christoph Hellwig wrote:
> I'm currently trying to get clean git imports of the XFS userspace
> repositories. These are funky in the way they were initially kept in
> ptools, and SGI-internal SCM that was built ontop of RCS which changeset
> added ontop. So we know that commits actually were done in atomic
> changesets. But ptools has the "nice" feature of allowing both per-file
> and per-changeset commits. Due to the per-file commits git-cvsimport
> often misdetects a single changeset as multiple individual changes, ala:
>
>
> commit 0d47d43b5878c6e7d7b516a793a82f0076d22089
> Author: Barry Naujok <bnaujok@sgi.com>
> Date: Mon Jul 16 15:52:53 2007 +0000
>
> Perform parallel processing based on AG stride/concat unit
> Merge of master-melb:xfs-cmds:29143a by kenmcd.
>
> Queue up AGs per thread based on ag stride
>
> commit 1fa4685db126fd3071e008a6d18f9d51209ab305
> Author: Barry Naujok <bnaujok@sgi.com>
> Date: Mon Jul 16 15:52:53 2007 +0000
>
> Perform parallel processing based on AG stride/concat unit
> Merge of master-melb:xfs-cmds:29143a by kenmcd.
>
> Handle ag stride command line option and setup threads as required
>
> commit a73288784e77c2411687f6778adb4c0b0f9dcdff
> Author: Barry Naujok <bnaujok@sgi.com>
> Date: Mon Jul 16 15:52:53 2007 +0000
>
> Perform parallel processing based on AG stride/concat unit
> Merge of master-melb:xfs-cmds:29143a by kenmcd.
>
> Execute bits changed from x-- to ---
> Queue up AGs per thread based on ag stride
>
> and so on.
>
> Any idea how to tell git-cvsimport that if we have exactly the same
> timestamp, and maybe the same author it really is the same changeset and
> we want to merge the commit message?
Why not using a fancy git-filterbranch script to squash them together
instead ? It's probably less work than to try to modify your cvs
importer to work the exact way you want.
--
·O· Pierre Habouzit
··O madcoder@debian.org
OOO http://www.madism.org
[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* git-cvsimport fuzzy commit log matching?
From: Christoph Hellwig @ 2008-12-23 11:03 UTC (permalink / raw)
To: Matthias Urlichs; +Cc: git
I'm currently trying to get clean git imports of the XFS userspace
repositories. These are funky in the way they were initially kept in
ptools, and SGI-internal SCM that was built ontop of RCS which changeset
added ontop. So we know that commits actually were done in atomic
changesets. But ptools has the "nice" feature of allowing both per-file
and per-changeset commits. Due to the per-file commits git-cvsimport
often misdetects a single changeset as multiple individual changes, ala:
commit 0d47d43b5878c6e7d7b516a793a82f0076d22089
Author: Barry Naujok <bnaujok@sgi.com>
Date: Mon Jul 16 15:52:53 2007 +0000
Perform parallel processing based on AG stride/concat unit
Merge of master-melb:xfs-cmds:29143a by kenmcd.
Queue up AGs per thread based on ag stride
commit 1fa4685db126fd3071e008a6d18f9d51209ab305
Author: Barry Naujok <bnaujok@sgi.com>
Date: Mon Jul 16 15:52:53 2007 +0000
Perform parallel processing based on AG stride/concat unit
Merge of master-melb:xfs-cmds:29143a by kenmcd.
Handle ag stride command line option and setup threads as required
commit a73288784e77c2411687f6778adb4c0b0f9dcdff
Author: Barry Naujok <bnaujok@sgi.com>
Date: Mon Jul 16 15:52:53 2007 +0000
Perform parallel processing based on AG stride/concat unit
Merge of master-melb:xfs-cmds:29143a by kenmcd.
Execute bits changed from x-- to ---
Queue up AGs per thread based on ag stride
and so on.
Any idea how to tell git-cvsimport that if we have exactly the same
timestamp, and maybe the same author it really is the same changeset and
we want to merge the commit message?
^ permalink raw reply
* Re: [PATCH] strbuf_readlink semantics update.
From: Pierre Habouzit @ 2008-12-23 10:21 UTC (permalink / raw)
To: git, Linus Torvalds; +Cc: Junio C Hamano
In-Reply-To: <1230026749-25360-1-git-send-email-madcoder@debian.org>
[-- Attachment #1: Type: text/plain, Size: 4021 bytes --]
when readlink fails, the strbuf shall not be destroyed. It's not how
read_file_or_gitlink works for example.
Fix strbuf_readlink callers to destroy the buffer when appropriate.
Fix read_old_data possible leaks in case of errors, since even when no
data has been read, the strbufs may have grown to prepare the reads.
strbuf_release must be called on them.
Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
I know it somehow add lines to the callers, but it's actually more
important to keep consistency among the strbuf APIs than to save 10
SLOCs.
builtin-apply.c | 8 ++++++--
combine-diff.c | 1 +
diff.c | 4 +++-
read-cache.c | 4 +++-
sha1_file.c | 1 +
strbuf.c | 2 +-
6 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/builtin-apply.c b/builtin-apply.c
index 07244b0..c1fe9ca 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -2306,8 +2306,10 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
/* We have a patched copy in memory use that */
strbuf_add(&buf, tpatch->result, tpatch->resultsize);
} else if (cached) {
- if (read_file_or_gitlink(ce, &buf))
+ if (read_file_or_gitlink(ce, &buf)) {
+ strbuf_release(&buf);
return error("read of %s failed", patch->old_name);
+ }
} else if (patch->old_name) {
if (S_ISGITLINK(patch->old_mode)) {
if (ce) {
@@ -2320,8 +2322,10 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
patch->fragments = NULL;
}
} else {
- if (read_old_data(st, patch->old_name, &buf))
+ if (read_old_data(st, patch->old_name, &buf)) {
+ strbuf_release(&buf);
return error("read of %s failed", patch->old_name);
+ }
}
}
diff --git a/combine-diff.c b/combine-diff.c
index bccc018..674745d 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -706,6 +706,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
struct strbuf buf = STRBUF_INIT;
if (strbuf_readlink(&buf, elem->path, st.st_size) < 0) {
+ strbuf_release(&buf);
error("readlink(%s): %s", elem->path,
strerror(errno));
return;
diff --git a/diff.c b/diff.c
index b57d9ac..41f7e1c 100644
--- a/diff.c
+++ b/diff.c
@@ -1778,8 +1778,10 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
if (S_ISLNK(st.st_mode)) {
struct strbuf sb = STRBUF_INIT;
- if (strbuf_readlink(&sb, s->path, s->size))
+ if (strbuf_readlink(&sb, s->path, s->size)) {
+ strbuf_release(&sb);
goto err_empty;
+ }
s->size = sb.len;
s->data = strbuf_detach(&sb, NULL);
s->should_free = 1;
diff --git a/read-cache.c b/read-cache.c
index db166da..9673d91 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -104,8 +104,10 @@ static int ce_compare_link(struct cache_entry *ce, size_t expected_size)
enum object_type type;
struct strbuf sb = STRBUF_INIT;
- if (strbuf_readlink(&sb, ce->name, expected_size))
+ if (strbuf_readlink(&sb, ce->name, expected_size)) {
+ strbuf_release(&sb);
return -1;
+ }
buffer = read_sha1_file(ce->sha1, &type, &size);
if (buffer) {
diff --git a/sha1_file.c b/sha1_file.c
index 52d1ead..a62b53d 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -2538,6 +2538,7 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
case S_IFLNK:
if (strbuf_readlink(&sb, path, st->st_size)) {
char *errstr = strerror(errno);
+ strbuf_release(&sb);
return error("readlink(\"%s\"): %s", path,
errstr);
}
diff --git a/strbuf.c b/strbuf.c
index 254a7ee..b1f2a97 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -311,7 +311,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
/* .. the buffer was too small - try again */
hint *= 2;
}
- strbuf_release(sb);
+ sb->buf[sb->len] = '\0';
return -1;
}
--
1.6.1.rc4.306.g849c2
[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply related
* [PATCH] strbuf_readlink semantics update.
From: Pierre Habouzit @ 2008-12-23 10:05 UTC (permalink / raw)
To: git, Linus Torvalds; +Cc: Pierre Habouzit, Junio C Hamano, Git Mailing List
In-Reply-To: <alpine.LFD.2.00.0812171042120.14014@localhost.localdomain>
strbuf_* operations are meant to append their results to a current buffer
rather than _replace_ its content. Modify strbuf_readlink accordingly.
Current callers only operate on empty buffers at the moment and this
semantic change doesn't break any current code.
Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
strbuf.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/strbuf.c b/strbuf.c
index bdf4954..254a7ee 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -299,12 +299,12 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
int len;
strbuf_grow(sb, hint);
- len = readlink(path, sb->buf, hint);
+ len = readlink(path, sb->buf + sb->len, hint);
if (len < 0) {
if (errno != ERANGE)
break;
} else if (len < hint) {
- strbuf_setlen(sb, len);
+ strbuf_setlen(sb, sb->len + len);
return 0;
}
--
1.6.1.rc4.304.g3da087
^ permalink raw reply related
* Re: Git merge conflicts and encoding of logs
From: Junio C Hamano @ 2008-12-23 10:04 UTC (permalink / raw)
To: Johannes Sixt; +Cc: Junichi Uekawa, git
In-Reply-To: <4950A422.5030100@viscovery.net>
Johannes Sixt <j.sixt@viscovery.net> writes:
> Junio C Hamano schrieb:
>> <<<<<<< HEAD:foo
>> これはサイドブランチの変更です。
>> やはり JIS コードで書いてます。
>> =======
>> 日本語のファイルです。
>> JIS コードで書いてます。
>>>>>>>>> master:foo
>>
>> The above will probably come out as UTF-8 in this mail text, but the point
>> is that the confict side markers do not have anything but filename and the
>> branch name. I am still scratching my head trying to see where in the
>> merge-recursive codepath you got snippet of log message.
>
> Try rebase -i instead of merge: This should put summary lines onto the
> conflict markers.
Ah, that's cherry-pick.
The fix should be around the area this weather-balloon patch touches.
Note that this does not correctly work yet, and it seems that somewhere the
string is truncated.
But I won't be debugging it further for now...
----
builtin-revert.c | 15 ++++++++++++++-
1 files changed, 14 insertions(+), 1 deletions(-)
diff --git c/builtin-revert.c w/builtin-revert.c
index d48313c..47ff16f 100644
--- c/builtin-revert.c
+++ w/builtin-revert.c
@@ -244,6 +244,19 @@ static struct tree *empty_tree(void)
return tree;
}
+static char *branch_label_to_output_encoding(char *oneline)
+{
+ if (git_log_output_encoding &&
+ strcmp(git_log_output_encoding, git_commit_encoding)) {
+ char *it = reencode_string(oneline,
+ git_log_output_encoding,
+ git_commit_encoding);
+ if (it)
+ return it;
+ }
+ return oneline;
+}
+
static int revert_or_cherry_pick(int argc, const char **argv)
{
unsigned char head[20];
@@ -373,7 +386,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
read_cache();
init_merge_options(&o);
o.branch1 = "HEAD";
- o.branch2 = oneline;
+ o.branch2 = branch_label_to_output_encoding(oneline);
head_tree = parse_tree_indirect(head);
next_tree = next ? next->tree : empty_tree();
^ permalink raw reply related
* Re: Git merge conflicts and encoding of logs
From: Johannes Sixt @ 2008-12-23 8:41 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Junichi Uekawa, git
In-Reply-To: <7vprjjfgi7.fsf@gitster.siamese.dyndns.org>
Junio C Hamano schrieb:
> <<<<<<< HEAD:foo
> これはサイドブランチの変更です。
> やはり JIS コードで書いてます。
> =======
> 日本語のファイルです。
> JIS コードで書いてます。
>>>>>>>> master:foo
>
> The above will probably come out as UTF-8 in this mail text, but the point
> is that the confict side markers do not have anything but filename and the
> branch name. I am still scratching my head trying to see where in the
> merge-recursive codepath you got snippet of log message.
Try rebase -i instead of merge: This should put summary lines onto the
conflict markers.
-- Hannes
^ permalink raw reply
* [PATCH] handle_remote_ls_ctx can parsing href starting at http://
From: Kirill A. Korinskiy @ 2008-12-23 8:31 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Kirill A. Korinskiy
In-Reply-To: <7v3aghnv1t.fsf@gitster.siamese.dyndns.org>
The program call remote_ls() to get remote objects over http;
handle_remote_ls_ctx() is used to parse it's response to populated
"struct remote_ls_ctx" that is returned from remote_ls().
The handle_remote_ls_ctx() function assumed that the server will
returned local path in href field, but RFC 4918 demand of support full
URI (http://localhost/repo.git for example).
This resulted in push failure (git-http-push ask server
PROPFIND /repo.git/alhost:8080/repo.git/refs/) when a server returned
full URI.
Signed-off-by: Kirill A. Korinskiy <catap@catap.ru>
---
http-push.c | 25 +++++++++++++++++++------
1 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/http-push.c b/http-push.c
index 7c6460919bf3eba10c46cede11ffdd9c53fd2dd2..a4b7d08663504a57008f66a39fffe293f62c1d08 100644
--- a/http-push.c
+++ b/http-push.c
@@ -87,6 +87,7 @@ static struct object_list *objects;
struct repo
{
char *url;
+ char *path;
int path_len;
int has_info_refs;
int can_update_info_refs;
@@ -1424,9 +1425,19 @@ static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
ls->userFunc(ls);
}
} else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
- ls->dentry_name = xmalloc(strlen(ctx->cdata) -
+ char *path = ctx->cdata;
+ if (*ctx->cdata == 'h') {
+ path = strstr(path, "//");
+ if (path) {
+ path = strchr(path+2, '/');
+ }
+ }
+ if (path) {
+ path += remote->path_len;
+ }
+ ls->dentry_name = xmalloc(strlen(path) -
remote->path_len + 1);
- strcpy(ls->dentry_name, ctx->cdata + remote->path_len);
+ strcpy(ls->dentry_name, path + remote->path_len);
} else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
ls->dentry_flags |= IS_DIR;
}
@@ -2206,10 +2217,11 @@ int main(int argc, char **argv)
if (!remote->url) {
char *path = strstr(arg, "//");
remote->url = arg;
+ remote->path_len = strlen(arg);
if (path) {
- path = strchr(path+2, '/');
- if (path)
- remote->path_len = strlen(path);
+ remote->path = strchr(path+2, '/');
+ if (remote->path)
+ remote->path_len = strlen(remote->path);
}
continue;
}
@@ -2238,8 +2250,9 @@ int main(int argc, char **argv)
rewritten_url = xmalloc(strlen(remote->url)+2);
strcpy(rewritten_url, remote->url);
strcat(rewritten_url, "/");
+ remote->path = rewritten_url + (remote->path - remote->url);
+ remote->path_len++;
remote->url = rewritten_url;
- ++remote->path_len;
}
/* Verify DAV compliance/lock support */
--
1.5.6.5
^ permalink raw reply related
* Re: Git merge conflicts and encoding of logs
From: Junio C Hamano @ 2008-12-23 8:22 UTC (permalink / raw)
To: Junichi Uekawa; +Cc: git
In-Reply-To: <87lju7h4yb.dancerj%dancer@netfort.gr.jp>
[-- Attachment #1: Type: text/plain, Size: 2267 bytes --]
Junichi Uekawa <dancer@netfort.gr.jp> writes:
> Git merge conflict will insert '<<< first line of commit log message'
> '===' '>>>' markers to the text file that is causing a conflict.
>
> Unfortunately, the encoding of the text file may be different from the
> log message encoding, and that results in a file which has a mixed
> encoding (which is pretty hard to edit from any editor BTW).
>
> My use case is editing platex files (iso-2022-jp encoded) with log
> messages of utf-8.
>
> ... Thinking about it, it's probably the same encoding problem as git
> blame.
What 69cd8f6 (builtin-blame: Reencode commit messages according to git-log
rules., 2008-10-22) does to git-blame is to re-encode the data taken from
the commit log to i18n.logoutputencoding, and put that in the datastream.
If your commit object have names and messages in utf-8, and if you set
i18n.logoutputencoding to iso-2022-jp, that would reencode data taken from
the commit object in iso-2022-jp and sprinkle them in the blame
datastream.
The issue would be certainly similar, *if* anything on your <<</===/>>>
lines came from commit log message, but I couldn't trigger what you
describe. I prepared a history of this shape:
B
/
o---A
with ISO-2022-JP payload and UTF-8 commit log message. Then, I added:
[i18n]
logoutputencoding = iso-2022-jp
which lets me read "git log -p --all" quite comfortably. Everything comes
out as good old JISX0208. So far, so good.
Then while on branch B, I tried to merge A, which resulted in conflicts
that looked like this:
<<<<<<< HEAD:foo
これはサイドブランチの変更です。
やはり JIS コードで書いてます。
=======
日本語のファイルです。
JIS コードで書いてます。
>>>>>>> master:foo
The above will probably come out as UTF-8 in this mail text, but the point
is that the confict side markers do not have anything but filename and the
branch name. I am still scratching my head trying to see where in the
merge-recursive codepath you got snippet of log message.
A bundle from my test repository is attached. You can use it to reproduce
the repository like this:
$ cd /var/tmp && mkdir test && cd test && git init
$ git pull ../x.bndl master
$ git fetch ../x.bndl side:side
[-- Attachment #2: a bundle of the sample history --]
[-- Type: application/octet-stream, Size: 917 bytes --]
^ permalink raw reply
* Re: Merge two repos with history included? (was Re: How do I..?)
From: Alexander Gavrilov @ 2008-12-23 7:58 UTC (permalink / raw)
To: Miklos Vajna; +Cc: Dylan Martin, git
In-Reply-To: <20081223004407.GZ21154@genesis.frugalware.org>
On Tuesday 23 December 2008 03:44:07 Miklos Vajna wrote:
> On Mon, Dec 22, 2008 at 02:04:06PM -0800, Dylan Martin <dmartin@sccd.ctc.edu> wrote:
> > I checked, and my initial SVN to git conversion does contain history.
> >
> > I'm trying to add an exising repo as a subdir of my main repo with
> > history included. Can anyone tell me how to do that?
>
> I would try the following: Let's say you have super.git and foo.git, and
> you want to merge foo.git to the subdirectory 'foo' of super.git. Then
> you can do in foo.git:
>
> mkdir foo
> mv * foo
> git add foo
> git commit -a
>
> Then in super.git:
>
> git pull path/to/foo.git master
>
> And then git log --follow should work just fine on any merged files as
> well.
>
If the conversion is one-shot, you can also try rewriting commits like this:
cd foo.git
git filter-branch --commit-filter 'TREE=$1; shift; git commit-tree $(echo -e "040000 tree $TREE\tfoo" | git mktree) "$@"' master
(Maybe there is a simpler way, though)
I used this once to merge together the history of a bunch of interrelated (and now dead) projects.
Alexander
^ permalink raw reply
* Git merge conflicts and encoding of logs
From: Junichi Uekawa @ 2008-12-23 4:48 UTC (permalink / raw)
To: git
Hi,
Git merge conflict will insert '<<< first line of commit log message'
'===' '>>>' markers to the text file that is causing a conflict.
Unfortunately, the encoding of the text file may be different from the
log message encoding, and that results in a file which has a mixed
encoding (which is pretty hard to edit from any editor BTW).
My use case is editing platex files (iso-2022-jp encoded) with log
messages of utf-8.
http://git.debian.org/?p=tokyodebian/monthly-report.git;a=summary
... Thinking about it, it's probably the same encoding problem as git
blame. Is there already a good way to fix this or is this something
that needs fixing?
regards,
junichi
--
dancer@{netfort.gr.jp,debian.org}
^ permalink raw reply
* Re: Git with Hudson
From: Stephen Haberman @ 2008-12-23 3:00 UTC (permalink / raw)
To: Tim Visher; +Cc: Indy Nagpal, git
In-Reply-To: <c115fd3c0812181758m5fe4af95s860181d25f1992ee@mail.gmail.com>
> I'm interested! Please publish away!
Here's the "git2" Hudson plugin that worked well for us:
http://github.com/stephenh/hudson-git2
There is a git.hpi file checked into the target directory so that you
don't have to build it yourself.
- Stephen
^ permalink raw reply
* [announce] gc
From: Stephen Haberman @ 2008-12-23 3:24 UTC (permalink / raw)
To: git
Hi,
This is just a small collections of hooks, scripts, and practices I
developed while working on a not-distributed/corporate project.
A quick list is: svn-like revision numbers (via tagging every commit)
(don't flame me, please), combined diff-enabled commit emails*, Hudson
hooks, trac hooks, branch locking, same-repo-separate-DAG git
server-side config storage and export-on-push, and developer-side
push/pull scripts that "just work".
http://github.com/stephenh/gc
Hopefully others find it as useful.
Thanks,
Stephen
^ permalink raw reply
* Re: [PATCH] t9129: skip the last two tests if UTF-8 locale not available
From: Junio C Hamano @ 2008-12-23 1:52 UTC (permalink / raw)
To: Miklos Vajna; +Cc: Peter van der Does, git
In-Reply-To: <1229994564-5153-1-git-send-email-vmiklos@frugalware.org>
Miklos Vajna <vmiklos@frugalware.org> writes:
> Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
> ---
>
> On Mon, Dec 22, 2008 at 12:50:49PM -0800, Junio C Hamano <gitster@pobox.com> wrote:
>> I think some tests play nicer than this one and skip tests that want
>> UTF-8 locales; you may want to teach this script the same trick.
>
> What about this?
>
> Tesed on two Linux boxes (where I have / do not have UTF-8) and HP-UX
> (where I do not have, either).
Thanks.
Peter, is this the only test that you had trouble with, and does Miklos's
patch help your environment?
^ permalink raw reply
* [PATCH] t9129: skip the last two tests if UTF-8 locale not available
From: Miklos Vajna @ 2008-12-23 1:09 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Peter van der Does, git
In-Reply-To: <7vzliogcie.fsf@gitster.siamese.dyndns.org>
Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
---
On Mon, Dec 22, 2008 at 12:50:49PM -0800, Junio C Hamano <gitster@pobox.com> wrote:
> I think some tests play nicer than this one and skip tests that want
> UTF-8 locales; you may want to teach this script the same trick.
What about this?
Tesed on two Linux boxes (where I have / do not have UTF-8) and HP-UX
(where I do not have, either).
t/t9129-git-svn-i18n-commitencoding.sh | 30 +++++++++++++++++-------------
1 files changed, 17 insertions(+), 13 deletions(-)
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 938b7fe..8a9dde4 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -60,21 +60,25 @@ do
'
done
-test_expect_success 'ISO-8859-1 should match UTF-8 in svn' '
-(
- cd ISO-8859-1 &&
- compare_svn_head_with "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
-)
-'
-
-for H in EUCJP ISO-2022-JP
-do
- test_expect_success '$H should match UTF-8 in svn' '
+if locale -a |grep -q en_US.utf8; then
+ test_expect_success 'ISO-8859-1 should match UTF-8 in svn' '
(
- cd $H &&
- compare_svn_head_with "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
+ cd ISO-8859-1 &&
+ compare_svn_head_with "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
)
'
-done
+
+ for H in EUCJP ISO-2022-JP
+ do
+ test_expect_success '$H should match UTF-8 in svn' '
+ (
+ cd $H &&
+ compare_svn_head_with "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
+ )
+ '
+ done
+else
+ say "UTF-8 locale not available, test skipped"
+fi
test_done
--
1.6.1.rc1.35.gae26e.dirty
^ permalink raw reply related
* Re: Merge two repos with history included? (was Re: How do I..?)
From: Miklos Vajna @ 2008-12-23 0:44 UTC (permalink / raw)
To: Dylan Martin; +Cc: git
In-Reply-To: <e1a4c7f60812221404k57a5e150kac74f53c784b6b4a@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1124 bytes --]
On Mon, Dec 22, 2008 at 02:04:06PM -0800, Dylan Martin <dmartin@sccd.ctc.edu> wrote:
> I tried converting an existing SVN repo to git and then adding it to
> my main git repo using the subtree merge technique described at
> http://www.kernel.org/pub/software/scm/git/docs/howto/using-merge-subtree.html.
> I now have the various files in my repo, but they have no history.
They have, but you are right about that - due to the nature of subtree
merge - git log <path> will show only the merge commits. (However git
blame works fine, for example.)
> I checked, and my initial SVN to git conversion does contain history.
>
> I'm trying to add an exising repo as a subdir of my main repo with
> history included. Can anyone tell me how to do that?
I would try the following: Let's say you have super.git and foo.git, and
you want to merge foo.git to the subdirectory 'foo' of super.git. Then
you can do in foo.git:
mkdir foo
mv * foo
git add foo
git commit -a
Then in super.git:
git pull path/to/foo.git master
And then git log --follow should work just fine on any merged files as
well.
[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* [JGIT PATCH 13/13] Add basic git daemon support to publish receive-pack
From: Shawn O. Pearce @ 2008-12-23 0:27 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229992043-1053-13-git-send-email-spearce@spearce.org>
The git:// daemon service receives anonymous TCP connections and runs
commands as they are received.
Currently we only support the server portion of send-pack/receive-pack,
so that is the only service registered in our Daemon class.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../services/org.spearce.jgit.pgm.TextBuiltin | 1 +
.../src/org/spearce/jgit/pgm/Daemon.java | 125 ++++++++
.../src/org/spearce/jgit/transport/Daemon.java | 309 ++++++++++++++++++++
.../org/spearce/jgit/transport/DaemonClient.java | 106 +++++++
.../org/spearce/jgit/transport/DaemonService.java | 120 ++++++++
.../spearce/jgit/transport/TransportGitAnon.java | 3 +-
6 files changed, 662 insertions(+), 2 deletions(-)
create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Daemon.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/DaemonClient.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/DaemonService.java
diff --git a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin
index e2e7938..5fb0953 100644
--- a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin
+++ b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin
@@ -1,4 +1,5 @@
org.spearce.jgit.pgm.Branch
+org.spearce.jgit.pgm.Daemon
org.spearce.jgit.pgm.DiffTree
org.spearce.jgit.pgm.Fetch
org.spearce.jgit.pgm.Glog
diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Daemon.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Daemon.java
new file mode 100644
index 0000000..aafc82e
--- /dev/null
+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Daemon.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.pgm;
+
+import java.io.File;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+import org.spearce.jgit.lib.Repository;
+import org.spearce.jgit.transport.DaemonService;
+
+@Command(common = true, usage = "Export repositories over git://")
+class Daemon extends TextBuiltin {
+ @Option(name = "--port", metaVar = "PORT", usage = "port number to listen on")
+ int port = org.spearce.jgit.transport.Daemon.DEFAULT_PORT;
+
+ @Option(name = "--listen", metaVar = "HOSTNAME", usage = "hostname (or ip) to listen on")
+ String host;
+
+ @Option(name = "--enable", metaVar = "SERVICE", usage = "enable the service in all repositories", multiValued = true)
+ final List<String> enable = new ArrayList<String>();
+
+ @Option(name = "--disable", metaVar = "SERVICE", usage = "disable the service in all repositories", multiValued = true)
+ final List<String> disable = new ArrayList<String>();
+
+ @Option(name = "--allow-override", metaVar = "SERVICE", usage = "configure the service in daemon.servicename", multiValued = true)
+ final List<String> canOverride = new ArrayList<String>();
+
+ @Option(name = "--forbid-override", metaVar = "SERVICE", usage = "configure the service in daemon.servicename", multiValued = true)
+ final List<String> forbidOverride = new ArrayList<String>();
+
+ @Argument(metaVar = "DIRECTORY", usage = "directories to export")
+ final List<File> directory = new ArrayList<File>();
+
+ @Override
+ protected void run() throws Exception {
+ final org.spearce.jgit.transport.Daemon d;
+
+ d = new org.spearce.jgit.transport.Daemon(
+ host != null ? new InetSocketAddress(host, port)
+ : new InetSocketAddress(port));
+
+ for (final String n : enable)
+ service(d, n).setEnabled(true);
+ for (final String n : disable)
+ service(d, n).setEnabled(false);
+
+ for (final String n : canOverride)
+ service(d, n).setOverridable(true);
+ for (final String n : forbidOverride)
+ service(d, n).setOverridable(false);
+
+ if (directory.isEmpty()) {
+ export(d, db);
+ } else {
+ for (final File f : directory) {
+ out.println("Exporting " + f.getAbsolutePath());
+ d.exportDirectory(f);
+ }
+ }
+ d.start();
+ out.println("Listening on " + d.getAddress());
+ }
+
+ private DaemonService service(final org.spearce.jgit.transport.Daemon d,
+ final String n) {
+ final DaemonService svc = d.getService(n);
+ if (svc == null)
+ throw die("Service '" + n + "' not supported");
+ return svc;
+ }
+
+ private void export(final org.spearce.jgit.transport.Daemon daemon,
+ final Repository repo) {
+ File d = repo.getDirectory();
+ String name = d.getName();
+ while (name.equals(".git") || name.equals(".")) {
+ d = d.getParentFile();
+ name = d.getName();
+ }
+ if (!name.endsWith(".git"))
+ name += ".git";
+
+ out.println("Exporting current repository as \"" + name + "\"");
+ daemon.exportRepository(name, repo);
+ }
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
new file mode 100644
index 0000000..c225740
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.transport;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.spearce.jgit.lib.Repository;
+
+/** Basic daemon for the anonymous <code>git://</code> transport protocol. */
+public class Daemon {
+ /** 9418: IANA assigned port number for Git. */
+ public static final int DEFAULT_PORT = 9418;
+
+ private static final int BACKLOG = 5;
+
+ private static final Pattern SAFE_REPOSITORY_NAME = Pattern
+ .compile("^[A-Za-z][A-Za-z0-9/_ -]+(\\.git)?$");
+
+ private InetSocketAddress myAddress;
+
+ private final DaemonService[] services;
+
+ private final ThreadGroup processors;
+
+ private Map<String, Repository> exports;
+
+ private Collection<File> exportBase;
+
+ private boolean run;
+
+ private Thread acceptThread;
+
+ /** Configure a daemon to listen on any available network port. */
+ public Daemon() {
+ this(null);
+ }
+
+ /**
+ * Configure a new daemon for the specified network address.
+ *
+ * @param addr
+ * address to listen for connections on. If null, any available
+ * port will be chosen on all network interfaces.
+ */
+ public Daemon(final InetSocketAddress addr) {
+ myAddress = addr;
+ exports = new HashMap<String, Repository>();
+ exportBase = new ArrayList<File>();
+ processors = new ThreadGroup("Git-Daemon");
+
+ services = new DaemonService[] { new DaemonService("receive-pack",
+ "receivepack") {
+ @Override
+ protected void execute(final DaemonClient dc, final Repository db)
+ throws IOException {
+ final ReceivePack rp = new ReceivePack(db);
+ rp.receive(dc.getInputStream(), dc.getOutputStream(), null);
+ }
+ } };
+ }
+
+ /** @return the address connections are received on. */
+ public synchronized InetSocketAddress getAddress() {
+ return myAddress;
+ }
+
+ /**
+ * Lookup a supported service so it can be reconfigured.
+ *
+ * @param name
+ * name of the service; e.g. "receive-pack"/"git-receive-pack" or
+ * "upload-pack"/"git-upload-pack".
+ * @return the service; null if this daemon implementation doesn't support
+ * the requested service type.
+ */
+ public synchronized DaemonService getService(String name) {
+ if (!name.startsWith("git-"))
+ name = "git-" + name;
+ for (final DaemonService s : services) {
+ if (s.getCommandName().equals(name))
+ return s;
+ }
+ return null;
+ }
+
+ /**
+ * Add a single repository to the set that is exported by this daemon.
+ * <p>
+ * The existence (or lack-thereof) of <code>git-daemon-export-ok</code> is
+ * ignored by this method. The repository is always published.
+ *
+ * @param name
+ * name the repository will be published under.
+ * @param db
+ * the repository instance.
+ */
+ public void exportRepository(final String name, final Repository db) {
+ synchronized (exports) {
+ exports.put(name, db);
+ }
+ }
+
+ /**
+ * Recursively export all Git repositories within a directory.
+ *
+ * @param dir
+ * the directory to export. This directory must not itself be a
+ * git repository, but any directory below it which has a file
+ * named <code>git-daemon-export-ok</code> will be published.
+ */
+ public void exportDirectory(final File dir) {
+ synchronized (exportBase) {
+ exportBase.add(dir);
+ }
+ }
+
+ /**
+ * Start this daemon on a background thread.
+ *
+ * @throws IOException
+ * the server socket could not be opened.
+ * @throws IllegalStateException
+ * the daemon is already running.
+ */
+ public synchronized void start() throws IOException {
+ if (acceptThread != null)
+ throw new IllegalStateException("Daemon already running");
+
+ final ServerSocket listenSock = new ServerSocket(
+ myAddress != null ? myAddress.getPort() : 0, BACKLOG,
+ myAddress != null ? myAddress.getAddress() : null);
+ myAddress = (InetSocketAddress) listenSock.getLocalSocketAddress();
+
+ run = true;
+ acceptThread = new Thread(processors, "Git-Daemon-Accept") {
+ public void run() {
+ while (isRunning()) {
+ try {
+ startClient(listenSock.accept());
+ } catch (InterruptedIOException e) {
+ // Test again to see if we should keep accepting.
+ } catch (IOException e) {
+ break;
+ }
+ }
+
+ try {
+ listenSock.close();
+ } catch (IOException err) {
+ //
+ } finally {
+ synchronized (Daemon.this) {
+ acceptThread = null;
+ }
+ }
+ }
+ };
+ acceptThread.start();
+ }
+
+ /** @return true if this daemon is receiving connections. */
+ public synchronized boolean isRunning() {
+ return run;
+ }
+
+ /** Stop this daemon. */
+ public synchronized void stop() {
+ if (acceptThread != null) {
+ run = false;
+ acceptThread.interrupt();
+ }
+ }
+
+ private void startClient(final Socket s) {
+ final DaemonClient dc = new DaemonClient(this);
+
+ final SocketAddress peer = s.getRemoteSocketAddress();
+ if (peer instanceof InetSocketAddress)
+ dc.setRemoteAddress(((InetSocketAddress) peer).getAddress());
+
+ new Thread(processors, "Git-Daemon-Client " + peer.toString()) {
+ public void run() {
+ try {
+ dc.execute(new BufferedInputStream(s.getInputStream()),
+ new BufferedOutputStream(s.getOutputStream()));
+ } catch (IOException e) {
+ // Ignore unexpected IO exceptions from clients
+ e.printStackTrace();
+ } finally {
+ try {
+ s.getInputStream().close();
+ } catch (IOException e) {
+ // Ignore close exceptions
+ }
+ try {
+ s.getOutputStream().close();
+ } catch (IOException e) {
+ // Ignore close exceptions
+ }
+ }
+ }
+ }.start();
+ }
+
+ synchronized DaemonService matchService(final String cmd) {
+ for (final DaemonService d : services) {
+ if (d.handles(cmd))
+ return d;
+ }
+ return null;
+ }
+
+ Repository openRepository(String name) {
+ if (!name.startsWith("/"))
+ return null;
+ name = name.substring(1);
+
+ Repository db;
+ synchronized (exports) {
+ db = exports.get(name);
+ if (db != null)
+ return db;
+
+ db = exports.get(name + ".git");
+ if (db != null)
+ return db;
+ }
+
+ if (SAFE_REPOSITORY_NAME.matcher(name).matches()) {
+ final File[] search;
+ synchronized (exportBase) {
+ search = exportBase.toArray(new File[exportBase.size()]);
+ }
+ for (final File f : search) {
+ db = openRepository(new File(f, name));
+ if (db != null)
+ return db;
+
+ db = openRepository(new File(f, name + ".git"));
+ if (db != null)
+ return db;
+
+ db = openRepository(new File(f, name + "/.git"));
+ if (db != null)
+ return db;
+ }
+ }
+ return null;
+ }
+
+ private Repository openRepository(final File d) {
+ if (d.isDirectory() && new File(d, "git-daemon-export-ok").exists()) {
+ try {
+ return new Repository(d);
+ } catch (IOException err) {
+ // Ignore
+ }
+ }
+ return null;
+ }
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/DaemonClient.java b/org.spearce.jgit/src/org/spearce/jgit/transport/DaemonClient.java
new file mode 100644
index 0000000..636cf22
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/DaemonClient.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+
+/** Active network client of {@link Daemon}. */
+public class DaemonClient {
+ private final Daemon daemon;
+
+ private InetAddress peer;
+
+ private InputStream rawIn;
+
+ private OutputStream rawOut;
+
+ DaemonClient(final Daemon d) {
+ daemon = d;
+ }
+
+ void setRemoteAddress(final InetAddress ia) {
+ peer = ia;
+ }
+
+ /** @return the daemon which spawned this client. */
+ public Daemon getDaemon() {
+ return daemon;
+ }
+
+ /** @return Internet address of the remote client. */
+ public InetAddress getRemoteAddress() {
+ return peer;
+ }
+
+ /** @return input stream to read from the connected client. */
+ public InputStream getInputStream() {
+ return rawIn;
+ }
+
+ /** @return output stream to send data to the connected client. */
+ public OutputStream getOutputStream() {
+ return rawOut;
+ }
+
+ void execute(final InputStream in, final OutputStream out)
+ throws IOException {
+ rawIn = in;
+ rawOut = out;
+
+ String cmd = new PacketLineIn(rawIn).readStringNoLF();
+ if (cmd == null || cmd.length() == 0)
+ return;
+
+ final int nul = cmd.indexOf('\0');
+ if (nul >= 0) {
+ // Newer clients hide a "host" header behind this byte.
+ // Currently we don't use it for anything, so we ignore
+ // this portion of the command.
+ //
+ cmd = cmd.substring(0, nul);
+ }
+
+ final DaemonService srv = getDaemon().matchService(cmd);
+ if (srv == null)
+ return;
+ srv.execute(this, cmd);
+ }
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/DaemonService.java b/org.spearce.jgit/src/org/spearce/jgit/transport/DaemonService.java
new file mode 100644
index 0000000..775a506
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/DaemonService.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.transport;
+
+import java.io.IOException;
+
+import org.spearce.jgit.lib.Repository;
+
+/** A service exposed by {@link Daemon} over anonymous <code>git://</code>. */
+public abstract class DaemonService {
+ private final String command;
+
+ private final String config;
+
+ private boolean enabled;
+
+ private boolean overridable;
+
+ protected DaemonService(final String cmdName, final String cfgName) {
+ command = cmdName.startsWith("git-") ? cmdName : "git-" + cmdName;
+ config = cfgName;
+ overridable = true;
+ }
+
+ /** @return is this service enabled for invocation? */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * @param on
+ * true to allow this service to be used; false to deny it.
+ */
+ public void setEnabled(final boolean on) {
+ enabled = on;
+ }
+
+ /** @return can this service be configured in the repository config file? */
+ public boolean isOverridable() {
+ return overridable;
+ }
+
+ /**
+ * @param on
+ * true to permit repositories to override this service's enabled
+ * state with the <code>daemon.servicename</code> config setting.
+ */
+ public void setOverridable(final boolean on) {
+ overridable = on;
+ }
+
+ /** @return name of the command requested by clients. */
+ public String getCommandName() {
+ return command;
+ }
+
+ /**
+ * Determine if this service can handle the requested command.
+ *
+ * @param commandLine
+ * input line from the client.
+ * @return true if this command can accept the given command line.
+ */
+ public boolean handles(final String commandLine) {
+ return command.length() + 1 < commandLine.length()
+ && commandLine.charAt(command.length()) == ' '
+ && commandLine.startsWith(command);
+ }
+
+ void execute(final DaemonClient client, final String commandLine)
+ throws IOException {
+ final String name = commandLine.substring(command.length() + 1);
+ final Repository db = client.getDaemon().openRepository(name);
+ if (db == null)
+ return;
+ boolean on = isEnabled();
+ if (isOverridable())
+ on = db.getConfig().getBoolean("daemon", config, on);
+ if (on)
+ execute(client, db);
+ }
+
+ protected abstract void execute(DaemonClient client, Repository db)
+ throws IOException;
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java
index a80c335..a11f293 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java
@@ -56,8 +56,7 @@
* source projects, as there are no authentication or authorization overheads.
*/
class TransportGitAnon extends PackTransport {
- /** IANA assigned port number for Git. */
- static final int GIT_PORT = 9418;
+ static final int GIT_PORT = Daemon.DEFAULT_PORT;
static boolean canHandle(final URIish uri) {
return "git".equals(uri.getScheme());
--
1.6.1.rc4.301.g5497a
^ permalink raw reply related
* [JGIT PATCH 12/13] Implement the git-receive-pack process in Java
From: Shawn O. Pearce @ 2008-12-23 0:27 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229992043-1053-12-git-send-email-spearce@spearce.org>
This implementation provides a basic git-receive-pack service within
Java. Two hooks APIs are supported before and after commands are
executed within the connection, allowing daemons to customize the
behavior of the updates.
Logic to bind the ReceivePack class to a pipe or network socket is
omitted, as it depends on the transport. SSH servers will need a
pure Java SSH implementation such as Apache MINA SSHD. Anonymous
push over git:// requires a basic git-daemon functionality. Local
pipe access might use pure-Java pipes, or System.in/System.out.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../org/spearce/jgit/transport/PacketLineIn.java | 12 +
.../spearce/jgit/transport/PostReceiveHook.java | 77 ++
.../org/spearce/jgit/transport/PreReceiveHook.java | 94 +++
.../org/spearce/jgit/transport/ReceiveCommand.java | 223 ++++++
.../org/spearce/jgit/transport/ReceivePack.java | 793 ++++++++++++++++++++
5 files changed, 1199 insertions(+), 0 deletions(-)
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/PostReceiveHook.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/PreReceiveHook.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/ReceiveCommand.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineIn.java b/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineIn.java
index f87517d..ef218be 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineIn.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineIn.java
@@ -111,6 +111,18 @@ String readString() throws IOException {
return RawParseUtils.decode(Constants.CHARSET, raw, 0, len);
}
+ String readStringNoLF() throws IOException {
+ int len = readLength();
+ if (len == 0)
+ return "";
+
+ len -= 4; // length header (4 bytes)
+
+ final byte[] raw = new byte[len];
+ NB.readFully(in, raw, 0, len);
+ return RawParseUtils.decode(Constants.CHARSET, raw, 0, len);
+ }
+
private void readLF() throws IOException {
if (in.read() != '\n')
throw new IOException("Protocol error: expected LF");
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PostReceiveHook.java b/org.spearce.jgit/src/org/spearce/jgit/transport/PostReceiveHook.java
new file mode 100644
index 0000000..060efb3
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/PostReceiveHook.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.transport;
+
+import java.util.Collection;
+
+/**
+ * Hook invoked by {@link ReceivePack} after all updates are executed.
+ * <p>
+ * The hook is called after all commands have been processed. Only commands with
+ * a status of {@link ReceiveCommand.Result#OK} are passed into the hook. To get
+ * all commands within the hook, see {@link ReceivePack#getAllCommands()}.
+ * <p>
+ * Any post-receive hook implementation should not update the status of a
+ * command, as the command has already completed or failed, and the status has
+ * already been returned to the client.
+ * <p>
+ * Hooks should execute quickly, as they block the server and the client from
+ * completing the connection.
+ */
+public interface PostReceiveHook {
+ /** A simple no-op hook. */
+ public static final PostReceiveHook NULL = new PostReceiveHook() {
+ public void onPostReceive(final ReceivePack rp,
+ final Collection<ReceiveCommand> commands) {
+ // Do nothing.
+ }
+ };
+
+ /**
+ * Invoked after all commands are executed and status has been returned.
+ *
+ * @param rp
+ * the process handling the current receive. Hooks may obtain
+ * details about the destination repository through this handle.
+ * @param commands
+ * unmodifiable set of successfully completed commands. May be
+ * the empty set.
+ */
+ public void onPostReceive(ReceivePack rp,
+ Collection<ReceiveCommand> commands);
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PreReceiveHook.java b/org.spearce.jgit/src/org/spearce/jgit/transport/PreReceiveHook.java
new file mode 100644
index 0000000..0af466d
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/PreReceiveHook.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.transport;
+
+import java.util.Collection;
+
+/**
+ * Hook invoked by {@link ReceivePack} before any updates are executed.
+ * <p>
+ * The hook is called with any commands that are deemed valid after parsing them
+ * from the client and applying the standard receive configuration options to
+ * them:
+ * <ul>
+ * <li><code>receive.denyDenyDeletes</code></li>
+ * <li><code>receive.denyNonFastForwards</code></li>
+ * </ul>
+ * This means the hook will not receive a non-fast-forward update command if
+ * denyNonFastForwards is set to true in the configuration file. To get all
+ * commands within the hook, see {@link ReceivePack#getAllCommands()}.
+ * <p>
+ * As the hook is invoked prior to the commands being executed, the hook may
+ * choose to block any command by setting its result status with
+ * {@link ReceiveCommand#setResult(ReceiveCommand.Result)}.
+ * <p>
+ * The hook may also choose to perform the command itself (or merely pretend
+ * that it has performed the command), by setting the result status to
+ * {@link ReceiveCommand.Result#OK}.
+ * <p>
+ * Hooks should run quickly, as they block the caller thread and the client
+ * process from completing.
+ * <p>
+ * Hooks may send optional messages back to the client via methods on
+ * {@link ReceivePack}. Implementors should be aware that not all network
+ * transports support this output, so some (or all) messages may simply be
+ * discarded. These messages should be advisory only.
+ */
+public interface PreReceiveHook {
+ /** A simple no-op hook. */
+ public static final PreReceiveHook NULL = new PreReceiveHook() {
+ public void onPreReceive(final ReceivePack rp,
+ final Collection<ReceiveCommand> commands) {
+ // Do nothing.
+ }
+ };
+
+ /**
+ * Invoked just before commands are executed.
+ * <p>
+ * See the class description for how this method can impact execution.
+ *
+ * @param rp
+ * the process handling the current receive. Hooks may obtain
+ * details about the destination repository through this handle.
+ * @param commands
+ * unmodifiable set of valid commands still pending execution.
+ * May be the empty set.
+ */
+ public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands);
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/ReceiveCommand.java b/org.spearce.jgit/src/org/spearce/jgit/transport/ReceiveCommand.java
new file mode 100644
index 0000000..b07b976
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/ReceiveCommand.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.transport;
+
+import org.spearce.jgit.lib.ObjectId;
+import org.spearce.jgit.lib.Ref;
+
+/**
+ * A command being processed by {@link ReceivePack}.
+ * <p>
+ * This command instance roughly translates to the server side representation of
+ * the {@link RemoteRefUpdate} created by the client.
+ */
+public class ReceiveCommand {
+ /** Type of operation requested. */
+ public static enum Type {
+ /** Create a new ref; the ref must not already exist. */
+ CREATE,
+
+ /**
+ * Update an existing ref with a fast-forward update.
+ * <p>
+ * During a fast-forward update no changes will be lost; only new
+ * commits are inserted into the ref.
+ */
+ UPDATE,
+
+ /**
+ * Update an existing ref by potentially discarding objects.
+ * <p>
+ * The current value of the ref is not fully reachable from the new
+ * value of the ref, so a successful command may result in one or more
+ * objects becoming unreachable.
+ */
+ UPDATE_NONFASTFORWARD,
+
+ /** Delete an existing ref; the ref should already exist. */
+ DELETE;
+ }
+
+ /** Result of the update command. */
+ public static enum Result {
+ /** The command has not yet been attempted by the server. */
+ NOT_ATTEMPTED,
+
+ /** The server is configured to deny creation of this ref. */
+ REJECTED_NOCREATE,
+
+ /** The server is configured to deny deletion of this ref. */
+ REJECTED_NODELETE,
+
+ /** The update is a non-fast-forward update and isn't permitted. */
+ REJECTED_NONFASTFORWARD,
+
+ /** The update affects <code>HEAD</code> and cannot be permitted. */
+ REJECTED_CURRENT_BRANCH,
+
+ /**
+ * One or more objects aren't in the repository.
+ * <p>
+ * This is severe indication of either repository corruption on the
+ * server side, or a bug in the client wherein the client did not supply
+ * all required objects during the pack transfer.
+ */
+ REJECTED_MISSING_OBJECT,
+
+ /** Other failure; see {@link ReceiveCommand#getMessage()}. */
+ REJECTED_OTHER_REASON,
+
+ /** The ref could not be locked and updated atomically; try again. */
+ LOCK_FAILURE,
+
+ /** The change was completed successfully. */
+ OK;
+ }
+
+ private final ObjectId oldId;
+
+ private final ObjectId newId;
+
+ private final String name;
+
+ private Type type;
+
+ private Ref ref;
+
+ private Result status;
+
+ private String message;
+
+ /**
+ * Create a new command for {@link ReceivePack}.
+ *
+ * @param oldId
+ * the old object id; must not be null. Use
+ * {@link ObjectId#zeroId()} to indicate a ref creation.
+ * @param newId
+ * the new object id; must not be null. Use
+ * {@link ObjectId#zeroId()} to indicate a ref deletion.
+ * @param name
+ * name of the ref being affected.
+ */
+ public ReceiveCommand(final ObjectId oldId, final ObjectId newId,
+ final String name) {
+ this.oldId = oldId;
+ this.newId = newId;
+ this.name = name;
+
+ type = Type.UPDATE;
+ if (ObjectId.zeroId().equals(oldId))
+ type = Type.CREATE;
+ if (ObjectId.zeroId().equals(newId))
+ type = Type.DELETE;
+ status = Result.NOT_ATTEMPTED;
+ }
+
+ /** @return the old value the client thinks the ref has. */
+ public ObjectId getOldId() {
+ return oldId;
+ }
+
+ /** @return the requested new value for this ref. */
+ public ObjectId getNewId() {
+ return newId;
+ }
+
+ /** @return the name of the ref being updated. */
+ public String getRefName() {
+ return name;
+ }
+
+ /** @return the type of this command; see {@link Type}. */
+ public Type getType() {
+ return type;
+ }
+
+ /** @return the ref, if this was advertised by the connection. */
+ public Ref getRef() {
+ return ref;
+ }
+
+ /** @return the current status code of this command. */
+ public Result getResult() {
+ return status;
+ }
+
+ /** @return the message associated with a failure status. */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * Set the status of this command.
+ *
+ * @param s
+ * the new status code for this command.
+ */
+ public void setResult(final Result s) {
+ setResult(s, null);
+ }
+
+ /**
+ * Set the status of this command.
+ *
+ * @param s
+ * new status code for this command.
+ * @param m
+ * optional message explaining the new status.
+ */
+ public void setResult(final Result s, final String m) {
+ status = s;
+ message = m;
+ }
+
+ void setRef(final Ref r) {
+ ref = r;
+ }
+
+ void setType(final Type t) {
+ type = t;
+ }
+
+ @Override
+ public String toString() {
+ return getType().name() + ": " + getOldId().name() + " "
+ + getNewId().name() + " " + getRefName();
+ }
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java b/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
new file mode 100644
index 0000000..95519b8
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/ReceivePack.java
@@ -0,0 +1,793 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.transport;
+
+import java.io.BufferedWriter;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.spearce.jgit.errors.MissingObjectException;
+import org.spearce.jgit.errors.PackProtocolException;
+import org.spearce.jgit.lib.Constants;
+import org.spearce.jgit.lib.NullProgressMonitor;
+import org.spearce.jgit.lib.ObjectId;
+import org.spearce.jgit.lib.Ref;
+import org.spearce.jgit.lib.RefComparator;
+import org.spearce.jgit.lib.RefUpdate;
+import org.spearce.jgit.lib.Repository;
+import org.spearce.jgit.lib.RepositoryConfig;
+import org.spearce.jgit.revwalk.ObjectWalk;
+import org.spearce.jgit.revwalk.RevCommit;
+import org.spearce.jgit.revwalk.RevObject;
+import org.spearce.jgit.revwalk.RevWalk;
+import org.spearce.jgit.transport.ReceiveCommand.Result;
+
+/**
+ * Implements the server side of a push connection, receiving objects.
+ */
+public class ReceivePack {
+ static final String CAPABILITY_REPORT_STATUS = BasePackPushConnection.CAPABILITY_REPORT_STATUS;
+
+ static final String CAPABILITY_DELETE_REFS = BasePackPushConnection.CAPABILITY_DELETE_REFS;
+
+ /** Database we write the stored objects into. */
+ private final Repository db;
+
+ /** Revision traversal support over {@link #db}. */
+ private final RevWalk walk;
+
+ /** Should an incoming transfer validate objects? */
+ private boolean checkReceivedObjects;
+
+ /** Should an incoming transfer permit create requests? */
+ private boolean allowCreates;
+
+ /** Should an incoming transfer permit delete requests? */
+ private boolean allowDeletes;
+
+ /** Should an incoming transfer permit non-fast-forward requests? */
+ private boolean allowNonFastForwards;
+
+ /** Hook to validate the update commands before execution. */
+ private PreReceiveHook preReceive;
+
+ /** Hook to report on the commands after execution. */
+ private PostReceiveHook postReceive;
+
+ private InputStream rawIn;
+
+ private OutputStream rawOut;
+
+ private PacketLineIn pckIn;
+
+ private PacketLineOut pckOut;
+
+ private PrintWriter msgs;
+
+ /** The refs we advertised as existing at the start of the connection. */
+ private Map<String, Ref> refs;
+
+ /** Capabilities requested by the client. */
+ private Set<String> enabledCapablities;
+
+ /** Commands to execute, as received by the client. */
+ private List<ReceiveCommand> commands;
+
+ /** An exception caught while unpacking and fsck'ing the objects. */
+ private Throwable unpackError;
+
+ /** if {@link #enabledCapablities} has {@link #CAPABILITY_REPORT_STATUS} */
+ private boolean reportStatus;
+
+ /**
+ * Create a new pack receive for an open repository.
+ *
+ * @param into
+ * the destination repository.
+ */
+ public ReceivePack(final Repository into) {
+ db = into;
+ walk = new RevWalk(db);
+
+ final RepositoryConfig cfg = db.getConfig();
+ checkReceivedObjects = cfg.getBoolean("receive", "fsckobjects", false);
+ allowCreates = true;
+ allowDeletes = !cfg.getBoolean("receive", "denydeletes", false);
+ allowNonFastForwards = !cfg.getBoolean("receive",
+ "denynonfastforwards", false);
+ preReceive = PreReceiveHook.NULL;
+ postReceive = PostReceiveHook.NULL;
+ }
+
+ /** @return the repository this receive completes into. */
+ public final Repository getRepository() {
+ return db;
+ }
+
+ /** @return the RevWalk instance used by this connection. */
+ public final RevWalk getRevWalk() {
+ return walk;
+ }
+
+ /**
+ * @return true if this instance will verify received objects are formatted
+ * correctly. Validating objects requires more CPU time on this side
+ * of the connection.
+ */
+ public boolean isCheckReceivedObjects() {
+ return checkReceivedObjects;
+ }
+
+ /**
+ * @param check
+ * true to enable checking received objects; false to assume all
+ * received objects are valid.
+ */
+ public void setCheckReceivedObjects(final boolean check) {
+ checkReceivedObjects = check;
+ }
+
+ /** @return true if the client can request refs to be created. */
+ public boolean isAllowCreates() {
+ return allowCreates;
+ }
+
+ /**
+ * @param canCreate
+ * true to permit create ref commands to be processed.
+ */
+ public void setAllowCreates(final boolean canCreate) {
+ allowCreates = canCreate;
+ }
+
+ /** @return true if the client can request refs to be deleted. */
+ public boolean isAllowDeletes() {
+ return allowDeletes;
+ }
+
+ /**
+ * @param canDelete
+ * true to permit delete ref commands to be processed.
+ */
+ public void setAllowDeletes(final boolean canDelete) {
+ allowDeletes = canDelete;
+ }
+
+ /**
+ * @return true if the client can request non-fast-forward updates of a ref,
+ * possibly making objects unreachable.
+ */
+ public boolean isAllowNonFastForwards() {
+ return allowNonFastForwards;
+ }
+
+ /**
+ * @param canRewind
+ * true to permit the client to ask for non-fast-forward updates
+ * of an existing ref.
+ */
+ public void setAllowNonFastForwards(final boolean canRewind) {
+ allowNonFastForwards = canRewind;
+ }
+
+ /** @return get the hook invoked before updates occur. */
+ public PreReceiveHook getPreReceiveHook() {
+ return preReceive;
+ }
+
+ /**
+ * Set the hook which is invoked prior to commands being executed.
+ * <p>
+ * Only valid commands (those which have no obvious errors according to the
+ * received input and this instance's configuration) are passed into the
+ * hook. The hook may mark a command with a result of any value other than
+ * {@link Result#NOT_ATTEMPTED} to block its execution.
+ * <p>
+ * The hook may be called with an empty command collection if the current
+ * set is completely invalid.
+ *
+ * @param h
+ * the hook instance; may be null to disable the hook.
+ */
+ public void setPreReceiveHook(final PreReceiveHook h) {
+ preReceive = h != null ? h : PreReceiveHook.NULL;
+ }
+
+ /** @return get the hook invoked after updates occur. */
+ public PostReceiveHook getPostReceiveHook() {
+ return postReceive;
+ }
+
+ /**
+ * Set the hook which is invoked after commands are executed.
+ * <p>
+ * Only successful commands (type is {@link Result#OK}) are passed into the
+ * hook. The hook may be called with an empty command collection if the
+ * current set all resulted in an error.
+ *
+ * @param h
+ * the hook instance; may be null to disable the hook.
+ */
+ public void setPostReceiveHook(final PostReceiveHook h) {
+ postReceive = h != null ? h : PostReceiveHook.NULL;
+ }
+
+ /** @return all of the command received by the current request. */
+ public List<ReceiveCommand> getAllCommands() {
+ return Collections.unmodifiableList(commands);
+ }
+
+ /**
+ * Send an error message to the client, if it supports receiving them.
+ * <p>
+ * If the client doesn't support receiving messages, the message will be
+ * discarded, with no other indication to the caller or to the client.
+ * <p>
+ * {@link PreReceiveHook}s should always try to use
+ * {@link ReceiveCommand#setResult(Result, String)} with a result status of
+ * {@link Result#REJECTED_OTHER_REASON} to indicate any reasons for
+ * rejecting an update. Messages attached to a command are much more likely
+ * to be returned to the client.
+ *
+ * @param what
+ * string describing the problem identified by the hook. The
+ * string must not end with an LF, and must not contain an LF.
+ */
+ public void sendError(final String what) {
+ sendMessage("error", what);
+ }
+
+ /**
+ * Send a message to the client, if it supports receiving them.
+ * <p>
+ * If the client doesn't support receiving messages, the message will be
+ * discarded, with no other indication to the caller or to the client.
+ *
+ * @param what
+ * string describing the problem identified by the hook. The
+ * string must not end with an LF, and must not contain an LF.
+ */
+ public void sendMessage(final String what) {
+ sendMessage("remote", what);
+ }
+
+ private void sendMessage(final String type, final String what) {
+ if (msgs != null)
+ msgs.println(type + ": " + what);
+ }
+
+ /**
+ * Execute the receive task on the socket.
+ *
+ * @param input
+ * raw input to read client commands and pack data from. Caller
+ * must ensure the input is buffered, otherwise read performance
+ * may suffer.
+ * @param output
+ * response back to the Git network client. Caller must ensure
+ * the output is buffered, otherwise write performance may
+ * suffer.
+ * @param messages
+ * secondary "notice" channel to send additional messages out
+ * through. When run over SSH this should be tied back to the
+ * standard error channel of the command execution. For most
+ * other network connections this should be null.
+ * @throws IOException
+ */
+ public void receive(final InputStream input, final OutputStream output,
+ final OutputStream messages) throws IOException {
+ try {
+ rawIn = input;
+ rawOut = output;
+
+ pckIn = new PacketLineIn(rawIn);
+ pckOut = new PacketLineOut(rawOut);
+ if (messages != null) {
+ msgs = new PrintWriter(new BufferedWriter(
+ new OutputStreamWriter(messages, Constants.CHARSET),
+ 8192)) {
+ @Override
+ public void println() {
+ print('\n');
+ }
+ };
+ }
+
+ enabledCapablities = new HashSet<String>();
+ commands = new ArrayList<ReceiveCommand>();
+
+ service();
+ } finally {
+ try {
+ if (msgs != null) {
+ msgs.flush();
+ }
+ } finally {
+ rawIn = null;
+ rawOut = null;
+ pckIn = null;
+ pckOut = null;
+ msgs = null;
+ refs = null;
+ enabledCapablities = null;
+ commands = null;
+ }
+ }
+ }
+
+ private void service() throws IOException {
+ sendAdvertisedRefs();
+ recvCommands();
+ if (!commands.isEmpty()) {
+ enableCapabilities();
+
+ if (needPack()) {
+ try {
+ receivePack();
+ if (isCheckReceivedObjects())
+ checkConnectivity();
+ unpackError = null;
+ } catch (IOException err) {
+ unpackError = err;
+ } catch (RuntimeException err) {
+ unpackError = err;
+ } catch (Error err) {
+ unpackError = err;
+ }
+ }
+
+ if (unpackError == null) {
+ validateCommands();
+ executeCommands();
+ }
+
+ if (reportStatus) {
+ sendStatusReport(true, new Reporter() {
+ void sendString(final String s) throws IOException {
+ pckOut.writeString(s + "\n");
+ }
+ });
+ pckOut.end();
+ } else if (msgs != null) {
+ sendStatusReport(false, new Reporter() {
+ void sendString(final String s) throws IOException {
+ msgs.println(s);
+ }
+ });
+ msgs.flush();
+ }
+
+ postReceive.onPostReceive(this, filterCommands(Result.OK));
+ }
+ }
+
+ private void sendAdvertisedRefs() throws IOException {
+ refs = db.getAllRefs();
+
+ final StringBuilder m = new StringBuilder(100);
+ final char[] idtmp = new char[2 * Constants.OBJECT_ID_LENGTH];
+ final Iterator<Ref> i = RefComparator.sort(refs.values()).iterator();
+ {
+ if (i.hasNext()) {
+ final Ref r = i.next();
+ format(m, idtmp, r.getObjectId(), r.getOrigName());
+ } else {
+ format(m, idtmp, ObjectId.zeroId(), "capabilities^{}");
+ }
+ m.append('\0');
+ m.append(' ');
+ m.append(CAPABILITY_DELETE_REFS);
+ m.append(' ');
+ m.append(CAPABILITY_REPORT_STATUS);
+ m.append(' ');
+ writeAdvertisedRef(m);
+ }
+
+ while (i.hasNext()) {
+ final Ref r = i.next();
+ format(m, idtmp, r.getObjectId(), r.getOrigName());
+ writeAdvertisedRef(m);
+ }
+ pckOut.end();
+ }
+
+ private void format(final StringBuilder m, final char[] idtmp,
+ final ObjectId id, final String name) {
+ m.setLength(0);
+ id.copyTo(idtmp, m);
+ m.append(' ');
+ m.append(name);
+ }
+
+ private void writeAdvertisedRef(final StringBuilder m) throws IOException {
+ m.append('\n');
+ pckOut.writeString(m.toString());
+ }
+
+ private void recvCommands() throws IOException {
+ for (;;) {
+ String line;
+ try {
+ line = pckIn.readStringNoLF();
+ } catch (EOFException eof) {
+ if (commands.isEmpty())
+ return;
+ throw eof;
+ }
+
+ if (commands.isEmpty()) {
+ final int nul = line.indexOf('\0');
+ if (nul >= 0) {
+ for (String c : line.substring(nul + 1).split(" "))
+ enabledCapablities.add(c);
+ line = line.substring(0, nul);
+ }
+ }
+
+ if (line.length() == 0)
+ break;
+ if (line.length() < 83) {
+ final String m = "error: invalid protocol: wanted 'old new ref'";
+ sendError(m);
+ throw new PackProtocolException(m);
+ }
+
+ final ObjectId oldId = ObjectId.fromString(line.substring(0, 40));
+ final ObjectId newId = ObjectId.fromString(line.substring(41, 81));
+ final String name = line.substring(82);
+ final ReceiveCommand cmd = new ReceiveCommand(oldId, newId, name);
+ cmd.setRef(refs.get(cmd.getRefName()));
+ commands.add(cmd);
+ }
+ }
+
+ private void enableCapabilities() {
+ reportStatus = enabledCapablities.contains(CAPABILITY_REPORT_STATUS);
+ }
+
+ private boolean needPack() {
+ for (final ReceiveCommand cmd : commands) {
+ if (cmd.getType() != ReceiveCommand.Type.DELETE)
+ return true;
+ }
+ return false;
+ }
+
+ private void receivePack() throws IOException {
+ final IndexPack ip = IndexPack.create(db, rawIn);
+ ip.setFixThin(true);
+ ip.setObjectChecking(isCheckReceivedObjects());
+ ip.index(NullProgressMonitor.INSTANCE);
+ ip.renameAndOpenPack();
+ }
+
+ private void checkConnectivity() throws IOException {
+ final ObjectWalk ow = new ObjectWalk(db);
+ for (final ReceiveCommand cmd : commands) {
+ if (cmd.getResult() != Result.NOT_ATTEMPTED)
+ continue;
+ if (cmd.getType() == ReceiveCommand.Type.DELETE)
+ continue;
+ ow.markStart(ow.parseAny(cmd.getNewId()));
+ }
+ for (final Ref ref : refs.values())
+ ow.markUninteresting(ow.parseAny(ref.getObjectId()));
+ ow.checkConnectivity();
+ }
+
+ private void validateCommands() {
+ for (final ReceiveCommand cmd : commands) {
+ final Ref ref = cmd.getRef();
+ if (cmd.getResult() != Result.NOT_ATTEMPTED)
+ continue;
+
+ if (cmd.getType() == ReceiveCommand.Type.DELETE
+ && !isAllowDeletes()) {
+ // Deletes are not supported on this repository.
+ //
+ cmd.setResult(Result.REJECTED_NODELETE);
+ continue;
+ }
+
+ if (cmd.getType() == ReceiveCommand.Type.CREATE) {
+ if (!isAllowCreates()) {
+ cmd.setResult(Result.REJECTED_NOCREATE);
+ continue;
+ }
+
+ if (ref != null && !isAllowNonFastForwards()) {
+ // Creation over an existing ref is certainly not going
+ // to be a fast-forward update. We can reject it early.
+ //
+ cmd.setResult(Result.REJECTED_NONFASTFORWARD);
+ continue;
+ }
+
+ if (ref != null) {
+ // A well behaved client shouldn't have sent us an
+ // update command for a ref we advertised to it.
+ //
+ cmd.setResult(Result.REJECTED_OTHER_REASON, "ref exists");
+ continue;
+ }
+ }
+
+ if (cmd.getType() == ReceiveCommand.Type.DELETE && ref != null
+ && !ObjectId.zeroId().equals(cmd.getOldId())
+ && !ref.getObjectId().equals(cmd.getOldId())) {
+ // Delete commands can be sent with the old id matching our
+ // advertised value, *OR* with the old id being 0{40}. Any
+ // other requested old id is invalid.
+ //
+ cmd.setResult(Result.REJECTED_OTHER_REASON,
+ "invalid old id sent");
+ continue;
+ }
+
+ if (cmd.getType() == ReceiveCommand.Type.UPDATE) {
+ if (ref == null) {
+ // The ref must have been advertised in order to be updated.
+ //
+ cmd.setResult(Result.REJECTED_OTHER_REASON, "no such ref");
+ continue;
+ }
+
+ if (!ref.getObjectId().equals(cmd.getOldId())) {
+ // A properly functioning client will send the same
+ // object id we advertised.
+ //
+ cmd.setResult(Result.REJECTED_OTHER_REASON,
+ "invalid old id sent");
+ continue;
+ }
+
+ // Is this possibly a non-fast-forward style update?
+ //
+ RevObject oldObj, newObj;
+ try {
+ oldObj = walk.parseAny(cmd.getOldId());
+ } catch (IOException e) {
+ cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd
+ .getOldId().name());
+ continue;
+ }
+
+ try {
+ newObj = walk.parseAny(cmd.getNewId());
+ } catch (IOException e) {
+ cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd
+ .getNewId().name());
+ continue;
+ }
+
+ if (oldObj instanceof RevCommit && newObj instanceof RevCommit) {
+ try {
+ if (!walk.isMergedInto((RevCommit) oldObj,
+ (RevCommit) newObj)) {
+ cmd
+ .setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
+ }
+ } catch (MissingObjectException e) {
+ cmd.setResult(Result.REJECTED_MISSING_OBJECT, e
+ .getMessage());
+ } catch (IOException e) {
+ cmd.setResult(Result.REJECTED_OTHER_REASON);
+ }
+ } else {
+ cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
+ }
+ }
+
+ if (!cmd.getRefName().startsWith(Constants.R_REFS)
+ || !Repository.isValidRefName(cmd.getRefName())) {
+ cmd.setResult(Result.REJECTED_OTHER_REASON, "funny refname");
+ }
+ }
+ }
+
+ private void executeCommands() {
+ preReceive.onPreReceive(this, filterCommands(Result.NOT_ATTEMPTED));
+ for (final ReceiveCommand cmd : filterCommands(Result.NOT_ATTEMPTED))
+ execute(cmd);
+ }
+
+ private void execute(final ReceiveCommand cmd) {
+ try {
+ final RefUpdate ru = db.updateRef(cmd.getRefName());
+ switch (cmd.getType()) {
+ case DELETE:
+ if (!ObjectId.zeroId().equals(cmd.getOldId())) {
+ // We can only do a CAS style delete if the client
+ // didn't bork its delete request by sending the
+ // wrong zero id rather than the advertised one.
+ //
+ ru.setExpectedOldObjectId(cmd.getOldId());
+ }
+ ru.setForceUpdate(true);
+ status(cmd, ru.delete(walk));
+ break;
+
+ case CREATE:
+ case UPDATE:
+ case UPDATE_NONFASTFORWARD:
+ ru.setForceUpdate(isAllowNonFastForwards());
+ ru.setExpectedOldObjectId(cmd.getOldId());
+ ru.setNewObjectId(cmd.getNewId());
+ ru.setRefLogMessage("push", true);
+ status(cmd, ru.update(walk));
+ break;
+ }
+ } catch (IOException err) {
+ cmd.setResult(Result.REJECTED_OTHER_REASON, "lock error: "
+ + err.getMessage());
+ }
+ }
+
+ private void status(final ReceiveCommand cmd, final RefUpdate.Result result) {
+ switch (result) {
+ case NOT_ATTEMPTED:
+ cmd.setResult(Result.NOT_ATTEMPTED);
+ break;
+
+ case LOCK_FAILURE:
+ case IO_FAILURE:
+ cmd.setResult(Result.LOCK_FAILURE);
+ break;
+
+ case NO_CHANGE:
+ case NEW:
+ case FORCED:
+ case FAST_FORWARD:
+ cmd.setResult(Result.OK);
+ break;
+
+ case REJECTED:
+ cmd.setResult(Result.REJECTED_NONFASTFORWARD);
+ break;
+
+ case REJECTED_CURRENT_BRANCH:
+ cmd.setResult(Result.REJECTED_CURRENT_BRANCH);
+ break;
+
+ default:
+ cmd.setResult(Result.REJECTED_OTHER_REASON, result.name());
+ break;
+ }
+ }
+
+ private List<ReceiveCommand> filterCommands(final Result want) {
+ final List<ReceiveCommand> r = new ArrayList<ReceiveCommand>(commands
+ .size());
+ for (final ReceiveCommand cmd : commands) {
+ if (cmd.getResult() == want)
+ r.add(cmd);
+ }
+ return r;
+ }
+
+ private void sendStatusReport(final boolean forClient, final Reporter out)
+ throws IOException {
+ if (unpackError != null) {
+ out.sendString("unpack error " + unpackError.getMessage());
+ if (forClient) {
+ for (final ReceiveCommand cmd : commands) {
+ out.sendString("ng " + cmd.getRefName()
+ + " n/a (unpacker error)");
+ }
+ }
+ return;
+ }
+
+ if (forClient)
+ out.sendString("unpack ok");
+ for (final ReceiveCommand cmd : commands) {
+ if (cmd.getResult() == Result.OK) {
+ if (forClient)
+ out.sendString("ok " + cmd.getRefName());
+ continue;
+ }
+
+ final StringBuilder r = new StringBuilder();
+ r.append("ng ");
+ r.append(cmd.getRefName());
+ r.append(" ");
+
+ switch (cmd.getResult()) {
+ case NOT_ATTEMPTED:
+ r.append("server bug; ref not processed");
+ break;
+
+ case REJECTED_NOCREATE:
+ r.append("creation prohibited");
+ break;
+
+ case REJECTED_NODELETE:
+ r.append("deletion prohibited");
+ break;
+
+ case REJECTED_NONFASTFORWARD:
+ r.append("non-fast forward");
+ break;
+
+ case REJECTED_CURRENT_BRANCH:
+ r.append("branch is currently checked out");
+ break;
+
+ case REJECTED_MISSING_OBJECT:
+ if (cmd.getMessage() == null)
+ r.append("missing object(s)");
+ else if (cmd.getMessage().length() == 2 * Constants.OBJECT_ID_LENGTH)
+ r.append("object " + cmd.getMessage() + " missing");
+ else
+ r.append(cmd.getMessage());
+ break;
+
+ case REJECTED_OTHER_REASON:
+ if (cmd.getMessage() == null)
+ r.append("unspecified reason");
+ else
+ r.append(cmd.getMessage());
+ break;
+
+ case LOCK_FAILURE:
+ r.append("failed to lock");
+ break;
+
+ case OK:
+ // We shouldn't have reached this case (see 'ok' case above).
+ continue;
+ }
+ out.sendString(r.toString());
+ }
+ }
+
+ static abstract class Reporter {
+ abstract void sendString(String s) throws IOException;
+ }
+}
--
1.6.1.rc4.301.g5497a
^ permalink raw reply related
* [JGIT PATCH 11/13] Allow null new ObjectId during RefUpdate.delete
From: Shawn O. Pearce @ 2008-12-23 0:27 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229992043-1053-11-git-send-email-spearce@spearce.org>
If we are deleting a ref we really don't care about what the
new ObjectId value should be; it just doesn't matter. If we
didn't set the value we should consider it the same as if we
had a MissingObjectException, which means treat the value as
null and perform a force update test.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../src/org/spearce/jgit/lib/RefUpdate.java | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java
index 0c9ce91..1417f2c 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java
@@ -424,7 +424,7 @@ private Result updateImpl(final RevWalk walk, final Store store)
private static RevObject safeParse(final RevWalk rw, final AnyObjectId id)
throws IOException {
try {
- return rw.parseAny(id);
+ return id != null ? rw.parseAny(id) : null;
} catch (MissingObjectException e) {
// We can expect some objects to be missing, like if we are
// trying to force a deletion of a branch and the object it
--
1.6.1.rc4.301.g5497a
^ permalink raw reply related
* [JGIT PATCH 10/13] Add compare-and-swap semantics to RefUpdate
From: Shawn O. Pearce @ 2008-12-23 0:27 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229992043-1053-10-git-send-email-spearce@spearce.org>
This permits the caller to set the value it expects to find in the
Ref upon obtaining the update lock. If the ref value doesn't match
then the update is aborted with Result.LOCK_FAILURE, so the caller
can recover gracefully without making unexpected changes.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../src/org/spearce/jgit/lib/RefUpdate.java | 30 ++++++++++++++++++++
1 files changed, 30 insertions(+), 0 deletions(-)
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java
index 235c2fd..0c9ce91 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java
@@ -149,6 +149,9 @@
/** Old value of the ref, obtained after we lock it. */
private ObjectId oldValue;
+ /** If non-null, the value {@link #oldValue} must have to continue. */
+ private ObjectId expValue;
+
/** Result of the update operation. */
private Result result = Result.NOT_ATTEMPTED;
@@ -190,6 +193,27 @@ public void setNewObjectId(final AnyObjectId id) {
}
/**
+ * @return the expected value of the ref after the lock is taken, but before
+ * update occurs. Null to avoid the compare and swap test. Use
+ * {@link ObjectId#zeroId()} to indicate expectation of a
+ * non-existant ref.
+ */
+ public ObjectId getExpectedOldObjectId() {
+ return expValue;
+ }
+
+ /**
+ * @param id
+ * the expected value of the ref after the lock is taken, but
+ * before update occurs. Null to avoid the compare and swap test.
+ * Use {@link ObjectId#zeroId()} to indicate expectation of a
+ * non-existant ref.
+ */
+ public void setExpectedOldObjectId(final AnyObjectId id) {
+ expValue = id != null ? id.toObjectId() : null;
+ }
+
+ /**
* Check if this update wants to forcefully change the ref.
*
* @return true if this update should ignore merge tests.
@@ -370,6 +394,12 @@ private Result updateImpl(final RevWalk walk, final Store store)
return Result.LOCK_FAILURE;
try {
oldValue = db.idOf(getName());
+ if (expValue != null) {
+ final ObjectId o;
+ o = oldValue != null ? oldValue : ObjectId.zeroId();
+ if (!expValue.equals(o))
+ return Result.LOCK_FAILURE;
+ }
if (oldValue == null)
return store.store(lock, Result.NEW);
--
1.6.1.rc4.301.g5497a
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox