From edae5f0c20b7e71a717b59873ddb8eeddfa49b86 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 23 Apr 2008 09:47:17 -0700 Subject: [PATCH 1/6] write-tree: properly detect failure to write tree objects Tomasz Fortuna reported that "git commit" does not error out properly when it cannot write tree objects out. "git write-tree" shares the same issue, as the failure to notice the error is deep in the logic to write tree objects out recursively. Signed-off-by: Junio C Hamano --- cache-tree.c | 7 +++-- t/t0004-unwritable.sh | 67 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100755 t/t0004-unwritable.sh diff --git a/cache-tree.c b/cache-tree.c index 50b35264f..cfe937b0f 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -341,8 +341,11 @@ static int update_one(struct cache_tree *it, if (dryrun) hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1); - else - write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1); + else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) { + strbuf_release(&buffer); + return -1; + } + strbuf_release(&buffer); it->entry_count = i; #if DEBUG diff --git a/t/t0004-unwritable.sh b/t/t0004-unwritable.sh new file mode 100755 index 000000000..9255c63c0 --- /dev/null +++ b/t/t0004-unwritable.sh @@ -0,0 +1,67 @@ +#!/bin/sh + +test_description='detect unwritable repository and fail correctly' + +. ./test-lib.sh + +test_expect_success setup ' + + >file && + git add file && + git commit -m initial && + echo >file && + git add file + +' + +test_expect_success 'write-tree should notice unwritable repository' ' + + ( + chmod a-w .git/objects + test_must_fail git write-tree + ) + status=$? + chmod 775 .git/objects + (exit $status) + +' + +test_expect_success 'commit should notice unwritable repository' ' + + ( + chmod a-w .git/objects + test_must_fail git commit -m second + ) + status=$? + chmod 775 .git/objects + (exit $status) + +' + +test_expect_success 'update-index should notice unwritable repository' ' + + ( + echo a >file && + chmod a-w .git/objects + test_must_fail git update-index file + ) + status=$? + chmod 775 .git/objects + (exit $status) + +' + +test_expect_success 'add should notice unwritable repository' ' + + ( + echo b >file && + chmod a-w .git/objects + test_must_fail git add file + ) + status=$? + chmod 775 .git/objects + (exit $status) + +' + +test_done From 837f3b765840e789b73d3b08b38c0541cf8e324e Mon Sep 17 00:00:00 2001 From: Matt Graham Date: Wed, 23 Apr 2008 09:13:51 -0400 Subject: [PATCH 2/6] Linked glossary from cvs-migration page Coming from CVS, I found the git glossary vital to learning git and learning how terms in git correlate to the cvs terminology with which I am familiar. This patch links the glossary from the cvs-migration page so cvs users will be able to fine the glossary as soon as they start looking at git documents. Signed-off-by: Matt Graham Acked-by: J. Bruce Fields Signed-off-by: Junio C Hamano --- Documentation/cvs-migration.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/cvs-migration.txt b/Documentation/cvs-migration.txt index ea9890022..00f2e36b2 100644 --- a/Documentation/cvs-migration.txt +++ b/Documentation/cvs-migration.txt @@ -8,7 +8,8 @@ designating a single shared repository which people can synchronize with; this document explains how to do that. Some basic familiarity with git is required. This -link:tutorial.html[tutorial introduction to git] should be sufficient. +link:tutorial.html[tutorial introduction to git] and the +link:glossary.html[git glossary] should be sufficient. Developing against a shared repository -------------------------------------- From 3ef6a1fe95b0a5aaca21a86651850ef5d62327fa Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 23 Apr 2008 05:21:45 -0400 Subject: [PATCH 3/6] t5516: remove ambiguity test (1) This test tried to push into a remote with ambiguous refs in remotes/$x/master and remotes/$y/master. However, the remote never actually tells us about the refs/remotes hierarchy, so we don't even see this ambiguity. The test happened to pass because we were simply looking for failure, and the test fails for another reason: the dst refspec does not exist and does not begin with refs/, making it invalid. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t5516-fetch-push.sh | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 9d2dc33cb..352e83bdc 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -178,19 +178,7 @@ test_expect_success 'push with weak ambiguity (2)' ' ' -test_expect_success 'push with ambiguity (1)' ' - - mk_test remotes/origin/master remotes/frotz/master && - if git push testrepo master:master - then - echo "Oops, should have failed" - false - else - check_push_result $the_first_commit remotes/origin/master remotes/frotz/master - fi -' - -test_expect_success 'push with ambiguity (2)' ' +test_expect_success 'push with ambiguity' ' mk_test heads/frotz tags/frotz && if git push testrepo master:frotz From 3ffb58be0a779b47e1e4d3ea584ba301461a3a77 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 23 Apr 2008 21:28:36 -0400 Subject: [PATCH 4/6] doc/git-gc: add a note about what is collected It seems to be a FAQ that people try running git-gc, and then get puzzled about why the size of their .git directory didn't change. This note mentions the reasons why things might unexpectedly get kept. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-gc.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt index d424a4ecb..b6b5ce151 100644 --- a/Documentation/git-gc.txt +++ b/Documentation/git-gc.txt @@ -104,6 +104,21 @@ The optional configuration variable 'gc.pruneExpire' controls how old the unreferenced loose objects have to be before they are pruned. The default is "2 weeks ago". + +Notes +----- + +git-gc tries very hard to be safe about the garbage it collects. In +particular, it will keep not only objects referenced by your current set +of branches and tags, but also objects referenced by the index, remote +tracking branches, refs saved by linkgit:git-filter-branch[1] in +refs/original/, or reflogs (which may references commits in branches +that were later amended or rewound). + +If you are expecting some objects to be collected and they aren't, check +all of those locations and decide whether it makes sense in your case to +remove those references. + See Also -------- linkgit:git-prune[1] From f8aae120345f511e59bb008e8de2a8f6e65cf377 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 23 Apr 2008 05:16:06 -0400 Subject: [PATCH 5/6] push: allow unqualified dest refspecs to DWIM Previously, a push like: git push remote src:dst would go through the following steps: 1. check for an unambiguous 'dst' on the remote; if it exists, then push to that ref 2. otherwise, check if 'dst' begins with 'refs/'; if it does, create a new ref 3. otherwise, complain because we don't know where in the refs hierarchy to put 'dst' However, in some cases, we can guess about the ref type of 'dst' based on the ref type of 'src'. Specifically, before complaining we now check: 2.5. if 'src' resolves to a ref starting with refs/heads or refs/tags, then prepend that to 'dst' So now this creates a new branch on the remote, whereas it previously failed with an error message: git push master:newbranch Note that, by design, we limit this DWIM behavior only to source refs which resolve exactly (including symrefs which resolve to existing refs). We still complain on a partial destination refspec if the source is a raw sha1, or a ref expression such as 'master~10'. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- remote.c | 32 +++++++++++++++++++++++++++++--- t/t5516-fetch-push.sh | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/remote.c b/remote.c index 06ad15627..2d9af4023 100644 --- a/remote.c +++ b/remote.c @@ -812,6 +812,26 @@ static struct ref *make_linked_ref(const char *name, struct ref ***tail) return ret; } +static char *guess_ref(const char *name, struct ref *peer) +{ + struct strbuf buf = STRBUF_INIT; + unsigned char sha1[20]; + + const char *r = resolve_ref(peer->name, sha1, 1, NULL); + if (!r) + return NULL; + + if (!prefixcmp(r, "refs/heads/")) + strbuf_addstr(&buf, "refs/heads/"); + else if (!prefixcmp(r, "refs/tags/")) + strbuf_addstr(&buf, "refs/tags/"); + else + return NULL; + + strbuf_addstr(&buf, name); + return strbuf_detach(&buf, NULL); +} + static int match_explicit(struct ref *src, struct ref *dst, struct ref ***dst_tail, struct refspec *rs, @@ -820,6 +840,7 @@ static int match_explicit(struct ref *src, struct ref *dst, struct ref *matched_src, *matched_dst; const char *dst_value = rs->dst; + char *dst_guess; if (rs->pattern) return errs; @@ -866,10 +887,15 @@ static int match_explicit(struct ref *src, struct ref *dst, case 0: if (!memcmp(dst_value, "refs/", 5)) matched_dst = make_linked_ref(dst_value, dst_tail); + else if((dst_guess = guess_ref(dst_value, matched_src))) + matched_dst = make_linked_ref(dst_guess, dst_tail); else - error("dst refspec %s does not match any " - "existing ref on the remote and does " - "not start with refs/.", dst_value); + error("unable to push to unqualified destination: %s\n" + "The destination refspec neither matches an " + "existing ref on the remote nor\n" + "begins with refs/, and we are unable to " + "guess a prefix based on the source ref.", + dst_value); break; default: matched_dst = NULL; diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index f93a100f8..0a757d5b9 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -273,6 +273,37 @@ test_expect_success 'push with colon-less refspec (4)' ' ' +test_expect_success 'push head with non-existant, incomplete dest' ' + + mk_test && + git push testrepo master:branch && + check_push_result $the_commit heads/branch + +' + +test_expect_success 'push tag with non-existant, incomplete dest' ' + + mk_test && + git tag -f v1.0 && + git push testrepo v1.0:tag && + check_push_result $the_commit tags/tag + +' + +test_expect_success 'push sha1 with non-existant, incomplete dest' ' + + mk_test && + test_must_fail git push testrepo `git rev-parse master`:foo + +' + +test_expect_success 'push ref expression with non-existant, incomplete dest' ' + + mk_test && + test_must_fail git push testrepo master^:branch + +' + test_expect_success 'push with HEAD' ' mk_test heads/master && @@ -311,6 +342,15 @@ test_expect_success 'push with +HEAD' ' ' +test_expect_success 'push HEAD with non-existant, incomplete dest' ' + + mk_test && + git checkout master && + git push testrepo HEAD:branch && + check_push_result $the_commit heads/branch + +' + test_expect_success 'push with config remote.*.push = HEAD' ' mk_test heads/local && From 1ce89cc4bb18b0e66f7073562d6634f6a9841335 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 22 Apr 2008 07:11:13 -0400 Subject: [PATCH 6/6] remote: create fetch config lines with '+' Since git-remote always uses remote tracking branches, it should be safe to always force updates of those branches. I.e., we should generate fetch = +refs/heads/*:refs/remotes/$remote/* instead of fetch = refs/heads/*:refs/remotes/$remote/* This was the behavior of the perl version, which seems to have been lost in the C rewrite. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-remote.c | 1 + t/t5505-remote.sh | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/builtin-remote.c b/builtin-remote.c index 8fe31dbd9..4149f3b3c 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -105,6 +105,7 @@ static int add(int argc, const char **argv) struct path_list_item *item = track.items + i; strbuf_reset(&buf2); + strbuf_addch(&buf2, '+'); if (mirror) strbuf_addf(&buf2, "refs/%s:refs/%s", item->path, item->path); diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index af2d07779..48ff2d424 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -77,6 +77,16 @@ test_expect_success 'add another remote' ' ) ' +test_expect_success 'remote forces tracking branches' ' +( + cd test && + case `git config remote.second.fetch` in + +*) true ;; + *) false ;; + esac +) +' + test_expect_success 'remove remote' ' ( cd test &&