{"thread":{"id":"27826","subject":"[PATCH v3 01/23] transport-helper: fix minor leak in push_refs_with_export","startedAt":"2011-07-16T13:03:20Z","lastAt":"2011-07-23T11:28:00Z","messageCount":29,"participants":["Sverre Rabbelier","Junio C Hamano","Jeff King","Dmitry Ivankov"],"isPatch":true,"patchVersion":3,"patchTotal":23},"messages":[{"id":"171461","messageId":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":null,"subject":"[PATCH v3 00/23] remote-helper improvements","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:20Z","receivedAt":"2011-07-16T13:03:20Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Incorperated feedback from Junio, Johannes and Peff. I also included\nDmitry's patch that adds GITDIR, which I modified to just remove the\nsupport for a gitdir command.\n\nDmitry Ivankov (1):\n  remote-helpers: export GIT_DIR variable to helpers\n\nJeff King (6):\n  transport-helper: fix minor leak in push_refs_with_export\n  t5800: factor out some ref tests\n  t5800: document some non-functional parts of remote helpers\n  git-remote-testgit: import non-HEAD refs\n  transport-helper: don't feed bogus refs to export push\n  git_remote_helpers: push all refs during a non-local export\n\nSverre Rabbelier (16):\n  t5800: use skip_all instead of prereq\n  remote-curl: accept empty line as terminator\n  git-remote-testgit: only push for non-local repositories\n  git-remote-testgit: fix error handling\n  fast-import: introduce 'done' command\n  fast-export: support done feature\n  transport-helper: factor out push_update_refs_status\n  transport-helper: check status code of finish_command\n  transport-helper: use the new done feature where possible\n  transport-helper: update ref status after push with export\n  transport-helper: change import semantics\n  transport-helper: Use capname for refspec capability too\n  transport-helper: implement marks location as capability\n  transport-helper: die early on encountering deleted refs\n  t5800: document inability to push new branch with old content\n  t5800: point out that deleting branches does not work\n\n Documentation/git-fast-export.txt    |    4 +\n Documentation/git-fast-import.txt    |   25 ++++\n Documentation/git-remote-helpers.txt |    3 +\n builtin/fast-export.c                |    9 ++\n fast-import.c                        |    8 +\n git-remote-testgit.py                |   62 +++++----\n git_remote_helpers/git/exporter.py   |   15 ++-\n git_remote_helpers/git/importer.py   |   32 ++++-\n git_remote_helpers/git/non_local.py  |   20 +--\n git_remote_helpers/git/repo.py       |    7 +-\n git_remote_helpers/util.py           |   81 +++++++++++\n remote-curl.c                        |   10 ++-\n t/t5800-remote-helpers.sh            |  112 +++++++++++++---\n t/t9300-fast-import.sh               |   42 ++++++\n transport-helper.c                   |  251 ++++++++++++++++++----------------\n 15 files changed, 494 insertions(+), 187 deletions(-)\n\n-- \n1.7.5.1.292.g728120\n"},{"id":"171460","messageId":"1310821424-4750-2-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 01/23] transport-helper: fix minor leak in push_refs_with_export","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:21Z","receivedAt":"2011-07-16T13:03:21Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"From: Jeff King <peff@peff.net>\n\n\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged\n\n transport-helper.c |    1 +\n 1 files changed, 1 insertions(+), 0 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 660147f..b560b64 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -728,6 +728,7 @@ static int push_refs_with_export(struct transport *transport,\n \t\t\tstrbuf_addf(&buf, \"^%s\", private);\n \t\t\tstring_list_append(&revlist_args, strbuf_detach(&buf, NULL));\n \t\t}\n+\t\tfree(private);\n \n \t\tstring_list_append(&revlist_args, ref->name);\n \n-- \n1.7.5.1.292.g728120\n"},{"id":"171462","messageId":"1310821424-4750-3-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 02/23] t5800: factor out some ref tests","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:22Z","receivedAt":"2011-07-16T13:03:22Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"From: Jeff King <peff@peff.net>\n\nThese are a little hard to read, and I'm about to add more\njust like them. Plus the failure output is nicer if we use\ntest_cmp than a comparison with \"test\".\n\nSigned-off-by: Jeff King <peff@peff.net>\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged\n\n t/t5800-remote-helpers.sh |   12 ++++++++----\n 1 files changed, 8 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh\nindex 1fb6380..3a37ad0 100755\n--- a/t/t5800-remote-helpers.sh\n+++ b/t/t5800-remote-helpers.sh\n@@ -17,6 +17,12 @@ then\n \ttest_set_prereq PYTHON_24\n fi\n \n+compare_refs() {\n+\tgit --git-dir=\"$1/.git\" rev-parse --verify $2 >expect &&\n+\tgit --git-dir=\"$3/.git\" rev-parse --verify $4 >actual &&\n+\ttest_cmp expect actual\n+}\n+\n test_expect_success PYTHON_24 'setup repository' '\n \tgit init --bare server/.git &&\n \tgit clone server public &&\n@@ -59,8 +65,7 @@ test_expect_success PYTHON_24 'pushing to local repo' '\n \techo content >>file &&\n \tgit commit -a -m three &&\n \tgit push) &&\n-\tHEAD=$(git --git-dir=localclone/.git rev-parse --verify HEAD) &&\n-\ttest $HEAD = $(git --git-dir=server/.git rev-parse --verify HEAD)\n+\tcompare_refs localclone HEAD server HEAD\n '\n \n test_expect_success PYTHON_24 'synch with changes from localclone' '\n@@ -73,8 +78,7 @@ test_expect_success PYTHON_24 'pushing remote local repo' '\n \techo content >>file &&\n \tgit commit -a -m four &&\n \tgit push) &&\n-\tHEAD=$(git --git-dir=clone/.git rev-parse --verify HEAD) &&\n-\ttest $HEAD = $(git --git-dir=server/.git rev-parse --verify HEAD)\n+\tcompare_refs clone HEAD server HEAD\n '\n \n test_done\n-- \n1.7.5.1.292.g728120\n"},{"id":"171463","messageId":"1310821424-4750-4-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 03/23] t5800: use skip_all instead of prereq","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:23Z","receivedAt":"2011-07-16T13:03:23Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"All tests require python 2.4 or higher.\n\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged\n\n t/t5800-remote-helpers.sh |   34 +++++++++++++++++++---------------\n 1 files changed, 19 insertions(+), 15 deletions(-)\n\ndiff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh\nindex 3a37ad0..f6796e3 100755\n--- a/t/t5800-remote-helpers.sh\n+++ b/t/t5800-remote-helpers.sh\n@@ -7,15 +7,19 @@ test_description='Test remote-helper import and export commands'\n \n . ./test-lib.sh\n \n-if test_have_prereq PYTHON && \"$PYTHON_PATH\" -c '\n+if ! test_have_prereq PYTHON ; then\n+\tskip_all='skipping git-remote-hg tests, python not available'\n+\ttest_done\n+fi\n+\n+\"$PYTHON_PATH\" -c '\n import sys\n if sys.hexversion < 0x02040000:\n     sys.exit(1)\n-'\n-then\n-    # Requires Python 2.4 or newer\n-\ttest_set_prereq PYTHON_24\n-fi\n+' || {\n+\tskip_all='skipping git-remote-hg tests, python version < 2.4'\n+\ttest_done\n+}\n \n compare_refs() {\n \tgit --git-dir=\"$1/.git\" rev-parse --verify $2 >expect &&\n@@ -23,7 +27,7 @@ compare_refs() {\n \ttest_cmp expect actual\n }\n \n-test_expect_success PYTHON_24 'setup repository' '\n+test_expect_success 'setup repository' '\n \tgit init --bare server/.git &&\n \tgit clone server public &&\n \t(cd public &&\n@@ -33,34 +37,34 @@ test_expect_success PYTHON_24 'setup repository' '\n \t git push origin master)\n '\n \n-test_expect_success PYTHON_24 'cloning from local repo' '\n+test_expect_success 'cloning from local repo' '\n \tgit clone \"testgit::${PWD}/server\" localclone &&\n \ttest_cmp public/file localclone/file\n '\n \n-test_expect_success PYTHON_24 'cloning from remote repo' '\n+test_expect_success 'cloning from remote repo' '\n \tgit clone \"testgit::file://${PWD}/server\" clone &&\n \ttest_cmp public/file clone/file\n '\n \n-test_expect_success PYTHON_24 'create new commit on remote' '\n+test_expect_success 'create new commit on remote' '\n \t(cd public &&\n \t echo content >>file &&\n \t git commit -a -m two &&\n \t git push)\n '\n \n-test_expect_success PYTHON_24 'pulling from local repo' '\n+test_expect_success 'pulling from local repo' '\n \t(cd localclone && git pull) &&\n \ttest_cmp public/file localclone/file\n '\n \n-test_expect_success PYTHON_24 'pulling from remote remote' '\n+test_expect_success 'pulling from remote remote' '\n \t(cd clone && git pull) &&\n \ttest_cmp public/file clone/file\n '\n \n-test_expect_success PYTHON_24 'pushing to local repo' '\n+test_expect_success 'pushing to local repo' '\n \t(cd localclone &&\n \techo content >>file &&\n \tgit commit -a -m three &&\n@@ -68,12 +72,12 @@ test_expect_success PYTHON_24 'pushing to local repo' '\n \tcompare_refs localclone HEAD server HEAD\n '\n \n-test_expect_success PYTHON_24 'synch with changes from localclone' '\n+test_expect_success 'synch with changes from localclone' '\n \t(cd clone &&\n \t git pull)\n '\n \n-test_expect_success PYTHON_24 'pushing remote local repo' '\n+test_expect_success 'pushing remote local repo' '\n \t(cd clone &&\n \techo content >>file &&\n \tgit commit -a -m four &&\n-- \n1.7.5.1.292.g728120\n"},{"id":"171483","messageId":"1310821424-4750-5-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 04/23] t5800: document some non-functional parts of remote helpers","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:24Z","receivedAt":"2011-07-16T13:03:24Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"From: Jeff King <peff@peff.net>\n\nThese are all things one might expect to work in a helper\nthat is capable of handling multiple branches (which our\ntestgit helper in theory should be able to do, as it is\nbacked by git). All of these bugs are specific to the\nimport/export codepaths, so they don't affect helpers like\ngit-remote-curl that use fetch/push commands.\n\nThe first and fourth tests are about fetching and pushing\nnew refs, and demonstrate bugs in the git_remote_helpers\nlibrary (so they would be most likely to impact helpers for\nother VCSs which import/export git).\n\nThe second test is about importing multiple refs; it\ndemonstrates a bug in git-remote-testgit, which is mostly\nfor exercising the test code. Therefore it probably doesn't\naffect anyone in practice.\n\nThe third test demonstrates a bug in git's side of the\nhelper code when the upstream has added refs that we do not\nhave locally. This could impact git users who use remote\nhelpers to access foreign VCSs.\n\nAll of those bugs have fixes later in this series.\n\nThe fifth test is the most complex, and does not have a fix\nin this series. It tests pushing a ref via the export\nmechanism to a new name on the remote side (i.e.,\n\"git push $remote old:new\").\n\nThe problem is that we push all of the work of generating\nthe export stream onto fast-export, but we have no way of\ncommunicating to fast-export that this name mapping is\nhappening. So we tell fast-export to generate a stream with\nthe commits for \"old\", but we can't tell it to label them\nall as \"new\".\n\nSigned-off-by: Jeff King <peff@peff.net>\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged\n\n t/t5800-remote-helpers.sh |   47 +++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 47 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh\nindex f6796e3..9db8ca8 100755\n--- a/t/t5800-remote-helpers.sh\n+++ b/t/t5800-remote-helpers.sh\n@@ -85,4 +85,51 @@ test_expect_success 'pushing remote local repo' '\n \tcompare_refs clone HEAD server HEAD\n '\n \n+test_expect_failure 'fetch new branch' '\n+\t(cd public &&\n+\t git checkout -b new &&\n+\t echo content >>file &&\n+\t git commit -a -m five &&\n+\t git push origin new\n+\t) &&\n+\t(cd localclone &&\n+\t git fetch origin new\n+\t) &&\n+\tcompare_refs public HEAD localclone FETCH_HEAD\n+'\n+\n+test_expect_failure 'fetch multiple branches' '\n+\t(cd localclone &&\n+\t git fetch\n+\t) &&\n+\tcompare_refs server master localclone refs/remotes/origin/master &&\n+\tcompare_refs server new localclone refs/remotes/origin/new\n+'\n+\n+test_expect_failure 'push when remote has extra refs' '\n+\t(cd clone &&\n+\t echo content >>file &&\n+\t git commit -a -m six &&\n+\t git push\n+\t) &&\n+\tcompare_refs clone master server master\n+'\n+\n+test_expect_failure 'push new branch by name' '\n+\t(cd clone &&\n+\t git checkout -b new-name  &&\n+\t echo content >>file &&\n+\t git commit -a -m seven &&\n+\t git push origin new-name\n+\t) &&\n+\tcompare_refs clone HEAD server refs/heads/new-name\n+'\n+\n+test_expect_failure 'push new branch with old:new refspec' '\n+\t(cd clone &&\n+\t git push origin new-name:new-refspec\n+\t) &&\n+\tcompare_refs clone HEAD server refs/heads/new-refspec\n+'\n+\n test_done\n-- \n1.7.5.1.292.g728120\n"},{"id":"171478","messageId":"1310821424-4750-6-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 05/23] git-remote-testgit: import non-HEAD refs","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:25Z","receivedAt":"2011-07-16T13:03:25Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"From: Jeff King <peff@peff.net>\n\nUpon receiving an \"import\" command, the testgit remote\nhelper would ignore the ref asked for by git and generate a\nfast-export stream based on HEAD. Instead, we should\nactually give git the ref it asked for.\n\nThis requires adding a new parameter to the export_repo\nmethod in the remote-helpers python library, which may be\nused by code outside of git.git. We use a default parameter\nso that callers without the new parameter will get the same\nbehavior as before.\n\nSigned-off-by: Jeff King <peff@peff.net>\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged\n\n git-remote-testgit.py              |    2 +-\n git_remote_helpers/git/exporter.py |    9 +++++++--\n t/t5800-remote-helpers.sh          |    2 +-\n 3 files changed, 9 insertions(+), 4 deletions(-)\n\ndiff --git a/git-remote-testgit.py b/git-remote-testgit.py\nindex df9d512..e4a99a3 100644\n--- a/git-remote-testgit.py\n+++ b/git-remote-testgit.py\n@@ -122,7 +122,7 @@ def do_import(repo, args):\n         die(\"Need gitdir to import\")\n \n     repo = update_local_repo(repo)\n-    repo.exporter.export_repo(repo.gitdir)\n+    repo.exporter.export_repo(repo.gitdir, args)\n \n \n def do_export(repo, args):\ndiff --git a/git_remote_helpers/git/exporter.py b/git_remote_helpers/git/exporter.py\nindex f40f9d6..bc39163 100644\n--- a/git_remote_helpers/git/exporter.py\n+++ b/git_remote_helpers/git/exporter.py\n@@ -15,7 +15,7 @@ class GitExporter(object):\n \n         self.repo = repo\n \n-    def export_repo(self, base):\n+    def export_repo(self, base, refs=None):\n         \"\"\"Exports a fast-export stream for the given directory.\n \n         Simply delegates to git fast-epxort and pipes it through sed\n@@ -23,8 +23,13 @@ class GitExporter(object):\n         default refs/heads. This is to demonstrate how the export\n         data can be stored under it's own ref (using the refspec\n         capability).\n+\n+        If None, refs defaults to [\"HEAD\"].\n         \"\"\"\n \n+        if not refs:\n+            refs = [\"HEAD\"]\n+\n         dirname = self.repo.get_base_path(base)\n         path = os.path.abspath(os.path.join(dirname, 'testgit.marks'))\n \n@@ -42,7 +47,7 @@ class GitExporter(object):\n         if os.path.exists(path):\n             args.append(\"--import-marks=\" + path)\n \n-        args.append(\"HEAD\")\n+        args.extend(refs)\n \n         p1 = subprocess.Popen(args, stdout=subprocess.PIPE)\n \ndiff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh\nindex 9db8ca8..ca115cc 100755\n--- a/t/t5800-remote-helpers.sh\n+++ b/t/t5800-remote-helpers.sh\n@@ -85,7 +85,7 @@ test_expect_success 'pushing remote local repo' '\n \tcompare_refs clone HEAD server HEAD\n '\n \n-test_expect_failure 'fetch new branch' '\n+test_expect_success 'fetch new branch' '\n \t(cd public &&\n \t git checkout -b new &&\n \t echo content >>file &&\n-- \n1.7.5.1.292.g728120\n"},{"id":"171482","messageId":"1310821424-4750-7-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 06/23] transport-helper: don't feed bogus refs to export push","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:26Z","receivedAt":"2011-07-16T13:03:26Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"From: Jeff King <peff@peff.net>\n\nWhen we want to push to a remote helper that has the\n\"export\" capability, we collect all of the refs we want to\npush and then feed them to fast-export.\n\nHowever, the list of refs is actually a list of remote refs,\nnot local refs. The mapped local refs are included via the\npeer_ref pointer. So when we add an argument to our\nfast-export command line, we must be sure to use the local\npeer_ref name (and if there is no local name, it is because\nwe are not actually sending that ref, or we may not even\nhave the ref at all).\n\nSigned-off-by: Jeff King <peff@peff.net>\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged\n\n t/t5800-remote-helpers.sh |    2 +-\n transport-helper.c        |    3 ++-\n 2 files changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh\nindex ca115cc..ceb0010 100755\n--- a/t/t5800-remote-helpers.sh\n+++ b/t/t5800-remote-helpers.sh\n@@ -106,7 +106,7 @@ test_expect_failure 'fetch multiple branches' '\n \tcompare_refs server new localclone refs/remotes/origin/new\n '\n \n-test_expect_failure 'push when remote has extra refs' '\n+test_expect_success 'push when remote has extra refs' '\n \t(cd clone &&\n \t echo content >>file &&\n \t git commit -a -m six &&\ndiff --git a/transport-helper.c b/transport-helper.c\nindex b560b64..34d18aa 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -730,7 +730,8 @@ static int push_refs_with_export(struct transport *transport,\n \t\t}\n \t\tfree(private);\n \n-\t\tstring_list_append(&revlist_args, ref->name);\n+\t\tif (ref->peer_ref)\n+\t\t\tstring_list_append(&revlist_args, ref->peer_ref->name);\n \n \t}\n \n-- \n1.7.5.1.292.g728120\n"},{"id":"171477","messageId":"1310821424-4750-8-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 07/23] git_remote_helpers: push all refs during a non-local export","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:27Z","receivedAt":"2011-07-16T13:03:27Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"From: Jeff King <peff@peff.net>\n\nWhen a remote helper exports to a non-local git repo, the\nsteps are roughly:\n\n  1. fast-export into a local staging area; the set of\n     interesting refs is defined by what is in the fast-export\n     stream\n\n  2. git push from the staging area to the non-local repo\n\nIn the second step, we should explicitly push all refs, not\njust matching ones. This will let us push refs that do not\nyet exist in the remote repo.\n\nSigned-off-by: Jeff King <peff@peff.net>\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged\n\n git_remote_helpers/git/non_local.py |    2 +-\n t/t5800-remote-helpers.sh           |    2 +-\n 2 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/git_remote_helpers/git/non_local.py b/git_remote_helpers/git/non_local.py\nindex f27389b..c53e074 100644\n--- a/git_remote_helpers/git/non_local.py\n+++ b/git_remote_helpers/git/non_local.py\n@@ -63,7 +63,7 @@ class NonLocalGit(object):\n         if not os.path.exists(path):\n             die(\"could not find repo at %s\", path)\n \n-        args = [\"git\", \"--git-dir=\" + path, \"push\", \"--quiet\", self.repo.gitpath]\n+        args = [\"git\", \"--git-dir=\" + path, \"push\", \"--quiet\", self.repo.gitpath, \"--all\"]\n         child = subprocess.Popen(args)\n         if child.wait() != 0:\n             raise CalledProcessError\ndiff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh\nindex ceb0010..12f471c 100755\n--- a/t/t5800-remote-helpers.sh\n+++ b/t/t5800-remote-helpers.sh\n@@ -115,7 +115,7 @@ test_expect_success 'push when remote has extra refs' '\n \tcompare_refs clone master server master\n '\n \n-test_expect_failure 'push new branch by name' '\n+test_expect_success 'push new branch by name' '\n \t(cd clone &&\n \t git checkout -b new-name  &&\n \t echo content >>file &&\n-- \n1.7.5.1.292.g728120\n"},{"id":"171465","messageId":"1310821424-4750-9-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 08/23] remote-helpers: export GIT_DIR variable to helpers","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:28Z","receivedAt":"2011-07-16T13:03:28Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"From: Dmitry Ivankov <divanorama@gmail.com>\n\nThe gitdir capability is recognized by git and can be used to tell\nthe helper where the .git directory is. But it is not mentioned in\nthe documentation and considered worse than if gitdir was passed\nvia GIT_DIR environment variable.\n\nRemove support for the gitdir capability and export GIT_DIR instead.\nTeach testgit to use env instead of the now-removed gitdir command.\n\n[sr: fixed up documentation]\n\nSigned-off-by: Dmitry Ivankov <divanorama@gmail.com>\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  New in this series.\n\n Documentation/git-remote-helpers.txt |    3 +++\n git-remote-testgit.py                |   14 +-------------\n transport-helper.c                   |   15 ++++++++++-----\n 3 files changed, 14 insertions(+), 18 deletions(-)\n\ndiff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt\nindex 58f6ad4..18b8341 100644\n--- a/Documentation/git-remote-helpers.txt\n+++ b/Documentation/git-remote-helpers.txt\n@@ -47,6 +47,9 @@ arguments. The first argument specifies a remote repository as in git;\n it is either the name of a configured remote or a URL. The second\n argument specifies a URL; it is usually of the form\n '<transport>://<address>', but any arbitrary string is possible.\n+The 'GIT_DIR' environment variable is set up for the remote helper\n+and can be used to determine where to store additional data or from\n+which directory to invoke auxiliary git commands.\n \n When git encounters a URL of the form '<transport>://<address>', where\n '<transport>' is a protocol that it cannot handle natively, it\ndiff --git a/git-remote-testgit.py b/git-remote-testgit.py\nindex e4a99a3..b0c1e9b 100644\n--- a/git-remote-testgit.py\n+++ b/git-remote-testgit.py\n@@ -35,7 +35,7 @@ def get_repo(alias, url):\n     prefix = 'refs/testgit/%s/' % alias\n     debug(\"prefix: '%s'\", prefix)\n \n-    repo.gitdir = \"\"\n+    repo.gitdir = os.environ[\"GIT_DIR\"]\n     repo.alias = alias\n     repo.prefix = prefix\n \n@@ -70,7 +70,6 @@ def do_capabilities(repo, args):\n \n     print \"import\"\n     print \"export\"\n-    print \"gitdir\"\n     print \"refspec refs/heads/*:%s*\" % repo.prefix\n \n     print # end capabilities\n@@ -150,22 +149,11 @@ def do_export(repo, args):\n     repo.non_local.push(repo.gitdir)\n \n \n-def do_gitdir(repo, args):\n-    \"\"\"Stores the location of the gitdir.\n-    \"\"\"\n-\n-    if not args:\n-        die(\"gitdir needs an argument\")\n-\n-    repo.gitdir = ' '.join(args)\n-\n-\n COMMANDS = {\n     'capabilities': do_capabilities,\n     'list': do_list,\n     'import': do_import,\n     'export': do_export,\n-    'gitdir': do_gitdir,\n }\n \n \ndiff --git a/transport-helper.c b/transport-helper.c\nindex 34d18aa..6cccb20 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -105,6 +105,12 @@ static struct child_process *get_helper(struct transport *transport)\n \tint refspec_alloc = 0;\n \tint duped;\n \tint code;\n+\tchar git_dir_buf[sizeof(GIT_DIR_ENVIRONMENT) + PATH_MAX + 1];\n+\tconst char *helper_env[] = {\n+\t\tgit_dir_buf,\n+\t\tNULL\n+\t};\n+\n \n \tif (data->helper)\n \t\treturn data->helper;\n@@ -120,6 +126,10 @@ static struct child_process *get_helper(struct transport *transport)\n \thelper->argv[2] = remove_ext_force(transport->url);\n \thelper->git_cmd = 0;\n \thelper->silent_exec_failure = 1;\n+\n+\tsnprintf(git_dir_buf, sizeof(git_dir_buf), \"%s=%s\", GIT_DIR_ENVIRONMENT, get_git_dir());\n+\thelper->env = helper_env;\n+\n \tcode = start_command(helper);\n \tif (code < 0 && errno == ENOENT)\n \t\tdie(\"Unable to find remote helper for '%s'\", data->name);\n@@ -174,11 +184,6 @@ static struct child_process *get_helper(struct transport *transport)\n \t\t\trefspecs[refspec_nr++] = strdup(buf.buf + strlen(\"refspec \"));\n \t\t} else if (!strcmp(capname, \"connect\")) {\n \t\t\tdata->connect = 1;\n-\t\t} else if (!strcmp(buf.buf, \"gitdir\")) {\n-\t\t\tstruct strbuf gitdir = STRBUF_INIT;\n-\t\t\tstrbuf_addf(&gitdir, \"gitdir %s\\n\", get_git_dir());\n-\t\t\tsendline(data, &gitdir);\n-\t\t\tstrbuf_release(&gitdir);\n \t\t} else if (mandatory) {\n \t\t\tdie(\"Unknown mandatory capability %s. This remote \"\n \t\t\t    \"helper probably needs newer version of Git.\\n\",\n-- \n1.7.5.1.292.g728120\n"},{"id":"171479","messageId":"1310821424-4750-10-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 09/23] remote-curl: accept empty line as terminator","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:29Z","receivedAt":"2011-07-16T13:03:29Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"This went unnoticed because the transport helper infrastructore did\nnot check the return value of the helper, nor did the helper print\nanything before exiting.\n\nWhile at it also make sure that the stream doesn't end unexpectedly.\n\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Added the 'check fo unexpected EOF' chunk.\n\n remote-curl.c |   10 +++++++++-\n 1 files changed, 9 insertions(+), 1 deletions(-)\n\ndiff --git a/remote-curl.c b/remote-curl.c\nindex b5be25c..30554f4 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -857,7 +857,14 @@ int main(int argc, const char **argv)\n \thttp_init(remote);\n \n \tdo {\n-\t\tif (strbuf_getline(&buf, stdin, '\\n') == EOF)\n+\t\tif (strbuf_getline(&buf, stdin, '\\n') == EOF) {\n+\t\t\tif (ferror(stdin))\n+\t\t\t\tfprintf(stderr, \"Error reading command stream\\n\");\n+\t\t\telse\n+\t\t\t\tfprintf(stderr, \"Unexpected end of command stream\\n\");\n+\t\t\treturn 1;\n+\t\t}\n+\t\tif (buf.len == 0)\n \t\t\tbreak;\n \t\tif (!prefixcmp(buf.buf, \"fetch \")) {\n \t\t\tif (nongit)\n@@ -897,6 +904,7 @@ int main(int argc, const char **argv)\n \t\t\tprintf(\"\\n\");\n \t\t\tfflush(stdout);\n \t\t} else {\n+\t\t\tfprintf(stderr, \"Unknown command '%s'\\n\", buf.buf);\n \t\t\treturn 1;\n \t\t}\n \t\tstrbuf_reset(&buf);\n-- \n1.7.5.1.292.g728120\n"},{"id":"171464","messageId":"1310821424-4750-11-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 10/23] git-remote-testgit: only push for non-local repositories","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:30Z","receivedAt":"2011-07-16T13:03:30Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Trying to push for local repositories will fail since there is no\nlocal checkout in .git/info/... to push from as that is only used for\nnon-local repositories (local repositories are pushed to directly).\n\nThis went unnoticed because the transport helper infrastructure does\nnot check the return value of the helper.\n\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged\n\n git-remote-testgit.py |    4 +++-\n 1 files changed, 3 insertions(+), 1 deletions(-)\n\ndiff --git a/git-remote-testgit.py b/git-remote-testgit.py\nindex b0c1e9b..cdbc494 100644\n--- a/git-remote-testgit.py\n+++ b/git-remote-testgit.py\n@@ -146,7 +146,9 @@ def do_export(repo, args):\n \n     update_local_repo(repo)\n     repo.importer.do_import(repo.gitdir)\n-    repo.non_local.push(repo.gitdir)\n+\n+    if not repo.local:\n+        repo.non_local.push(repo.gitdir)\n \n \n COMMANDS = {\n-- \n1.7.5.1.292.g728120\n"},{"id":"171466","messageId":"1310821424-4750-12-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 11/23] git-remote-testgit: fix error handling","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:31Z","receivedAt":"2011-07-16T13:03:31Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"If fast-export did not complete successfully the error handling code\nitself would error out.\n\nThis was broken in commit 23b093ee0 (Brandon Casey, Wed Jun 9 2010,\nRemove python 2.5'isms). Revert that commit an introduce our own copy\nof check_call in util.py instead.\n\nTested by changing 'if retcode' to 'if not retcode' temporarily.\n\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Included the definition of CalledProcessError if subprocess does\n  not already provide it to make sure everything still works on\n  python 2.4.\n\n git_remote_helpers/git/exporter.py  |    6 ++--\n git_remote_helpers/git/importer.py  |    6 ++--\n git_remote_helpers/git/non_local.py |   18 +++---------\n git_remote_helpers/git/repo.py      |    7 +++--\n git_remote_helpers/util.py          |   47 +++++++++++++++++++++++++++++++++++\n 5 files changed, 62 insertions(+), 22 deletions(-)\n\ndiff --git a/git_remote_helpers/git/exporter.py b/git_remote_helpers/git/exporter.py\nindex bc39163..9ee5f96 100644\n--- a/git_remote_helpers/git/exporter.py\n+++ b/git_remote_helpers/git/exporter.py\n@@ -2,6 +2,8 @@ import os\n import subprocess\n import sys\n \n+from git_remote_helpers.util import check_call\n+\n \n class GitExporter(object):\n     \"\"\"An exporter for testgit repositories.\n@@ -53,6 +55,4 @@ class GitExporter(object):\n \n         args = [\"sed\", \"s_refs/heads/_\" + self.repo.prefix + \"_g\"]\n \n-        child = subprocess.Popen(args, stdin=p1.stdout)\n-        if child.wait() != 0:\n-            raise CalledProcessError\n+        check_call(args, stdin=p1.stdout)\ndiff --git a/git_remote_helpers/git/importer.py b/git_remote_helpers/git/importer.py\nindex 70a7127..02a719a 100644\n--- a/git_remote_helpers/git/importer.py\n+++ b/git_remote_helpers/git/importer.py\n@@ -1,6 +1,8 @@\n import os\n import subprocess\n \n+from git_remote_helpers.util import check_call\n+\n \n class GitImporter(object):\n     \"\"\"An importer for testgit repositories.\n@@ -35,6 +37,4 @@ class GitImporter(object):\n         if os.path.exists(path):\n             args.append(\"--import-marks=\" + path)\n \n-        child = subprocess.Popen(args)\n-        if child.wait() != 0:\n-            raise CalledProcessError\n+        check_call(args)\ndiff --git a/git_remote_helpers/git/non_local.py b/git_remote_helpers/git/non_local.py\nindex c53e074..e700250 100644\n--- a/git_remote_helpers/git/non_local.py\n+++ b/git_remote_helpers/git/non_local.py\n@@ -1,7 +1,7 @@\n import os\n import subprocess\n \n-from git_remote_helpers.util import die, warn\n+from git_remote_helpers.util import check_call, die, warn\n \n \n class NonLocalGit(object):\n@@ -29,9 +29,7 @@ class NonLocalGit(object):\n         os.makedirs(path)\n         args = [\"git\", \"clone\", \"--bare\", \"--quiet\", self.repo.gitpath, path]\n \n-        child = subprocess.Popen(args)\n-        if child.wait() != 0:\n-            raise CalledProcessError\n+        check_call(args)\n \n         return path\n \n@@ -45,14 +43,10 @@ class NonLocalGit(object):\n             die(\"could not find repo at %s\", path)\n \n         args = [\"git\", \"--git-dir=\" + path, \"fetch\", \"--quiet\", self.repo.gitpath]\n-        child = subprocess.Popen(args)\n-        if child.wait() != 0:\n-            raise CalledProcessError\n+        check_call(args)\n \n         args = [\"git\", \"--git-dir=\" + path, \"update-ref\", \"refs/heads/master\", \"FETCH_HEAD\"]\n-        child = subprocess.Popen(args)\n-        if child.wait() != 0:\n-            raise CalledProcessError\n+        child = check_call(args)\n \n     def push(self, base):\n         \"\"\"Pushes from the non-local repo to base.\n@@ -64,6 +58,4 @@ class NonLocalGit(object):\n             die(\"could not find repo at %s\", path)\n \n         args = [\"git\", \"--git-dir=\" + path, \"push\", \"--quiet\", self.repo.gitpath, \"--all\"]\n-        child = subprocess.Popen(args)\n-        if child.wait() != 0:\n-            raise CalledProcessError\n+        child = check_call(args)\ndiff --git a/git_remote_helpers/git/repo.py b/git_remote_helpers/git/repo.py\nindex 58e1cdb..acbf8d7 100644\n--- a/git_remote_helpers/git/repo.py\n+++ b/git_remote_helpers/git/repo.py\n@@ -1,6 +1,9 @@\n import os\n import subprocess\n \n+from git_remote_helpers.util import check_call\n+\n+\n def sanitize(rev, sep='\\t'):\n     \"\"\"Converts a for-each-ref line to a name/value pair.\n     \"\"\"\n@@ -53,9 +56,7 @@ class GitRepo(object):\n         path = \".cached_revs\"\n         ofile = open(path, \"w\")\n \n-        child = subprocess.Popen(args, stdout=ofile)\n-        if child.wait() != 0:\n-            raise CalledProcessError\n+        check_call(args, stdout=ofile)\n         output = open(path).readlines()\n         self.revmap = dict(sanitize(i) for i in output)\n         if \"HEAD\" in self.revmap:\ndiff --git a/git_remote_helpers/util.py b/git_remote_helpers/util.py\nindex dce83e6..1652c65 100644\n--- a/git_remote_helpers/util.py\n+++ b/git_remote_helpers/util.py\n@@ -11,6 +11,21 @@ import sys\n import os\n import subprocess\n \n+try:\n+    from subprocess import CalledProcessError\n+except ImportError:\n+    # from python2.7:subprocess.py\n+    # Exception classes used by this module.\n+    class CalledProcessError(Exception):\n+        \"\"\"This exception is raised when a process run by check_call() returns\n+        a non-zero exit status.  The exit status will be stored in the\n+        returncode attribute.\"\"\"\n+        def __init__(self, returncode, cmd):\n+            self.returncode = returncode\n+            self.cmd = cmd\n+        def __str__(self):\n+            return \"Command '%s' returned non-zero exit status %d\" % (self.cmd, self.returncode)\n+\n \n # Whether or not to show debug messages\n DEBUG = False\n@@ -128,6 +143,38 @@ def run_command (args, cwd = None, shell = False, add_env = None,\n     return (exit_code, output, errors)\n \n \n+# from python2.7:subprocess.py\n+def call(*popenargs, **kwargs):\n+    \"\"\"Run command with arguments.  Wait for command to complete, then\n+    return the returncode attribute.\n+\n+    The arguments are the same as for the Popen constructor.  Example:\n+\n+    retcode = call([\"ls\", \"-l\"])\n+    \"\"\"\n+    return subprocess.Popen(*popenargs, **kwargs).wait()\n+\n+\n+# from python2.7:subprocess.py\n+def check_call(*popenargs, **kwargs):\n+    \"\"\"Run command with arguments.  Wait for command to complete.  If\n+    the exit code was zero then return, otherwise raise\n+    CalledProcessError.  The CalledProcessError object will have the\n+    return code in the returncode attribute.\n+\n+    The arguments are the same as for the Popen constructor.  Example:\n+\n+    check_call([\"ls\", \"-l\"])\n+    \"\"\"\n+    retcode = call(*popenargs, **kwargs)\n+    if retcode:\n+        cmd = kwargs.get(\"args\")\n+        if cmd is None:\n+            cmd = popenargs[0]\n+        raise CalledProcessError(retcode, cmd)\n+    return 0\n+\n+\n def file_reader_method (missing_ok = False):\n     \"\"\"Decorator for simplifying reading of files.\n \n-- \n1.7.5.1.292.g728120\n"},{"id":"171467","messageId":"1310821424-4750-13-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 12/23] fast-import: introduce 'done' command","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:32Z","receivedAt":"2011-07-16T13:03:32Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Add a 'done' command that causes fast-import to stop reading from the\nstream and exit.\n\nIf the new --done command line flag was passed on the command line\n(or a \"feature done\" declaration included at the start of the stream),\nmake the 'done' command mandatory.  So \"git fast-import --done\"'s\ninput format will be prefix-free, making errors easier to detect when\nthey show up as early termination at some convenient time of the\nupstream of a pipe writing to fast-import.\n\nAnother possible application of the 'done' command would to be allow a\nfast-import stream that is only a small part of a larger encapsulating\nstream to be easily parsed, leaving the file offset after the \"done\\n\"\nso the other application can pick up from there.  This patch does not\nteach fast-import to do that --- fast-import still uses buffered input\n(stdio).\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged\n\n Documentation/git-fast-import.txt |   25 ++++++++++++++++++++++\n fast-import.c                     |    8 +++++++\n t/t9300-fast-import.sh            |   42 +++++++++++++++++++++++++++++++++++++\n 3 files changed, 75 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt\nindex 249249a..0fc68a9 100644\n--- a/Documentation/git-fast-import.txt\n+++ b/Documentation/git-fast-import.txt\n@@ -101,6 +101,12 @@ OPTIONS\n \twhen the `cat-blob` command is encountered in the stream.\n \tThe default behaviour is to write to `stdout`.\n \n+--done::\n+\tRequire a `done` command at the end of the stream.\n+\tThis option might be useful for detecting errors that\n+\tcause the frontend to terminate before it has started to\n+\twrite a stream.\n+\n --export-pack-edges=<file>::\n \tAfter creating a packfile, print a line of data to\n \t<file> listing the filename of the packfile and the last\n@@ -330,6 +336,11 @@ and control the current import process.  More detailed discussion\n \tstandard output.  This command is optional and is not needed\n \tto perform an import.\n \n+`done`::\n+\tMarks the end of the stream. This command is optional\n+\tunless the `done` feature was requested using the\n+\t`--done` command line option or `feature done` command.\n+\n `cat-blob`::\n \tCauses fast-import to print a blob in 'cat-file --batch'\n \tformat to the file descriptor set with `--cat-blob-fd` or\n@@ -1015,6 +1026,11 @@ notes::\n \tVersions of fast-import not supporting notes will exit\n \twith a message indicating so.\n \n+done::\n+\tError out if the stream ends without a 'done' command.\n+\tWithout this feature, errors causing the frontend to end\n+\tabruptly at a convenient point in the stream can go\n+\tundetected.\n \n `option`\n ~~~~~~~~\n@@ -1044,6 +1060,15 @@ not be passed as option:\n * cat-blob-fd\n * force\n \n+`done`\n+~~~~~~\n+If the `done` feature is not in use, treated as if EOF was read.\n+This can be used to tell fast-import to finish early.\n+\n+If the `--done` command line option or `feature done` command is\n+in use, the `done` command is mandatory and marks the end of the\n+stream.\n+\n Crash Reports\n -------------\n If fast-import is supplied invalid input it will terminate with a\ndiff --git a/fast-import.c b/fast-import.c\nindex 78d9786..8a8a915 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -354,6 +354,7 @@ static unsigned int cmd_save = 100;\n static uintmax_t next_mark;\n static struct strbuf new_data = STRBUF_INIT;\n static int seen_data_command;\n+static int require_explicit_termination;\n \n /* Signal handling */\n static volatile sig_atomic_t checkpoint_requested;\n@@ -3139,6 +3140,8 @@ static int parse_one_feature(const char *feature, int from_stream)\n \t\trelative_marks_paths = 1;\n \t} else if (!strcmp(feature, \"no-relative-marks\")) {\n \t\trelative_marks_paths = 0;\n+\t} else if (!strcmp(feature, \"done\")) {\n+\t\trequire_explicit_termination = 1;\n \t} else if (!strcmp(feature, \"force\")) {\n \t\tforce_update = 1;\n \t} else if (!strcmp(feature, \"notes\") || !strcmp(feature, \"ls\")) {\n@@ -3288,6 +3291,8 @@ int main(int argc, const char **argv)\n \t\t\tparse_reset_branch();\n \t\telse if (!strcmp(\"checkpoint\", command_buf.buf))\n \t\t\tparse_checkpoint();\n+\t\telse if (!strcmp(\"done\", command_buf.buf))\n+\t\t\tbreak;\n \t\telse if (!prefixcmp(command_buf.buf, \"progress \"))\n \t\t\tparse_progress();\n \t\telse if (!prefixcmp(command_buf.buf, \"feature \"))\n@@ -3307,6 +3312,9 @@ int main(int argc, const char **argv)\n \tif (!seen_data_command)\n \t\tparse_argv();\n \n+\tif (require_explicit_termination && feof(stdin))\n+\t\tdie(\"stream ends early\");\n+\n \tend_packfile();\n \n \tdump_branches();\ndiff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh\nindex 2a53640..f256475 100755\n--- a/t/t9300-fast-import.sh\n+++ b/t/t9300-fast-import.sh\n@@ -2197,6 +2197,48 @@ test_expect_success 'R: quiet option results in no stats being output' '\n     test_cmp empty output\n '\n \n+test_expect_success 'R: feature done means terminating \"done\" is mandatory' '\n+\techo feature done | test_must_fail git fast-import &&\n+\ttest_must_fail git fast-import --done </dev/null\n+'\n+\n+test_expect_success 'R: terminating \"done\" with trailing gibberish is ok' '\n+\tgit fast-import <<-\\EOF &&\n+\tfeature done\n+\tdone\n+\ttrailing gibberish\n+\tEOF\n+\tgit fast-import <<-\\EOF\n+\tdone\n+\tmore trailing gibberish\n+\tEOF\n+'\n+\n+test_expect_success 'R: terminating \"done\" within commit' '\n+\tcat >expect <<-\\EOF &&\n+\tOBJID\n+\t:000000 100644 OBJID OBJID A\thello.c\n+\t:000000 100644 OBJID OBJID A\thello2.c\n+\tEOF\n+\tgit fast-import <<-EOF &&\n+\tcommit refs/heads/done-ends\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<EOT\n+\tCommit terminated by \"done\" command\n+\tEOT\n+\tM 100644 inline hello.c\n+\tdata <<EOT\n+\tHello, world.\n+\tEOT\n+\tC hello.c hello2.c\n+\tdone\n+\tEOF\n+\tgit rev-list done-ends |\n+\tgit diff-tree -r --stdin --root --always |\n+\tsed -e \"s/$_x40/OBJID/g\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n cat >input <<EOF\n option git non-existing-option\n EOF\n-- \n1.7.5.1.292.g728120\n"},{"id":"171468","messageId":"1310821424-4750-14-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 13/23] fast-export: support done feature","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:33Z","receivedAt":"2011-07-16T13:03:33Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"If fast-export is being used to generate a fast-import stream that\nwill be used afterwards it is desirable to indicate the end of the\nstream with the new 'done' command.\n\nAdd a flag that causes fast-export to end with 'done'.\n\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged\n\n Documentation/git-fast-export.txt |    4 ++++\n builtin/fast-export.c             |    9 +++++++++\n 2 files changed, 13 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt\nindex 781bd6e..e3f8453 100644\n--- a/Documentation/git-fast-export.txt\n+++ b/Documentation/git-fast-export.txt\n@@ -82,6 +82,10 @@ marks the same across runs.\n \tallow that.  So fake a tagger to be able to fast-import the\n \toutput.\n \n+--use-done-feature::\n+\tStart the stream with a 'feature done' stanza, and terminate\n+\tit with a 'done' command.\n+\n --no-data::\n \tSkip output of blob objects and instead refer to blobs via\n \ttheir original SHA-1 hash.  This is useful when rewriting the\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex daf1945..becef85 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -26,6 +26,7 @@ static int progress;\n static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;\n static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ABORT;\n static int fake_missing_tagger;\n+static int use_done_feature;\n static int no_data;\n static int full_tree;\n \n@@ -627,6 +628,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)\n \t\t\t     \"Fake a tagger when tags lack one\"),\n \t\tOPT_BOOLEAN(0, \"full-tree\", &full_tree,\n \t\t\t     \"Output full tree for each commit\"),\n+\t\tOPT_BOOLEAN(0, \"use-done-feature\", &use_done_feature,\n+\t\t\t     \"Use the done feature to terminate the stream\"),\n \t\t{ OPTION_NEGBIT, 0, \"data\", &no_data, NULL,\n \t\t\t\"Skip output of blob data\",\n \t\t\tPARSE_OPT_NOARG | PARSE_OPT_NEGHELP, NULL, 1 },\n@@ -648,6 +651,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)\n \tif (argc > 1)\n \t\tusage_with_options (fast_export_usage, options);\n \n+\tif (use_done_feature)\n+\t\tprintf(\"feature done\\n\");\n+\n \tif (import_filename)\n \t\timport_marks(import_filename);\n \n@@ -675,5 +681,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)\n \tif (export_filename)\n \t\texport_marks(export_filename);\n \n+\tif (use_done_feature)\n+\t\tprintf(\"done\\n\");\n+\n \treturn 0;\n }\n-- \n1.7.5.1.292.g728120\n"},{"id":"171481","messageId":"1310821424-4750-15-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 14/23] transport-helper: factor out push_update_refs_status","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:34Z","receivedAt":"2011-07-16T13:03:34Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"The update ref status part of push is useful for the export command\nas well, factor it out into it's own function.\n\nAlso factor out push_update_ref_status to avoid a long loop without\nan explicit condition with a non-trivial body.\n\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged\n\n transport-helper.c |  153 ++++++++++++++++++++++++++++-----------------------\n 1 files changed, 84 insertions(+), 69 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 6cccb20..dd8dd2c 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -559,6 +559,88 @@ static int fetch(struct transport *transport,\n \treturn -1;\n }\n \n+static void push_update_ref_status(struct strbuf *buf,\n+\t\t\t\t   struct ref **ref,\n+\t\t\t\t   struct ref *remote_refs)\n+{\n+\tchar *refname, *msg;\n+\tint status;\n+\n+\tif (!prefixcmp(buf->buf, \"ok \")) {\n+\t\tstatus = REF_STATUS_OK;\n+\t\trefname = buf->buf + 3;\n+\t} else if (!prefixcmp(buf->buf, \"error \")) {\n+\t\tstatus = REF_STATUS_REMOTE_REJECT;\n+\t\trefname = buf->buf + 6;\n+\t} else\n+\t\tdie(\"expected ok/error, helper said '%s'\\n\", buf->buf);\n+\n+\tmsg = strchr(refname, ' ');\n+\tif (msg) {\n+\t\tstruct strbuf msg_buf = STRBUF_INIT;\n+\t\tconst char *end;\n+\n+\t\t*msg++ = '\\0';\n+\t\tif (!unquote_c_style(&msg_buf, msg, &end))\n+\t\t\tmsg = strbuf_detach(&msg_buf, NULL);\n+\t\telse\n+\t\t\tmsg = xstrdup(msg);\n+\t\tstrbuf_release(&msg_buf);\n+\n+\t\tif (!strcmp(msg, \"no match\")) {\n+\t\t\tstatus = REF_STATUS_NONE;\n+\t\t\tfree(msg);\n+\t\t\tmsg = NULL;\n+\t\t}\n+\t\telse if (!strcmp(msg, \"up to date\")) {\n+\t\t\tstatus = REF_STATUS_UPTODATE;\n+\t\t\tfree(msg);\n+\t\t\tmsg = NULL;\n+\t\t}\n+\t\telse if (!strcmp(msg, \"non-fast forward\")) {\n+\t\t\tstatus = REF_STATUS_REJECT_NONFASTFORWARD;\n+\t\t\tfree(msg);\n+\t\t\tmsg = NULL;\n+\t\t}\n+\t}\n+\n+\tif (*ref)\n+\t\t*ref = find_ref_by_name(*ref, refname);\n+\tif (!*ref)\n+\t\t*ref = find_ref_by_name(remote_refs, refname);\n+\tif (!*ref) {\n+\t\twarning(\"helper reported unexpected status of %s\", refname);\n+\t\treturn;\n+\t}\n+\n+\tif ((*ref)->status != REF_STATUS_NONE) {\n+\t\t/*\n+\t\t * Earlier, the ref was marked not to be pushed, so ignore the ref\n+\t\t * status reported by the remote helper if the latter is 'no match'.\n+\t\t */\n+\t\tif (status == REF_STATUS_NONE)\n+\t\t\treturn;\n+\t}\n+\n+\t(*ref)->status = status;\n+\t(*ref)->remote_status = msg;\n+}\n+\n+static void push_update_refs_status(struct helper_data *data,\n+\t\t\t\t    struct ref *remote_refs)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct ref *ref = remote_refs;\n+\tfor (;;) {\n+\t\trecvline(data, &buf);\n+\t\tif (!buf.len)\n+\t\t\tbreak;\n+\n+\t\tpush_update_ref_status(&buf, &ref, remote_refs);\n+\t}\n+\tstrbuf_release(&buf);\n+}\n+\n static int push_refs_with_push(struct transport *transport,\n \t\tstruct ref *remote_refs, int flags)\n {\n@@ -613,76 +695,9 @@ static int push_refs_with_push(struct transport *transport,\n \n \tstrbuf_addch(&buf, '\\n');\n \tsendline(data, &buf);\n-\n-\tref = remote_refs;\n-\twhile (1) {\n-\t\tchar *refname, *msg;\n-\t\tint status;\n-\n-\t\trecvline(data, &buf);\n-\t\tif (!buf.len)\n-\t\t\tbreak;\n-\n-\t\tif (!prefixcmp(buf.buf, \"ok \")) {\n-\t\t\tstatus = REF_STATUS_OK;\n-\t\t\trefname = buf.buf + 3;\n-\t\t} else if (!prefixcmp(buf.buf, \"error \")) {\n-\t\t\tstatus = REF_STATUS_REMOTE_REJECT;\n-\t\t\trefname = buf.buf + 6;\n-\t\t} else\n-\t\t\tdie(\"expected ok/error, helper said '%s'\\n\", buf.buf);\n-\n-\t\tmsg = strchr(refname, ' ');\n-\t\tif (msg) {\n-\t\t\tstruct strbuf msg_buf = STRBUF_INIT;\n-\t\t\tconst char *end;\n-\n-\t\t\t*msg++ = '\\0';\n-\t\t\tif (!unquote_c_style(&msg_buf, msg, &end))\n-\t\t\t\tmsg = strbuf_detach(&msg_buf, NULL);\n-\t\t\telse\n-\t\t\t\tmsg = xstrdup(msg);\n-\t\t\tstrbuf_release(&msg_buf);\n-\n-\t\t\tif (!strcmp(msg, \"no match\")) {\n-\t\t\t\tstatus = REF_STATUS_NONE;\n-\t\t\t\tfree(msg);\n-\t\t\t\tmsg = NULL;\n-\t\t\t}\n-\t\t\telse if (!strcmp(msg, \"up to date\")) {\n-\t\t\t\tstatus = REF_STATUS_UPTODATE;\n-\t\t\t\tfree(msg);\n-\t\t\t\tmsg = NULL;\n-\t\t\t}\n-\t\t\telse if (!strcmp(msg, \"non-fast forward\")) {\n-\t\t\t\tstatus = REF_STATUS_REJECT_NONFASTFORWARD;\n-\t\t\t\tfree(msg);\n-\t\t\t\tmsg = NULL;\n-\t\t\t}\n-\t\t}\n-\n-\t\tif (ref)\n-\t\t\tref = find_ref_by_name(ref, refname);\n-\t\tif (!ref)\n-\t\t\tref = find_ref_by_name(remote_refs, refname);\n-\t\tif (!ref) {\n-\t\t\twarning(\"helper reported unexpected status of %s\", refname);\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\tif (ref->status != REF_STATUS_NONE) {\n-\t\t\t/*\n-\t\t\t * Earlier, the ref was marked not to be pushed, so ignore the ref\n-\t\t\t * status reported by the remote helper if the latter is 'no match'.\n-\t\t\t */\n-\t\t\tif (status == REF_STATUS_NONE)\n-\t\t\t\tcontinue;\n-\t\t}\n-\n-\t\tref->status = status;\n-\t\tref->remote_status = msg;\n-\t}\n \tstrbuf_release(&buf);\n+\n+\tpush_update_refs_status(data, remote_refs);\n \treturn 0;\n }\n \n-- \n1.7.5.1.292.g728120\n"},{"id":"171469","messageId":"1310821424-4750-16-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 15/23] transport-helper: check status code of finish_command","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:35Z","receivedAt":"2011-07-16T13:03:35Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Previously the status code of all helpers were ignored, allowing\nerrors that occur to go unnoticed if the error text output by the\nhelper is not noticed.\n\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged\n\n transport-helper.c |   23 +++++++++++++++--------\n 1 files changed, 15 insertions(+), 8 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex dd8dd2c..e02f4a3 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -209,6 +209,7 @@ static int disconnect_helper(struct transport *transport)\n {\n \tstruct helper_data *data = transport->data;\n \tstruct strbuf buf = STRBUF_INIT;\n+\tint res = 0;\n \n \tif (data->helper) {\n \t\tif (debug)\n@@ -220,13 +221,13 @@ static int disconnect_helper(struct transport *transport)\n \t\tclose(data->helper->in);\n \t\tclose(data->helper->out);\n \t\tfclose(data->out);\n-\t\tfinish_command(data->helper);\n+\t\tres = finish_command(data->helper);\n \t\tfree((char *)data->helper->argv[0]);\n \t\tfree(data->helper->argv);\n \t\tfree(data->helper);\n \t\tdata->helper = NULL;\n \t}\n-\treturn 0;\n+\treturn res;\n }\n \n static const char *unsupported_options[] = {\n@@ -304,12 +305,13 @@ static void standard_options(struct transport *t)\n \n static int release_helper(struct transport *transport)\n {\n+\tint res = 0;\n \tstruct helper_data *data = transport->data;\n \tfree_refspec(data->refspec_nr, data->refspecs);\n \tdata->refspecs = NULL;\n-\tdisconnect_helper(transport);\n+\tres = disconnect_helper(transport);\n \tfree(transport->data);\n-\treturn 0;\n+\treturn res;\n }\n \n static int fetch_with_fetch(struct transport *transport,\n@@ -415,8 +417,11 @@ static int fetch_with_import(struct transport *transport,\n \t\tsendline(data, &buf);\n \t\tstrbuf_reset(&buf);\n \t}\n-\tdisconnect_helper(transport);\n-\tfinish_command(&fastimport);\n+\tif (disconnect_helper(transport))\n+\t\tdie(\"Error while disconnecting helper\");\n+\tif (finish_command(&fastimport))\n+\t\tdie(\"Error while running fast-import\");\n+\n \tfree(fastimport.argv);\n \tfastimport.argv = NULL;\n \n@@ -760,8 +765,10 @@ static int push_refs_with_export(struct transport *transport,\n \t\tdie(\"Couldn't run fast-export\");\n \n \tdata->no_disconnect_req = 1;\n-\tfinish_command(&exporter);\n-\tdisconnect_helper(transport);\n+\tif (finish_command(&exporter))\n+\t\tdie(\"Error while running fast-export\");\n+\tif (disconnect_helper(transport))\n+\t\tdie(\"Error while disconnecting helper\");\n \treturn 0;\n }\n \n-- \n1.7.5.1.292.g728120\n"},{"id":"171480","messageId":"1310821424-4750-17-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 16/23] transport-helper: use the new done feature where possible","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:36Z","receivedAt":"2011-07-16T13:03:36Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"In other words, use fast-export --use-done-feature to add a 'done'\ncommand at the end of streams passed to remote helpers' \"import\"\ncommands, and teach the remote helpers implementing \"export\" to use\nthe 'done' command in turn when producing their streams.\n\nThe trailing \\n in the protocol signals the helper that the\nconnection is about to close, allowing it to do whatever cleanup\nneccesary.\n\nPreviously, the connection would already be closed by the\ntime the trailing \\n was to be written. Now that the remote-helper\nprotocol uses the new done command in its fast-import streams, this\nis no longer the case and we can safely write the trailing \\n.\n\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  This is a squash of \"18/20 transport-helper: export is no longer\n  always the last command\" which added the no_disconnect_req.\n\n git-remote-testgit.py |    2 ++\n transport-helper.c    |    9 ++-------\n 2 files changed, 4 insertions(+), 7 deletions(-)\n\ndiff --git a/git-remote-testgit.py b/git-remote-testgit.py\nindex cdbc494..af4d040 100644\n--- a/git-remote-testgit.py\n+++ b/git-remote-testgit.py\n@@ -123,6 +123,8 @@ def do_import(repo, args):\n     repo = update_local_repo(repo)\n     repo.exporter.export_repo(repo.gitdir, args)\n \n+    print \"done\"\n+\n \n def do_export(repo, args):\n     \"\"\"Imports a fast-import stream from git to testgit.\ndiff --git a/transport-helper.c b/transport-helper.c\nindex e02f4a3..4c0d861 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -380,8 +380,9 @@ static int get_exporter(struct transport *transport,\n \t/* we need to duplicate helper->in because we want to use it after\n \t * fastexport is done with it. */\n \tfastexport->out = dup(helper->in);\n-\tfastexport->argv = xcalloc(4 + revlist_args->nr, sizeof(*fastexport->argv));\n+\tfastexport->argv = xcalloc(5 + revlist_args->nr, sizeof(*fastexport->argv));\n \tfastexport->argv[argc++] = \"fast-export\";\n+\tfastexport->argv[argc++] = \"--use-done-feature\";\n \tif (export_marks)\n \t\tfastexport->argv[argc++] = export_marks;\n \tif (import_marks)\n@@ -417,11 +418,8 @@ static int fetch_with_import(struct transport *transport,\n \t\tsendline(data, &buf);\n \t\tstrbuf_reset(&buf);\n \t}\n-\tif (disconnect_helper(transport))\n-\t\tdie(\"Error while disconnecting helper\");\n \tif (finish_command(&fastimport))\n \t\tdie(\"Error while running fast-import\");\n-\n \tfree(fastimport.argv);\n \tfastimport.argv = NULL;\n \n@@ -764,11 +762,8 @@ static int push_refs_with_export(struct transport *transport,\n \t\t\t export_marks, import_marks, &revlist_args))\n \t\tdie(\"Couldn't run fast-export\");\n \n-\tdata->no_disconnect_req = 1;\n \tif (finish_command(&exporter))\n \t\tdie(\"Error while running fast-export\");\n-\tif (disconnect_helper(transport))\n-\t\tdie(\"Error while disconnecting helper\");\n \treturn 0;\n }\n \n-- \n1.7.5.1.292.g728120\n"},{"id":"171470","messageId":"1310821424-4750-18-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 17/23] transport-helper: update ref status after push with export","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:37Z","receivedAt":"2011-07-16T13:03:37Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Also add check_output from python 2.7.\n\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged, although see later patches that demonstrate that\n  deleted refs don't work.\n\n git-remote-testgit.py              |    6 +++++-\n git_remote_helpers/git/importer.py |   28 +++++++++++++++++++++++++++-\n git_remote_helpers/util.py         |   34 ++++++++++++++++++++++++++++++++++\n transport-helper.c                 |    1 +\n 4 files changed, 67 insertions(+), 2 deletions(-)\n\ndiff --git a/git-remote-testgit.py b/git-remote-testgit.py\nindex af4d040..0b5928d 100644\n--- a/git-remote-testgit.py\n+++ b/git-remote-testgit.py\n@@ -147,11 +147,15 @@ def do_export(repo, args):\n     sys.stdout.flush()\n \n     update_local_repo(repo)\n-    repo.importer.do_import(repo.gitdir)\n+    changed = repo.importer.do_import(repo.gitdir)\n \n     if not repo.local:\n         repo.non_local.push(repo.gitdir)\n \n+    for ref in changed:\n+        print \"ok %s\" % ref\n+    print\n+\n \n COMMANDS = {\n     'capabilities': do_capabilities,\ndiff --git a/git_remote_helpers/git/importer.py b/git_remote_helpers/git/importer.py\nindex 02a719a..5c6b595 100644\n--- a/git_remote_helpers/git/importer.py\n+++ b/git_remote_helpers/git/importer.py\n@@ -1,7 +1,7 @@\n import os\n import subprocess\n \n-from git_remote_helpers.util import check_call\n+from git_remote_helpers.util import check_call, check_output\n \n \n class GitImporter(object):\n@@ -16,6 +16,18 @@ class GitImporter(object):\n \n         self.repo = repo\n \n+    def get_refs(self, gitdir):\n+        \"\"\"Returns a dictionary with refs.\n+        \"\"\"\n+        args = [\"git\", \"--git-dir=\" + gitdir, \"for-each-ref\", \"refs/heads\"]\n+        lines = check_output(args).strip().split('\\n')\n+        refs = {}\n+        for line in lines:\n+            value, name = line.split(' ')\n+            name = name.strip('commit\\t')\n+            refs[name] = value\n+        return refs\n+\n     def do_import(self, base):\n         \"\"\"Imports a fast-import stream to the given directory.\n \n@@ -32,9 +44,23 @@ class GitImporter(object):\n         if not os.path.exists(dirname):\n             os.makedirs(dirname)\n \n+        refs_before = self.get_refs(gitdir)\n+\n         args = [\"git\", \"--git-dir=\" + gitdir, \"fast-import\", \"--quiet\", \"--export-marks=\" + path]\n \n         if os.path.exists(path):\n             args.append(\"--import-marks=\" + path)\n \n         check_call(args)\n+\n+        refs_after = self.get_refs(gitdir)\n+\n+        changed = {}\n+\n+        for name, value in refs_after.iteritems():\n+            if refs_before.get(name) == value:\n+                continue\n+\n+            changed[name] = value\n+\n+        return changed\ndiff --git a/git_remote_helpers/util.py b/git_remote_helpers/util.py\nindex 1652c65..fbbb01b 100644\n--- a/git_remote_helpers/util.py\n+++ b/git_remote_helpers/util.py\n@@ -175,6 +175,40 @@ def check_call(*popenargs, **kwargs):\n     return 0\n \n \n+# from python2.7:subprocess.py\n+def check_output(*popenargs, **kwargs):\n+    r\"\"\"Run command with arguments and return its output as a byte string.\n+\n+    If the exit code was non-zero it raises a CalledProcessError.  The\n+    CalledProcessError object will have the return code in the returncode\n+    attribute and output in the output attribute.\n+\n+    The arguments are the same as for the Popen constructor.  Example:\n+\n+    >>> check_output([\"ls\", \"-l\", \"/dev/null\"])\n+    'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\\n'\n+\n+    The stdout argument is not allowed as it is used internally.\n+    To capture standard error in the result, use stderr=STDOUT.\n+\n+    >>> check_output([\"/bin/sh\", \"-c\",\n+    ...               \"ls -l non_existent_file ; exit 0\"],\n+    ...              stderr=STDOUT)\n+    'ls: non_existent_file: No such file or directory\\n'\n+    \"\"\"\n+    if 'stdout' in kwargs:\n+        raise ValueError('stdout argument not allowed, it will be overridden.')\n+    process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)\n+    output, unused_err = process.communicate()\n+    retcode = process.poll()\n+    if retcode:\n+        cmd = kwargs.get(\"args\")\n+        if cmd is None:\n+            cmd = popenargs[0]\n+        raise subprocess.CalledProcessError(retcode, cmd)\n+    return output\n+\n+\n def file_reader_method (missing_ok = False):\n     \"\"\"Decorator for simplifying reading of files.\n \ndiff --git a/transport-helper.c b/transport-helper.c\nindex 4c0d861..a8f69b0 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -764,6 +764,7 @@ static int push_refs_with_export(struct transport *transport,\n \n \tif (finish_command(&exporter))\n \t\tdie(\"Error while running fast-export\");\n+\tpush_update_refs_status(data, remote_refs);\n \treturn 0;\n }\n \n-- \n1.7.5.1.292.g728120\n"},{"id":"171473","messageId":"1310821424-4750-19-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 18/23] transport-helper: change import semantics","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:38Z","receivedAt":"2011-07-16T13:03:38Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Currently the helper must somehow guess how many import statements to\nread before it starts outputting its fast-export stream. This is\nbecause the remote helper infrastructure runs fast-import only once,\nso the helper is forced to output one stream for all import commands\nit will receive. The only reason this worked in the past was because\nonly one ref was imported at a time.\n\nChange the semantics of the import statement such that it matches\nthat of the push statement. That is, the import statement is followed\nby a series of import statements that are terminated by a '\\n'.\n\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  As Jonathan suggested we now follow push' example, rather than\n  'list'. It makes the remote-testgit code a bit longer, but it means\n  less changes to remote-helper.c.\n\n git-remote-testgit.py     |   16 +++++++++++++++-\n t/t5800-remote-helpers.sh |    2 +-\n transport-helper.c        |    3 +++\n 3 files changed, 19 insertions(+), 2 deletions(-)\n\ndiff --git a/git-remote-testgit.py b/git-remote-testgit.py\nindex 0b5928d..1ed7a56 100644\n--- a/git-remote-testgit.py\n+++ b/git-remote-testgit.py\n@@ -120,8 +120,22 @@ def do_import(repo, args):\n     if not repo.gitdir:\n         die(\"Need gitdir to import\")\n \n+    ref = args[0]\n+    refs = [ref]\n+\n+    while True:\n+        line = sys.stdin.readline()\n+        if line == '\\n':\n+            break\n+        if not line.startswith('import '):\n+            die(\"Expected import line.\")\n+\n+        # strip of leading 'import '\n+        ref = line[7:].strip()\n+        refs.append(ref)\n+\n     repo = update_local_repo(repo)\n-    repo.exporter.export_repo(repo.gitdir, args)\n+    repo.exporter.export_repo(repo.gitdir, refs)\n \n     print \"done\"\n \ndiff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh\nindex 12f471c..1c62001 100755\n--- a/t/t5800-remote-helpers.sh\n+++ b/t/t5800-remote-helpers.sh\n@@ -98,7 +98,7 @@ test_expect_success 'fetch new branch' '\n \tcompare_refs public HEAD localclone FETCH_HEAD\n '\n \n-test_expect_failure 'fetch multiple branches' '\n+test_expect_success 'fetch multiple branches' '\n \t(cd localclone &&\n \t git fetch\n \t) &&\ndiff --git a/transport-helper.c b/transport-helper.c\nindex a8f69b0..0c00be9 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -418,6 +418,9 @@ static int fetch_with_import(struct transport *transport,\n \t\tsendline(data, &buf);\n \t\tstrbuf_reset(&buf);\n \t}\n+\n+\twrite_constant(data->helper->in, \"\\n\");\n+\n \tif (finish_command(&fastimport))\n \t\tdie(\"Error while running fast-import\");\n \tfree(fastimport.argv);\n-- \n1.7.5.1.292.g728120\n"},{"id":"171475","messageId":"1310821424-4750-20-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 19/23] transport-helper: Use capname for refspec capability too","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:39Z","receivedAt":"2011-07-16T13:03:39Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Previously the refspec capability could not be listed as\nrequired or their parsing would break.\n\nMost likely the reason the second hunk wasn't caught is because the\nseries that added 'refspec' as capability, and the one that added\nrequired capabilities were done in parallel.\n\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged.\n\n transport-helper.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 0c00be9..0cfc9ae 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -181,7 +181,7 @@ static struct child_process *get_helper(struct transport *transport)\n \t\t\tALLOC_GROW(refspecs,\n \t\t\t\t   refspec_nr + 1,\n \t\t\t\t   refspec_alloc);\n-\t\t\trefspecs[refspec_nr++] = strdup(buf.buf + strlen(\"refspec \"));\n+\t\t\trefspecs[refspec_nr++] = strdup(capname + strlen(\"refspec \"));\n \t\t} else if (!strcmp(capname, \"connect\")) {\n \t\t\tdata->connect = 1;\n \t\t} else if (mandatory) {\n-- \n1.7.5.1.292.g728120\n"},{"id":"171471","messageId":"1310821424-4750-21-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 20/23] transport-helper: implement marks location as capability","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:40Z","receivedAt":"2011-07-16T13:03:40Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Now that the gitdir location is exported as an environment variable\nthis can be implemented elegantly without requiring any explicit\nflushes nor an ad-hoc exchange of values.\n\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  Unchanged.\n\n git-remote-testgit.py |   24 +++++++++++-------------\n transport-helper.c    |   47 ++++++++++++++++++-----------------------------\n 2 files changed, 29 insertions(+), 42 deletions(-)\n\ndiff --git a/git-remote-testgit.py b/git-remote-testgit.py\nindex 1ed7a56..e9c832b 100644\n--- a/git-remote-testgit.py\n+++ b/git-remote-testgit.py\n@@ -72,6 +72,17 @@ def do_capabilities(repo, args):\n     print \"export\"\n     print \"refspec refs/heads/*:%s*\" % repo.prefix\n \n+    dirname = repo.get_base_path(repo.gitdir)\n+\n+    if not os.path.exists(dirname):\n+        os.makedirs(dirname)\n+\n+    path = os.path.join(dirname, 'testgit.marks')\n+\n+    print \"*export-marks %s\" % path\n+    if os.path.exists(path):\n+        print \"*import-marks %s\" % path\n+\n     print # end capabilities\n \n \n@@ -147,19 +158,6 @@ def do_export(repo, args):\n     if not repo.gitdir:\n         die(\"Need gitdir to export\")\n \n-    dirname = repo.get_base_path(repo.gitdir)\n-\n-    if not os.path.exists(dirname):\n-        os.makedirs(dirname)\n-\n-    path = os.path.join(dirname, 'testgit.marks')\n-    print path\n-    if os.path.exists(path):\n-        print path\n-    else:\n-        print \"\"\n-    sys.stdout.flush()\n-\n     update_local_repo(repo)\n     changed = repo.importer.do_import(repo.gitdir)\n \ndiff --git a/transport-helper.c b/transport-helper.c\nindex 0cfc9ae..74c3122 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -23,6 +23,8 @@ struct helper_data {\n \t\tpush : 1,\n \t\tconnect : 1,\n \t\tno_disconnect_req : 1;\n+\tchar *export_marks;\n+\tchar *import_marks;\n \t/* These go from remote name (as in \"list\") to private name */\n \tstruct refspec *refspecs;\n \tint refspec_nr;\n@@ -184,6 +186,16 @@ static struct child_process *get_helper(struct transport *transport)\n \t\t\trefspecs[refspec_nr++] = strdup(capname + strlen(\"refspec \"));\n \t\t} else if (!strcmp(capname, \"connect\")) {\n \t\t\tdata->connect = 1;\n+\t\t} else if (!prefixcmp(capname, \"export-marks \")) {\n+\t\t\tstruct strbuf arg = STRBUF_INIT;\n+\t\t\tstrbuf_addstr(&arg, \"--export-marks=\");\n+\t\t\tstrbuf_addstr(&arg, capname + strlen(\"export-marks \"));\n+\t\t\tdata->export_marks = strbuf_detach(&arg, NULL);\n+\t\t} else if (!prefixcmp(capname, \"import-marks\")) {\n+\t\t\tstruct strbuf arg = STRBUF_INIT;\n+\t\t\tstrbuf_addstr(&arg, \"--import-marks=\");\n+\t\t\tstrbuf_addstr(&arg, capname + strlen(\"import-marks \"));\n+\t\t\tdata->import_marks = strbuf_detach(&arg, NULL);\n \t\t} else if (mandatory) {\n \t\t\tdie(\"Unknown mandatory capability %s. This remote \"\n \t\t\t    \"helper probably needs newer version of Git.\\n\",\n@@ -369,10 +381,9 @@ static int get_importer(struct transport *transport, struct child_process *fasti\n \n static int get_exporter(struct transport *transport,\n \t\t\tstruct child_process *fastexport,\n-\t\t\tconst char *export_marks,\n-\t\t\tconst char *import_marks,\n \t\t\tstruct string_list *revlist_args)\n {\n+\tstruct helper_data *data = transport->data;\n \tstruct child_process *helper = get_helper(transport);\n \tint argc = 0, i;\n \tmemset(fastexport, 0, sizeof(*fastexport));\n@@ -383,10 +394,10 @@ static int get_exporter(struct transport *transport,\n \tfastexport->argv = xcalloc(5 + revlist_args->nr, sizeof(*fastexport->argv));\n \tfastexport->argv[argc++] = \"fast-export\";\n \tfastexport->argv[argc++] = \"--use-done-feature\";\n-\tif (export_marks)\n-\t\tfastexport->argv[argc++] = export_marks;\n-\tif (import_marks)\n-\t\tfastexport->argv[argc++] = import_marks;\n+\tif (data->export_marks)\n+\t\tfastexport->argv[argc++] = data->export_marks;\n+\tif (data->import_marks)\n+\t\tfastexport->argv[argc++] = data->import_marks;\n \n \tfor (i = 0; i < revlist_args->nr; i++)\n \t\tfastexport->argv[argc++] = revlist_args->items[i].string;\n@@ -713,7 +724,6 @@ static int push_refs_with_export(struct transport *transport,\n \tstruct ref *ref;\n \tstruct child_process *helper, exporter;\n \tstruct helper_data *data = transport->data;\n-\tchar *export_marks = NULL, *import_marks = NULL;\n \tstruct string_list revlist_args = STRING_LIST_INIT_NODUP;\n \tstruct strbuf buf = STRBUF_INIT;\n \n@@ -721,26 +731,6 @@ static int push_refs_with_export(struct transport *transport,\n \n \twrite_constant(helper->in, \"export\\n\");\n \n-\trecvline(data, &buf);\n-\tif (debug)\n-\t\tfprintf(stderr, \"Debug: Got export_marks '%s'\\n\", buf.buf);\n-\tif (buf.len) {\n-\t\tstruct strbuf arg = STRBUF_INIT;\n-\t\tstrbuf_addstr(&arg, \"--export-marks=\");\n-\t\tstrbuf_addbuf(&arg, &buf);\n-\t\texport_marks = strbuf_detach(&arg, NULL);\n-\t}\n-\n-\trecvline(data, &buf);\n-\tif (debug)\n-\t\tfprintf(stderr, \"Debug: Got import_marks '%s'\\n\", buf.buf);\n-\tif (buf.len) {\n-\t\tstruct strbuf arg = STRBUF_INIT;\n-\t\tstrbuf_addstr(&arg, \"--import-marks=\");\n-\t\tstrbuf_addbuf(&arg, &buf);\n-\t\timport_marks = strbuf_detach(&arg, NULL);\n-\t}\n-\n \tstrbuf_reset(&buf);\n \n \tfor (ref = remote_refs; ref; ref = ref->next) {\n@@ -761,8 +751,7 @@ static int push_refs_with_export(struct transport *transport,\n \n \t}\n \n-\tif (get_exporter(transport, &exporter,\n-\t\t\t export_marks, import_marks, &revlist_args))\n+\tif (get_exporter(transport, &exporter, &revlist_args))\n \t\tdie(\"Couldn't run fast-export\");\n \n \tif (finish_command(&exporter))\n-- \n1.7.5.1.292.g728120\n"},{"id":"171474","messageId":"1310821424-4750-22-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[PATCH v3 21/23] transport-helper: die early on encountering deleted refs","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:41Z","receivedAt":"2011-07-16T13:03:41Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Remote helpers do not support deleting refs by means of the 'export'\ncommand sincethe fast-import protocol does not support it.\n\nCheck explicitly for deleted refs and die early.\n\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  New in this series.\n\n transport-helper.c |    4 ++++\n 1 files changed, 4 insertions(+), 0 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 74c3122..4eab844 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -746,6 +746,10 @@ static int push_refs_with_export(struct transport *transport,\n \t\t}\n \t\tfree(private);\n \n+\t\tif (ref->deletion) {\n+\t\t\tdie(\"remote-helpers do not support ref deletion\");\n+\t\t}\n+\n \t\tif (ref->peer_ref)\n \t\t\tstring_list_append(&revlist_args, ref->peer_ref->name);\n \n-- \n1.7.5.1.292.g728120\n"},{"id":"171472","messageId":"1310821424-4750-23-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[RFD/PATCH v3 22/23] t5800: document inability to push new branch with old content","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:42Z","receivedAt":"2011-07-16T13:03:42Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"\nSigned-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n---\n\n  So, I'm not sure what's going on here. It just says:\n  \"Everything up to date\" when you try to do a push like this.\n\n t/t5800-remote-helpers.sh |    8 ++++++++\n 1 files changed, 8 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh\nindex 1c62001..68f8418 100755\n--- a/t/t5800-remote-helpers.sh\n+++ b/t/t5800-remote-helpers.sh\n@@ -125,6 +125,14 @@ test_expect_success 'push new branch by name' '\n \tcompare_refs clone HEAD server refs/heads/new-name\n '\n \n+test_expect_failure 'push new branch with old content' '\n+\t(cd clone &&\n+\t git checkout -b existing &&\n+\t git push origin existing\n+\t) &&\n+\tcompare_refs clone refs/heads/existing server refs/heads/existing\n+'\n+\n test_expect_failure 'push new branch with old:new refspec' '\n \t(cd clone &&\n \t git push origin new-name:new-refspec\n-- \n1.7.5.1.292.g728120\n"},{"id":"171476","messageId":"1310821424-4750-24-git-send-email-srabbelier@gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"[RDD/PATCH v3 23/23] t5800: point out that deleting branches does not work","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-16T13:03:43Z","receivedAt":"2011-07-16T13:03:43Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"This test actually breaks the repositories involved somehow, so it is\nnot enabled by default.\n---\n\n  Not meant for inclusion.\n\n t/t5800-remote-helpers.sh |   11 +++++++++++\n 1 files changed, 11 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh\nindex 68f8418..ad85888 100755\n--- a/t/t5800-remote-helpers.sh\n+++ b/t/t5800-remote-helpers.sh\n@@ -133,6 +133,17 @@ test_expect_failure 'push new branch with old content' '\n \tcompare_refs clone refs/heads/existing server refs/heads/existing\n '\n \n+test_expect_failure BROKEN 'delete branch' '\n+\t(cd clone &&\n+\t git checkout -b delete-me &&\n+\t echo content >>file &&\n+\t git commit -a -m eight &&\n+\t git push origin delete-me\n+\t git push origin :delete-me) &&\n+\ttest_must_fail git --git-dir=\"server/.git\" rev-parse --verify delete-me\n+'\n+\n+\n test_expect_failure 'push new branch with old:new refspec' '\n \t(cd clone &&\n \t git push origin new-name:new-refspec\n-- \n1.7.5.1.292.g728120\n"},{"id":"171525","messageId":"7vzkkcxyxa.fsf@alter.siamese.dyndns.org","threadId":"27826","inReplyTo":"1310821424-4750-8-git-send-email-srabbelier@gmail.com","subject":"Re: [PATCH v3 07/23] git_remote_helpers: push all refs during a non-local export","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-07-17T23:36:17Z","receivedAt":"2011-07-17T23:36:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Sverre Rabbelier <srabbelier@gmail.com> writes:\n\n> From: Jeff King <peff@peff.net>\n>\n> When a remote helper exports to a non-local git repo, the\n> steps are roughly:\n>\n>   1. fast-export into a local staging area; the set of\n>      interesting refs is defined by what is in the fast-export\n>      stream\n>\n>   2. git push from the staging area to the non-local repo\n>\n> In the second step, we should explicitly push all refs, not\n> just matching ones. This will let us push refs that do not\n> yet exist in the remote repo.\n>\n> Signed-off-by: Jeff King <peff@peff.net>\n> Signed-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n> ---\n>\n>   Unchanged\n\nThere used to be \"This does not deal with forced (not-fast-forward) pushes.\"\nat the end of the message, no?\n"},{"id":"171535","messageId":"20110718032829.GA2468@sigill.intra.peff.net","threadId":"27826","inReplyTo":"1310821424-4750-1-git-send-email-srabbelier@gmail.com","subject":"Re: [PATCH v3 00/23] remote-helper improvements","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-07-18T03:28:29Z","receivedAt":"2011-07-18T03:28:29Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Jul 16, 2011 at 03:03:20PM +0200, Sverre Rabbelier wrote:\n\n> Incorperated feedback from Junio, Johannes and Peff. I also included\n> Dmitry's patch that adds GITDIR, which I modified to just remove the\n> support for a gitdir command.\n\nI read through these, and with the giant disclaimer that:\n\n  1. The entirety of my remote helper knowledge is from working on the\n     patches in this series that are mine.\n\n  2. I don't really know anything about writing good-looking python\n     code.\n\nThe patches up to 21 (i.e., not the RFD ones) all made sense to me. At\nleast, the goals from the commit messages looked sane, and the patches\nseemed to implement the goals reasonably.\n\n-Peff\n"},{"id":"171570","messageId":"CA+gfSn_Cxkmrz6k0ScZ+fTNZvzU7oA9Li5G1VBFtkohzYOFm7w@mail.gmail.com","threadId":"27826","inReplyTo":"1310821424-4750-19-git-send-email-srabbelier@gmail.com","subject":"Re: [PATCH v3 18/23] transport-helper: change import semantics","fromName":"Dmitry Ivankov","fromEmail":"divanorama@gmail.com","sentAt":"2011-07-18T11:13:44Z","receivedAt":"2011-07-18T11:13:44Z","isPatch":true,"sender":{"key":"divanorama@gmail.com","avatar":"https://avatars.githubusercontent.com/u/158999?v=4"},"body":"Hi,\n\nI'll comment on \"[PATCH v3 16/23] transport-helper: use the new done\nfeature where possible\" (\nhttp://thread.gmane.org/gmane.comp.version-control.git/177255/focus=177274)\nfirst. There is a hunk for transport-helper.c\n@@ -417,11 +418,8 @@ static int fetch_with_import(struct transport *transport,\n \t\tsendline(data, &buf);\n \t\tstrbuf_reset(&buf);\n \t}\n-\tif (disconnect_helper(transport))\n-\t\tdie(\"Error while disconnecting helper\");\n \tif (finish_command(&fastimport))\n \t\tdie(\"Error while running fast-import\");\n-\n \tfree(fastimport.argv);\n \tfastimport.argv = NULL;\n\nThis is related to the done feature but a helper can use the done\nfeature without this hunk.\nAnd it does change the import semantics, so I'd suggest moving the\nhunk here, to\n\"[PATCH v3 18/23] transport-helper: change import semantics\".\n\nOn Sat, Jul 16, 2011 at 7:03 PM, Sverre Rabbelier <srabbelier@gmail.com> wrote:\n> Currently the helper must somehow guess how many import statements to\n> read before it starts outputting its fast-export stream. This is\n> because the remote helper infrastructure runs fast-import only once,\n> so the helper is forced to output one stream for all import commands\n> it will receive. The only reason this worked in the past was because\n> only one ref was imported at a time.\n>\n> Change the semantics of the import statement such that it matches\n> that of the push statement. That is, the import statement is followed\n> by a series of import statements that are terminated by a '\\n'.\n\nI'll add more comments to the \"import\" job termination.\n\nBefore these two patches:\nThe helper input was closed which caused it to exit just after writing\nthe import stream.\n(first, helper can terminate on eof; second it can terminate on '\\n'\njust before eof; third, it may know that the import always was the\nlast command and terminate before reading '\\n').\nThe transport-helper.o didn't held a copy of helper's stdout, so once\nthe helper exits this end of import-stream pipe becomes closed.\nAnd then the importer saw EOF on stdin and could terminate normally.\n\nAfter these patches:\ntransport-helper waits for importer to terminate, so the helper must either\na) crash or exit during or just after the import and reading no more\nthan '\\n' (blocking read beyond this character may hang us up)\nor\nb) use the 'done' feature to make importer terminate normally\n\nSo the old helper won't hang us up if it terminates (or at least\ncloses stdout) on just after the import or if it can read '\\n' (but\nnot blocking read further) and terminate.\nBut if it just ignores empty lines and waits for new commands or eof\nas an exit indicator, it all hangs.\n\nSadly, old documentation didn't mention many of these quirks, but on\nthe other hand the old semantic is a bit broken and not documented so\nit's all ok.\n\nWe should definitely add a new description for import command to\nDocumentation/git-remote-helpers.txt\nSomething roughly like this addition:\n\nA batch sequence of one or more import commands is terminated\nwith a blank line. Single fast-import stream should be produced for\nthe whole batch.\nIf the helper is able to proceed with more commands after the import\n(in earlier versions import used to be the last command for a helper)\nit must use the \"done\" feature to indicate the end of this batch's\nimport stream for the importer, otherwise the importer will wait forever\nand the caller will wait for the importer to finish.\n\nAside from documenting and squashing I think this patch is the best\nway to proceed with improving the import command.\n\n>\n> Signed-off-by: Sverre Rabbelier <srabbelier@gmail.com>\n> ---\n>\n>  As Jonathan suggested we now follow push' example, rather than\n>  'list'. It makes the remote-testgit code a bit longer, but it means\n>  less changes to remote-helper.c.\n>\n>  git-remote-testgit.py     |   16 +++++++++++++++-\n>  t/t5800-remote-helpers.sh |    2 +-\n>  transport-helper.c        |    3 +++\n>  3 files changed, 19 insertions(+), 2 deletions(-)\n>\n> diff --git a/git-remote-testgit.py b/git-remote-testgit.py\n> index 0b5928d..1ed7a56 100644\n> --- a/git-remote-testgit.py\n> +++ b/git-remote-testgit.py\n> @@ -120,8 +120,22 @@ def do_import(repo, args):\n>     if not repo.gitdir:\n>         die(\"Need gitdir to import\")\n>\n> +    ref = args[0]\n> +    refs = [ref]\n> +\n> +    while True:\n> +        line = sys.stdin.readline()\n> +        if line == '\\n':\n> +            break\n> +        if not line.startswith('import '):\n> +            die(\"Expected import line.\")\n> +\n> +        # strip of leading 'import '\n> +        ref = line[7:].strip()\n> +        refs.append(ref)\n> +\n>     repo = update_local_repo(repo)\n> -    repo.exporter.export_repo(repo.gitdir, args)\n> +    repo.exporter.export_repo(repo.gitdir, refs)\n>\n>     print \"done\"\n>\n> diff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh\n> index 12f471c..1c62001 100755\n> --- a/t/t5800-remote-helpers.sh\n> +++ b/t/t5800-remote-helpers.sh\n> @@ -98,7 +98,7 @@ test_expect_success 'fetch new branch' '\n>        compare_refs public HEAD localclone FETCH_HEAD\n>  '\n>\n> -test_expect_failure 'fetch multiple branches' '\n> +test_expect_success 'fetch multiple branches' '\n>        (cd localclone &&\n>         git fetch\n>        ) &&\n> diff --git a/transport-helper.c b/transport-helper.c\n> index a8f69b0..0c00be9 100644\n> --- a/transport-helper.c\n> +++ b/transport-helper.c\n> @@ -418,6 +418,9 @@ static int fetch_with_import(struct transport *transport,\n>                sendline(data, &buf);\n>                strbuf_reset(&buf);\n>        }\n> +\n> +       write_constant(data->helper->in, \"\\n\");\n> +\n>        if (finish_command(&fastimport))\n>                die(\"Error while running fast-import\");\n>        free(fastimport.argv);\n> --\n> 1.7.5.1.292.g728120\n>\n>\n"},{"id":"171884","messageId":"CAGdFq_g7OQjsCka16=O6vA9CfyDjEtmxfzEs7S6f8Za0kVQ3nw@mail.gmail.com","threadId":"27826","inReplyTo":"7vzkkcxyxa.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 07/23] git_remote_helpers: push all refs during a non-local export","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-23T11:27:44Z","receivedAt":"2011-07-23T11:27:44Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\nOn Mon, Jul 18, 2011 at 01:36, Junio C Hamano <gitster@pobox.com> wrote:\n> There used to be \"This does not deal with forced (not-fast-forward) pushes.\"\n> at the end of the message, no?\n\nYes it did, not sure what happened to that. FIxed.\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"171885","messageId":"CAGdFq_h6ci03fztEe-FOzCUNmWrN_dKS8z6qcrCOrGa613R7=w@mail.gmail.com","threadId":"27826","inReplyTo":"20110718032829.GA2468@sigill.intra.peff.net","subject":"Re: [PATCH v3 00/23] remote-helper improvements","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-07-23T11:28:00Z","receivedAt":"2011-07-23T11:28:00Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\nOn Mon, Jul 18, 2011 at 05:28, Jeff King <peff@peff.net> wrote:\n> The patches up to 21 (i.e., not the RFD ones) all made sense to me. At\n> least, the goals from the commit messages looked sane, and the patches\n> seemed to implement the goals reasonably.\n\nThanks for reviewing :).\n\n-- \nCheers,\n\nSverre Rabbelier\n"}]}