From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
This series replaces three related topics from pu:
js/remote-set-head
jk/head-lookup
js/remote-display
It is based on master. I re-ordered the original commits such that all
the refactoring outside of builtin-remote is done first, followed by
some small cleanups of builtin-remote itself, and finally ending with
the new builtin-remote functionality (set-head) and changes to its
"show" output. I think it is easier to review this way and the history
will be cleaner.
The end result is largely unchanged from what is currently in pu, but
the intermediate results obviously differ to account for the
re-ordering. I've verified that each intermediate result compiles and
passes t5505-remote.sh. I also ran the full test-suite after the first
group of refactoring, and again at the end.
I've diffed each of the changed files at the end of this series against
what is currently in pu to verify I didn't miss anything and I saw
nothing of note.
So I think this series is clean, and doesn't need an extensive
re-review, but a quick look-over would be appreciated.
I signed off on Jeff's patches; please remove my SoB from those if it is
inappropriate for me to have done so.
Thanks,
j.
Jay Soffian (17):
move duplicated get_local_heads() to remote.c
move duplicated ref_newer() to remote.c
move locate_head() to remote.c
remote: simplify guess_remote_head()
remote: let guess_remote_head() optionally return all matches
remote: make match_refs() copy src ref before assigning to peer_ref
remote: make match_refs() not short-circuit
string-list: new for_each_string_list() function
builtin-remote: refactor duplicated cleanup code
builtin-remote: remove unused code in get_ref_states
builtin-remote: rename variables and eliminate redundant function call
builtin-remote: make get_remote_ref_states() always populate states.tracked
builtin-remote: fix two inconsistencies in the output of "show <remote>"
builtin-remote: teach show to display remote HEAD
builtin-remote: add set-head subcommand
builtin-remote: new show output style
builtin-remote: new show output style for push refspecs
Jeff King (4):
test scripts: refactor start_httpd helper
add basic http clone/fetch tests
refactor find_ref_by_name() to accept const list
remote: make guess_remote_head() use exact HEAD lookup if it is available
Documentation/git-remote.txt | 28 ++-
Makefile | 1 +
builtin-clone.c | 41 +---
builtin-remote.c | 563 ++++++++++++++++++++++++++------
builtin-send-pack.c | 79 +-----
cache.h | 2 +-
contrib/completion/git-completion.bash | 2 +-
http-push.c | 72 +----
refs.c | 4 +-
remote.c | 136 ++++++++-
remote.h | 12 +
string-list.c | 10 +
string-list.h | 5 +
t/lib-httpd.sh | 9 +-
t/t5505-remote.sh | 114 +++++--
t/t5540-http-push.sh | 9 +-
t/t5550-http-fetch.sh | 57 ++++
17 files changed, 818 insertions(+), 326 deletions(-)
create mode 100755 t/t5550-http-fetch.sh
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
From: Jeff King <redacted>
Since it doesn't actually touch its argument, this makes
sense.
However, we still want to return a non-const version (which
requires a cast) so that this:
struct ref *a, *b;
a = find_ref_by_name(b);
works. Unfortunately, you can also silently strip the const
from a variable:
struct ref *a;
const struct ref *b;
a = find_ref_by_name(b);
This is a classic C const problem because there is no way to
say "return the type with the same constness that was passed
to us"; we provide the same semantics as standard library
functions like strchr.
Signed-off-by: Jeff King <redacted>
Signed-off-by: Jay Soffian <redacted>
---
cache.h | 2 +-
refs.c | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
From: Jeff King <redacted>
There are some redirects and some error checking that need
to be done by the caller; let's move both into the
start_httpd function so that all callers don't have to
repeat them (there is only one caller now, but another will
follow in this series).
This doesn't violate any assumptions that aren't already
being made by lib-httpd, which is happy to say "skipping"
and call test_done for a number of other cases.
Signed-off-by: Jeff King <redacted>
Signed-off-by: Jay Soffian <redacted>
---
t/lib-httpd.sh | 9 +++++++--
t/t5540-http-push.sh | 8 +-------
2 files changed, 8 insertions(+), 9 deletions(-)
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
From: Jeff King <redacted>
This was mostly being tested implicitly by the "http push"
tests. But making a separate test script means that:
- we will run fetch tests even when http pushing support
is not built
- when there are failures on fetching, they are easier to
see and isolate, as they are not in the middle of push
tests
This script defaults to running the webserver on port 5550,
and puts the original t5540 on port 5540, so that the two
can be run simultaneously without conflict (but both still
respect an externally set LIB_HTTPD_PORT).
Signed-off-by: Jeff King <redacted>
Signed-off-by: Jay Soffian <redacted>
---
Makefile | 1 +
t/t5540-http-push.sh | 1 +
t/t5550-http-fetch.sh | 46 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 48 insertions(+), 0 deletions(-)
create mode 100755 t/t5550-http-fetch.sh
@@ -11,6 +11,7 @@ This test runs various sanity checks on http-push.'ROOT_PATH="$PWD"LIB_HTTPD_DAV=t+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'}ifgithttp-push>/dev/null2>&1||[$?-eq128]then
@@ -0,0 +1,46 @@+#!/bin/sh++test_description='test fetching over http'+../test-lib.sh++iftest-n"$NO_CURL";then+say'skipping test, git built without http support'+test_done+fi++."$TEST_DIRECTORY"/lib-httpd.sh+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'}+start_httpd++test_expect_success'setup repository''+echocontent>file&&+gitaddfile&&+gitcommit-mone+'++test_expect_success'create http-accessible bare repository''+mkdir"$HTTPD_DOCUMENT_ROOT_PATH/repo.git"&&+(cd"$HTTPD_DOCUMENT_ROOT_PATH/repo.git"&&+git--bareinit&&+echo"exec git update-server-info">hooks/post-update&&+chmod+xhooks/post-update+)&&+gitremoteaddpublic"$HTTPD_DOCUMENT_ROOT_PATH/repo.git"&&+gitpushpublicmaster:master+'++test_expect_success'clone http repository''+gitclone$HTTPD_URL/repo.gitclone&&+test_cmpfileclone/file+'++test_expect_success'fetch changes via http''+echocontent>>file&&+gitcommit-a-mtwo&&+gitpushpublic+(cdclone&&gitpull)&&+test_cmpfileclone/file+'++stop_httpd+test_done
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
Add a convenience function for iterating over a string_list's items via
a callback.
Signed-off-by: Jay Soffian <redacted>
---
string-list.c | 10 ++++++++++
string-list.h | 5 +++++
2 files changed, 15 insertions(+), 0 deletions(-)
@@ -20,6 +20,11 @@ void string_list_clear(struct string_list *list, int free_util);typedefvoid(*string_list_clear_func_t)(void*p,constchar*str);voidstring_list_clear_func(structstring_list*list,string_list_clear_func_tclearfunc);+/* Use this function to iterate over each item */+typedefint(*string_list_each_func_t)(structstring_list_item*,void*);+intfor_each_string_list(string_list_each_func_t,+structstring_list*list,void*cb_data);+/* Use these functions only on sorted lists: */intstring_list_has_string(conststructstring_list*list,constchar*string);intstring_list_find_insert_index(conststructstring_list*list,constchar*string,
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
get_local_heads() appears to have been copied from builtin-send-pack.c
to http-push.c via cut and paste. This patch moves the function and its
helper one_local_ref() to remote.c.
The two copies of one_local_ref() were not identical. I used the more
recent version from builtin-send-pack.c after confirming with Jeff King
that it was an oversight that commit 30affa1e did not update both
copies.
This is in preparation for being able to call it from builtin-remote.c
Signed-off-by: Jay Soffian <redacted>
---
builtin-send-pack.c | 29 ++---------------------------
http-push.c | 23 ++---------------------
remote.c | 26 ++++++++++++++++++++++++++
remote.h | 1 +
4 files changed, 31 insertions(+), 48 deletions(-)
@@ -133,33 +133,8 @@ static int ref_newer(const unsigned char *new_sha1,returnfound;}-staticstructref*local_refs,**local_tail;staticstructref*remote_refs,**remote_tail;-staticintone_local_ref(constchar*refname,constunsignedchar*sha1,intflag,void*cb_data)-{-structref*ref;-intlen;--/* we already know it starts with refs/ to get here */-if(check_ref_format(refname+5))-return0;--len=strlen(refname)+1;-ref=xcalloc(1,sizeof(*ref)+len);-hashcpy(ref->new_sha1,sha1);-memcpy(ref->name,refname,len);-*local_tail=ref;-local_tail=&ref->next;-return0;-}--staticvoidget_local_heads(void)-{-local_tail=&local_refs;-for_each_ref(one_local_ref,NULL);-}-staticintreceive_status(intin,structref*refs){structref*hint;
@@ -387,7 +362,7 @@ static int refs_pushed(struct ref *ref)staticintdo_send_pack(intin,intout,structremote*remote,constchar*dest,intnr_refspec,constchar**refspec){-structref*ref;+structref*ref,*local_refs;intnew_refs;intask_for_status_report=0;intallow_deleting_refs=0;
@@ -405,7 +380,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest/* No funny business with the matcher */remote_tail=get_remote_heads(in,&remote_refs,0,NULL,REF_NORMAL,&extra_have);-get_local_heads();+local_refs=get_local_heads();/* Does the other end support the reporting? */if(server_supports("report-status"))
@@ -2195,7 +2176,7 @@ int main(int argc, char **argv)intrc=0;inti;intnew_refs;-structref*ref;+structref*ref,*local_refs;char*rewritten_url=NULL;git_extract_argv0_path(argv[0]);
@@ -2302,7 +2283,7 @@ int main(int argc, char **argv)fetch_indices();/* Get a list of all local and remote heads to validate refspecs */-get_local_heads();+local_refs=get_local_heads();fprintf(stderr,"Fetching remote heads...\n");get_dav_remote_heads();
@@ -1376,3 +1376,29 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)base,num_ours,num_theirs);return1;}++staticintone_local_ref(constchar*refname,constunsignedchar*sha1,intflag,void*cb_data)+{+structref***local_tail=cb_data;+structref*ref;+intlen;++/* we already know it starts with refs/ to get here */+if(check_ref_format(refname+5))+return0;++len=strlen(refname)+1;+ref=xcalloc(1,sizeof(*ref)+len);+hashcpy(ref->new_sha1,sha1);+memcpy(ref->name,refname,len);+**local_tail=ref;+*local_tail=&ref->next;+return0;+}++structref*get_local_heads(void)+{+structref*local_refs,**local_tail=&local_refs;+for_each_ref(one_local_ref,&local_tail);+returnlocal_refs;+}
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
In some instances, match_refs() sets the peer_ref field of refs in the
dst list such that it points to a ref in the src list. This prevents
callers from freeing both the src and dst lists, as doing so would cause
a double-free since free_refs() frees the peer_ref.
As well, the following configuration causes two refs in the dst list to
have the same peer_ref, which can also lead to a double-free:
push = refs/heads/master:refs/heads/backup
push = refs/heads/master:refs/heads/master
Existing callers of match_heads() call it only once and then terminate,
w/o ever bothering to free the src or dst lists, so this is not
currently a problem.
This patch modifies match_refs() to first copy any refs it plucks from
the src list before assigning them as a peer_ref. This allows
builtin-remote, a future caller, to free the src and dst lists.
Signed-off-by: Jay Soffian <redacted>
---
remote.c | 7 +++++--
1 files changed, 5 insertions(+), 2 deletions(-)
@@ -938,6 +939,7 @@ static int match_explicit(struct ref *src, struct ref *dst,matched_src=matched_dst=NULL;switch(count_refspec_match(rs->src,src,&matched_src)){case1:+copy_src=1;break;case0:/* The source could be in the get_sha1() format
@@ -947,6 +949,7 @@ static int match_explicit(struct ref *src, struct ref *dst,matched_src=try_explicit_object_name(rs->src);if(!matched_src)returnerror("src refspec %s does not match any.",rs->src);+copy_src=0;break;default:returnerror("src refspec %s matches more than one.",rs->src);
@@ -992,7 +995,7 @@ static int match_explicit(struct ref *src, struct ref *dst,returnerror("dst ref %s receives from more than one src.",matched_dst->name);else{-matched_dst->peer_ref=matched_src;+matched_dst->peer_ref=copy_src?copy_ref(matched_src):matched_src;matched_dst->force=rs->force;}return0;
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
match_refs() returns non-zero if there is an error in
match_explicit_refs(), without handling any remaining pattern ref specs.
Its existing callers exit upon receiving non-zero, so a partial result
is of no consequence to them; however a new caller, builtin-remote, is
interested in the complete result even if there are errors in
match_explicit_refs().
Signed-off-by: Jay Soffian <redacted>
---
remote.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
This patch moves identical lines of code into a cleanup function. The
function has two callers and is about to gain a third.
Also removed a bogus NEEDSWORK comment per Daniel Barkalow:
Actually, the comment is wrong; "remote" comes from remote_get(),
which returns things from a cache in remote.c; there could be a
remote_put() to let the code know that the caller is done with the
object, but it wouldn't presently do anything.
Signed-off-by: Jay Soffian <redacted>
---
builtin-remote.c | 17 +++++++++--------
1 files changed, 9 insertions(+), 8 deletions(-)
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
Remote and stale branches are emitted in alphabetical order, but new and
tracked branches are not. So sort the latter to be consistent with the
former. This also lets us use more efficient string_list_has_string()
instead of unsorted_string_list_has_string().
"show <remote>" prunes symrefs, but "show <remote> -n" does not. Fix the
latter to match the former.
Signed-off-by: Jay Soffian <redacted>
---
builtin-remote.c | 15 ++++++++++-----
t/t5505-remote.sh | 2 +-
2 files changed, 11 insertions(+), 6 deletions(-)
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
get_ref_states() populates the util pointer of the string_list_item's
that it adds to states->new and states->tracked, but nothing ever uses
the pointer, so we can get rid of the extra code.
Signed-off-by: Jay Soffian <redacted>
---
builtin-remote.c | 13 +++----------
1 files changed, 3 insertions(+), 10 deletions(-)
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
Determining HEAD is ambiguous since it is done by comparing SHA1s.
In the case of multiple matches we return refs/heads/master if it
matches, else we return the first match we encounter. builtin-remote
needs all matches returned to it, so add a flag for it to request such.
To be simple and consistent, the return value is now a copy (including
peer_ref) of the matching refs.
Originally contributed by Jeff King along with the prior commit as a
single patch.
Signed-off-by: Jay Soffian <redacted>
---
builtin-clone.c | 2 +-
remote.c | 36 ++++++++++++++++++++++++++----------
remote.h | 14 ++++++++------
3 files changed, 35 insertions(+), 17 deletions(-)
@@ -510,7 +510,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)mapped_refs=write_remote_refs(refs,&refspec,reflog_msg.buf);remote_head=find_ref_by_name(refs,"HEAD");-head_points_at=guess_remote_head(remote_head,mapped_refs);+head_points_at=guess_remote_head(remote_head,mapped_refs,0);}else{warning("You appear to have cloned an empty repository.");
@@ -1452,24 +1452,40 @@ struct ref *get_local_heads(void)returnlocal_refs;}-conststructref*guess_remote_head(conststructref*head,-conststructref*refs)+structref*copy_ref_with_peer(conststructref*src)+{+structref*dst=copy_ref(src);+dst->peer_ref=copy_ref(src->peer_ref);+returndst;+}++structref*guess_remote_head(conststructref*head,+conststructref*refs,+intall){conststructref*r;+structref*list=NULL;+structref**tail=&list;if(!head)returnNULL;/* If refs/heads/master could be right, it is. */-r=find_ref_by_name(refs,"refs/heads/master");-if(r&&!hashcmp(r->old_sha1,head->old_sha1))-returnr;+if(!all){+r=find_ref_by_name(refs,"refs/heads/master");+if(r&&!hashcmp(r->old_sha1,head->old_sha1))+returncopy_ref_with_peer(r);+}/* Look for another ref that points there */-for(r=refs;r;r=r->next)-if(r!=head&&!hashcmp(r->old_sha1,head->old_sha1))-returnr;+for(r=refs;r;r=r->next){+if(r!=head&&!hashcmp(r->old_sha1,head->old_sha1)){+*tail=copy_ref_with_peer(r);+tail=&((*tail)->next);+if(!all)+break;+}+}-/* Nothing is the same */-returnNULL;+returnlist;}
@@ -139,12 +139,14 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);intformat_tracking_info(structbranch*branch,structstrbuf*sb);structref*get_local_heads(void);-/*-*LookforarefinrefswhoseSHA1matcheshead,firstcheckingif-*refs/heads/mastermatches.ReturnNULLifnothingmatchesorifhead-*isNULL.+*FindrefsfromalistwhicharelikelytobepointedtobythegivenHEAD+*ref.If'all'isfalse,returnsthemostlikelyref;otherwise,returnsa+*listofallcandidaterefs.Ifnomatchisfound(or'head'isNULL),+*returnsNULL.Allreturnsarenewlyallocatedandshouldbefreed.*/-conststructref*guess_remote_head(conststructref*head,-conststructref*refs);+structref*guess_remote_head(conststructref*head,+conststructref*refs,+intall);+#endif
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
ref_newer() appears to have been copied from builtin-send-pack.c to
http-push.c via cut and paste. This patch moves the function and its
helper unmark_and_free() to remote.c. There was a slight difference
between the two implementations, one used TMP_MARK for the mark, the
other used 1. Per Jeff King, I went with TMP_MARK as more correct.
This is in preparation for being able to call it from builtin-remote.c
Signed-off-by: Jay Soffian <redacted>
---
builtin-send-pack.c | 50 --------------------------------------------------
http-push.c | 49 -------------------------------------------------
remote.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
remote.h | 1 +
4 files changed, 50 insertions(+), 99 deletions(-)
@@ -84,55 +83,6 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extreturn0;}-staticvoidunmark_and_free(structcommit_list*list,unsignedintmark)-{-while(list){-structcommit_list*temp=list;-temp->item->object.flags&=~mark;-list=temp->next;-free(temp);-}-}--staticintref_newer(constunsignedchar*new_sha1,-constunsignedchar*old_sha1)-{-structobject*o;-structcommit*old,*new;-structcommit_list*list,*used;-intfound=0;--/* Both new and old must be commit-ish and new is descendant of-*old.Otherwisewerequire--force.-*/-o=deref_tag(parse_object(old_sha1),NULL,0);-if(!o||o->type!=OBJ_COMMIT)-return0;-old=(structcommit*)o;--o=deref_tag(parse_object(new_sha1),NULL,0);-if(!o||o->type!=OBJ_COMMIT)-return0;-new=(structcommit*)o;--if(parse_commit(new)<0)-return0;--used=list=NULL;-commit_list_insert(new,&list);-while(list){-new=pop_most_recent_commit(&list,1);-commit_list_insert(new,&used);-if(new==old){-found=1;-break;-}-}-unmark_and_free(list,1);-unmark_and_free(used,1);-returnfound;-}-staticstructref*remote_refs,**remote_tail;staticintreceive_status(intin,structref*refs)
@@ -1843,55 +1843,6 @@ static int is_zero_sha1(const unsigned char *sha1)return1;}-staticvoidunmark_and_free(structcommit_list*list,unsignedintmark)-{-while(list){-structcommit_list*temp=list;-temp->item->object.flags&=~mark;-list=temp->next;-free(temp);-}-}--staticintref_newer(constunsignedchar*new_sha1,-constunsignedchar*old_sha1)-{-structobject*o;-structcommit*old,*new;-structcommit_list*list,*used;-intfound=0;--/* Both new and old must be commit-ish and new is descendant of-*old.Otherwisewerequire--force.-*/-o=deref_tag(parse_object(old_sha1),NULL,0);-if(!o||o->type!=OBJ_COMMIT)-return0;-old=(structcommit*)o;--o=deref_tag(parse_object(new_sha1),NULL,0);-if(!o||o->type!=OBJ_COMMIT)-return0;-new=(structcommit*)o;--if(parse_commit(new)<0)-return0;--used=list=NULL;-commit_list_insert(new,&list);-while(list){-new=pop_most_recent_commit(&list,TMP_MARK);-commit_list_insert(new,&used);-if(new==old){-found=1;-break;-}-}-unmark_and_free(list,TMP_MARK);-unmark_and_free(used,TMP_MARK);-returnfound;-}-staticvoidadd_remote_info_ref(structremote_ls_ctx*ls){structstrbuf*buf=(structstrbuf*)ls->userData;
@@ -1269,6 +1270,54 @@ int resolve_remote_symref(struct ref *ref, struct ref *list)return1;}+staticvoidunmark_and_free(structcommit_list*list,unsignedintmark)+{+while(list){+structcommit_list*temp=list;+temp->item->object.flags&=~mark;+list=temp->next;+free(temp);+}+}++intref_newer(constunsignedchar*new_sha1,constunsignedchar*old_sha1)+{+structobject*o;+structcommit*old,*new;+structcommit_list*list,*used;+intfound=0;++/* Both new and old must be commit-ish and new is descendant of+*old.Otherwisewerequire--force.+*/+o=deref_tag(parse_object(old_sha1),NULL,0);+if(!o||o->type!=OBJ_COMMIT)+return0;+old=(structcommit*)o;++o=deref_tag(parse_object(new_sha1),NULL,0);+if(!o||o->type!=OBJ_COMMIT)+return0;+new=(structcommit*)o;++if(parse_commit(new)<0)+return0;++used=list=NULL;+commit_list_insert(new,&list);+while(list){+new=pop_most_recent_commit(&list,TMP_MARK);+commit_list_insert(new,&used);+if(new==old){+found=1;+break;+}+}+unmark_and_free(list,TMP_MARK);+unmark_and_free(used,TMP_MARK);+returnfound;+}+/**Returntrueifthereisanythingtoreport,otherwisefalse.*/
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
This function had complications which made it hard to extend.
- It used to do two things: find the HEAD ref, and then find a
matching ref, optionally returning the former via assignment to a
passed-in pointer. Since finding HEAD is a one-liner, just have a
caller do it themselves and pass it as an argument.
- It used to manually search through the ref list for
refs/heads/master; this can be a one-line call to
find_ref_by_name.
Originally contributed by Jeff King along with the next commit as a
single patch.
Signed-off-by: Jay Soffian <redacted>
---
I split Jeff's patch into two as it makes the diff a littler easier on
the eyes and the history is clearer this way.
builtin-clone.c | 4 ++--
remote.c | 31 ++++++++-----------------------
remote.h | 13 ++++++-------
3 files changed, 16 insertions(+), 32 deletions(-)
@@ -509,8 +509,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)mapped_refs=write_remote_refs(refs,&refspec,reflog_msg.buf);-head_points_at=guess_remote_head(refs,mapped_refs,-&remote_head);+remote_head=find_ref_by_name(refs,"HEAD");+head_points_at=guess_remote_head(remote_head,mapped_refs);}else{warning("You appear to have cloned an empty repository.");
@@ -1452,37 +1452,22 @@ struct ref *get_local_heads(void)returnlocal_refs;}-conststructref*guess_remote_head(conststructref*refs,-conststructref*mapped_refs,-conststructref**remote_head_p)+conststructref*guess_remote_head(conststructref*head,+conststructref*refs){-conststructref*remote_head=NULL;-conststructref*remote_master=NULL;conststructref*r;-for(r=refs;r;r=r->next)-if(!strcmp(r->name,"HEAD"))-remote_head=r;-for(r=mapped_refs;r;r=r->next)-if(!strcmp(r->name,"refs/heads/master"))-remote_master=r;--if(remote_head_p)-*remote_head_p=remote_head;--/* If there's no HEAD value at all, never mind. */-if(!remote_head)+if(!head)returnNULL;/* If refs/heads/master could be right, it is. */-if(remote_master&&!hashcmp(remote_master->old_sha1,-remote_head->old_sha1))-returnremote_master;+r=find_ref_by_name(refs,"refs/heads/master");+if(r&&!hashcmp(r->old_sha1,head->old_sha1))+returnr;/* Look for another ref that points there */-for(r=mapped_refs;r;r=r->next)-if(r!=remote_head&&-!hashcmp(r->old_sha1,remote_head->old_sha1))+for(r=refs;r;r=r->next)+if(r!=head&&!hashcmp(r->old_sha1,head->old_sha1))returnr;/* Nothing is the same */
@@ -139,13 +139,12 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);intformat_tracking_info(structbranch*branch,structstrbuf*sb);structref*get_local_heads(void);+/*-*LookinrefsforHEAD.ThenlookforamatchingSHA1inmapped_refs,-*firstcheckingifrefs/heads/mastermatches.ReturnNULLifnothingmatches-*orifthereisnoHEADinrefs.remote_head_pisassignedHEADifnotNULL.+*LookforarefinrefswhoseSHA1matcheshead,firstcheckingif+*refs/heads/mastermatches.ReturnNULLifnothingmatchesorifhead+*isNULL.*/-conststructref*guess_remote_head(conststructref*refs,-conststructref*mapped_refs,-conststructref**remote_head_p);-+conststructref*guess_remote_head(conststructref*head,+conststructref*refs);#endif
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
Move locate_head() to remote.c and rename it to guess_remote_head() to
more accurately reflect what it does. This is in preparation for being
able to call it from builtin-remote.c
Signed-off-by: Jay Soffian <redacted>
---
builtin-clone.c | 41 +++--------------------------------------
remote.c | 37 +++++++++++++++++++++++++++++++++++++
remote.h | 9 +++++++++
3 files changed, 49 insertions(+), 38 deletions(-)
@@ -293,43 +294,6 @@ static void remove_junk_on_signal(int signo)raise(signo);}-staticconststructref*locate_head(conststructref*refs,-conststructref*mapped_refs,-conststructref**remote_head_p)-{-conststructref*remote_head=NULL;-conststructref*remote_master=NULL;-conststructref*r;-for(r=refs;r;r=r->next)-if(!strcmp(r->name,"HEAD"))-remote_head=r;--for(r=mapped_refs;r;r=r->next)-if(!strcmp(r->name,"refs/heads/master"))-remote_master=r;--if(remote_head_p)-*remote_head_p=remote_head;--/* If there's no HEAD value at all, never mind. */-if(!remote_head)-returnNULL;--/* If refs/heads/master could be right, it is. */-if(remote_master&&!hashcmp(remote_master->old_sha1,-remote_head->old_sha1))-returnremote_master;--/* Look for another ref that points there */-for(r=mapped_refs;r;r=r->next)-if(r!=remote_head&&-!hashcmp(r->old_sha1,remote_head->old_sha1))-returnr;--/* Nothing is the same */-returnNULL;-}-staticstructref*write_remote_refs(conststructref*refs,structrefspec*refspec,constchar*reflog){
@@ -545,7 +509,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)mapped_refs=write_remote_refs(refs,&refspec,reflog_msg.buf);-head_points_at=locate_head(refs,mapped_refs,&remote_head);+head_points_at=guess_remote_head(refs,mapped_refs,+&remote_head);}else{warning("You appear to have cloned an empty repository.");
@@ -1451,3 +1451,40 @@ struct ref *get_local_heads(void)for_each_ref(one_local_ref,&local_tail);returnlocal_refs;}++conststructref*guess_remote_head(conststructref*refs,+conststructref*mapped_refs,+conststructref**remote_head_p)+{+conststructref*remote_head=NULL;+conststructref*remote_master=NULL;+conststructref*r;+for(r=refs;r;r=r->next)+if(!strcmp(r->name,"HEAD"))+remote_head=r;++for(r=mapped_refs;r;r=r->next)+if(!strcmp(r->name,"refs/heads/master"))+remote_master=r;++if(remote_head_p)+*remote_head_p=remote_head;++/* If there's no HEAD value at all, never mind. */+if(!remote_head)+returnNULL;++/* If refs/heads/master could be right, it is. */+if(remote_master&&!hashcmp(remote_master->old_sha1,+remote_head->old_sha1))+returnremote_master;++/* Look for another ref that points there */+for(r=mapped_refs;r;r=r->next)+if(r!=remote_head&&+!hashcmp(r->old_sha1,remote_head->old_sha1))+returnr;++/* Nothing is the same */+returnNULL;+}
@@ -139,4 +139,13 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);intformat_tracking_info(structbranch*branch,structstrbuf*sb);structref*get_local_heads(void);+/*+*LookinrefsforHEAD.ThenlookforamatchingSHA1inmapped_refs,+*firstcheckingifrefs/heads/mastermatches.ReturnNULLifnothingmatches+*orifthereisnoHEADinrefs.remote_head_pisassignedHEADifnotNULL.+*/+conststructref*guess_remote_head(conststructref*refs,+conststructref*mapped_refs,+conststructref**remote_head_p);+#endif
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
When not querying the remote, show() was having to populate
states.tracked itself. It makes more sense for get_remote_ref_states()
to do this consistently. Since show() is the only caller of
get_remote_ref_states() with query=0, this change does not affect
other callers.
Signed-off-by: Jay Soffian <redacted>
---
builtin-remote.c | 33 ++++++++++++++++-----------------
1 files changed, 16 insertions(+), 17 deletions(-)
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
The existing output of "git remote show <remote>" with respect to push
ref specs is basically just to show the raw refspec. This patch teaches
the command to interpret the refspecs and show how each branch will be
pushed to the destination. The output gives the user an idea of what
"git push" should do if it is run w/o any arguments.
Example new output:
1a. Typical output with no push refspec (i.e. matching branches only)
$ git remote show origin
* remote origin
[...]
Local refs configured for 'git push':
master pushes to master (up to date)
next pushes to next (local out of date)
1b. Same as above, w/o querying the remote:
$ git remote show origin -n
* remote origin
[...]
Local ref configured for 'git push' (status not queried):
(matching) pushes to (matching)
2a. With a forcing refspec (+), and a new topic
(something like push = refs/heads/*:refs/heads/*):
$ git remote show origin
* remote origin
[...]
Local refs configured for 'git push':
master pushes to master (fast forwardable)
new-topic pushes to new-topic (create)
next pushes to next (local out of date)
pu forces to pu (up to date)
2b. Same as above, w/o querying the remote
$ git remote show origin -n
* remote origin
[...]
Local refs configured for 'git push' (status not queried):
master pushes to master
new-topic pushes to new-topic
next pushes to next
pu forces to pu
3. With a remote configured as a mirror:
* remote backup
[...]
Local refs will be mirrored by 'git push'
Signed-off-by: Jay Soffian <redacted>
---
builtin-remote.c | 201 ++++++++++++++++++++++++++++++++++++++++++++++++----
t/t5505-remote.sh | 30 ++++++--
2 files changed, 207 insertions(+), 24 deletions(-)
@@ -693,9 +808,12 @@ static int get_remote_ref_states(const char *name,get_ref_states(remote_refs,states);if(query&GET_HEAD_NAMES)get_head_names(remote_refs,states);+if(query&GET_PUSH_REF_STATES)+get_push_ref_states(remote_refs,states);}else{for_each_ref(append_ref_to_tracked_list,states);sort_string_list(&states->tracked);+get_push_ref_states_noquery(states);}return0;
@@ -704,7 +822,7 @@ static int get_remote_ref_states(const char *name,structshow_info{structstring_list*list;structref_states*states;-intwidth;+intwidth,width2;intany_rebase;};
@@ -799,6 +917,58 @@ int show_local_info_item(struct string_list_item *item, void *cb_data)return0;}+intadd_push_to_show_info(structstring_list_item*push_item,void*cb_data)+{+structshow_info*show_info=cb_data;+structpush_info*push_info=push_item->util;+structstring_list_item*item;+intn;+if((n=strlen(push_item->string))>show_info->width)+show_info->width=n;+if((n=strlen(push_info->dest))>show_info->width2)+show_info->width2=n;+item=string_list_append(push_item->string,show_info->list);+item->util=push_item->util;+return0;+}++intshow_push_info_item(structstring_list_item*item,void*cb_data)+{+structshow_info*show_info=cb_data;+structpush_info*push_info=item->util;+char*src=item->string,*status=NULL;++switch(push_info->status){+casePUSH_STATUS_CREATE:+status="create";+break;+casePUSH_STATUS_DELETE:+status="delete";+src="(none)";+break;+casePUSH_STATUS_UPTODATE:+status="up to date";+break;+casePUSH_STATUS_FASTFORWARD:+status="fast forwardable";+break;+casePUSH_STATUS_OUTOFDATE:+status="local out of date";+break;+casePUSH_STATUS_NOTQUERIED:+break;+}+if(status)+printf(" %-*s %s to %-*s (%s)\n",show_info->width,src,+push_info->forced?"forces":"pushes",+show_info->width2,push_info->dest,status);+else+printf(" %-*s %s to %s\n",show_info->width,src,+push_info->forced?"forces":"pushes",+push_info->dest);+return0;+}+staticintshow(intargc,constchar**argv){intno_query=0,result=0,query_flag=0;
@@ -867,19 +1037,18 @@ static int show(int argc, const char **argv)string_list_clear(info.list,0);/* git push info */-if(states.remote->push_refspec_nr){-printf(" Local branch%s pushed with 'git push'\n",-states.remote->push_refspec_nr>1?-"es":"");-for(i=0;i<states.remote->push_refspec_nr;i++){-structrefspec*spec=states.remote->push+i;-printf(" %s%s%s%s\n",-spec->force?"+":"",-abbrev_branch(spec->src),-spec->dst?":":"",-spec->dst?abbrev_branch(spec->dst):"");-}-}+if(states.remote->mirror)+printf(" Local refs will be mirrored by 'git push'\n");++info.width=info.width2=0;+for_each_string_list(add_push_to_show_info,&states.push,&info);+sort_string_list(info.list);+if(info.list->nr)+printf(" Local ref%s configured for 'git push'%s:\n",+info.list->nr>1?"s":"",+no_query?" (status not queried)":"");+for_each_string_list(show_push_info_item,info.list,&info);+string_list_clear(info.list,0);free_remote_ref_states(&states);}
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
- The variable name "remote" is used as both a "char *" and as a "struct
remote *"; this is confusing, so rename the former to remote_name.
- Consistently refer to the refs returned by transport_get_remote_refs()
as remote_refs.
- There is no need to call "sort_string_list(&branch_list)" as
branch_list is populated via string_list_insert(), which maintains its
order.
Signed-off-by: Jay Soffian <redacted>
---
In the original series this is two commits, but after re-ordering it
made sense to squash the two together as they were both very minimal.
builtin-remote.c | 24 ++++++++++++------------
1 files changed, 12 insertions(+), 12 deletions(-)
@@ -238,13 +237,14 @@ static int handle_one_branch(const char *refname,return0;}-staticintget_ref_states(conststructref*ref,structref_states*states)+staticintget_ref_states(conststructref*remote_refs,structref_states*states){structref*fetch_map=NULL,**tail=&fetch_map;+structref*ref;inti;for(i=0;i<states->remote->fetch_refspec_nr;i++)-if(get_fetch_map(ref,states->remote->fetch+i,&tail,1))+if(get_fetch_map(remote_refs,states->remote->fetch+i,&tail,1))die("Could not get fetch map for refspec %s",states->remote->fetch_refspec[i]);
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
This is in preparation for teaching remote how to set
refs/remotes/<remote>/HEAD to match what HEAD is set to at <remote>, but
is useful in its own right.
Signed-off-by: Jay Soffian <redacted>
---
builtin-remote.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++----
t/t5505-remote.sh | 12 +++++++++-
2 files changed, 59 insertions(+), 7 deletions(-)
@@ -668,7 +694,10 @@ static int get_remote_ref_states(const char *name,remote_refs=transport_get_remote_refs(transport);transport_disconnect(transport);-get_ref_states(remote_refs,states);+if(query&GET_REF_STATES)+get_ref_states(remote_refs,states);+if(query&GET_HEAD_NAMES)+get_head_names(remote_refs,states);}else{for_each_ref(append_ref_to_tracked_list,states);sort_string_list(&states->tracked);
@@ -679,7 +708,7 @@ static int get_remote_ref_states(const char *name,staticintshow(intargc,constchar**argv){-intno_query=0,result=0;+intno_query=0,result=0,query_flag=0;structoptionoptions[]={OPT_GROUP("show specific options"),OPT_BOOLEAN('n',NULL,&no_query,"do not query remotes"),
@@ -692,15 +721,30 @@ static int show(int argc, const char **argv)if(argc<1)returnshow_all();+if(!no_query)+query_flag=(GET_REF_STATES|GET_HEAD_NAMES);+memset(&states,0,sizeof(states));for(;argc;argc--,argv++){inti;-get_remote_ref_states(*argv,&states,!no_query);+get_remote_ref_states(*argv,&states,query_flag);printf("* remote %s\n URL: %s\n",*argv,states.remote->url_nr>0?states.remote->url[0]:"(no URL)");+if(no_query)+printf(" HEAD branch: (not queried)\n");+elseif(!states.heads.nr)+printf(" HEAD branch: (unknown)\n");+elseif(states.heads.nr==1)+printf(" HEAD branch: %s\n",states.heads.items[0].string);+else{+printf(" HEAD branch (remote HEAD is ambiguous,"+" may be one of the following):\n");+for(i=0;i<states.heads.nr;i++)+printf(" %s\n",states.heads.items[i].string);+}for(i=0;i<branch_list.nr;i++){structstring_list_item*branch=branch_list.items+i;
@@ -343,7 +351,7 @@ test_expect_success '"remote show" does not show symbolic refs' 'gitcloneonethree&&(cdthree&&gitremoteshoworigin>output&&-!grepHEAD<output&&+!grep"^ *HEAD$"<output&&!grep-istale<output)'
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
The existing output of "git remote show <remote>" is too verbose for the
information it provides. This patch teaches it to provide more
information in less space.
The output for push refspecs is addressed in the next patch.
Before the patch:
$ git remote show origin
* remote origin
URL: git://git.kernel.org/pub/scm/git/git.git
HEAD branch: master
Remote branch merged with 'git pull' while on branch master
master
Remote branch merged with 'git pull' while on branch next
next
Remote branches merged with 'git pull' while on branch octopus
foo bar baz frotz
New remote branch (next fetch will store in remotes/origin)
html
Stale tracking branch (use 'git remote prune')
bogus
Tracked remote branches
maint
man
master
next
pu
todo
After this patch:
$ git remote show origin
* remote origin
URL: git://git.kernel.org/pub/scm/git/git.git
HEAD branch: master
Remote branches:
bogus stale (use 'git remote prune' to remove)
html new (next fetch will store in remotes/origin)
maint tracked
man tracked
master tracked
next tracked
pu tracked
todo tracked
Local branches configured for 'git pull':
master rebases onto remote master
next rebases onto remote next
octopus merges with remote foo
and with remote bar
and with remote baz
and with remote frotz
$ git remote show origin -n
* remote origin
URL: git://git.kernel.org/pub/scm/git/git.git
HEAD branch: (not queried)
Remote branches: (status not queried)
bogus
maint
man
master
next
pu
todo
Local branches configured for 'git pull':
master rebases onto remote master
next rebases onto remote next
octopus merges with remote foo
and with remote bar
and with remote baz
and with remote frotz
Signed-off-by: Jay Soffian <redacted>
---
builtin-remote.c | 178 ++++++++++++++++++++++++++++++++++++++++-------------
t/t5505-remote.sh | 38 ++++++-----
2 files changed, 157 insertions(+), 59 deletions(-)
@@ -187,9 +192,9 @@ static int config_read_branches(const char *key, const char *value, void *cb)info=item->util;if(type==REMOTE){if(info->remote_name)-warning("more than one branch.%s",key);+warning("more than one %s",orig_key);info->remote_name=xstrdup(value);-}else{+}elseif(type==MERGE){char*space=strchr(value,' ');value=abbrev_branch(value);while(space){
@@ -695,6 +688,7 @@ static int get_remote_ref_states(const char *name,remote_refs=transport_get_remote_refs(transport);transport_disconnect(transport);+states->queried=1;if(query&GET_REF_STATES)get_ref_states(remote_refs,states);if(query&GET_HEAD_NAMES)
@@ -707,6 +701,104 @@ static int get_remote_ref_states(const char *name,return0;}+structshow_info{+structstring_list*list;+structref_states*states;+intwidth;+intany_rebase;+};++intadd_remote_to_show_info(structstring_list_item*item,void*cb_data)+{+structshow_info*info=cb_data;+intn=strlen(item->string);+if(n>info->width)+info->width=n;+string_list_insert(item->string,info->list);+return0;+}++intshow_remote_info_item(structstring_list_item*item,void*cb_data)+{+structshow_info*info=cb_data;+structref_states*states=info->states;+constchar*name=item->string;++if(states->queried){+constchar*fmt="%s";+constchar*arg="";+if(string_list_has_string(&states->new,name)){+fmt=" new (next fetch will store in remotes/%s)";+arg=states->remote->name;+}elseif(string_list_has_string(&states->tracked,name))+arg=" tracked";+elseif(string_list_has_string(&states->stale,name))+arg=" stale (use 'git remote prune' to remove)";+else+arg=" ???";+printf(" %-*s",info->width,name);+printf(fmt,arg);+printf("\n");+}else+printf(" %s\n",name);++return0;+}++intadd_local_to_show_info(structstring_list_item*branch_item,void*cb_data)+{+structshow_info*show_info=cb_data;+structref_states*states=show_info->states;+structbranch_info*branch_info=branch_item->util;+structstring_list_item*item;+intn;++if(!branch_info->merge.nr||!branch_info->remote_name||+strcmp(states->remote->name,branch_info->remote_name))+return0;+if((n=strlen(branch_item->string))>show_info->width)+show_info->width=n;+if(branch_info->rebase)+show_info->any_rebase=1;++item=string_list_insert(branch_item->string,show_info->list);+item->util=branch_info;++return0;+}++intshow_local_info_item(structstring_list_item*item,void*cb_data)+{+structshow_info*show_info=cb_data;+structbranch_info*branch_info=item->util;+structstring_list*merge=&branch_info->merge;+constchar*also;+inti;++if(branch_info->rebase&&branch_info->merge.nr>1){+error("invalid branch.%s.merge; cannot rebase onto > 1 branch",+item->string);+return0;+}++printf(" %-*s ",show_info->width,item->string);+if(branch_info->rebase){+printf("rebases onto remote %s\n",merge->items[0].string);+return0;+}elseif(show_info->any_rebase){+printf(" merges with remote %s\n",merge->items[0].string);+also=" and with remote";+}else{+printf("merges with remote %s\n",merge->items[0].string);+also=" and with remote";+}+for(i=1;i<merge->nr;i++)+printf(" %-*s %s %s\n",show_info->width,"",also,+merge->items[i].string);++return0;+}+staticintshow(intargc,constchar**argv){intno_query=0,result=0,query_flag=0;
@@ -747,32 +844,29 @@ static int show(int argc, const char **argv)printf(" %s\n",states.heads.items[i].string);}-for(i=0;i<branch_list.nr;i++){-structstring_list_item*branch=branch_list.items+i;-structbranch_info*info=branch->util;-intj;+/* remote branch info */+info.width=0;+for_each_string_list(add_remote_to_show_info,&states.new,&info);+for_each_string_list(add_remote_to_show_info,&states.tracked,&info);+for_each_string_list(add_remote_to_show_info,&states.stale,&info);+if(info.list->nr)+printf(" Remote branch%s:%s\n",+info.list->nr>1?"es":"",+no_query?" (status not queried)":"");+for_each_string_list(show_remote_info_item,info.list,&info);+string_list_clear(info.list,0);-if(!info->merge.nr||strcmp(*argv,info->remote_name))-continue;-printf(" Remote branch%s merged with 'git pull' "-"while on branch %s\n ",-info->merge.nr>1?"es":"",-branch->string);-for(j=0;j<info->merge.nr;j++)-printf(" %s",info->merge.items[j].string);-printf("\n");-}--if(!no_query){-show_list(" New remote branch%s (next fetch "-"will store in remotes/%s)",-&states.new,states.remote->name);-show_list(" Stale tracking branch%s (use 'git remote "-"prune')",&states.stale,"");-}--show_list(" Tracked remote branch%s",&states.tracked,"");+/* git pull info */+info.width=0;+info.any_rebase=0;+for_each_string_list(add_local_to_show_info,&branch_list,&info);+if(info.list->nr)+printf(" Local branch%s configured for 'git pull':\n",+info.list->nr>1?"es":"");+for_each_string_list(show_local_info_item,info.list,&info);+string_list_clear(info.list,0);+/* git push info */if(states.remote->push_refspec_nr){printf(" Local branch%s pushed with 'git push'\n",states.remote->push_refspec_nr>1?
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
Provide a porcelain command for setting and deleting
$GIT_DIR/remotes/<remote>/HEAD.
While we're at it, document what $GIT_DIR/remotes/<remote>/HEAD is all
about.
Signed-off-by: Jay Soffian <redacted>
---
Documentation/git-remote.txt | 28 +++++++++++++-
builtin-remote.c | 62 ++++++++++++++++++++++++++++++++
contrib/completion/git-completion.bash | 2 +-
t/t5505-remote.sh | 40 ++++++++++++++++++++
4 files changed, 129 insertions(+), 3 deletions(-)
@@ -53,8 +54,7 @@ is created. You can give more than one `-t <branch>` to track multiple branches without grabbing all branches. + With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set-up to point at remote's `<master>` branch instead of whatever-branch the `HEAD` at the remote repository actually points at.+up to point at remote's `<master>` branch. See also the set-head command. + In mirror mode, enabled with `\--mirror`, the refs will not be stored in the 'refs/remotes/' namespace, but in 'refs/heads/'. This option
@@ -76,6 +76,30 @@ the configuration file format. Remove the remote named <name>. All remote tracking branches and configuration settings for the remote are removed.+'set-head'::++Sets or deletes the default branch (`$GIT_DIR/remotes/<name>/HEAD`) for+the named remote. Having a default branch for a remote is not required,+but allows the name of the remote to be specified in lieu of a specific+branch. For example, if the default branch for `origin` is set to+`master`, then `origin` may be specified wherever you would normally+specify `origin/master`.+++With `-d`, `$GIT_DIR/remotes/<name>/HEAD` is deleted.+++With `-a`, the remote is queried to determine its `HEAD`, then+`$GIT_DIR/remotes/<name>/HEAD` is set to the same branch. e.g., if the remote+`HEAD` is pointed at `next`, "`git remote set-head origin -a`" will set+`$GIT_DIR/refs/remotes/origin/HEAD` to `refs/remotes/origin/next`. This will+only work if `refs/remotes/origin/next` already exists; if not it must be+fetched first.+++Use `<branch>` to set `$GIT_DIR/remotes/<name>/HEAD` explicitly. e.g., "git+remote set-head origin master" will set `$GIT_DIR/refs/remotes/origin/HEAD` to+`refs/remotes/origin/master`. This will only work if+`refs/remotes/origin/master` already exists; if not it must be fetched first.+++ 'show':: Gives some information about the remote <name>.
@@ -792,6 +793,65 @@ static int show(int argc, const char **argv)returnresult;}+staticintset_head(intargc,constchar**argv)+{+inti,opt_a=0,opt_d=0,result=0;+structstrbufbuf=STRBUF_INIT,buf2=STRBUF_INIT;+char*head_name=NULL;++structoptionoptions[]={+OPT_GROUP("set-head specific options"),+OPT_BOOLEAN('a',"auto",&opt_a,+"set refs/remotes/<name>/HEAD according to remote"),+OPT_BOOLEAN('d',"delete",&opt_d,+"delete refs/remotes/<name>/HEAD"),+OPT_END()+};+argc=parse_options(argc,argv,options,builtin_remote_usage,0);+if(argc)+strbuf_addf(&buf,"refs/remotes/%s/HEAD",argv[0]);++if(!opt_a&&!opt_d&&argc==2){+head_name=xstrdup(argv[1]);+}elseif(opt_a&&!opt_d&&argc==1){+structref_statesstates;+memset(&states,0,sizeof(states));+get_remote_ref_states(argv[0],&states,GET_HEAD_NAMES);+if(!states.heads.nr)+result|=error("Cannot determine remote HEAD");+elseif(states.heads.nr>1){+result|=error("Multiple remote HEAD branches. "+"Please choose one explicitly with:");+for(i=0;i<states.heads.nr;i++)+fprintf(stderr," git remote set-head %s %s\n",+argv[0],states.heads.items[i].string);+}else+head_name=xstrdup(states.heads.items[0].string);+free_remote_ref_states(&states);+}elseif(opt_d&&!opt_a&&argc==1){+if(delete_ref(buf.buf,NULL,REF_NODEREF))+result|=error("Could not delete %s",buf.buf);+}else+usage_with_options(builtin_remote_usage,options);++if(head_name){+unsignedcharsha1[20];+strbuf_addf(&buf2,"refs/remotes/%s/%s",argv[0],head_name);+/* make sure it's valid */+if(!resolve_ref(buf2.buf,sha1,1,NULL))+result|=error("Not a valid ref: %s",buf2.buf);+elseif(create_symref(buf.buf,buf2.buf,"remote set-head"))+result|=error("Could not setup %s",buf.buf);+if(opt_a)+printf("%s/HEAD set to %s\n",argv[0],head_name);+free(head_name);+}++strbuf_release(&buf);+strbuf_release(&buf2);+returnresult;+}+staticintprune(intargc,constchar**argv){intdry_run=0,result=0;
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
From: Jeff King <redacted>
Our usual method for determining the ref pointed to by HEAD
is to compare HEAD's sha1 to the sha1 of all refs, trying to
find a unique match.
However, some transports actually get to look at HEAD
directly; we should make use of that information when it is
available. Currently, only http remotes support this
feature.
Signed-off-by: Jeff King <redacted>
Signed-off-by: Jay Soffian <redacted>
---
remote.c | 10 ++++++++++
t/t5550-http-fetch.sh | 11 +++++++++++
2 files changed, 21 insertions(+), 0 deletions(-)
@@ -1475,6 +1475,16 @@ struct ref *guess_remote_head(const struct ref *head,if(!head)returnNULL;+/*+*Sometransportssupportdirectlypeekingat+*whereHEADpoints;ifthatisthecase,then+*wedon'thavetoguess.+*/+if(head->symref){+r=find_ref_by_name(refs,head->symref);+returnr?copy_ref_with_peer(r):NULL;+}+/* If refs/heads/master could be right, it is. */if(!all){r=find_ref_by_name(refs,"refs/heads/master");
Hmm. This should probably be:
dst->peer_ref = src->peer_ref ? copy_ref(src->peer_ref) : NULL;
(or copy_ref should return NULL when given NULL). I also wonder if the
copied ref's peer_ref should be explicitly NULL'd.
I don't think it matters for the current code, since we always feed it
"matched refs" which have a peer, but I think it is good to be a little
more defensive in such a generically-named function.
And yes, this bug was in my original patch. :)
-Peff
From: Jeff King <hidden> Date: 2016-06-15 22:46:17
On Thu, Feb 26, 2009 at 09:37:29AM -0500, Jeff King wrote:
Hmm. This should probably be:
dst->peer_ref = src->peer_ref ? copy_ref(src->peer_ref) : NULL;
(or copy_ref should return NULL when given NULL). I also wonder if the
copied ref's peer_ref should be explicitly NULL'd.
BTW, all of my "probably" and "I wonder" here are because I think the
"peer ref" pointer is a little vague as a concept. E.g., I think in most
cases src->peer_ref->peer_ref != src.
Rather than having ref structs with "next" and "peer" pointers, I think
a more natural data structure would be a list (or array) of "ref pairs".
But you didn't create that with this series, and I don't think it is
worth the major surgery to change it now.
-Peff
From: Jeff King <hidden> Date: 2016-06-15 22:46:17
On Wed, Feb 25, 2009 at 03:32:07AM -0500, Jay Soffian wrote:
This series replaces three related topics from pu:
js/remote-set-head
jk/head-lookup
js/remote-display
With the exception of my comments on 08/21, this looks ready for 'next'
to me. Though I have to admit, given the number of patches and the
number of times I've looked at these changes before, my eyes started to
glaze over near the end. ;)
I signed off on Jeff's patches; please remove my SoB from those if it is
inappropriate for me to have done so.
I think that is fine. It is really about "I think this code is OK
license-wise to go into the project". So if I SoB and you have no reason
to doubt me, and you didn't change anything (or you added minor things
which you would SoB), then your SoB makes sense.
-Peff
From: Junio C Hamano <hidden> Date: 2016-06-15 22:46:17
Jay Soffian [off-list ref] writes:
This series replaces three related topics from pu:
js/remote-set-head
jk/head-lookup
js/remote-display
...
So I think this series is clean, and doesn't need an extensive
re-review, but a quick look-over would be appreciated.
I have looked at the diff between:
(1) the above three topics merged to 'next'; and
(2) this 21-patch series applied to 'master' then merged to 'next'.
There indeed does not seem major changes, but there are some:
- get_head_names() lost "const char *remote_name" parameter that was
unused (good);
- guess_remote_head() uses a local variable "r" instead of using an
additional variable "m" (does not matter);
- "builtin-send-pack.c" lost otherwise unused inclusion of "tag.h"
(good);
- one_local_ref() and get_local_heads() are moved around (does not
matter);
- t5505-remote.sh has one more test (ok).
- The old series had distracting removal of a few blank lines in
cmd_clone() and get_remote_ref_states(), but with this round these are
gone (good).
Overall, the new series certainly looks cleaner.
I signed off on Jeff's patches; please remove my SoB from those if it is
inappropriate for me to have done so.
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:17
On Thu, Feb 26, 2009 at 9:40 AM, Jeff King [off-list ref] wrote:
On Thu, Feb 26, 2009 at 09:37:29AM -0500, Jeff King wrote:
quoted
Hmm. This should probably be:
dst->peer_ref = src->peer_ref ? copy_ref(src->peer_ref) : NULL;
(or copy_ref should return NULL when given NULL). I also wonder if the
copied ref's peer_ref should be explicitly NULL'd.
Well, if you wanted to be consistent about things (and I apologize if gmail
mangles the lines), I'd probably do something like:
@@ -1480,22 +1481,20 @@ struct ref *guess_remote_head(const struct ref *head,*whereHEADpoints;ifthatisthecase,then*wedon'thavetoguess.*/-if(head->symref){-r=find_ref_by_name(refs,head->symref);-returnr?copy_ref_with_peer(r):NULL;-}+if(head->symref)+returncopy_ref(find_ref_by_name(refs,head->symref));/* If refs/heads/master could be right, it is. */if(!all){r=find_ref_by_name(refs,"refs/heads/master");if(r&&!hashcmp(r->old_sha1,head->old_sha1))-returncopy_ref_with_peer(r);+returncopy_ref(r);}/* Look for another ref that points there */for(r=refs;r;r=r->next){if(r!=head&&!hashcmp(r->old_sha1,head->old_sha1)){-*tail=copy_ref_with_peer(r);+*tail=copy_ref(r);tail=&((*tail)->next);if(!all)break;
Then peer_ref is consistently a copy, so we can free it consistently, we don't
need two separate copy functions, and copy_ref returns NULL upon receiving
NULL like most of the other foo_ref functions.
BTW, all of my "probably" and "I wonder" here are because I think the
"peer ref" pointer is a little vague as a concept. E.g., I think in most
cases src->peer_ref->peer_ref != src.
Rather than having ref structs with "next" and "peer" pointers, I think
a more natural data structure would be a list (or array) of "ref pairs".
Actually, we don't need most of the fields in the peer_ref, so we could
probably just embed the extra fields that we need in a peer_struct inside the
ref struct. I can add this to my git todo list.
j.
From: Jeff King <hidden> Date: 2016-06-15 22:46:18
On Thu, Feb 26, 2009 at 01:47:45PM -0500, Jay Soffian wrote:
Well, if you wanted to be consistent about things (and I apologize if gmail
mangles the lines), I'd probably do something like:
[...]
Then peer_ref is consistently a copy, so we can free it consistently, we don't
need two separate copy functions, and copy_ref returns NULL upon receiving
NULL like most of the other foo_ref functions.
Good point. That is much cleaner, IMHO, and probably worth doing as part
of this series. Though I hesitate to make you reroll _again_ for such a
small cleanup. Maybe it is worth just putting on top.
quoted
Rather than having ref structs with "next" and "peer" pointers, I think
a more natural data structure would be a list (or array) of "ref pairs".
Actually, we don't need most of the fields in the peer_ref, so we could
probably just embed the extra fields that we need in a peer_struct inside the
ref struct. I can add this to my git todo list.
Sure, that might turn out cleaner (though you may run into problems
where you want to pass a "struct ref" to a helper function but you have
only the fake "peer_struct").
But while that cleanup might be nice, I don't think it is probably worth
the pain, assuming you are done messing with remotes for a little while,
now.
-Peff
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:18
To ensure that copied refs can always be freed w/o causing a
double-free, make copy_ref() perform a deep copy.
Also have copy_ref() return NULL if asked to copy NULL to simplify
things for the caller.
Background: currently copy_ref() performs a shallow copy. This is fine
for current callers who never free the result and/or only copy refs
which contain NULL pointers. But copy_ref() is about to gain a new
caller (guess_remote_head()) which copies refs where peer_ref is not
NULL and the caller of guess_remote_head() will want to free the result.
Signed-off-by: Jay Soffian <redacted>
---
This is a new patch, which should come after 07/21 in the original
series - http://article.gmane.org/gmane.comp.version-control.git/111394
remote.c | 18 +++++++++++++-----
1 files changed, 13 insertions(+), 5 deletions(-)
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:18
Jeff, Junio,
I didn't want to resend the whole series (gmane 111394), so what I did was
create a new patch which comes after patch 7 in that series. Call it patch 7a.
The rest of that series is then identical (modulo line number offsets in
patches touching remote.c) except for patches 8 and 19. Patches 7a, and the
new 8 and 19 follow this email.
You should be able to do this:
$ git checkout js/remote-improvements
$ git format-patch master
$ git reset --hard $(git merge-base master HEAD)
Copy new "[PATCH 07a/21] remote: make copy_ref() perform a deep copy"
to 0007a-remote-make-copy_ref-perform-a-deep-copy.patch
Copy new "[PATCH 08/21] remote: let guess_remote_head() optionally return all matches"
to 0008-remote-let-guess_remote_head-optionally-return-al.patch
Copy new "[PATCH 19/21] remote: make guess_remote_head() use exact HEAD lookup if it is available"
to 0019-remote-make-guess_remote_head-use-exact-HEAD-look.patch
$ git am -3 *.patch
For those playing along at home, js/remote-improvements is currently
commits 661763a..5e4c90a.
Which should give you a new js/remote-improvements that looks like this:
1 test scripts: refactor start_httpd helper
2 add basic http clone/fetch tests
3 refactor find_ref_by_name() to accept const list
4 move duplicated get_local_heads() to remote.c
5 move duplicated ref_newer() to remote.c
6 move locate_head() to remote.c
7 remote: simplify guess_remote_head()
8 remote: make copy_ref() perform a deep copy
9 remote: let guess_remote_head() optionally return all matches
10 remote: make match_refs() copy src ref before assigning to peer_ref
11 remote: make match_refs() not short-circuit
12 string-list: new for_each_string_list() function
13 builtin-remote: refactor duplicated cleanup code
14 builtin-remote: remove unused code in get_ref_states
15 builtin-remote: rename variables and eliminate redundant function call
16 builtin-remote: make get_remote_ref_states() always populate states.tracked
17 builtin-remote: fix two inconsistencies in the output of "show <remote>"
18 builtin-remote: teach show to display remote HEAD
19 builtin-remote: add set-head subcommand
20 remote: make guess_remote_head() use exact HEAD lookup if it is available
21 builtin-remote: new show output style
22 builtin-remote: new show output style for push refspecs
Here's the new diff stat:
Documentation/git-remote.txt | 28 ++-
Makefile | 1 +
builtin-clone.c | 41 +---
builtin-remote.c | 561 ++++++++++++++++++++++++++------
builtin-send-pack.c | 79 +-----
cache.h | 2 +-
contrib/completion/git-completion.bash | 2 +-
http-push.c | 72 +----
refs.c | 4 +-
remote.c | 145 ++++++++-
remote.h | 12 +
string-list.c | 10 +
string-list.h | 5 +
t/lib-httpd.sh | 9 +-
t/t5505-remote.sh | 114 +++++--
t/t5540-http-push.sh | 9 +-
t/t5550-http-fetch.sh | 57 ++++
17 files changed, 821 insertions(+), 330 deletions(-)
create mode 100755 t/t5550-http-fetch.sh
And here's the inter-diff:
@@ -1480,22 +1481,20 @@ struct ref *guess_remote_head(const struct ref *head,*whereHEADpoints;ifthatisthecase,then*wedon'thavetoguess.*/-if(head->symref){-r=find_ref_by_name(refs,head->symref);-returnr?copy_ref_with_peer(r):NULL;-}+if(head->symref)+returncopy_ref(find_ref_by_name(refs,head->symref));/* If refs/heads/master could be right, it is. */if(!all){r=find_ref_by_name(refs,"refs/heads/master");if(r&&!hashcmp(r->old_sha1,head->old_sha1))-returncopy_ref_with_peer(r);+returncopy_ref(r);}/* Look for another ref that points there */for(r=refs;r;r=r->next){if(r!=head&&!hashcmp(r->old_sha1,head->old_sha1)){-*tail=copy_ref_with_peer(r);+*tail=copy_ref(r);tail=&((*tail)->next);if(!all)break;
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:18
Determining HEAD is ambiguous since it is done by comparing SHA1s.
In the case of multiple matches we return refs/heads/master if it
matches, else we return the first match we encounter. builtin-remote
needs all matches returned to it, so add a flag for it to request such.
To be simple and consistent, the return value is now a copy (including
peer_ref) of the matching refs.
Originally contributed by Jeff King along with the prior commit as a
single patch.
Signed-off-by: Jay Soffian <redacted>
---
This patch replaces 08/21 in the original series
series - http://article.gmane.org/gmane.comp.version-control.git/111394
builtin-clone.c | 2 +-
remote.c | 29 +++++++++++++++++++----------
remote.h | 14 ++++++++------
3 files changed, 28 insertions(+), 17 deletions(-)
@@ -510,7 +510,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)mapped_refs=write_remote_refs(refs,&refspec,reflog_msg.buf);remote_head=find_ref_by_name(refs,"HEAD");-head_points_at=guess_remote_head(remote_head,mapped_refs);+head_points_at=guess_remote_head(remote_head,mapped_refs,0);}else{warning("You appear to have cloned an empty repository.");
@@ -1460,24 +1460,33 @@ struct ref *get_local_heads(void)returnlocal_refs;}-conststructref*guess_remote_head(conststructref*head,-conststructref*refs)+structref*guess_remote_head(conststructref*head,+conststructref*refs,+intall){conststructref*r;+structref*list=NULL;+structref**tail=&list;if(!head)returnNULL;/* If refs/heads/master could be right, it is. */-r=find_ref_by_name(refs,"refs/heads/master");-if(r&&!hashcmp(r->old_sha1,head->old_sha1))-returnr;+if(!all){+r=find_ref_by_name(refs,"refs/heads/master");+if(r&&!hashcmp(r->old_sha1,head->old_sha1))+returncopy_ref(r);+}/* Look for another ref that points there */-for(r=refs;r;r=r->next)-if(r!=head&&!hashcmp(r->old_sha1,head->old_sha1))-returnr;+for(r=refs;r;r=r->next){+if(r!=head&&!hashcmp(r->old_sha1,head->old_sha1)){+*tail=copy_ref(r);+tail=&((*tail)->next);+if(!all)+break;+}+}-/* Nothing is the same */-returnNULL;+returnlist;}
@@ -139,12 +139,14 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);intformat_tracking_info(structbranch*branch,structstrbuf*sb);structref*get_local_heads(void);-/*-*LookforarefinrefswhoseSHA1matcheshead,firstcheckingif-*refs/heads/mastermatches.ReturnNULLifnothingmatchesorifhead-*isNULL.+*FindrefsfromalistwhicharelikelytobepointedtobythegivenHEAD+*ref.If'all'isfalse,returnsthemostlikelyref;otherwise,returnsa+*listofallcandidaterefs.Ifnomatchisfound(or'head'isNULL),+*returnsNULL.Allreturnsarenewlyallocatedandshouldbefreed.*/-conststructref*guess_remote_head(conststructref*head,-conststructref*refs);+structref*guess_remote_head(conststructref*head,+conststructref*refs,+intall);+#endif
From: Jay Soffian <hidden> Date: 2016-06-15 22:46:18
From: Jeff King <redacted>
Our usual method for determining the ref pointed to by HEAD
is to compare HEAD's sha1 to the sha1 of all refs, trying to
find a unique match.
However, some transports actually get to look at HEAD
directly; we should make use of that information when it is
available. Currently, only http remotes support this
feature.
Signed-off-by: Jeff King <redacted>
Signed-off-by: Jay Soffian <redacted>
---
This patch replaces 19/21 in the original series
series - http://article.gmane.org/gmane.comp.version-control.git/111394
remote.c | 8 ++++++++
t/t5550-http-fetch.sh | 11 +++++++++++
2 files changed, 19 insertions(+), 0 deletions(-)
@@ -1476,6 +1476,14 @@ struct ref *guess_remote_head(const struct ref *head,if(!head)returnNULL;+/*+*Sometransportssupportdirectlypeekingat+*whereHEADpoints;ifthatisthecase,then+*wedon'thavetoguess.+*/+if(head->symref)+returncopy_ref(find_ref_by_name(refs,head->symref));+/* If refs/heads/master could be right, it is. */if(!all){r=find_ref_by_name(refs,"refs/heads/master");
From: Jeff King <hidden> Date: 2016-06-15 22:46:18
On Fri, Feb 27, 2009 at 02:10:03PM -0500, Jay Soffian wrote:
I didn't want to resend the whole series (gmane 111394), so what I did was
create a new patch which comes after patch 7 in that series. Call it patch 7a.
The rest of that series is then identical (modulo line number offsets in
patches touching remote.c) except for patches 8 and 19. Patches 7a, and the
new 8 and 19 follow this email.