From: Daniel Barkalow <hidden> Date: 2016-06-15 22:47:06
transport_get() can call transport_shim_init() to have list and
fetch-ref operations handled by running a separate program as:
git shim-<something> <remote> [<url>]
This program then accepts, on its stdin, "list" and "fetch <hex>
<name>" commands; the former prints out a list of available refs and
either their hashes or what they are symrefs to, while the latter
fetches them into the local object database and prints a newline when done.
Signed-off-by: Daniel Barkalow <redacted>
---
This version uses the more traditional ordering for listing refs and fixes
a memory leak.
Documentation/git-shim.txt | 37 +++++++++++
Makefile | 1 +
transport-shim.c | 142 ++++++++++++++++++++++++++++++++++++++++++++
transport.h | 3 +
4 files changed, 183 insertions(+), 0 deletions(-)
create mode 100644 Documentation/git-shim.txt
create mode 100644 transport-shim.c
@@ -0,0 +1,37 @@+git-shim(1)+============++NAME+----+git-shim - Helper programs for interoperation with remote git++SYNOPSIS+--------+'git shim-<transport>' <remote>++DESCRIPTION+-----------++These programs are normally not used directly by end users, but are+invoked by various git programs that interact with remote repositories+when the repository they would operate on will be accessed using+transport code not linked into the main git binary.++COMMANDS+--------++Commands are given by the caller on the helper's standard input, one per line.++'list'::+ Lists the refs, one per line, if the format "<value>+ <name>". The value is either a hex sha1 hash or "@<dest>" for+ symrefs. After the complete list, outputs a blank line.++'fetch' ref::+ Fetches the given ref, writing the necessary objects to the+ database. Outputs a blank line when the fetch is complete.+++If a fatal error occurs, the program writes the error message to+stderr and exits. The caller should expect that a suitable error+message has been printed if the child closes the connection without+completing a valid response for the current command.
@@ -77,4 +77,7 @@ void transport_unlock_pack(struct transport *transport);inttransport_disconnect(structtransport*transport);char*transport_anonymize_url(constchar*url);+/* Transport methods defined outside transport.c */+voidtransport_shim_init(structtransport*transport,constchar*name);+#endif
From: Johannes Schindelin <hidden> Date: 2016-06-15 22:47:07
Hi,
On Tue, 28 Jul 2009, Daniel Barkalow wrote:
transport_get() can call transport_shim_init() to have list and
fetch-ref operations handled by running a separate program as:
As I commented already, "shim" is a meaningless word in the context of
Git. At _least_ call it something like "git-remote-<protocol>". Even
"git-fetch-<protocol>" would be better than "shim".
@@ -0,0 +1,37 @@+git-shim(1)+============++NAME+----+git-shim - Helper programs for interoperation with remote git
Actually, this is just one helper program, no? Why can't it be integrated
into transport.c?
At the very least, the text so far is misleading.
+COMMANDS
+--------
+
+Commands are given by the caller on the helper's standard input, one per line.
+
+'list'::
+ Lists the refs, one per line, if the format "<value>
+ <name>". The value is either a hex sha1 hash or "@<dest>" for
+ symrefs. After the complete list, outputs a blank line.
+
+'fetch' ref::
+ Fetches the given ref, writing the necessary objects to the
+ database. Outputs a blank line when the fetch is complete.
++
So you allow only 'list' and 'fetch'. That is very limiting, and you do
not even foresee a method to ask for the helper's capabilities. We
already saw how much pain that caused in the transport protocol, so please
do not repeat the mistake here.
+static int fetch_refs_via_shim(struct transport *transport,
+ int nr_heads, const struct ref **to_fetch)
Do you fetch only the refs, or also their objects? If the latter, the
name needs to be adjusted.
+{
+ struct child_process *shim;
+ const struct ref *posn;
+ struct strbuf buf = STRBUF_INIT;
+ int i, count;
+ FILE *file;
+
+ count = 0;
+ for (i = 0; i < nr_heads; i++) {
+ posn = to_fetch[i];
+ if (posn->status & REF_STATUS_UPTODATE)
+ continue;
+ count++;
+ }
This would be more readable IMO if it read like this:
for (count = i = 0; i < nr_heads; i++)
if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
count++;
+ if (count) {
+ shim = get_shim(transport);
It would be much better to say "if (!count) return 0;" here rather than
indenting a whole block of code, with no code after that.
From: Daniel Barkalow <hidden> Date: 2016-06-15 22:47:07
On Tue, 28 Jul 2009, Johannes Schindelin wrote:
Hi,
On Tue, 28 Jul 2009, Daniel Barkalow wrote:
quoted
transport_get() can call transport_shim_init() to have list and
fetch-ref operations handled by running a separate program as:
As I commented already, "shim" is a meaningless word in the context of
Git. At _least_ call it something like "git-remote-<protocol>". Even
"git-fetch-<protocol>" would be better than "shim".
I think git-remote-<protocol> is a good name; I'm not particularly tied to
"shim", although I think this should be able to support push as well as
fetch (that is, instead of http-push being a separate program that
transport.c knows how to run, have that be handled by transport-side code
that could support pushing with arbitrary protocols, and have the pushing
code built into the curl handler). I do think that I want to form the
command names differently depending on whether this is a helper that
transports git objects to a remote git repository using a protocol that
the helper is for, or a helper that interacts with a non-git remote
repository where the helper is particular to the foreign scm.
@@ -0,0 +1,37 @@+git-shim(1)+============++NAME+----+git-shim - Helper programs for interoperation with remote git
Actually, this is just one helper program, no? Why can't it be integrated
into transport.c?
No, this defines a pattern of helper programs, all of which should behave
as documented by the same man page, so that the same code can run any of
them.
quoted
+COMMANDS
+--------
+
+Commands are given by the caller on the helper's standard input, one per line.
+
+'list'::
+ Lists the refs, one per line, if the format "<value>
+ <name>". The value is either a hex sha1 hash or "@<dest>" for
+ symrefs. After the complete list, outputs a blank line.
+
+'fetch' ref::
+ Fetches the given ref, writing the necessary objects to the
+ database. Outputs a blank line when the fetch is complete.
++
So you allow only 'list' and 'fetch'. That is very limiting, and you do
not even foresee a method to ask for the helper's capabilities. We
already saw how much pain that caused in the transport protocol, so please
do not repeat the mistake here.
I'll put the "capabilities" command back in; I dropped it from this subset
of the foreign vcs helper protocol because there's not much variation
possible when the remote repository must be a git repository.
Why can't the caller check for this? Would this not make much more sense
to begin with?
Depending on the order that the user of transport.c calls commands, the
helper may or may not already be running (and it may or may not have been
closed by the previous command, in the future extension of also handling
importers which produce a fast-import stream). The caller could check for
the connection already being constructed, but all callers would have to
add the same check.
Good point; we can't really fail to disconnect, and we've already
determined that things haven't screwed up. It's left over from the pattern
used by some of the native protocol code, where it may determine that the
operation didn't work after all when the "I'm done" packet fails to
produce the "goodbye" response.
quoted
+static int fetch_refs_via_shim(struct transport *transport,
+ int nr_heads, const struct ref **to_fetch)
Do you fetch only the refs, or also their objects? If the latter, the
name needs to be adjusted.
This is inherited from the transport.h naming, which made sense years ago
(when it contrasted fetching objects by name of ref versus fetching
objects by hash); it is fetching objects as specified by struct refs.
quoted
+{
+ struct child_process *shim;
+ const struct ref *posn;
+ struct strbuf buf = STRBUF_INIT;
+ int i, count;
+ FILE *file;
+
+ count = 0;
+ for (i = 0; i < nr_heads; i++) {
+ posn = to_fetch[i];
+ if (posn->status & REF_STATUS_UPTODATE)
+ continue;
+ count++;
+ }
This would be more readable IMO if it read like this:
for (count = i = 0; i < nr_heads; i++)
if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
count++;
I think it's more readable to match the flow control of the later loop.
This is a dry run of the main loop, counting the number of times we
don't skip the important part. I think your version is a more readable way
of counting the number of not-up-to-date items, but it's not nearly so
readable a way of calculating how many times we'll reach the interesting
part of the loop further down.
quoted
+ if (count) {
+ shim = get_shim(transport);
It would be much better to say "if (!count) return 0;" here rather than
indenting a whole block of code, with no code after that.
Yeah, I confused myself while paring down the vcs output parsing code. I
think I actually want to future-proof this code to allow the helper to add
space-separated flags after the name, and I don't want this test.
From: Johannes Schindelin <hidden> Date: 2016-06-15 22:47:07
Hi,
On Tue, 28 Jul 2009, Daniel Barkalow wrote:
On Tue, 28 Jul 2009, Johannes Schindelin wrote:
quoted
On Tue, 28 Jul 2009, Daniel Barkalow wrote:
quoted
transport_get() can call transport_shim_init() to have list and
fetch-ref operations handled by running a separate program as:
As I commented already, "shim" is a meaningless word in the context of
Git. At _least_ call it something like "git-remote-<protocol>". Even
"git-fetch-<protocol>" would be better than "shim".
I think git-remote-<protocol> is a good name;
Good.
I think this should be able to support push as well as fetch
So you agree that it should be extensible. Also good.
I do think that I want to form the command names differently depending
on whether this is a helper that transports git objects to a remote git
repository using a protocol that the helper is for, or a helper that
interacts with a non-git remote repository where the helper is
particular to the foreign scm.
I think that is a rather bad decision: you should not _need_ to care on
the transport.c side. If the helper handles fetching and/or pushing:
fine. How it accomplishes that: don't care.
@@ -0,0 +1,37 @@+git-shim(1)+============++NAME+----+git-shim - Helper programs for interoperation with remote git
Actually, this is just one helper program, no? Why can't it be integrated
into transport.c?
No, this defines a pattern of helper programs, all of which should
behave as documented by the same man page, so that the same code can run
any of them.
See? That description _is_ misleading.
quoted
quoted
+COMMANDS
+--------
+
+Commands are given by the caller on the helper's standard input, one per line.
+
+'list'::
+ Lists the refs, one per line, if the format "<value>
+ <name>". The value is either a hex sha1 hash or "@<dest>" for
+ symrefs. After the complete list, outputs a blank line.
+
+'fetch' ref::
+ Fetches the given ref, writing the necessary objects to the
+ database. Outputs a blank line when the fetch is complete.
++
So you allow only 'list' and 'fetch'. That is very limiting, and you do
not even foresee a method to ask for the helper's capabilities. We
already saw how much pain that caused in the transport protocol, so please
do not repeat the mistake here.
I'll put the "capabilities" command back in; I dropped it from this
subset of the foreign vcs helper protocol because there's not much
variation possible when the remote repository must be a git repository.
Just because you do not foresee it now does not mean that it won't happen.
As I hinted at: it is rather likely that you do not foresee what kind of
capability negotiation we'll want in the future.
Let's learn from history.
Why can't the caller check for this? Would this not make much more sense
to begin with?
Depending on the order that the user of transport.c calls commands, the
helper may or may not already be running (and it may or may not have been
closed by the previous command, in the future extension of also handling
importers which produce a fast-import stream). The caller could check for
the connection already being constructed, but all callers would have to
add the same check.
Fair enough. That large deeply indented part that makes up the most part
of the function is very ugly, though.
quoted
quoted
+static int fetch_refs_via_shim(struct transport *transport,
+ int nr_heads, const struct ref **to_fetch)
Do you fetch only the refs, or also their objects? If the latter, the
name needs to be adjusted.
This is inherited from the transport.h naming, which made sense years ago
(when it contrasted fetching objects by name of ref versus fetching
objects by hash); it is fetching objects as specified by struct refs.
It is actually not inherited from the naming, as the member you assign
this function to does not contain the string "refs".
quoted
quoted
+{
+ struct child_process *shim;
+ const struct ref *posn;
+ struct strbuf buf = STRBUF_INIT;
+ int i, count;
+ FILE *file;
+
+ count = 0;
+ for (i = 0; i < nr_heads; i++) {
+ posn = to_fetch[i];
+ if (posn->status & REF_STATUS_UPTODATE)
+ continue;
+ count++;
+ }
This would be more readable IMO if it read like this:
for (count = i = 0; i < nr_heads; i++)
if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
count++;
I think it's more readable to match the flow control of the later loop.
Yeah, right. It was so readable that I bothered to comment.
I don't like decrement operators used with values, except for the common
string idioms. But I like:
for (; count; count--)
if ...
Which is even more ugly than your current version. Just my personal
taste, of course. Oh, and maybe also a little disagreement with the rest
of Git's source.
What about the return value of this write()? It can indicate error or
short write.
Yeah, I should use write_in_full(), although we know at this point that
shim->in is a pipe which has been drained entirely.
I am not at all sure that you can guarantee that at this point.
quoted
quoted
+ file = fdopen(shim->out, "r");
No check for file != NULL?
I don't think this can fail, if setting up the child process didn't fail.
Until somebody reports a segmentation fault because her operating system
ran out of file descriptors.
In any case, I would rather be safe than sorry. It is not a good habit to
get into, calling functions that can return NULL and not checking them
(even if you try to be super-clever).
Yeah, I confused myself while paring down the vcs output parsing code. I
think I actually want to future-proof this code to allow the helper to
add space-separated flags after the name, and I don't want this test.
Yeah, why not making it hard on the reviewer for the slight possibility
that some change will go in in the future, in the exact form you envisaged
it now?
IMHO it is not at all clear what you are doing here. At least a little
hint is in order if the code does not explain itself.
It's parsing the output of the "list" command, as given in the
documentation. Item starting with a '@' is a symref, otherwise it is a
sha1.
That's nice.
It would be even nicer if there was a comment in the patch so that future
puzzled readers do not know that they have to search the mailing list to
understand this code.
Ciao,
Dscho