From ff655a69df524c71464968f823a4826f2d43d7c6 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 2 Mar 2008 21:34:36 -0500 Subject: Remove unused variable in builtin-fetch find_non_local_tags Apparently fetch_map is passed through, but is not actually used. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano diff --git a/builtin-fetch.c b/builtin-fetch.c index ac335f2..f8b9542 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -452,8 +452,7 @@ static int add_existing(const char *refname, const unsigned char *sha1, return 0; } -static struct ref *find_non_local_tags(struct transport *transport, - struct ref *fetch_map) +static struct ref *find_non_local_tags(struct transport *transport) { static struct path_list existing_refs = { NULL, 0, 0, 0 }; struct path_list new_refs = { NULL, 0, 0, 1 }; @@ -547,7 +546,7 @@ static int do_fetch(struct transport *transport, /* if neither --no-tags nor --tags was specified, do automated tag * following ... */ if (tags == TAGS_DEFAULT && autotags) { - ref_map = find_non_local_tags(transport, fetch_map); + ref_map = find_non_local_tags(transport); if (ref_map) { transport_set_option(transport, TRANS_OPT_DEPTH, "0"); fetch_refs(transport, ref_map); -- cgit v0.10.2-6-g49f6 From 7f98428d4beb24b564acbbfa779391467aab6f03 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 2 Mar 2008 21:34:43 -0500 Subject: Remove unnecessary delaying of free_refs(ref_map) in builtin-fetch We can free this ref_map as soon as the fetch is complete. It is not used for the automatic tag following, nor is it used to disconnect the transport. This avoids some confusion about why we are holding onto these refs while following tags. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano diff --git a/builtin-fetch.c b/builtin-fetch.c index f8b9542..1348a0e 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -509,7 +509,7 @@ static struct ref *find_non_local_tags(struct transport *transport) static int do_fetch(struct transport *transport, struct refspec *refs, int ref_count) { - struct ref *ref_map, *fetch_map; + struct ref *ref_map; struct ref *rm; int autotags = (transport->remote->fetch_tags == 1); if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET) @@ -540,8 +540,7 @@ static int do_fetch(struct transport *transport, free_refs(ref_map); return 1; } - - fetch_map = ref_map; + free_refs(ref_map); /* if neither --no-tags nor --tags was specified, do automated tag * following ... */ @@ -554,8 +553,6 @@ static int do_fetch(struct transport *transport, free_refs(ref_map); } - free_refs(fetch_map); - transport_disconnect(transport); return 0; -- cgit v0.10.2-6-g49f6 From 5aaf7f2afb46621c371564b07225ff5e978fc4b5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 2 Mar 2008 21:34:51 -0500 Subject: Ensure tail pointer gets setup correctly when we fetch HEAD only If we ever decided to append onto the end of this list the tail pointer must be looking at the right memory cell at the end of the HEAD ref_map. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano diff --git a/builtin-fetch.c b/builtin-fetch.c index 1348a0e..5bce20f 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -157,6 +157,7 @@ static struct ref *get_ref_map(struct transport *transport, if (!ref_map) die("Couldn't find remote ref HEAD"); ref_map->merge = 1; + tail = &ref_map->next; } } ref_remove_duplicates(ref_map); -- cgit v0.10.2-6-g49f6 From c50b2b47999134a5da1f9a5ccb36855313e44e7d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 2 Mar 2008 21:35:00 -0500 Subject: Allow builtin-fetch's find_non_local_tags to append onto a list By allowing the function to append onto the end of an existing list we can do more interesting things, like join the list of tags we want to fetch into the first fetch, rather than the second. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano diff --git a/builtin-fetch.c b/builtin-fetch.c index 5bce20f..1d3ce77 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -453,7 +453,9 @@ static int add_existing(const char *refname, const unsigned char *sha1, return 0; } -static struct ref *find_non_local_tags(struct transport *transport) +static void find_non_local_tags(struct transport *transport, + struct ref **head, + struct ref ***tail) { static struct path_list existing_refs = { NULL, 0, 0, 0 }; struct path_list new_refs = { NULL, 0, 0, 1 }; @@ -462,8 +464,6 @@ static struct ref *find_non_local_tags(struct transport *transport) const unsigned char *ref_sha1; const struct ref *tag_ref; struct ref *rm = NULL; - struct ref *ref_map = NULL; - struct ref **tail = &ref_map; const struct ref *ref; for_each_ref(add_existing, &existing_refs); @@ -498,13 +498,11 @@ static struct ref *find_non_local_tags(struct transport *transport) strcpy(rm->peer_ref->name, ref_name); hashcpy(rm->old_sha1, ref_sha1); - *tail = rm; - tail = &rm->next; + **tail = rm; + *tail = &rm->next; } free(ref_name); } - - return ref_map; } static int do_fetch(struct transport *transport, @@ -546,7 +544,9 @@ static int do_fetch(struct transport *transport, /* if neither --no-tags nor --tags was specified, do automated tag * following ... */ if (tags == TAGS_DEFAULT && autotags) { - ref_map = find_non_local_tags(transport); + struct ref **tail = &ref_map; + ref_map = NULL; + find_non_local_tags(transport, &ref_map, &tail); if (ref_map) { transport_set_option(transport, TRANS_OPT_DEPTH, "0"); fetch_refs(transport, ref_map); -- cgit v0.10.2-6-g49f6 From 49d58fd0770cbb667a0d7532156ced803e482864 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 2 Mar 2008 21:35:10 -0500 Subject: Free the path_lists used to find non-local tags in git-fetch To support calling find_non_local_tags() more than once in a single git-fetch process we need the existing_refs to be stack-allocated so it resets on the second call. We also should free the path lists to avoid unnecessary memory leaking. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano diff --git a/builtin-fetch.c b/builtin-fetch.c index 1d3ce77..3758d48 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -457,7 +457,7 @@ static void find_non_local_tags(struct transport *transport, struct ref **head, struct ref ***tail) { - static struct path_list existing_refs = { NULL, 0, 0, 0 }; + struct path_list existing_refs = { NULL, 0, 0, 0 }; struct path_list new_refs = { NULL, 0, 0, 1 }; char *ref_name; int ref_name_len; @@ -503,6 +503,8 @@ static void find_non_local_tags(struct transport *transport, } free(ref_name); } + path_list_clear(&existing_refs, 0); + path_list_clear(&new_refs, 0); } static int do_fetch(struct transport *transport, -- cgit v0.10.2-6-g49f6 From 49aaddd102aff1f0fc986629f3dc22a872f202ce Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 2 Mar 2008 21:35:18 -0500 Subject: Teach upload-pack to log the received need lines to an fd To facilitate testing and verification of the requests sent by git-fetch to the remote side we permit logging the received packet lines to the file descriptor specified in GIT_DEBUG_SEND_PACK has been set. Special start and end lines are included to indicate the start and end of each connection. $ GIT_DEBUG_SEND_PACK=3 git fetch 3>UPLOAD_LOG $ cat UPLOAD_LOG #S want 8e10cf4e007ad7e003463c30c34b1050b039db78 multi_ack side-band-64k thin-pack ofs-delta want ddfa4a33562179aca1ace2bcc662244a17d0b503 #E #S want 3253df4d1cf6fb138b52b1938473bcfec1483223 multi_ack side-band-64k thin-pack ofs-delta #E >From the above trace the first connection opened by git-fetch was to download two refs (with values 8e and dd) and the second connection was opened to automatically follow an annotated tag (32). Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano diff --git a/upload-pack.c b/upload-pack.c index e5421db..660134a 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -35,6 +35,7 @@ static unsigned int timeout; * otherwise maximum packet size (up to 65520 bytes). */ static int use_sideband; +static int debug_fd; static void reset_timeout(void) { @@ -444,6 +445,8 @@ static void receive_needs(void) static char line[1000]; int len, depth = 0; + if (debug_fd) + write_in_full(debug_fd, "#S\n", 3); for (;;) { struct object *o; unsigned char sha1_buf[20]; @@ -451,6 +454,8 @@ static void receive_needs(void) reset_timeout(); if (!len) break; + if (debug_fd) + write_in_full(debug_fd, line, len); if (!prefixcmp(line, "shallow ")) { unsigned char sha1[20]; @@ -506,6 +511,8 @@ static void receive_needs(void) add_object_array(o, NULL, &want_obj); } } + if (debug_fd) + write_in_full(debug_fd, "#E\n", 3); if (depth == 0 && shallows.nr == 0) return; if (depth > 0) { @@ -631,6 +638,8 @@ int main(int argc, char **argv) die("'%s': unable to chdir or not a git archive", dir); if (is_repository_shallow()) die("attempt to fetch/clone from a shallow repository"); + if (getenv("GIT_DEBUG_SEND_PACK")) + debug_fd = atoi(getenv("GIT_DEBUG_SEND_PACK")); upload_pack(); return 0; } -- cgit v0.10.2-6-g49f6 From 767f176a1f11e07b9098f5094dbd9f4033e50504 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 2 Mar 2008 21:35:25 -0500 Subject: Make git-fetch follow tags we already have objects for sooner If autofollowing of tags is enabled, we see a new tag on the remote that we don't have, and we already have the SHA-1 object that the tag is peeled to, then we can fetch the tag while we are fetching the other objects on the first connection. This is a slight optimization for projects that have a habit of tagging a release commit after most users have already seen and downloaded that commit object through a prior fetch session. In such cases the users may still find new objects in branch heads, but the new tag will now also be part of the first pack transfer and the subsequent connection to autofollow tags is not required. Currently git.git does not benefit from this optimization as any release usually gets a new commit at the same time that it gets a new release tag, however git-gui.git and many other projects are in the habit of tagging fairly old commits. Users who did not already have the tagged commit still require opening a second connection to autofollow the tag, as we are unable to determine on the client side if $tag^{} will be sent to the client during the first transfer or not. Such computation must be performed on the remote side of the connection and is deferred to another series of changes. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano diff --git a/builtin-fetch.c b/builtin-fetch.c index 3758d48..a58efa6 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -101,6 +101,10 @@ static void add_merge_config(struct ref **head, } } +static void find_non_local_tags(struct transport *transport, + struct ref **head, + struct ref ***tail); + static struct ref *get_ref_map(struct transport *transport, struct refspec *refs, int ref_count, int tags, int *autotags) @@ -160,6 +164,8 @@ static struct ref *get_ref_map(struct transport *transport, tail = &ref_map->next; } } + if (tags == TAGS_DEFAULT && *autotags) + find_non_local_tags(transport, &ref_map, &tail); ref_remove_duplicates(ref_map); return ref_map; -- cgit v0.10.2-6-g49f6 From cf7f929a10f141d319d47c68646c88d5911de777 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 2 Mar 2008 21:35:33 -0500 Subject: Teach git-fetch to grab a tag at the same time as a commit If the situation is the following on the remote and L is the common base between both sides: T - tag1 S - tag2 / / L - A - O - O - B \ \ origin/master master and we have decided to fetch "master" to acquire the range L..B we can also nab tag S at the same time during the first connection, as we can clearly see from the refs advertised by upload-pack that S^{} = B and master = B. Unfortunately we still cannot nab T at the same time as we are not able to see that T^{} will also be in the range implied by L..B. Such computations must be performed on the remote side (not yet supported) or on the client side as post-processing (the current behavior). This optimization is an extension of the previous one in that it helps on projects which tend to publish both a new commit and a new tag, then lay idle for a while before publishing anything else. Most followers are able to download both the new commit and the new tag in one connection, rather than two. git.git tends to follow such patterns with its roughly once-daily updates from Junio. A protocol extension and additional server side logic would be necessary to also ensure T is grabbed on the first connection. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano diff --git a/builtin-fetch.c b/builtin-fetch.c index a58efa6..26c3d74 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -459,6 +459,17 @@ static int add_existing(const char *refname, const unsigned char *sha1, return 0; } +static int will_fetch(struct ref **head, const unsigned char *sha1) +{ + struct ref *rm = *head; + while (rm) { + if (!hashcmp(rm->old_sha1, sha1)) + return 1; + rm = rm->next; + } + return 0; +} + static void find_non_local_tags(struct transport *transport, struct ref **head, struct ref ***tail) @@ -495,7 +506,8 @@ static void find_non_local_tags(struct transport *transport, if (!path_list_has_path(&existing_refs, ref_name) && !path_list_has_path(&new_refs, ref_name) && - has_sha1_file(ref->old_sha1)) { + (has_sha1_file(ref->old_sha1) || + will_fetch(head, ref->old_sha1))) { path_list_insert(ref_name, &new_refs); rm = alloc_ref(strlen(ref_name) + 1); diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh new file mode 100755 index 0000000..45ff982 --- /dev/null +++ b/t/t5503-tagfollow.sh @@ -0,0 +1,124 @@ +#!/bin/sh + +test_description='test automatic tag following' + +. ./test-lib.sh + +# End state of the repository: +# +# T - tag1 S - tag2 +# / / +# L - A ------ O ------ B +# \ \ \ +# \ C - origin/cat \ +# origin/master master + +test_expect_success setup ' + test_tick && + echo ichi >file && + git add file && + git commit -m L && + L=$(git rev-parse --verify HEAD) && + + ( + mkdir cloned && + cd cloned && + git init-db && + git remote add -f origin .. + ) && + + test_tick && + echo A >file && + git add file && + git commit -m A && + A=$(git rev-parse --verify HEAD) +' + +U=UPLOAD_LOG + +cat - <expect +#S +want $A +#E +EOF +test_expect_success 'fetch A (new commit : 1 connection)' ' + rm -f $U + ( + cd cloned && + GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U && + test $A = $(git rev-parse --verify origin/master) + ) && + test -s $U && + cut -d" " -f1,2 $U >actual && + git diff expect actual +' + +test_expect_success "create tag T on A, create C on branch cat" ' + git tag -a -m tag1 tag1 $A && + T=$(git rev-parse --verify tag1) && + + git checkout -b cat && + echo C >file && + git add file && + git commit -m C && + C=$(git rev-parse --verify HEAD) && + git checkout master +' + +cat - <expect +#S +want $C +want $T +#E +EOF +test_expect_success 'fetch C, T (new branch, tag : 1 connection)' ' + rm -f $U + ( + cd cloned && + GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U && + test $C = $(git rev-parse --verify origin/cat) && + test $T = $(git rev-parse --verify tag1) && + test $A = $(git rev-parse --verify tag1^0) + ) && + test -s $U && + cut -d" " -f1,2 $U >actual && + git diff expect actual +' + +test_expect_success "create commits O, B, tag S on B" ' + test_tick && + echo O >file && + git add file && + git commit -m O && + + test_tick && + echo B >file && + git add file && + git commit -m B && + B=$(git rev-parse --verify HEAD) && + + git tag -a -m tag2 tag2 $B && + S=$(git rev-parse --verify tag2) +' + +cat - <expect +#S +want $B +want $S +#E +EOF +test_expect_success 'fetch B, S (commit and tag : 1 connection)' ' + rm -f $U + ( + cd cloned && + GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U && + test $B = $(git rev-parse --verify origin/master) && + test $B = $(git rev-parse --verify tag2^0) && + test $S = $(git rev-parse --verify tag2) + ) && + test -s $U && + cut -d" " -f1,2 $U >actual && + git diff expect actual +' + +test_done -- cgit v0.10.2-6-g49f6 From f0a24aa56e49a5cb03101ead0211c7080b96c9f1 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 3 Mar 2008 22:27:20 -0500 Subject: git-pack-objects: Automatically pack annotated tags if object was packed The new option "--include-tag" allows the caller to request that any annotated tag be included into the packfile if the object the tag references was also included as part of the packfile. This option can be useful on the server side of a native git transport, where the server knows what commits it is including into a packfile to update the client. If new annotated tags have been introduced then we can also include them in the packfile, saving the client from needing to request them through a second connection. This change only introduces the backend option and provides a test. Protocol extensions to make this useful in fetch-pack/upload-pack are still necessary to activate the logic during transport. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index 5c1bd3b..eed0a94 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -73,6 +73,11 @@ base-name:: as if all refs under `$GIT_DIR/refs` are specified to be included. +--include-tag:: + Include unasked-for annotated tags if the object they + reference was included in the resulting packfile. This + can be useful to send new tags to native git clients. + --window=[N], --depth=[N]:: These two options affect how the objects contained in the pack are stored using delta compression. The diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 2799e68..f504cff 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -15,6 +15,7 @@ #include "revision.h" #include "list-objects.h" #include "progress.h" +#include "refs.h" #ifdef THREADED_DELTA_SEARCH #include "thread-utils.h" @@ -27,7 +28,8 @@ git-pack-objects [{ -q | --progress | --all-progress }] \n\ [--window=N] [--window-memory=N] [--depth=N] \n\ [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\ [--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\ - [--stdout | base-name] [--keep-unreachable] [d && + git update-index --add d && + tree=`git write-tree` && + commit=`git commit-tree $tree sig && + echo "type commit" >>sig && + echo "tag mytag" >>sig && + echo "tagger $(git var GIT_COMMITTER_IDENT)" >>sig && + echo >>sig && + echo "our test tag" >>sig && + tag=`git mktag obj-list +' + +rm -rf clone.git +test_expect_success 'pack without --include-tag' ' + packname_1=$(git pack-objects \ + --window=0 \ + test-1 list.expect && + ( + GIT_DIR=clone.git && + export GIT_DIR && + test_must_fail git cat-file -e $tag && + git rev-list --objects $commit + ) >list.actual && + git diff list.expect list.actual +' + +rm -rf clone.git +test_expect_success 'pack with --include-tag' ' + packname_1=$(git pack-objects \ + --window=0 \ + --include-tag \ + test-2 list.expect && + ( + GIT_DIR=clone.git && + export GIT_DIR && + git rev-list --objects $tag + ) >list.actual && + git diff list.expect list.actual +' + +test_done -- cgit v0.10.2-6-g49f6 From 348e390b17e7a2b0618fbbfe8cdefa3d73ecbea2 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 3 Mar 2008 22:27:33 -0500 Subject: Teach fetch-pack/upload-pack about --include-tag The new protocol extension "include-tag" allows the client side of the connection (fetch-pack) to request that the server side of the native git protocol (upload-pack / pack-objects) use --include-tag as it prepares the packfile, thus ensuring that an annotated tag object will be included in the resulting packfile if the object it refers to was also included into the packfile. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt index 2b8ffe5..57598eb 100644 --- a/Documentation/git-fetch-pack.txt +++ b/Documentation/git-fetch-pack.txt @@ -8,7 +8,7 @@ git-fetch-pack - Receive missing objects from another repository SYNOPSIS -------- -'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=] [--depth=] [--no-progress] [-v] [:] [...] +'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=] [--depth=] [--no-progress] [-v] [:] [...] DESCRIPTION ----------- @@ -45,6 +45,12 @@ OPTIONS Spend extra cycles to minimize the number of objects to be sent. Use it on slower connection. +\--include-tag:: + If the remote side supports it, annotated tags objects will + be downloaded on the same connection as the other objects if + the object the tag references is downloaded. The caller must + otherwise determine the tags this option made available. + \--upload-pack=:: Use this to specify the path to 'git-upload-pack' on the remote side, if is not found on your $PATH. diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index b23e886..34fb6d5 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -18,7 +18,7 @@ static struct fetch_pack_args args = { }; static const char fetch_pack_usage[] = -"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=] [--depth=] [--no-progress] [-v] [:] [...]"; +"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=] [--depth=] [--no-progress] [-v] [:] [...]"; #define COMPLETE (1U << 0) #define COMMON (1U << 1) @@ -174,13 +174,14 @@ 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), (multi_ack ? " multi_ack" : ""), (use_sideband == 2 ? " side-band-64k" : ""), (use_sideband == 1 ? " side-band" : ""), (args.use_thin_pack ? " thin-pack" : ""), (args.no_progress ? " no-progress" : ""), + (args.include_tag ? " include-tag" : ""), " ofs-delta"); else packet_write(fd[1], "want %s\n", sha1_to_hex(remote)); @@ -680,6 +681,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) args.use_thin_pack = 1; continue; } + if (!strcmp("--include-tag", arg)) { + args.include_tag = 1; + continue; + } if (!strcmp("--all", arg)) { args.fetch_all = 1; continue; diff --git a/fetch-pack.h b/fetch-pack.h index 8d35ef6..8bd9c32 100644 --- a/fetch-pack.h +++ b/fetch-pack.h @@ -12,7 +12,8 @@ struct fetch_pack_args use_thin_pack:1, fetch_all:1, verbose:1, - no_progress:1; + no_progress:1, + include_tag:1; }; struct ref *fetch_pack(struct fetch_pack_args *args, diff --git a/upload-pack.c b/upload-pack.c index 660134a..b46dd36 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -27,7 +27,8 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=n static 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, use_include_tag; +static int no_progress; static struct object_array have_obj; static struct object_array want_obj; static unsigned int timeout; @@ -162,6 +163,8 @@ static void create_pack_file(void) argv[arg++] = "--progress"; if (use_ofs_delta) argv[arg++] = "--delta-base-offset"; + if (use_include_tag) + argv[arg++] = "--include-tag"; argv[arg++] = NULL; memset(&pack_objects, 0, sizeof(pack_objects)); @@ -494,6 +497,8 @@ static void receive_needs(void) use_sideband = DEFAULT_PACKET_MAX; if (strstr(line+45, "no-progress")) no_progress = 1; + if (strstr(line+45, "include-tag")) + use_include_tag = 1; /* We have sent all our refs already, and the other end * should have chosen out of them; otherwise they are @@ -565,7 +570,8 @@ 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" + " include-tag"; struct object *o = parse_object(sha1); if (!o) -- cgit v0.10.2-6-g49f6 From 41fa7d2eaeace0c5c23fc75a3d5cc9efbad467a5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 3 Mar 2008 22:27:40 -0500 Subject: Teach git-fetch to exploit server side automatic tag following If the remote peer upload-pack process supports the include-tag protocol extension then we can avoid running a second fetch cycle on the client side by letting the server send us the annotated tags along with the objects it is packing for us. In the following graph we can now fetch both "tag1" and "tag2" on the same connection that we fetched "master" from the remote when we only have L available on the local side: T - tag1 S - tag2 / / L - o ------ o ------ B \ \ \ \ origin/master master The objects for "tag1" are implicitly downloaded without our direct knowledge. The existing "quickfetch" optimization within git-fetch discovers that tag1 is complete after the first connection and does not open a second connection. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano diff --git a/builtin-fetch.c b/builtin-fetch.c index 26c3d74..55f611e 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -555,6 +555,8 @@ static int do_fetch(struct transport *transport, read_ref(rm->peer_ref->name, rm->peer_ref->old_sha1); } + if (tags == TAGS_DEFAULT && autotags) + transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1"); if (fetch_refs(transport, ref_map)) { free_refs(ref_map); return 1; @@ -568,6 +570,7 @@ static int do_fetch(struct transport *transport, ref_map = NULL; find_non_local_tags(transport, &ref_map, &tail); if (ref_map) { + transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL); transport_set_option(transport, TRANS_OPT_DEPTH, "0"); fetch_refs(transport, ref_map); } diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh index 45ff982..86e5b9b 100755 --- a/t/t5503-tagfollow.sh +++ b/t/t5503-tagfollow.sh @@ -121,4 +121,30 @@ test_expect_success 'fetch B, S (commit and tag : 1 connection)' ' git diff expect actual ' +cat - <expect +#S +want $B +want $S +#E +EOF +test_expect_success 'new clone fetch master and tags' ' + git branch -D cat + rm -f $U + ( + mkdir clone2 && + cd clone2 && + git init && + git remote add origin .. && + GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U && + test $B = $(git rev-parse --verify origin/master) && + test $S = $(git rev-parse --verify tag2) && + test $B = $(git rev-parse --verify tag2^0) && + test $T = $(git rev-parse --verify tag1) && + test $A = $(git rev-parse --verify tag1^0) + ) && + test -s $U && + cut -d" " -f1,2 $U >actual && + git diff expect actual +' + test_done diff --git a/transport.c b/transport.c index 166c1d1..393e0e8 100644 --- a/transport.c +++ b/transport.c @@ -560,6 +560,7 @@ static int close_bundle(struct transport *transport) struct git_transport_data { unsigned thin : 1; unsigned keep : 1; + unsigned followtags : 1; int depth; struct child_process *conn; int fd[2]; @@ -580,6 +581,9 @@ static int set_git_option(struct transport *connection, } else if (!strcmp(name, TRANS_OPT_THIN)) { data->thin = !!value; return 0; + } else if (!strcmp(name, TRANS_OPT_FOLLOWTAGS)) { + data->followtags = !!value; + return 0; } else if (!strcmp(name, TRANS_OPT_KEEP)) { data->keep = !!value; return 0; @@ -628,6 +632,7 @@ static int fetch_refs_via_pack(struct transport *transport, args.keep_pack = data->keep; args.lock_pack = 1; args.use_thin_pack = data->thin; + args.include_tag = data->followtags; args.verbose = transport->verbose > 0; args.depth = data->depth; diff --git a/transport.h b/transport.h index 6fb4526..8abfc0a 100644 --- a/transport.h +++ b/transport.h @@ -53,6 +53,9 @@ struct transport *transport_get(struct remote *, const char *); /* Limit the depth of the fetch if not null */ #define TRANS_OPT_DEPTH "depth" +/* Aggressively fetch annotated tags if possible */ +#define TRANS_OPT_FOLLOWTAGS "followtags" + /** * Returns 0 if the option was used, non-zero otherwise. Prints a * message to stderr if the option is not used. -- cgit v0.10.2-6-g49f6