builtin-clone: Implement git clone as a builtin command.
PATCH 1/1 builtin-clone: Implement git clone as a builtin command.
Still work-in-progress, local clones and --reference not fully functional.
---
Ok, don't flame me, I know this isn't appropriate at the moment with stabilization for 1.5.4 going on, but I just wanted to post a heads up on this work to avoid duplicate effort. It's one big patch at this point and I haven't even run the test suite yet, but that will change.
cheers,
Kristian
Makefile | 2 +- builtin-clone.c | 504 +++++++++++++++++++++++++ builtin-init-db.c | 119 +++---- builtin-rerere.c | 19 +- builtin.h | 1 + cache.h | 5 + git-clone.sh => contrib/examples/git-clone.sh | 0 copy.c | 21 + diff.c | 8 +- git.c | 1 + unpack-trees.c | 3 +- unpack-trees.h | 1 + 12 files changed, 595 insertions(+), 89 deletions(-) create mode 100644 builtin-clone.c rename git-clone.sh => contrib/examples/git-clone.sh (100%)
Makefile +1 -1
@@ -213,7 +213,6 @@ BASIC_LDFLAGS = SCRIPT_SH = \ git-bisect.sh git-checkout.sh \ - git-clone.sh \ git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \ git-pull.sh git-rebase.sh git-rebase--interactive.sh \ git-repack.sh git-request-pull.sh \
@@ -327,6 +326,7 @@ BUILTIN_OBJS = \ builtin-checkout-index.o \ builtin-check-ref-format.o \ builtin-clean.o \ + builtin-clone.o \ builtin-commit.o \ builtin-commit-tree.o \ builtin-count-objects.o \
builtin-clone.c new file +504 -0
504 lines not on this page · raw message
builtin-init-db.c +54 -65
156 lines not on this page · raw message
builtin-rerere.c +1 -18
@@ -267,23 +267,6 @@ static int diff_two(const char *file1, const char *label1,return 0 ; } -static int copy_file (const char *src,const char *dest) -{ -FILE *in, *out; -char buffer[32768 ]; -int count; - -if (!(in =fopen (src,"r" ))) -return error ("Could not open %s" , src); -if (!(out =fopen (dest,"w" ))) -return error ("Could not open %s" , dest); -while ((count =fread (buffer,1 ,sizeof (buffer), in))) -fwrite (buffer,1 , count, out); -fclose (in); -fclose (out); -return 0 ; -} -static int do_plain_rerere (struct path_list *rr,int fd) {struct path_list conflict = {NULL ,0 ,0 ,1 };
@@ -343,7 +326,7 @@ static int do_plain_rerere(struct path_list *rr, int fd)continue ;fprintf (stderr,"Recorded resolution for '%s'.\n" , path); -copy_file (path,rr_path (name,"postimage" )); +copy_file (path,rr_path (name,"postimage" ),0666 ); tail_optimization:if (i < rr->nr -1 ) memmove(rr->items + i,
builtin.h +1 -0
@@ -24,6 +24,7 @@ extern int cmd_check_attr(int argc, const char **argv, const char *prefix);extern int cmd_check_ref_format (int argc,const char **argv,const char *prefix);extern int cmd_cherry (int argc,const char **argv,const char *prefix);extern int cmd_cherry_pick (int argc,const char **argv,const char *prefix); +extern int cmd_clone (int argc,const char **argv,const char *prefix);extern int cmd_clean (int argc,const char **argv,const char *prefix);extern int cmd_commit (int argc,const char **argv,const char *prefix);extern int cmd_commit_tree (int argc,const char **argv,const char *prefix);
cache.h +5 -0
@@ -230,6 +230,10 @@ extern const char *prefix_filename(const char *prefix, int len, const char *pathextern void verify_filename (const char *prefix,const char *name);extern void verify_non_filename (const char *prefix,const char *name); +#define INIT_DB_QUIET 0x0001 + +extern int init_db (const char *git_dir,const char *template_dir,unsigned int flags); +#define alloc_nr (x) (((x)+16)*3/2)/*
@@ -588,6 +592,7 @@ extern const char *git_log_output_encoding;/* IO helper functions */ extern void maybe_flush_or_die (FILE *,const char *);extern int copy_fd (int ifd,int ofd); +extern int copy_file (const char *dst,const char *src,int mode);extern int read_in_full (int fd,void *buf,size_t count);extern int write_in_full (int fd,const void *buf,size_t count);extern void write_or_die (int fd,const void *buf,size_t count);
contrib/examples/git-clone.sh renamed from git-clone.sh +0 -0
copy.c +21 -0
@@ -34,3 +34,24 @@ int copy_fd(int ifd, int ofd)close (ifd);return 0 ; } + +int copy_file (const char *dst,const char *src,int mode) +{ +int fdi, fdo, status; + + mode = (mode &0111 ) ?0777 :0666 ; +if ((fdi =open (src,O_RDONLY )) <0 ) +return fdi; +if ((fdo =open (dst,O_WRONLY |O_CREAT |O_EXCL , mode)) <0 ) { +close (fdi); +return fdo; + } + status =copy_fd (fdi, fdo); +if (close (fdo) !=0 ) +return error ("%s: write error: %s" , dst,strerror (errno)); + +if (!status &&adjust_shared_perm (dst)) +return -1 ; + +return status; +}
diff.c +4 -4
@@ -258,8 +258,8 @@ static void print_line_count(int count) } } -static void copy_file (int prefix,const char *data,int size, -const char *set,const char *reset) +static void copy_file_with_prefix (int prefix,const char *data,int size, +const char *set,const char *reset) {int ch, nl_just_seen =1 ;while (0 < size--) {
@@ -310,9 +310,9 @@ static void emit_rewrite_diff(const char *name_a,print_line_count (lc_b);printf (" @@%s\n" , reset);if (lc_a) -copy_file ('-' , one->data, one->size, old, reset); +copy_file_with_prefix ('-' , one->data, one->size, old, reset);if (lc_b) -copy_file ('+' , two->data, two->size, new, reset); +copy_file_with_prefix ('+' , two->data, two->size, new, reset); }static int fill_mmfile (mmfile_t *mf,struct diff_filespec *one)
git.c +1 -0
@@ -302,6 +302,7 @@ static void handle_internal_command(int argc, const char **argv) { "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE }, { "cherry", cmd_cherry, RUN_SETUP }, { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE }, + { "clone", cmd_clone }, { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE }, { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE }, { "commit-tree", cmd_commit_tree, RUN_SETUP },
unpack-trees.c +2 -1
@@ -338,7 +338,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_optionsmemset (&df_conflict_list,0 ,sizeof (df_conflict_list)); df_conflict_list.next = &df_conflict_list;memset (&state,0 ,sizeof (state)); - state.base_dir ="" ; + state.base_dir = o->base_dir ? o->base_dir :"" ; + state.base_dir_len =strlen (state.base_dir); state.force =1 ; state.quiet =1 ; state.refresh_cache =1 ;
unpack-trees.h +1 -0
@@ -17,6 +17,7 @@ struct unpack_trees_options {int verbose_update;int aggressive;const char *prefix; +const char *base_dir;int pos;struct dir_struct *dir;merge_fn_t fn;
Signature · 1 line
1.5.3.4
Quoted 4 lines · Kristian Høgsberg
Ok, don't flame me, I know this isn't appropriate at the moment with stabilization for 1.5.4 going on, but I just wanted to post a heads up on this work to avoid duplicate effort. It's one big patch at this point and I haven't even run the test suite yet, but that will change.
Is that why you misspelled Junio's email address? :)
Also as a heads-up, I've got a builtin-checkout that I've got passing all the tests (plus a few to test stuff I originally hadn't implemented). This mostly involved correcting the "interesting" states that unpack_trees() can leave the index in memory when it returns and figuring out how the merge code works. I can send it off for review and testing to people who are interested and don't have other things they should be doing instead.
-Daniel
*This .sig left intentionally blank*
Quoted 8 lines · Daniel Barkalow
Quoted 4 lines · Kristian Høgsberg
Ok, don't flame me, I know this isn't appropriate at the moment with stabilization for 1.5.4 going on, but I just wanted to post a heads up on this work to avoid duplicate effort. It's one big patch at this point and I haven't even run the test suite yet, but that will change.
Is that why you misspelled Junio's email address? :)
Hehe, yeah, do not mess with maintainers in release mode :)
Quoted 6 lines
Also as a heads-up, I've got a builtin-checkout that I've got passing all the tests (plus a few to test stuff I originally hadn't implemented). This mostly involved correcting the "interesting" states that
unpack_trees()can leave the index in memory when it returns and figuring out how the merge code works. I can send it off for review and testing to people who are interested and don't have other things they should be doing instead.
Thanks, that's useful, I was already considering what to do next. I wouldn't mind having a look, but maybe it's better to not discuss new features on the list at this point. I appreciate the heads up though.
cheers,
Kristian
Quoted 11 lines · Kristian Høgsberg
Quoted 8 lines · Daniel Barkalow
Quoted 4 lines · Kristian Høgsberg
Ok, don't flame me, I know this isn't appropriate at the moment with stabilization for 1.5.4 going on, but I just wanted to post a heads up on this work to avoid duplicate effort. It's one big patch at this point and I haven't even run the test suite yet, but that will change.
Is that why you misspelled Junio's email address? :)
Hehe, yeah, do not mess with maintainers in release mode :)
Actually this is a bit unfortunate, regardless of everybody being in release and bugfix only mode.
I was hoping that the evolution path for clone would be to first make it a very thin wrapper around:
git init
git remote add -f
git checkout
sequence. Currently, the "origin" repository is not quite equal to other remotes added with "git remote add", but if we enhance "git remote add" a bit, we should be able to make this happen. This would hopefully lose a lot of code from git-clone. And then after we are done with that, rewrite the remaining thin wrapper in C.
There are a handful issues in that approach with the current git-remote, and that was why I also thought recent "git remote in C" by Dscho a bit unfortunate, as enhancements and interface fixes (both user and machine) tend to be much easier in scripted version.
What the current "git clone" does that are not naturally expressed by the above sequence are:
* HEAD discovery
The code can be lifted from the scripted version and transplanted to git-remote. And to make "origin" and other remotes added by "git remote add", this logic needs to be moved to "git remote".
However, before rewriting the "git remote" to C, it would be really nice if we can update the native protocol so that we can reliably find out which branch HEAD points at. The current code guesses, only because the native protocol does not carry that information [*1*]. Worse yet, even though the current code _knows_ this information when going over dumb protocols, it discards it to use the same guessing logic as used by the native protocol.
* --shared optimization
This is a very easy addition to "git remote add". You make sure that the added remote repository is on a local machine, and set up alternates to point at its object store.
* --reference optimization
This is a bit more involved than --shared. Half the power of this optimization is coming from setting up alternates to point at another local repository, which allows you not to have to _store_ duplicated objects yourself, but the other half is coming from being able to lie to the repository being cloned from that you have branches and tags that reference repository has, even though they are not your branches and tags, which allows you not to have to _download_ the objects to begin with.
I think this can be added to "git remote add" by making --reference also imply -f. Then while "git remote add" sets up the new remote, it can stash the borrowed refs somewhere, just like git-clone does, run the git-fetch, and then remove the borrowed refs once done.
* local optimization (the "cpio" thing)
I think this part needs to stay in git-clone even after we move the above to "git remote add".
[Footnote]
*1* Here is a demonstration of the necessary protocol extension.
-- >8 --
Implement show-symref protocol extension.
This updates the git native "upload-pack" protocol to carry extra information to show which branch HEAD symref points at. As is the other protocol extension, this is enabled only when both ends of the exchange supports it.
The receiving end currently does not do anything, and the logic needs to go to peek-remote more than it needs to go to fetch-pack, but one has to start from somewhere.
--- builtin-fetch-pack.c | 86 +++++++++++++++++++++++++++++++++---------------- upload-pack.c | 25 +++++++++++++- 2 files changed, 81 insertions(+), 30 deletions(-)
builtin-fetch-pack.c +58 -28
@@ -32,7 +32,7 @@ static const char fetch_pack_usage[] =#define MAX_IN_VAIN 256static struct commit_list *rev_list; -static int non_common_revs, multi_ack, use_sideband; +static int non_common_revs, multi_ack, use_sideband, show_symref;static void rev_list_push (struct commit *commit,int mark) {
@@ -141,6 +141,51 @@ static const unsigned char* get_rev(void)return commit->object.sha1; } +static void handle_shallow (int fd[2 ]) +{ +char line[1024 ]; +unsigned char sha1[20 ]; +int len; + +while ((len =packet_read_line (fd[0 ], line,sizeof (line)))) { +if (!prefixcmp (line,"shallow " )) { +if (get_sha1_hex (line +8 , sha1)) +die ("invalid shallow line: %s" , line); +register_shallow (sha1); +continue ; + } +if (!prefixcmp (line,"unshallow " )) { +if (get_sha1_hex (line +10 , sha1)) +die ("invalid unshallow line: %s" , line); +if (!lookup_object (sha1)) +die ("object not found: %s" , line); +/* make sure that it is parsed as shallow */ +parse_object (sha1); +if (unregister_shallow (sha1)) +die ("no shallow found: %s" , line); +continue ; + } +die ("expected shallow/unshallow, got %s" , line); + } +} + +static void handle_symref (int fd[2 ],struct ref *refs) +{ +char line[1024 ]; +int len; + +while ((len =packet_read_line (fd[0 ], line,sizeof (line)))) { +if (!prefixcmp (line,"symref " )) { +/* +* Here you would remember what symbolic ref +* pointed at what real ref to use that +* information later. +*/ +fputs (line, stderr); + } + } +} +static int find_common (int fd[2 ],unsigned char *result_sha1,struct ref *refs) {
@@ -173,8 +218,9 @@ static int find_common(int fd[2], unsigned char *result_sha1, }if (!fetching) - packet_write(fd[1], "want %s%s%s%s%s%s%s\n", + packet_write(fd[1], "want %s%s%s%s%s%s%s%s\n", sha1_to_hex(remote), + (show_symref ? " show-symref" : ""), (multi_ack ? " multi_ack" : ""), (use_sideband == 2 ? " side-band-64k" : ""), (use_sideband == 1 ? " side-band" : ""),
@@ -193,32 +239,11 @@ static int find_common(int fd[2], unsigned char *result_sha1,if (!fetching)return 1 ; -if (args.depth >0 ) { -char line[1024 ]; -unsigned char sha1[20 ]; -int len; - -while ((len =packet_read_line (fd[0 ], line,sizeof (line)))) { -if (!prefixcmp (line,"shallow " )) { -if (get_sha1_hex (line +8 , sha1)) -die ("invalid shallow line: %s" , line); -register_shallow (sha1); -continue ; - } -if (!prefixcmp (line,"unshallow " )) { -if (get_sha1_hex (line +10 , sha1)) -die ("invalid unshallow line: %s" , line); -if (!lookup_object (sha1)) -die ("object not found: %s" , line); -/* make sure that it is parsed as shallow */ -parse_object (sha1); -if (unregister_shallow (sha1)) -die ("no shallow found: %s" , line); -continue ; - } -die ("expected shallow/unshallow, got %s" , line); - } - } +if (args.depth >0 ) +handle_shallow (fd); + +if (show_symref) +handle_symref (fd, refs); flushes =0 ; retval =-1 ;
@@ -558,6 +583,11 @@ static struct ref *do_fetch_pack(int fd[2],get_remote_heads (fd[0 ], &ref,0 ,NULL ,0 );if (is_repository_shallow () && !server_supports ("shallow" ))die ("Server does not support shallow clients" ); +if (server_supports ("show-symref" )) { +if (args.verbose) +fprintf (stderr,"Server supports show-symref\n" ); + show_symref =1 ; + }if (server_supports ("multi_ack" )) {if (args.verbose)fprintf (stderr,"Server supports multi_ack\n" );
upload-pack.c +23 -2
@@ -27,7 +27,7 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nstatic unsigned long oldest_have;static int multi_ack, nr_our_refs; -static int use_thin_pack, use_ofs_delta, no_progress; +static int use_thin_pack, use_ofs_delta, no_progress, show_symref;static struct object_array have_obj;static struct object_array want_obj;static unsigned int timeout;
@@ -477,6 +477,10 @@ static void receive_needs(void) get_sha1_hex(line+5, sha1_buf))die ("git-upload-pack: protocol error, " "expected to get sha, not '%s'" , line); + +/* Protocol extensions */ +if (strstr (line+45 ,"show-symref" )) + show_symref =1 ;if (strstr (line+45 ,"multi_ack" )) multi_ack =1 ;if (strstr (line+45 ,"thin-pack" ))
@@ -557,7 +561,7 @@ static void receive_needs(void)static int send_ref (const char *refname,const unsigned char *sha1,int flag,void *cb_data) {static const char *capabilities ="multi_ack thin-pack side-band" -" side-band-64k ofs-delta shallow no-progress" ; +" side-band-64k ofs-delta shallow no-progress show-symref" ;struct object *o =parse_object (sha1);if (!o)
@@ -580,6 +584,18 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, voreturn 0 ; } +static int send_symref (const char *refname,const unsigned char *sha1,int flag,void *cb_data) +{ +unsigned char object_name[20 ]; +const char *symref; +int what; + + symref =resolve_ref (refname, object_name,1 , &what); +if (symref && (what &REF_ISSYMREF )) +packet_write (1 ,"symref %s %s\n" , refname, symref); +return 0 ; +} +static void upload_pack (void ) {reset_timeout ();
@@ -587,6 +603,11 @@ static void upload_pack(void)for_each_ref (send_ref,NULL );packet_flush (1 );receive_needs (); +if (show_symref) { +send_symref ("HEAD" ,NULL ,0 ,NULL ); +for_each_ref (send_symref,NULL ); +packet_flush (1 ); + }if (want_obj.nr) {get_common_commits ();create_pack_file ();
Signature · 1 line
1.5.3.7-1157-gbf82a
Quoted 10 lines · Junio C Hamano
Quoted 8 lines · Kristian Høgsberg
Quoted 7 lines · Daniel Barkalow
Quoted 4 lines · Kristian Høgsberg
Ok, don't flame me, I know this isn't appropriate at the moment with stabilization for 1.5.4 going on, but I just wanted to post a heads up on this work to avoid duplicate effort. It's one big patch at this point and I haven't even run the test suite yet, but that will change.
Quoted 6 lines
Quoted 3 lines
Quoted 1 line
Is that why you misspelled Junio's email address? :)
Hehe, yeah, do not mess with maintainers in release mode :)
Actually this is a bit unfortunate, regardless of everybody being in release and bugfix only mode.
Well, let's just pick up the discussion in January, I have a lot of other stuff I'm trying to do anyway :)
Quoted 8 lines
I was hoping that the evolution path for clone would be to first make it a very thin wrapper around:
git init
git remote add -f
git checkoutsequence.
However, let me just say that the patch I sent is almost just that. Part of the patch refactors init-db to be useful from clone, part of the code is option parsing and figuring out the git dir, work tree. Also, the part of the patch that does 'git checkout' is approximately 20 lines that end up calling unpack_tre() and then write_cache(). The bulk of the work here is really just builtin boilerplate code, option parsing and the builtin-clone tasks you describe below (HEAD discovery, --shared and --reference optimizations and the local hardlink optimization - all these are in the 500 line builtin-clone.c I sent).
And maybe it makes sense to use builtin-remote for the remote add -f part, but the fetch part of the patch is 10 lines to set up for fetch_pack(). So while I do agree that it makes sense to keep remotes handling in one place, doing the fetch_pack() in builtin-clone.c doesn't seem like a big duplication of code. And either way, I agree with Dscho, once we have either builtin-clone or builtin-fetch it's easier to share code and refactor, and there is not a strong reason to do one or the other first.
cheers,
Kristian
Quoted 10 lines · Kristian H�gsberg
However, let me just say that the patch I sent is almost just that. Part of the patch refactors init-db to be useful from clone, part of the code is option parsing and figuring out the git dir, work tree. Also, the part of the patch that does 'git checkout' is approximately 20 lines that end up calling
unpack_tre()and thenwrite_cache(). The bulk of the work here is really just builtin boilerplate code, option parsing and the builtin-clone tasks you describe below (HEAD discovery, --shared and --reference optimizations and the local hardlink optimization - all these are in the 500 linebuiltin-clone.cI sent).
Quoted 8 lines
And maybe it makes sense to use builtin-remote for the remote add -f part, but the fetch part of the patch is 10 lines to set up for
fetch_pack(). So while I do agree that it makes sense to keep remotes handling in one place, doing thefetch_pack()inbuiltin-clone.cdoesn't seem like a big duplication of code. And either way, I agree with Dscho, once we have either builtin-clone or builtin-fetch it's easier to share code and refactor, and there is not a strong reason to do one or the other first.
Er, we have builtin-fetch. We just don't have a way of calling it with all of the option parsing done, but that should be easy. I was expecting that step to get done when clone got converted, or maybe remote...
I agree that the checkout special case when the code knows in advance that you don't have anything checked out beforehand is particularly trivial, and it's probably just as easy to call unpack_trees() and write_cache() as to use an actual checkout implementation.
-Daniel
*This .sig left intentionally blank*
Quoted 21 lines · Daniel Barkalow
Quoted 10 lines · Kristian Hgsberg
However, let me just say that the patch I sent is almost just that. Part of the patch refactors init-db to be useful from clone, part of the code is option parsing and figuring out the git dir, work tree. Also, the part of the patch that does 'git checkout' is approximately 20 lines that end up calling
unpack_tre()and thenwrite_cache(). The bulk of the work here is really just builtin boilerplate code, option parsing and the builtin-clone tasks you describe below (HEAD discovery, --shared and --reference optimizations and the local hardlink optimization - all these are in the 500 linebuiltin-clone.cI sent).Quoted 8 lines
And maybe it makes sense to use builtin-remote for the remote add -f part, but the fetch part of the patch is 10 lines to set up for
fetch_pack(). So while I do agree that it makes sense to keep remotes handling in one place, doing thefetch_pack()inbuiltin-clone.cdoesn't seem like a big duplication of code. And either way, I agree with Dscho, once we have either builtin-clone or builtin-fetch it's easier to share code and refactor, and there is not a strong reason to do one or the other first.
Quoted 3 lines
Er, we have builtin-fetch. We just don't have a way of calling it with all of the option parsing done, but that should be easy. I was expecting that step to get done when clone got converted, or maybe remote...
Ugh, I meant builtin-remote there, sorry. I use fetch_pack() like the shell script does, and it seem a lot easier that trying to call fetch:
struct fetch_pack_args args;
args.uploadpack = option_upload_pack;
args.quiet = option_quiet;
args.fetch_all = 1;
args.lock_pack = 0;
args.keep_pack = 1;
args.depth = option_depth;
args.no_progress = 1;
refs = fetch_pack(&args, argv[0], 0, NULL, NULL);Kristian
Quoted 14 lines · Kristian H�gsberg
Ugh, I meant builtin-remote there, sorry. I use
fetch_pack()like the shell script does, and it seem a lot easier that trying to call fetch:struct fetch_pack_args args; args.uploadpack = option_upload_pack; args.quiet = option_quiet; args.fetch_all = 1; args.lock_pack = 0; args.keep_pack = 1; args.depth = option_depth; args.no_progress = 1; refs = fetch_pack(&args, argv[0], 0, NULL, NULL);
Ah, but that only works for git native protocol remote repositories. Calling fetch instead would mean that other protocols also work without any fuss.
-Daniel
*This .sig left intentionally blank*
Quoted 8 lines · Junio C Hamano
What the current "git clone" does that are not naturally expressed by the above sequence are:
* HEAD discovery
The code can be lifted from the scripted version and transplanted to git-remote. And to make "origin" and other remotes added by "git remote add", this logic needs to be moved to "git remote".
s/remote add", this/remote add" more equal, this/;
Hi,
Quoted 11 lines · Junio C Hamano
Quoted 9 lines · Kristian Høgsberg
Quoted 8 lines · Daniel Barkalow
Quoted 5 lines · Kristian Høgsberg
Ok, don't flame me, I know this isn't appropriate at the moment with stabilization for 1.5.4 going on, but I just wanted to post a heads up on this work to avoid duplicate effort. It's one big patch at this point and I haven't even run the test suite yet, but that will change.
Quoted 6 lines
Quoted 3 lines
Quoted 1 line
Is that why you misspelled Junio's email address? :)
Hehe, yeah, do not mess with maintainers in release mode :)
Actually this is a bit unfortunate, regardless of everybody being in release and bugfix only mode.
I can understand that feeling, but I have to say that I am actually quite pleased with the progress in direction of having most of git as builtins.
Quoted 8 lines
I was hoping that the evolution path for clone would be to first make it a very thin wrapper around:
git init
git remote add -f
git checkoutsequence.
Yeah, I thought so too, but I'll also gladly take the builtin first.
Quoted 4 lines
There are a handful issues in that approach with the current git-remote, and that was why I also thought recent "git remote in C" by Dscho a bit unfortunate, as enhancements and interface fixes (both user and machine) tend to be much easier in scripted version.
And here I have to disagree strongly. I _wasted_ a _week_ on trying to fix that stupid "add --mirror && prune" bug in the scripted version. It was absolutely horrible. And I felt like a moron after that week.
In contrast, it was easy as chocolate cake to fix it in the builtin remote.
Now, if you not only hinted in some mail that something is wrong with builtin-remote, but gave me some input, I could fix that in the builtin, too.
Quoted 9 lines
What the current "git clone" does that are not naturally expressed by the above sequence are:
* HEAD discovery
The code can be lifted from the scripted version and transplanted to git-remote. And to make "origin" and other remotes added by "git remote add", this logic needs to be moved to "git remote".
Quoted 7 lines
However, before rewriting the "git remote" to C, it would be really nice if we can update the native protocol so that we can reliably find out which branch HEAD points at. The current code guesses, only because the native protocol does not carry that information [*1*]. Worse yet, even though the current code _knows_ this information when going over dumb protocols, it discards it to use the same guessing logic as used by the native protocol.
I wonder why this should be easier with git remote in Perl. IMHO it is easier with git remote in C.
Quoted 5 lines
* --shared optimization
This is a very easy addition to "git remote add". You make sure that the added remote repository is on a local machine, and set up alternates to point at its object store.
Concur.
Since I want to lose that dependency on cpio on Windows (which we fake by using tar), I'll implement this in C anyway.
Ciao,
Dscho
Quoted 3 lines · Johannes Schindelin
Hi,
On Tue, 11 Dec 2007, Junio C Hamano wrote:
...
Quoted 10 lines
Quoted 5 lines
* --shared optimization
This is a very easy addition to "git remote add". You make sure that the added remote repository is on a local machine, and set up alternates to point at its object store.
Concur.
Since I want to lose that dependency on cpio on Windows (which we fake by using tar), I'll implement this in C anyway.
It's not used for --shared (which is just writing an alternates file), it's used for -l, hardlinking locally cloned repos. The code to replace cpio is already in the patch I sent, look for clone_local().
cheers,
Kristian
Hi,
Quoted 10 lines · Kristian H?gsberg
Quoted 1 line · Johannes Schindelin
On Tue, 11 Dec 2007, Junio C Hamano wrote:
...
Quoted 6 lines
Quoted 5 lines
* --shared optimization
This is a very easy addition to "git remote add". You make sure that the added remote repository is on a local machine, and set up alternates to point at its object store.
Quoted 8 lines
Quoted 4 lines
Concur.
Since I want to lose that dependency on cpio on Windows (which we fake by using tar), I'll implement this in C anyway.
It's not used for --shared (which is just writing an alternates file), it's used for -l, hardlinking locally cloned repos. The code to replace cpio is already in the patch I sent, look for
clone_local().
Sorry, that comment should have gone after another part of the original message.
My only two excuses are that I am ill, and am overloaded with work.
Ciao,
Dscho
Quoted 5 lines · Junio C Hamano
* HEAD discovery
The code can be lifted from the scripted version and transplanted to git-remote. And to make "origin" and other remotes added by "git remote add", this logic needs to be moved to "git remote".
A rough first attempt appended.
Cleaning up in "remote rm" is a bit of a pain once a remote can contain symbolic-refs. Would it make sense to add something like a "git update-ref -D <refname>" that deletes anything at that path with no checking?
Thanks for outlining these remote improvements, by the way, I really look forward to them. I'll do what I can, but will probably be much too slow....
--b.
commit fecdb5c0d118767c216302e2c91950cca04f9a26
Author: J. Bruce Fields <[email protected]> Date: Fri Dec 21 19:55:09 2007 -0500
git-remote: make add -f guess HEAD, as clone does
Has a few ugly bits.
git-remote.perl +61 -6
@@ -270,12 +270,57 @@ sub show_remote { return 0; } +sub guess_head { + my ($subdir, $head_sha1) = @_; + my $found = 0; + + for ($git->command('for-each-ref', "$subdir")) { + chomp; + m|^([0-9a-f]{40})\s[a-z]+\s$subdir/(.*)$| || die(); + my ($sha1, $ref) = ($1, $2); + + if ($sha1 eq $head_sha1) { + $found = $ref; + # prefer "master" if it matches: + if ($ref eq "master") { + last; + } + } + } + return $found +} + +sub fix_head { + my ($remote) = @_; + + my $subdir = "refs/remotes/$remote"; + my $head_sha1 = $git->command(qw(rev-parse --verify), "$subdir/HEAD"); + chomp($head_sha1); + + unlink($git->repo_path."/$subdir/HEAD"); + my $found = guess_head($subdir, $head_sha1); + if (!$found) { + # Just leave it as a a bare sha1 + $git->command("update-ref", "$subdir/HEAD", "$head_sha1"); + return; + } + $git->command("symbolic-ref", "$subdir/HEAD", "$subdir/$found"); + $git->command("config", "branch.$found.remote", "$remote"); + $git->command("config", "branch.$found.merge", "refs/heads/$found"); +} + sub add_remote { my ($name, $url, $opts) = @_; if (exists $remote->{$name}) { print STDERR "remote $name already exists.\n"; exit(1); } + + # Tricky!: magic HEAD setup should only be done when + # "mirror", "master", and "track" options aren't given. + # And for orthagonality perhaps we should also provide a + # "track head" optio., compatible with "track" but not + # the other two.... $git->command('config', "remote.$name.url", $url); my $track = $opts->{'track'} || ["*"];
@@ -286,7 +331,17 @@ sub add_remote { "+refs/heads/$_:refs/remotes/$name/$_"); } if ($opts->{'fetch'}) { - $git->command('fetch', $name); + if (!$opts->{'mirror'} && !$opts->{'master'} + && !$opts->{'track'}) { + my $refspec = "+refs/heads/*:refs/remotes/$name/*"; + + # XXX: can there be a remote refs/heads/HEAD?? + $git->command('fetch', $name, $refspec, + "HEAD:refs/remotes/$name/HEAD"); + fix_head($name); + } else { + $git->command('fetch', $name); + } } if (exists $opts->{'master'}) { $git->command('symbolic-ref', "refs/remotes/$name/HEAD",
@@ -338,11 +393,11 @@ sub rm_remote { } }; - my @refs = $git->command('for-each-ref', - '--format=%(refname) %(objectname)', "refs/remotes/$name"); - for (@refs) { - ($ref, $object) = split; - $git->command(qw(update-ref -d), $ref, $object); + # Ugh: update-ref doesn't work on symref (as "HEAD" may be), + # but the following won't work on packed refs, for example. + for ($git->command('for-each-ref', + '--format=%(refname)', "refs/remotes/$name")) { + unlink($git->repo_path."/$_"); } return 0; }
t/t5505-remote.sh +1 -1
@@ -69,7 +69,7 @@ test_expect_success 'add another remote' ' tokens_match "origin second" "$(git remote)" && check_remote_track origin master side && check_remote_track second master side another && - check_tracking_branch second master side another && + check_tracking_branch second HEAD master side another && git for-each-ref "--format=%(refname)" refs/remotes | sed -e "/^refs\/remotes\/origin\//d" \ -e "/^refs\/remotes\/second\//d" >actual &&